天天看點

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

繼續閱讀