3D游戏编程第三次作业

3D游戏编程第三次作业

1 太阳系

1.1 创建球体

首先创建8个球体,在设置大概的大小和坐标。
在这里插入图片描述

1.2 编写行星公转脚本

考虑到不同行星公转速度、公转法平面的不同,在行星公转脚本中主要设置了int类型的速度speed和Vector3类型的旋转轴dir。
在确定旋转轴时根据坐标进行计算。由以下图知道,假设行星和太阳初始状态中心坐标位于各一平面上,并且设旋转轴在y上的分量为-1,则可以由 d y d x = ∣ y ∣ x \frac{dy}{dx}=\frac{|y|}{x} dxdy=xy可以计算出旋转轴。
在这里插入图片描述
代码如下:


public class planetRo : MonoBehaviour
{
    public int speed;
    public GameObject target;
    public Vector3 dir;
    // Start is called before the first frame update
    void Start()
    {
        if(target.transform.position.y == transform.position.y){
            dir = Vector3.down;
        }
        else{
            Vector3 dif = transform.position - target.transform.position;
            dir = new Vector3(dif.y/dif.x,-1,0);
        }
    }

    // Update is called once per frame
    void Update()
    {
        transform.RotateAround(target.transform.position,dir,speed*Time.deltaTime);
    }
}

1.3 增加贴图和展示

在这里插入图片描述
在这里插入图片描述

2 牧师与魔鬼

2.1 游戏中提及的事物

游戏中的所有物体都是由脚本动态实例化预制生成,其中包含GameObject类型的船、岸、河、牧师和魔鬼,简单的使用长方体、球体来表示。还有使用OnGUI形成的按钮和提示框,按钮包括行驶按钮、重置按钮,提示框是在游戏结束时显示是否胜利用的。同时定义了一系列的变量和数组来确定游戏的状态,还有用于处理点击事件的Ray类型、RaycastHit类型的对象。

    Ray ray;
    RaycastHit hit;
    GameObject hitobj;
    public GameObject boat;
    GameObject[] depr = new GameObject[6];
    GameObject[] sceneobj = new GameObject[3];
    int status = 2;
    int boatPosition = 0;//船的位置,0代表右岸,1代表左岸
    bool running = false; // 船是否正在移动
    int[] place = {0,0,0,0,0,0};//代表角色的位置,0右岸,1是左岸,2在船上
    Vector3[,] target = new Vector3[2,3];//代表船和人物在河上可能出现的坐标位置[i,j],i=0右岸,1左岸,j=0座号0,1座号1,2船

2.2 玩家动作表

动作功能
点击岸上角色将角色放入船中
点击船上角色将角色放回岸上
点击”行驶“按钮将船上角色载到对岸
点击”重置“按钮将所有对象都设置为初始状态

当船在移动时,除了“行驶”按钮和“重置”以外的点击都没有用。

2.3 初始化

在游戏开始时需要将所有所需的对象使用预制初始化,使用Resocrces.Load()函数将所有对象加载,并且给牧师和魔鬼命名,以便于后续处理。在移动中,船和角色的初始和最终坐标都是固定的,使用一个Vector3类型的二元数组存储代表左岸、右岸中,这些物体移动的目标位置。定义一个Init()函数,将所有的变量都初始化、将所有角色和船的坐标初始化。

void Start()
    {
        for(int i = 0;i<6;i++){
            if(i<3){
                GameObject gobj = Resources.Load("魔鬼") as GameObject; 
                depr[i] = Instantiate(gobj);
                int tmp = i+1;
                depr[i].transform.name = "魔鬼"+ tmp.ToString();
            }
            else{
                GameObject gobj = Resources.Load("牧师") as GameObject;
                depr[i] = Instantiate(gobj);
                int tmp = i-2;
                depr[i].transform.name = "牧师"+ tmp.ToString();
            }
        }
        // 实例化船
        GameObject obj = Resources.Load("船") as GameObject;
        boat = Instantiate(obj);
        
        // 实例化岸
        for(int i = 0;i<2;i++){
            int side = i==0?1:-1;
            obj = Resources.Load("岸") as GameObject;
            sceneobj[i] = Instantiate(obj);
            sceneobj[i].transform.position = new Vector3(side*9,-5,10);
        }
        
        //实例化河
        obj = Resources.Load("河") as GameObject;
        sceneobj[2] = Instantiate(obj);
        sceneobj[2].transform.position = new Vector3(0,-5,10);


        target[0,0] = new Vector3(3.5f,0.25f,10);
        target[0,1] = new Vector3(4.5f,0.25f,10);
        target[0,2] = new Vector3(4,-1,10);
        target[1,0] = new Vector3(-4.5f,0.25f,10);
        target[1,1] = new Vector3(-3.5f,0.25f,10);
        target[1,2] = new Vector3(-4,-1,10);
        Init();
    }
    void Init(){
        for(int i = 0;i<6;i++){
            place[i] = 0;
            depr[i].transform.position = new Vector3(5.5f+i,0.5f,10);
        }
        boat.transform.position = target[0,2];
        boatPosition = 0;
        running = false;
        onBoat[0] = -1;
        onBoat[1] = -1;
        status =2;
    }

