天天看点

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

游戏事物:

3牧师,3恶魔,2河岸,河,船。

游戏故事:3牧师和3恶魔需要用一艘船全部到达彼岸,但是船上和岸上都不能出现恶魔比牧师多的情形,否则恶魔会把牧师K.O,玩家输掉比赛;直到所有牧师恶魔都到达对岸,玩家取得胜利。

MVC架构:

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

IUserAction:是个接口,定义了行为的类型。

UserGUI:创建GUI对象,实现玩家互动,处理玩家操作,并通过IUserAction接口实现具体行为。

ISceneController:是个接口,定义了加载资源的方法。

SSDirector:是个单实例对象,不受Unity内存管理,只有返回实例化对象的方法,也声明了一个ISceneController方便各个接口调用。

FirstController:实现了加载资源,处理对象运动,互动处理的所有具体方法。通过ISceneController和IUserAction两个接口实现具体互动。

规则表(列出玩家动作的表)

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

脚本和预制

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

游戏界面:(没有优化UI 有点吃藕)

游戏初始状态,左上方是所有操作按钮,分别对应牧师上下船,魔鬼上下船,由预制可知,方块是牧师,球是魔鬼:

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

上船:

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

GO:

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

(至于颜色为什么不一样的了,场景用的是实时光,要烘焙之后,重新加载才会保留之前的效果,我这里没有进行烘焙,并且这是我玩了好几次不断重新加载后再截的图,重新加载的意思是我游戏写的restart()调用的重新加载场景函数)

下船:

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

胜利:

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

(隐藏了之前的所有操作按钮,只显示Win!提示和Restart!按钮,点击Win!不会发生任何动作,点击Restart!会重新加载场景,游戏可以重新进行,如前文所讲,没有烘培,光照变了)

输掉比赛:

Unity 3D游戏开发学习笔记(2) 牧师与魔鬼

按钮设置与【胜利】类似

实现思路:

使用栈进行存储两个河岸的牧师和恶魔。比如A岸有3个牧师就push3个牧师进栈A,当牧师上传,则pop掉1个牧师,并把这个牧师的父对象设为船,位置变成相对位置后,能实现在船上跟着船移动。

实现代码:

