1. 程式人生 > >Unity 3D遊戲開發學習筆記(2) 牧師與魔鬼

Unity 3D遊戲開發學習筆記(2) 牧師與魔鬼

遊戲事物:

3牧師,3惡魔,2河岸,河,船。
遊戲故事:3牧師和3惡魔需要用一艘船全部到達彼岸,但是船上和岸上都不能出現惡魔比牧師多的情形,否則惡魔會把牧師K.O,玩家輸掉比賽;直到所有牧師惡魔都到達對岸,玩家取得勝利。

MVC架構:
這裡寫圖片描述

IUserAction:是個介面,定義了行為的型別。
UserGUI:建立GUI物件,實現玩家互動,處理玩家操作,並通過IUserAction介面實現具體行為。
ISceneController:是個介面,定義了載入資源的方法。
SSDirector:是個單例項物件,不受Unity記憶體管理,只有返回例項化物件的方法,也聲明瞭一個ISceneController方便各個介面呼叫。
FirstController:

實現了載入資源,處理物件運動,互動處理的所有具體方法。通過ISceneController和IUserAction兩個介面實現具體互動。

規則表(列出玩家動作的表)
這裡寫圖片描述

指令碼和預製
這裡寫圖片描述

遊戲介面:(沒有優化UI 有點吃藕)

遊戲初始狀態,左上方是所有操作按鈕,分別對應牧師上下船,魔鬼上下船,由預製可知,方塊是牧師,球是魔鬼:
這裡寫圖片描述
上船:
這裡寫圖片描述
GO:
這裡寫圖片描述
(至於顏色為什麼不一樣的了,場景用的是實時光,要烘焙之後,重新載入才會保留之前的效果,我這裡沒有進行烘焙,並且這是我玩了好幾次不斷重新載入後再截的圖,重新載入的意思是我遊戲寫的restart()呼叫的重新載入場景函式)
下船:
這裡寫圖片描述


勝利:
這裡寫圖片描述
(隱藏了之前的所有操作按鈕,只顯示Win!提示和Restart!按鈕,點選Win!不會發生任何動作,點選Restart!會重新載入場景,遊戲可以重新進行,如前文所講,沒有烘培,光照變了)

輸掉比賽:
這裡寫圖片描述
按鈕設定與【勝利】類似

實現思路:
使用棧進行儲存兩個河岸的牧師和惡魔。比如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]; // 船的容量是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 = 1f; // 牧師惡魔每個物件的間隔,不加f Unity編譯不過 Vector3 priestStartPos = new Vector3(-8f, 0, 0);//以下均為預設位置 Vector3 priestEndPos = new Vector3(8f, 0, 0); Vector3 devilStartPos = new Vector3(-5f, 0, 0); Vector3 devilEndPos = new Vector3(5f, 0, 0); Vector3 boatStartPos = new Vector3(-1.5f, 0, 0); Vector3 boatEndPos = new Vector3(1.5f, 0, 0); public float speed = 20; // 預設速度 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(-1.5f, 0f, 0f), Quaternion.identity) as GameObject; for(int i = 0; i < 3; 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 = 0; for (int i = 0; i < 2; i++) { if(boat[i] == null) capacity++; } return capacity; } void setCharacterPositions(Stack<GameObject> stack, Vector3 pos) { //設定牧師惡魔的具體位置,這方法厲害。。 GameObject[] array = stack.ToArray(); for (int i = 0; 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 != 0 && boatCapacity() != 0 && this.state == State.BSTART) //船在左岸,有牧師在岸上,船有位置 priestOnBoat_(priests_start.Pop()); if(priests_end.Count != 0 && boatCapacity() != 0 && this.state == State.BEND) priestOnBoat_(priests_end.Pop()); } public void priestOnBoat_(GameObject obj) { if (boatCapacity() != 0) { obj.transform.parent = boat_obj.transform; if (boat[0] == null) { boat[0] = obj; obj.transform.localPosition = new Vector3(-0.2f, 1.2f, 0f); } else { boat[1] = obj; obj.transform.localPosition = new Vector3(0.2f, 1.2f, 0f); } } } public void priestOffBoat() { for (int i = 0; i < 2; 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 != 0 && boatCapacity() != 0 && this.state == State.BSTART) devilOnBoat_(devils_start.Pop()); if(devils_end.Count != 0 && boatCapacity() != 0 && this.state == State.BEND) devilOnBoat_(devils_end.Pop()); } public void devilOnBoat_(GameObject obj) { if (boatCapacity() != 0) { obj.transform.parent = boat_obj.transform; if (boat[0] == null) { boat[0] = obj; obj.transform.localPosition = new Vector3(-0.2f, 1.2f, 0f); } else { boat[1] = obj; obj.transform.localPosition = new Vector3(0.2f, 1.2f, 0f); } } } public void devilOffBoat() { for (int i = 0; i < 2; 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() != 2) { 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 = 0, devils_s = 0, priests_e = 0, devils_e = 0; int pOnBoat = 0, dOnBoat = 0; if (priests_end.Count == 3 && devils_end.Count == 3) { this.state = State.WIN; return; } for (int i = 0; i < 2; ++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 != 0 && priests_s < devils_s) || (priests_e != 0 && 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 / 6;
        float height = Screen.height / 12;
        if(action.isWin()) { //贏了的話
            GUI.Button(new Rect(0,Screen.height-3f*height, Screen.width, height), "Win!");
            if(GUI.Button(new Rect(0,Screen.height-height, Screen.width, height), "Restart!")) {
                action.restart();
            }
        } else if(action.isLose()) { //輸了的話
            GUI.Button(new Rect(0,Screen.height-3f*height, Screen.width, height), "Lose!");
            if(GUI.Button(new Rect(0,Screen.height-height, Screen.width, height), "Restart!")) {
                action.restart();
            }
        } else { //遊戲還在進行中
            if(GUI.Button(new Rect(0, 0, width, height), "PriestOnBoat")) {
                action.priestOnBoat();
            }

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

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

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

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