牧師與魔鬼優化

我們知道,一個人物其實會有很多動作,雖然我之前做的牧師過河可以把動作和對象放在一起而且思路還能夠保持清晰,但那也僅僅是因爲這個遊戲只有基礎的移動動作。試想,一個大型動作遊戲,遊戲人物有奔跑,跳躍,開槍,開車等等複雜的動作,全部加到對象文件內部,一是代碼容量太大,可讀性差,理解性差,還有一個問題就是,如果我們以後想對這個遊戲人物在進行動作更新呢?又要寫進去一大堆代碼?所以,仔細想想,將對象與動作分離有很棒的用處,這種開發思維有助於我們更好地構建遊戲,同時,遊戲人物的擴展性也得到了極大地增強。這次,我就針對牧師過河進行了改進。

我的理解還很淺,在這裏記錄下我的進度,以便接下來的學習

基礎類

Base.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Com.Mygame;

namespace Com.Mygame {

    public class Director : System.Object {
        private static Director _instance;
        public SceneController currentSceneController { get; set; }

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

    public interface SceneController {
        void loadResources ();
    }

    public interface UserAction {
        void moveBoat();
        void characterIsClicked(MyCharacterController characterCtrl);
        void restart();
    }

    /*-----------------------------------MyCharacterController------------------------------------------*/
    public class MyCharacterController {
        readonly GameObject character;
        //readonly Moveable moveableScript;
        readonly ClickGUI clickGUI;
        readonly int characterType; // 0->牧師, 1->魔鬼
        public readonly float movingSpeed = 20;

        // change frequently
        bool IsInBoat;
        CoastController coastController;