FirstController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FirstController : MonoBehaviour, ISceneController, IUserAction {
    SSDirector my;

    public State state = State.BSTART;
    public enum State { BSTART, BSEMOVING, BESMOVING, BEND, WIN, LOSE };
    /* 借鉴别人的想法,进行枚举,列出一共6种状态,实际上也可以用123456表示,但这里可读性强。
     * BSTART:  boat stops on start shore 
     * BEND:    boat stops on end shore 
     * BSEMOVING:   boat is moving from start shore to end shore 
     * BESMOVING:   boat is moving from end shore to start shore 
     * WIN:     win 
     * LOSE:    lose 
     */  

    GameObject[] boat = new GameObject[]; // 船的容量是2,并且每个容量都是一个对象,用来存储实际的对象
    GameObject boat_obj; // 船本身

    Stack<GameObject> priests_start = new Stack<GameObject>();  
    Stack<GameObject> priests_end = new Stack<GameObject>();  
    Stack<GameObject> devils_start = new Stack<GameObject>(); //
    Stack<GameObject> devils_end = new Stack<GameObject>();
    /*
    使用栈进行存储两个河岸的牧师和恶魔。比如A岸有3个牧师就push3个牧师进栈A,
    当牧师上传,则pop掉1个牧师,并把这个牧师的父对象设为船,位置变成相对位置
    后,能实现在船上跟着船移动。
    */

    float gap = f; // 牧师恶魔每个对象的间隔,不加f Unity编译不过
    Vector3 priestStartPos = new Vector3(-f, , );//以下均为预设位置
    Vector3 priestEndPos = new Vector3(f, , );  
    Vector3 devilStartPos = new Vector3(-f, , );  
    Vector3 devilEndPos = new Vector3(f, , );

    Vector3 boatStartPos = new Vector3(-f, , );  
    Vector3 boatEndPos = new Vector3(f, , ); 

    public float speed = ; // 默认速度

    void Awake() {
        SSDirector director = SSDirector.getInstance();
        director.currentSceneController = this; // 设置“场记”,和“导演”联系在一起
        director.currentSceneController.LoadResources();
    }

    public void LoadResources () {
        GameObject myGame = Instantiate<GameObject> (Resources.Load<GameObject> ("prefabs/main"),
            Vector3.zero, Quaternion.identity);
        myGame.name = "main";
        //Debug.Log("load main...");
        boat_obj = Instantiate(Resources.Load("prefabs/Boat"), new Vector3(-f, f, f), Quaternion.identity) as GameObject;
        for(int i = ; i < ; i++) {
            priests_start.Push(Instantiate(Resources.Load("prefabs/Priest")) as GameObject); //加入栈中
            devils_start.Push(Instantiate(Resources.Load("prefabs/devil")) as GameObject);
        } 
    }

    int boatCapacity() { // 检测船是否有容量
        int capacity = ;
        for (int i = ; i < ; i++) {
            if(boat[i] == null) capacity++;
        }
        return capacity;
    }

    void setCharacterPositions(Stack<GameObject> stack, Vector3 pos) {  //设置牧师恶魔的具体位置,这方法厉害。。
        GameObject[] array = stack.ToArray();
        for (int i = ; i < stack.Count; ++i) {  
            array[i].transform.position = new Vector3(pos.x + gap*i, pos.y, pos.z);  
        }  
    }
    /*
    这里下面是具体的动作规则实现,具体和我文档里规则表可以对的上
    */
    public void priestOnBoat() { // 牧师上船,这个是没参数的,方便UserGUI调用。
        if(priests_start.Count !=  && boatCapacity() !=  && this.state == State.BSTART) //船在左岸,有牧师在岸上,船有位置
            priestOnBoat_(priests_start.Pop());
        if(priests_end.Count !=  && boatCapacity() !=  && this.state == State.BEND)
            priestOnBoat_(priests_end.Pop());
    }

    public void priestOnBoat_(GameObject obj) {
        if (boatCapacity() != ) {
            obj.transform.parent = boat_obj.transform;
            if (boat[] == null) {
                boat[] = obj;
                obj.transform.localPosition = new Vector3(-f, f, f);
            } else {
                boat[] = obj;
                obj.transform.localPosition = new Vector3(f, f, f);
            }
        }
    }

    public void priestOffBoat() {
        for (int i = ; i < ; i++) {
            if (boat[i] != null) {  
                if (this.state == State.BEND) {  
                    if (boat[i].name == "Priest(Clone)") {  
                        priests_end.Push(boat[i]);
                        boat[i].transform.parent = null;
                        boat[i] = null;
                        break;
                    }  
                }  
                else if (this.state == State.BSTART) {  
                    if (boat[i].name == "Priest(Clone)") {  
                        priests_start.Push(boat[i]);
                        boat[i].transform.parent = null;
                        boat[i] = null;
                        break;
                    }
                }
            }
        }
    }  

    public void devilOnBoat() {
        if(devils_start.Count !=  && boatCapacity() !=  && this.state == State.BSTART)
            devilOnBoat_(devils_start.Pop());
        if(devils_end.Count !=  && boatCapacity() !=  && this.state == State.BEND)
            devilOnBoat_(devils_end.Pop());
    }

    public void devilOnBoat_(GameObject obj) {
        if (boatCapacity() != ) {
            obj.transform.parent = boat_obj.transform;
            if (boat[] == null) {
                boat[] = obj;
                obj.transform.localPosition = new Vector3(-f, f, f);
            } else {
                boat[] = obj;
                obj.transform.localPosition = new Vector3(f, f, f);
            }
        }
    }

    public void devilOffBoat() {
        for (int i = ; i < ; i++) {
            if (boat[i] != null) {
                if (this.state == State.BEND) {  
                    if (boat[i].name == "Devil(Clone)") {  
                        devils_end.Push(boat[i]);
                        boat[i].transform.parent = null;
                        boat[i] = null;
                        break;
                    }  
                }  
                else if (this.state == State.BSTART) {  
                    if (boat[i].name == "Devil(Clone)") {  
                        devils_start.Push(boat[i]);
                        boat[i].transform.parent = null;
                        boat[i] = null;
                        break;
                    }
                }
            }
        }
    }

    public void moveBoat() {
        if(boatCapacity() != ) {
            if(this.state == State.BSTART) {
                this.state = State.BSEMOVING;
            }
            else if (this.state == State.BEND) {
                this.state = State.BESMOVING;
            }
        }
    }
    public void restart() {
        Application.LoadLevel (Application.loadedLevelName); // 重新加载
        state = State.BSTART; // 预设参数
    }

    void check() { //检查状态是处于停靠还是运动或是胜利或是输掉
        int priests_s = , devils_s = , priests_e = , devils_e = ;
        int pOnBoat = , dOnBoat = ;

        if (priests_end.Count ==  && devils_end.Count == ) {
            this.state = State.WIN;
            return;
        }

        for (int i = ; i < ; ++i) {  
            if (boat[i] != null && boat[i].name == "Priest(Clone)") pOnBoat++;
            else if (boat[i] != null && boat[i].name == "Devil(Clone)") dOnBoat++;
        }
        //Debug.Log(pOnBoat);

        if (this.state == State.BSTART) {
            priests_s = priests_start.Count + pOnBoat;
            devils_s = devils_start.Count + dOnBoat;
            priests_e = priests_end.Count;
            devils_e = devils_end.Count;
        }  
        else if (this.state == State.BEND) {
            priests_s = priests_start.Count;
            devils_s = devils_start.Count;
            priests_e = priests_end.Count + pOnBoat; 
            devils_e = devils_end.Count + dOnBoat;
        }  
        if ((priests_s !=  && priests_s < devils_s) || (priests_e !=  && priests_e < devils_e)) {
            this.state = State.LOSE;
        }  
    }

    public bool isWin() { //赢了的话
        if(this.state == State.WIN) return true;
        return false;
    }

    public bool isLose() { //输了的话
        if(this.state == State.LOSE) return true;
        return false;
    }
    void Start() {}

    void Update() { //设置牧师和恶魔的位置,并且开船的时候实现船移动
        setCharacterPositions(priests_start, priestStartPos); 
        setCharacterPositions(priests_end, priestEndPos);
        setCharacterPositions(devils_start, devilStartPos);
        setCharacterPositions(devils_end, devilEndPos);

        if (this.state == State.BSEMOVING) {
            boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatEndPos, speed*Time.deltaTime);  
            if (boat_obj.transform.position == boatEndPos) {  
                this.state = State.BEND;  
            }
        }  
        else if (this.state == State.BESMOVING) {
            boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatStartPos, speed*Time.deltaTime);  
            if (boat_obj.transform.position == boatStartPos) {  
                this.state = State.BSTART;  
            }
        } else {
            check();
        }
    }
}
           

