游戏事物:

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


MVC架构:

unity中没有navigation_mvc

IUserAction:是个接口,定义了行为的类型。
UserGUI:创建GUI对象,实现玩家互动,处理玩家操作,并通过IUserAction接口实现具体行为。
ISceneController:是个接口,定义了加载资源的方法。
SSDirector:是个单实例对象,不受Unity内存管理,只有返回实例化对象的方法,也声明了一个ISceneController方便各个接口调用。
FirstController:实现了加载资源,处理对象运动,互动处理的所有具体方法。通过ISceneController和IUserAction两个接口实现具体互动。


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

unity中没有navigation_unity中没有navigation_02


脚本和预制

unity中没有navigation_unity中没有navigation_03


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

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

unity中没有navigation_mvc_04


上船:

unity中没有navigation_ci_05


GO:

unity中没有navigation_unity_06


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

下船:

unity中没有navigation_3d游戏开发_07


胜利:

unity中没有navigation_unity_08


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

unity中没有navigation_unity中没有navigation_09


按钮设置与【胜利】类似


实现思路:
使用栈进行存储两个河岸的牧师和恶魔。比如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();
            }
        }
    }
}