        public MyCharacterController(string which_character) {

            if (which_character == "牧師") {
                character = Object.Instantiate (Resources.Load ("Perfabs/牧師", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
                characterType = 0;

            } else {
                character = Object.Instantiate (Resources.Load ("Perfabs/魔鬼", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
                characterType = 1;

            }
            //moveableScript = character.AddComponent (typeof(Moveable)) as Moveable;

            clickGUI = character.AddComponent (typeof(ClickGUI)) as ClickGUI;
            clickGUI.setController (this);
        }

        public void setName(string name) {
            character.name = name;
        }

        public void setPosition(Vector3 pos) {
            character.transform.position = pos;
        }

        /*
        public void moveToPosition(Vector3 destination) {
            moveableScript.setDestination(destination);
        }
        */

        public int getType() {  // 0->牧師, 1->魔鬼
            return characterType;
        }

        public string getName() {
            return character.name;
        }

        public void getOnBoat(BoatController boatCtrl) {
            coastController = null;
            character.transform.parent = boatCtrl.getGameobj().transform;
            IsInBoat = true;
        }

        public void getOnCoast(CoastController coastCtrl) {
            coastController = coastCtrl;
            character.transform.parent = null;
            IsInBoat = false;
        }

        public bool isOnBoat() {
            return IsInBoat;
        }

        public CoastController getCoastController() {
            return coastController;
        }

        public void Reset() {
            // moveableScript.Reset ();
            coastController = (Director.getInstance ().currentSceneController as FirstController).fromCoast;
            getOnCoast (coastController);
            setPosition (coastController.getEmptyPosition ());
            coastController.getOnCoast (this);
        }

        public Vector3 getPos() {
            return this.character.transform.position;
        }

        public GameObject getGameobj() {
            return this.character;
        }
    }

    /*-----------------------------------CoastController------------------------------------------*/
    public class CoastController {
        readonly GameObject coast;
        readonly Vector3 from_pos = new Vector3(13.2f, 3.47f, 0.0f);
        readonly Vector3 to_pos = new Vector3(-18.1f, 3.47f, 0.0f);
        readonly Vector3[] positions;
        readonly int to_or_from;    // to->-1, from->1

        // change frequently
        MyCharacterController[] passengerPlaner;

        public CoastController(string _to_or_from) {
            positions = new Vector3[] {new Vector3(4, 6.0f, 0), new Vector3(6, 6.0f, 0), new Vector3(8, 6.0f, 0), 
                new Vector3(10, 6.0f, 0), new Vector3(12, 6.0f, 0), new Vector3(14, 6.0f, 0)};

            passengerPlaner = new MyCharacterController[6];

            if (_to_or_from == "from") {
                coast = Object.Instantiate (Resources.Load ("Perfabs/岸", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;
                coast.name = "from";
                to_or_from = 1;
            } else {
                coast = Object.Instantiate (Resources.Load ("Perfabs/岸", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject;
                coast.name = "to";
                to_or_from = -1;
            }
        }

        public int getEmptyIndex() {
            for (int i = 0; i < passengerPlaner.Length; i++) {
                if (passengerPlaner [i] == null) {
                    return i;
                }
            }
            return -1;
        }

        public Vector3 getEmptyPosition() {
            Vector3 pos = positions [getEmptyIndex ()];
            pos.x *= to_or_from;
            return pos;
        }

        public void getOnCoast(MyCharacterController characterCtrl) {
            int index = getEmptyIndex ();
            passengerPlaner [index] = characterCtrl;
        }

        public MyCharacterController getOffCoast(string passenger_name) {   // 0->牧師, 1->魔鬼
            for (int i = 0; i < passengerPlaner.Length; i++) {
                if (passengerPlaner [i] != null && passengerPlaner [i].getName () == passenger_name) {
                    MyCharacterController charactorCtrl = passengerPlaner [i];
                    passengerPlaner [i] = null;
                    return charactorCtrl;
                }
            }
            Debug.Log ("cant find passenger on coast: " + passenger_name);
            return null;
        }

        public int get_to_or_from() {
            return to_or_from;
        }

        public int[] getCharacterNum() {
            int[] count = {0, 0};
            for (int i = 0; i < passengerPlaner.Length; i++) {
                if (passengerPlaner [i] == null)
                    continue;
                if (passengerPlaner [i].getType () == 0) {  // 0->牧師, 1->魔鬼
                    count[0]++;
                } else {
                    count[1]++;
                }
            }
            return count;
        }

        public void Reset() {
            passengerPlaner = new MyCharacterController[6];
        }
    }

    /*-----------------------------------BoatController------------------------------------------*/
    public class BoatController {
        readonly GameObject boat;
        //readonly Moveable moveableScript;
        readonly Vector3 fromPosition = new Vector3(3, 5, 0);
        readonly Vector3 toPosition = new Vector3(-8.0f, 5.0f, 0.0f);
        readonly Vector3[] from_positions;
        readonly Vector3[] to_positions;
        public readonly float movingSpeed = 20;

        GameObject cameraObj;
        GameObject lightObj;

        // change frequently
        int to_or_from; // to->-1; from->1
        MyCharacterController[] passenger = new MyCharacterController[2];

        public BoatController() {
            to_or_from = 1;

            from_positions = new Vector3[] { new Vector3(2, 6.0f, 0.0f), new Vector3(6, 6.0f, 0.0f)};
            to_positions = new Vector3[] { new Vector3(-9, 6.0f, 0.0f), new Vector3(-7, 6.0f, 0.0f) };

            boat = Object.Instantiate (Resources.Load ("Perfabs/船", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject;
            boat.name = "boat";

            //moveableScript = boat.AddComponent (typeof(Moveable)) as Moveable;
            boat.AddComponent (typeof(ClickGUI));



            attachCamera(); // add a camera moving with this boat
            attachLight();
        }

        private void attachCamera() {
            cameraObj = new GameObject("Camera_follow");    // 新建一個空對象叫做Camera_follow
            cameraObj.transform.parent = boat.transform;    // 先讓這個對象成爲boat的子對象
            // 調整一下Camera_follow與boat的相對方位
            cameraObj.transform.localPosition = new Vector3(0, 7, -8);
            cameraObj.transform.localRotation = Quaternion.Euler(10, 0, 0);

            cameraObj.AddComponent<Camera>();   // 添加Camera組件,讓這個空對象成爲一個攝像機
            Camera cameraComp = cameraObj.GetComponent<Camera>();   // 獲取攝像機組件
            cameraComp.fieldOfView = 40;

            // 將Resources/skybox添加爲這個攝像機的天空盒
            cameraObj.AddComponent<Skybox>().material = Resources.Load("skybox") as Material;
        }



        public Vector3 getDestination() {
            if (to_or_from == -1) {
                return fromPosition;
            } else {
                return toPosition;
            }
        }

        public void move() {
            if (to_or_from == -1) {
                to_or_from = 1;
            } else {
                to_or_from = -1;
            }
        }
        public int getEmptyIndex() {
            for (int i = 0; i < passenger.Length; i++) {
                if (passenger [i] == null) {
                    return i;
                }
            }
            return -1;
        }

        public bool isEmpty() {
            for (int i = 0; i < passenger.Length; i++) {
                if (passenger [i] != null) {
                    return false;
                }
            }
            return true;
        }

        public Vector3 getEmptyPosition() {
            Vector3 pos;
            int emptyIndex = getEmptyIndex ();
            if (to_or_from == -1) {
                pos = to_positions[emptyIndex];
            } else {
                pos = from_positions[emptyIndex];
            }
            return pos;
        }

        public void GetOnBoat(MyCharacterController characterCtrl) {
            int index = getEmptyIndex ();
            passenger [index] = characterCtrl;
        }

        public MyCharacterController GetOffBoat(string passenger_name) {
            for (int i = 0; i < passenger.Length; i++) {
                if (passenger [i] != null && passenger [i].getName () == passenger_name) {
                    MyCharacterController charactorCtrl = passenger [i];
                    passenger [i] = null;
                    return charactorCtrl;
                }
            }
            Debug.Log ("Cant find passenger in boat: " + passenger_name);
            return null;
        }

        public GameObject getGameobj() {
            return boat;
        }

        public int get_to_or_from() { // to->-1; from->1
            return to_or_from;
        }

        public int[] getCharacterNum() {
            int[] count = {0, 0};
            for (int i = 0; i < passenger.Length; i++) {
                if (passenger [i] == null)
                    continue;
                if (passenger [i].getType () == 0) {    // 0->牧師, 1->魔鬼
                    count[0]++;
                } else {
                    count[1]++;
                }
            }
            return count;
        }

        public void Reset() {
            //moveableScript.Reset ();
            if (to_or_from == -1) {
                move ();
            }
            boat.transform.position = fromPosition;
            passenger = new MyCharacterController[2];
        }
    }
}

CGUI.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Com.Mygame;

public class CGUI : MonoBehaviour {
    UserAction action;
    MyCharacterController characterController;

    public void setController(MyCharacterController characterCtrl) {
        characterController = characterCtrl;
    }

    void Start() {
        action = Director.getInstance ().currentSceneController as UserAction;
    }

    void OnMouseDown() {
        if (gameObject.name == "boat") {
            action.moveBoat ();
        } else {
            action.characterIsClicked (characterController);
        }
    }
}

UGUI.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Com.Mygame;

public class UGUI : MonoBehaviour {
    private UserAction action;
    public int status = 0;
    GUIStyle style;
    GUIStyle buttonStyle;

    void Start() {
        action = Director.getInstance ().currentSceneController as UserAction;

        style = new GUIStyle();
        style.fontSize = 40;
        style.alignment = TextAnchor.MiddleCenter;

        buttonStyle = new GUIStyle("button");
        buttonStyle.fontSize = 30;
    }
    void OnGUI() {
        if (status == 1) {
            GUI.Label(new Rect(450, 50, 200, 200), "You loose!");
            if (GUI.Button(new Rect(450, 400, 100, 50), "Reset"))
                status = 0;
                action.restart ();
            }
        } else if(status == 2) {
            GUI.Label(new Rect(450, 50, 200, 200), "You win!");
            if (GUI.Button(new Rect(450, 400, 100, 50), "Reset")) {
                status = 0;
                action.restart ();
            }
        }
    }
}

FirstController.cs

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


public class FirstController : MonoBehaviour, SceneController, UserAction {

    UserGUI userGUI;

    public CoastController From;
    public CoastController To;
    public BoatController boat;
    private MyCharacterController[] 角色;

    private FirstManager actionManager;

    void Awake() {
        Director director = Director.getInstance ();
        director.currentSceneController = this;
        userGUI = gameObject.AddComponent <UserGUI>() as UserGUI;
        角色 = new MyCharacterController[6];
        loadResources ();
    }

    void Start() {
        actionManager = GetComponent<FirstSceneActionManager>();
    }

    public void loadResources() {
        //GameObject water = Instantiate (Resources.Load ("Perfabs/Water", typeof(GameObject)), water_pos, Quaternion.identity, null) as GameObject;
        //water.name = "water";

        From = new CoastController ("from");
        toCoast = new CoastController ("to");
        boat = new BoatController ();

        loadCharacter ();
    }

    private void loadCharacter() {
        for (int i = 0; i < 3; i++) {
            MyCharacterController cha = new MyCharacterController ("牧師");
            cha.setName("牧師" + i);
            cha.setPosition (From.getEmptyPosition ());
            cha.getOnCoast (From);
            From.getOnCoast (cha);

            角色 [i] = cha;
        }

        for (int i = 0; i < 3; i++) {
            MyCharacterController cha = new MyCharacterController ("魔鬼");
            cha.setName("魔鬼" + i);
            cha.setPosition (From.getEmptyPosition ());
            cha.getOnCoast (From);
            From.getOnCoast (cha);

            角色 [i+3] = cha;
        }
    }


    public void moveBoat() {
        if (boat.isEmpty ())
            return;
        /*  old way to move boat
        boat.Move ();
        */
        actionManager.moveBoat(boat);
        boat.move();
        userGUI.status = check_game_over ();
    }

    public void characterIsClicked(MyCharacterController characterCtrl) {
        if (characterCtrl.isOnBoat ()) {
            CoastController whichCoast;
            if (boat.get_to_or_from () == -1) { // to->-1; from->1
                whichCoast = toCoast;
            } else {
                whichCoast = From;
            }

            boat.GetOffBoat (characterCtrl.getName());
            //characterCtrl.moveToPosition (whichCoast.getEmptyPosition ());
            actionManager.moveCharacter(characterCtrl, whichCoast.getEmptyPosition ());
            characterCtrl.getOnCoast (whichCoast);
            whichCoast.getOnCoast (characterCtrl);

        } else {                                    // character on coast
            CoastController whichCoast = characterCtrl.getCoastController ();

            if (boat.getEmptyIndex () == -1) {      // boat is full
                return;
            }

            if (whichCoast.get_to_or_from () != boat.get_to_or_from ()) // boat is not on the side of character
                return;

            whichCoast.getOffCoast(characterCtrl.getName());
            //characterCtrl.moveToPosition (boat.getEmptyPosition());
            actionManager.moveCharacter(characterCtrl, boat.getEmptyPosition());
            characterCtrl.getOnBoat (boat);
            boat.GetOnBoat (characterCtrl);
        }
        userGUI.status = check_game_over ();
    }

    int check_game_over() { // 0->not finish, 1->lose, 2->win
        int from_priest = 0;
        int from_devil = 0;
        int to_priest = 0;
        int to_devil = 0;

        int[] fromCount = From.getCharacterNum ();
        from_priest += fromCount[0];
        from_devil += fromCount[1];

        int[] toCount = toCoast.getCharacterNum ();
        to_priest += toCount[0];
        to_devil += toCount[1];

        if (to_priest + to_devil == 6)      // win
            return 2;

        int[] boatCount = boat.getCharacterNum ();
        if (boat.get_to_or_from () == -1) { // boat at To
            to_priest += boatCount[0];
            to_devil += boatCount[1];
        } else {    // boat at From
            from_priest += boatCount[0];
            from_devil += boatCount[1];
        }
        if (from_priest < from_devil && from_priest > 0) {      // lose
            return 1;
        }
        if (to_priest < to_devil && to_priest > 0) {
            return 1;
        }
        return 0;           // not finish
    }

    public void restart() {
        boat.reset ();
        From.reset ();
        To.reset ();
        for (int i = 0; i < 角色.Length; i++) {
            角色 [i].reset ();
        }
    }
}

Action

SSAction.cs

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

public class SSAction : ScriptableObject {

    public bool enable = true;
    public bool destory = false;

    public GameObject gameobject { get; set; }
    public Transform transform { get; set; }
    public ISSActionCallback callback { get; set; }

    protected SSAction() {}
    // Use this for initialization
    public virtual void Start () {
        throw new System.NotImplementedException();
    }

    // Update is called once per frame
    public virtual void Update () {
        Debug.Log("SSAction");
        throw new System.NotImplementedException();
    }
}

CCMoveToAction.cs

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

public class CCMoveToAction : SSAction {

    public Vector3 target;
    public float speed;

    public static CCMoveToAction GetSSAction(Vector3 target, float speed)
    {
        CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
        action.target = target;
        action.speed = speed;
        return action;
    }

    // Use this for initialization
    public override void Start () {

    }

    // Update is called once per frame
    public override void Update () {
        Debug.Log("CCMoveToAction");
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, 1 * Time.deltaTime);
        if (this.transform.position == target)
        {
            this.destory = true;
            this.callback.SSActionEvent(this);
        }
    }
}

ISSActionCallback.cs

這是一個接口,用於傳遞信息,通訊作用

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

public enum SSActionEventType : int { Started, Competeted }

public interface ISSActionCallback {

    void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
        int intParam = 0, string strParam = null, Object objectParam = null);
}

CCSequenceAction.cs

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

public class CCSequenceAction : SSAction, ISSActionCallback {

    public List<SSAction> sequence;
    public int repeat = -1;
    public int start = 0;

    public static CCSequenceAction GetSSAction(int repeat, int start, List<SSAction> sequence)
    {
        CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
        action.repeat = repeat;
        action.sequence = sequence;
        action.start = start;
        return action;
    }

    // Use this for initialization
    public override void Start () {
        foreach(SSAction action in sequence)
        {
            action.gameobject = this.gameobject;
            action.transform = this.transform;
            action.callback = this;
            action.Start();
        }
    }

    // Update is called once per frame
    public override void Update () {
        Debug.Log("CCSequenceAction");
        if (sequence.Count == 0) return;
        if (start < sequence.Count)
            sequence[start].Update();
    }

    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null)
    {
        source.destory = false;
        this.start++;
        if (this.start >= sequence.Count)
        {
            this.start = 0;
            if (repeat > 0) repeat--;
            if (repeat == 0)
            {
                this.destory = true;
                this.callback.SSActionEvent(this);
            }
        }
    }

    private void OnDestroy()
    {
        //destory
    }
}

SSActionManager.cs

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

public class SSActionManager : MonoBehaviour {

    public Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
    private List<SSAction> waitingAdd = new List<SSAction>();
    private List<int> waitingDelete = new List<int>();

    // Use this for initialization
    protected void Start () {

    }

    // Update is called once per frame
    protected void Update () {
        Debug.Log("SSActionManager");
        foreach (SSAction ac in waitingAdd) actions[ac.GetInstanceID()] = ac;
        waitingAdd.Clear();

        foreach(KeyValuePair<int, SSAction> kv in actions)
        {
            SSAction ac = kv.Value;
            if (ac.destory)
                waitingDelete.Add(ac.GetInstanceID());
            else if (ac.enable)
                ac.Update();
        }

        foreach(int key in waitingDelete)
        {
            SSAction ac = actions[key];
            actions.Remove(key);
            DestroyObject(ac);
        }
        waitingDelete.Clear();
    }

    public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager)
    {
        action.gameobject = gameobject;
        action.transform = gameobject.transform;
        action.callback = manager;
        waitingAdd.Add(action);
        action.Start();
    }
}

CCActionManager.cs

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

public class CCActionManager : SSActionManager, ISSActionCallback {

public void moveBoat(BoatController boat) {
        MoveToAction action = MoveToAction.getAction(boat.getDestination(), boat.movingSpeed);
        this.addAction(boat.getGameobj(), action, this);
    }

    public void moveCharacter(MyCharacterController characterCtrl, Vector3 destination) {
        Vector3 currentPos = characterCtrl.getPos();
        SSAction action1 = CCMoveToAction.GetSSAction(middlePos, characterCtrl.movingSpeed);
        SSAction seqAction = CCSequenceAction.GetSSAction(1, 0, new List<ObjAction>{action});
        this.addAction(characterCtrl.getGameobj(), seqAction, this);
    }
}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章