ISceneController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface ISceneController {
    void LoadResources();
}
           

IUserAction.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface IUserAction {
    //void priestOnBoat(GameObject obj);
    void priestOnBoat();
    void priestOffBoat();
    void devilOnBoat();
    void devilOffBoat();
    void moveBoat();
    void restart();
    bool isWin();
    bool isLose();
}
           

SSDirector.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSDirector : System.Object {
    private static SSDirector _instance;
    public ISceneController currentSceneController {get; set;}

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

UserGUI.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UserGUI : MonoBehaviour {

    private IUserAction action;
    void Start () {
        action = SSDirector.getInstance().currentSceneController as IUserAction;
    }

    // Update is called once per frame
    void OnGUI () {
        float width = Screen.width / ;
        float height = Screen.height / ;
        if(action.isWin()) { //赢了的话
            GUI.Button(new Rect(,Screen.height-f*height, Screen.width, height), "Win!");
            if(GUI.Button(new Rect(,Screen.height-height, Screen.width, height), "Restart!")) {
                action.restart();
            }
        } else if(action.isLose()) { //输了的话
            GUI.Button(new Rect(,Screen.height-f*height, Screen.width, height), "Lose!");
            if(GUI.Button(new Rect(,Screen.height-height, Screen.width, height), "Restart!")) {
                action.restart();
            }
        } else { //游戏还在进行中
            if(GUI.Button(new Rect(, , width, height), "PriestOnBoat")) {
                action.priestOnBoat();
            }

            if(GUI.Button(new Rect(+width, , width, height), "PriestOffBoat")) {
                action.priestOffBoat();
            }

            if(GUI.Button(new Rect(, +height, width, height), "DevilOnBoat")) {
                action.devilOnBoat();
            }

            if(GUI.Button(new Rect(+width, +height, width, height), "DevilOffBoat")) {
                action.devilOffBoat();
            }

            if(GUI.Button(new Rect(+*width, , width, height), "GO")) {
                action.moveBoat();
            }
        }
    }
}
           

继续阅读