3D游戏设计第四次作业

3D游戏设计第四次作业

1基本操作演练

1.1构建自己的游戏场景

下载Fantasy Skybox FREE后,使用其中的资源构建一个游戏场景,基本操作就是创建一个Terrain游戏对象,然后在平地上进行增高地形、使用各种地面素材给地面涂色、放置花和草。通过以上步骤构建了一个场景如下:
在这里插入图片描述

1.2总结游戏对象的使用

游戏对象的类型有以下几种:

游戏对象功能
Empty便于挂载代码
3D Object在场景中出现的3D物体
Effects产生各种效果
Light光照
Audio声源或混响区域
Video视频播放
UI与用户交互的各种控件
Camera摄像机

2 编程实践——牧师与魔鬼 动作分离版

要求:添加一个裁判类来通知场景控制器游戏是否结束。

2.1构造mvc架构

由于上一次作业中没有将脚本构造成mvc架构,所以首先将脚本修改为mvc架构。主要的类和接口有如下几个:导演类SSDircetor,场景控制器接口ISceneControl,场景控制类FirstController,用户动作接口IUserAction,用户交互类UserGUI。结构的划分主要参考老师课件。
首先是导演类,可以通过它获取正在运行的场景控制器。

public class SSDirector : System.Object
{
    //导演类 
    private static SSDirector _instance;
    public ISceneController currentSceneController{get;set;}
    public bool running {get;set;}

    public static SSDirector getInstance(){
        if(_instance == null){
            _instance = new SSDirector();
        }
        return _instance;
    }

}

接着是场景控制器接口,其主要是提供加载资源的抽象函数。加载资源的函数是每一个场景控制器应该实现的方法。

public interface ISceneController
{
    void LoadResources();
   
}

场景控制器类在这里只需要使用一个,它实现加载预制,更新每一帧的动作,响应点击事件,移动船,获得当前状态的功能。

public class FirstController : MonoBehaviour, ISceneController, IUserAction
{
    Ray ray;
    RaycastHit hit;
    GameObject hitobj;
    public GameObject boat;
    GameObject[] depr = new GameObject[6];
    GameObject[] sceneobj = new GameObject[3];
    int status = 2;
    int[] onBoat = {-1,-1};
    int boatPosition = 0;//船的位置,0代表右岸,1代表左岸
    bool running = false; // 船是否正在移动
    public 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船


    void Awake(){
        SSDirector director = SSDirector.getInstance();
        // Judge judge = new Judge();
        director.currentSceneController = this;
        director.currentSceneController.LoadResources();

        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();
        
    }
    public void LoadResources(){
        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);
    }

    public 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;
    }
    // Start is called before the first frame update
    void Start()
    {

    }
    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);
                    }
                }
   
                
            }
        }
    }



    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(Judge.GameStatus()==1){
            status = 1;
        }
    }
    public int getStatus(){
        return status;
    }
    
    public void runBoat(){
        if(onBoat[0]!=-1||onBoat[1]!=-1){
            int ans = Judge.GameStatus();
            if(ans==2){   
                boatPosition = boatPosition^1;
                running = true;
            }
            else if(ans==0){
                status = 0;
            }
            
        }
    }
    
}

用户动作接口主要应用于场景控制器与GUI的方法,因此其功能是提供运行船、获得当前状态、初始化的抽象函数。

public interface IUserAction
{
    public void runBoat();
    public int getStatus();
    public void Init();
}

同时在FirstController中也实现了IUserAction接口,使得能够在UserGUI中调用相应的方法。将FirstController转换为IUserAction类型,从而完成对用户交互的响应。

public class UserGUI : MonoBehaviour
{
    private IUserAction action;
    // Start is called before the first frame update
    void Start()
    {
        action = SSDirector.getInstance().currentSceneController as IUserAction;
        
    }

    // Update is called once per frame
    void OnGUI(){
        float width = Screen.width /6;
        float height = Screen.height /12;
        if(GUI.Button(new Rect(20,10,80,40),"重置")){
            action.Init();
        }
        int status = action.getStatus();
        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),"行驶")){
            action.runBoat();    
        }
    }
}

2.2构造裁判类

裁判类通过从导演类得到当前的场景控制器,接着根据场景控制器中表示当前各个角色的位置的数组place(其中0表示在右岸,1表示在左岸,下标小于3的是表示魔鬼,其余为牧师)判断当前游戏的状态,是进行中、胜利、失败。在通过场景控制器调用Judge.GameStatus得到游戏状态。

public class Judge:MonoBehaviour
{
    public static FirstController controller;
    void Start(){
        controller = SSDirector.getInstance().currentSceneController as FirstController;
        
    }
    public static int GameStatus(){
        int lDevils=0, rDevils=0, lPriests = 0,rPriests = 0;
        for(int i = 0;i<6;i++){
            if(i<3){
                if(controller.place[i]==0) rDevils++;
                if(controller.place[i]==1) lDevils++;
            }
            else{
                if(controller.place[i]==0) rPriests++;
                if(controller.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.3运行与展示

需要将三个脚本挂载到游戏对象中:FirstController、UserGUI、Judge。成功运行后的效果与上一次作业类似,因此没有做视频展示。
游戏进行:
在这里插入图片描述
游戏失败:
在这里插入图片描述
游戏胜利:
在这里插入图片描述
代码在gitee仓库:https://gitee.com/k_co/3-d-games/tree/master/HW4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值