2.4处理角色点击事件

当点击一个角色时,根据角色当前状态、船的状态等来控制位置变化。当点击的角色与船不在同一边、当船上已经满员、当船在行驶中时,点击角色都是无效的。剩下的就是上船、下船。在完成点击响应后,进行判断是否全部角色都到达左岸,若是,则游戏胜利,进行标记。

void hitAction(GameObject obj,int num){
       if(place[num]==2){
           if(onBoat[0]==num){
               onBoat[0] = -1;
               
           }
           else if(onBoat[1]==num){
               onBoat[1] = -1;
           }
           int mul = boatPosition==0?1:-1;
           place[num] = boatPosition;
           obj.transform.position = new Vector3((5.5f+num)*mul,0.5f,10);
       }
       else if(place[num]==boatPosition){
           int pos = -1;
           if(onBoat[0]==-1){
               onBoat[0] = num;
               pos = 0;
           }
           else if(onBoat[1]==-1){
               onBoat[1] = num;
               pos = 1;
           }
           else return;
           obj.transform.position = target[boatPosition,pos];
           place[num] = 2;
       }
       if(GameStatus()==1){
           status = 1;
       }
   }

2.5 OnGUI定义

OnGUI函数提供了两个按钮和游戏结束信息的显示板。首先根据游戏状态来确定显示信息,再进行“行驶”按钮的交互,并且只有当穿上有角色时才能够行驶。在“行驶”按钮的点击事件响应中,当玩家做出的动作使得游戏失败时,就改表示游戏状态的变量为响应的值,从而得到游戏失败的信息。

void OnGUI(){
        if(GUI.Button(new Rect(20,10,80,40),"重置")){
            Init();
        }
        if(status==1){
            GUI.Box(new Rect(Screen.width/2-75,Screen.height/2-50,150,100),"牧师和魔鬼成功到岸!");
            
        }
        else if(status == 0){
            // 失败
            GUI.Box(new Rect(Screen.width/2-75,Screen.height/2-50,150,100),"牧师被魔鬼吃掉了!");

        }
        if(GUI.Button(new Rect(Screen.width-100,Screen.height-50,80,40),"行驶")){
            if(onBoat[0]!=-1||onBoat[1]!=-1){
                
                int ans = GameStatus();
                if(ans==2){   
                    boatPosition = boatPosition^1;
                    running = true;
                }
                else if(ans==0){
                    status = 0;
                }
                
            }

                
        }
        
    }

2.6 游戏状态的确定

定义0代表失败,1代表胜利,2代表进行中,游戏状态的判断很简单,当某一岸上魔鬼数量大于牧师时则失败,当魔鬼和牧师全部都到达左岸时则成功。

int GameStatus(){
        int lDevils=0, rDevils=0, lPriests = 0,rPriests = 0;
        for(int i = 0;i<6;i++){
            if(i<3){
                if(place[i]==0) rDevils++;
                if(place[i]==1) lDevils++;
            }
            else{
                if(place[i]==0) rPriests++;
                if(place[i]==1) lPriests++;
            }
        }
        if(lDevils>lPriests&&lPriests!=0 || rDevils>rPriests&&rPriests!=0){
            return 0;
        }
        else if(lDevils==lPriests&&lDevils==3){
            return 1;
        }
        return 2;
    }

2.7 update函数

当船正在行驶时,则使用MoveTowards函数改变船和船上角色坐标变换。当玩家点击角色时,则调用前文完成的hitAction()函数来进行处理。

void Update()
    {
        if(running){
            
            var step = 10*Time.deltaTime;
            boat.transform.position = Vector3.MoveTowards(boat.transform.position,target[boatPosition,2],step);
            if(onBoat[0]!=-1)
            depr[onBoat[0]].transform.position = Vector3.MoveTowards(
                depr[onBoat[0]].transform.position,target[boatPosition,0],step);
            if(onBoat[1]!=-1)
            depr[onBoat[1]].transform.position = Vector3.MoveTowards(
                depr[onBoat[1]].transform.position,target[boatPosition,1],step);
            if(Vector3.Distance(boat.transform.position,target[boatPosition,2])<0.01f){
                
                running = false;
            }
        }
        else if(Input.GetMouseButtonDown(0)&&!running&&status==2){
            ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if(Physics.Raycast(ray, out hit)){
                hitobj = hit.collider.gameObject;
                for(int i = 1;i<4;i++){
                    if(hitobj.name=="魔鬼"+i){
                        hitAction(hitobj,i-1);

                    }
                    else if(hitobj.name=="牧师"+i){
                        hitAction(hitobj,i+2);
                    }
                }
   
                
            }
        }
    }

2.8 展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
视频展示:https://www.bilibili.com/video/BV1Ve411V7QQ/?spm_id_from=333.999.0.0&vd_source=aa1381bc757a530f161653f21b4b7d29
代码链接:https://gitee.com/k_co/3-d-games/tree/master/HW3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值