欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

unity3d学习笔记(三)魔鬼与牧师

程序员文章站 2024-03-18 19:37:34
...

游戏规则

* 你要运用智慧帮助3个牧师(方块)和3个魔鬼(圆球)渡河。
* 船最多可以载2名游戏角色。
* 船上有游戏角色时,你才可以点击这个船,让船移动到对岸。
* 当有一侧岸的魔鬼数多余牧师数时(包括船上的魔鬼和牧师),魔鬼就会失去控制,吃掉牧师(如果这一侧没有牧师则不会失败),游戏失败。
* 当所有游戏角色都上到对岸时,游戏胜利。

项目截图:

unity3d学习笔记(三)魔鬼与牧师
unity3d学习笔记(三)魔鬼与牧师
unity3d学习笔记(三)魔鬼与牧师

游戏使用MVC结构
unity的每个GameObject都是model
model有Controller来控制,boat由BoatController来控制,coast由CoastController控制,character有characterController来控制,同时FirstController为最高一层的Controller,控制其他Controller,同时来控制场景的加载
同时还有一个Director对象,一个项目只能有一个director,用来协调各个controller之间的通信
View就是UserGUI和ClickGUI,它们展示游戏结果,并提供用户交互的渠道(点击物体和按钮)。
Director的定义
  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;
        }

    }

director在游戏中只有一个实例,方便协调各个controller之间的通信.
Director类使用了单例模式。第一次调用Director.getInstance()时,会创建一个新的Director对象,保存在_instance,此后每次调用getInstance,都回返回_instance。也就是说Director最多只有一个实例。这样,我们在任何Script中的任何地方通过Director.getInstance()都能得到同一个Director对象,也就可以获得同一个currentSceneController,这样我们就可以轻易实现类与类之间的通信

SceneController 加载场景的接口,需要由firstController实现

  public interface SceneController
    {
        void loadResources();
    }

UserAction 用于相应用户的点击,同样需要firstController实现

public interface UserAction
    {
        void moveBoat();//用户点击船时,船移动
        void characterIsClicked(MyCharactorController characterCtrl);//用户点击角色是,角色上船或上岸
        void restart();//用户点击restart按钮,重新开始游戏
    }

Moveable

Moveable是一个可以挂载在GameObject上的类,用于控制GameObject的移动

 public class Moveable:MonoBehaviour
    {
        float speed = 20f;
        Vector3 dest;
        private void Update()
        {
                if (transform.position == dest)
                {
                    return;
                }
                    transform.position = Vector3.MoveTowards(transform.position, dest, speed * Time.deltaTime);
        }
        public void setDestionation(Vector3 _dest)
        {
            dest = _dest;
        }
    }

ClickUI用来相应用户点击

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

public class ClickGUI : MonoBehaviour
{
    UserAction action;
    MyCharactorController characterController;

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

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

    void OnMouseDown()
    {
        if (gameObject.name == "boat")//点击了船,船移动
        {
            action.moveBoat();
        }
        else //否则是角色上船/上岸
        {
            action.characterIsClicked(characterController);
        }
    }
}

BoatController

     public class BoatController
    {
        GameObject boat;
        Moveable moveable;
        Vector3 startPosition = new Vector3(4, 0.7f, 0);
        Vector3 endPosition = new Vector3(-4, 0.7f, 0);
        Vector3[] start_positions;
        Vector3[] end_positions;
        MyCharactorController[] passenger = new MyCharactorController[2];
        int start_or_end; // 1 start -1 end
        public BoatController()
        {
            start_or_end = 1;
            start_positions = new Vector3[] { new Vector3(3F, 1.5F, 0), new Vector3(4.5F, 1.5F, 0) };
            end_positions = new Vector3[] { new Vector3(-5F, 1.5F, 0), new Vector3(-3.5F, 1.5F, 0) };
            boat = Object.Instantiate(Resources.Load("prefab/boat", typeof(GameObject)), startPosition, Quaternion.identity, null) as GameObject;
            boat.name = "boat";

            moveable = boat.AddComponent(typeof(Moveable)) as Moveable;
            boat.AddComponent(typeof(ClickGUI));//给船添加点击事件
        }
        public void move()
        {
            if (start_or_end == 1)
            {
                moveable.setDestionation(endPosition);
                start_or_end = -1;
            }
            else
            {
                moveable.setDestionation(startPosition);
                start_or_end = 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 (start_or_end == -1)
            {
                pos = end_positions[emptyIndex];
            }
            else
            {
                pos = start_positions[emptyIndex];
            }
            return pos;
        }
        //魔鬼或牧师上船时,添加船员
        public void addCharacterCtrl(MyCharactorController characterCtrl)
        {
            int index = getEmptyIndex();
            passenger[index] = characterCtrl;
        }
        //魔鬼或牧师上岸时,添加船员
        public MyCharactorController removeCharacterCtrl(string passenger_name)
        {
            for (int i = 0; i < passenger.Length; i++)
            {
                if (passenger[i] != null && passenger[i].getName() == passenger_name)
                {
                    MyCharactorController 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_start_or_end()
        {
            return start_or_end;
        }

        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->priest, 1->devil
                    count[0]++;
                }
                else
                {
                    count[1]++;
                }
            }
            return count;
        }

        public void reset()
        {
            moveable.reset();
            if (start_or_end == -1)
            {
                move();
            }
            passenger = new MyCharactorController[2];
        }

    }
BoastController
    public class CoastController
    {
        GameObject coast;
        Vector3 start = new Vector3(11, 0.5f, 0);
        Vector3 end = new Vector3(-11, 0.5f, 0);
        int start_or_end; //1 start -1 end
        Vector3[] positions;
        MyCharactorController[] passengerPlaner;
        public CoastController(string _to_or_from)
        {
            positions = new Vector3[] {new Vector3(7F,2F,0), new Vector3(8.5F,2f,0), new Vector3(10,2,0),
                new Vector3(11.5F,2,0), new Vector3(13,2F,0), new Vector3(14.5F,2F,0)};

            passengerPlaner = new MyCharactorController[6];

            if (_to_or_from == "start")
            {
                coast = Object.Instantiate(Resources.Load("prefab/coast", typeof(GameObject)), start, Quaternion.identity, null) as GameObject;
                coast.name = "start";
                start_or_end = 1;
            }
            else
            {
                coast = Object.Instantiate(Resources.Load("prefab/coast", typeof(GameObject)), end, Quaternion.identity, null) as GameObject;
                coast.name = "end";
                start_or_end = -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 *= start_or_end;
            return pos;
        }
        public int getStartOrEnd()
        {
            return start_or_end;
        }
        //上岸时,增加成员
        public void addCharacter(MyCharactorController characterCtrl)
        {
            int index = getEmptyIndex();
            passengerPlaner[index] = characterCtrl;
        }
        //上船时,减少岸上的成员
        public MyCharactorController removeCharacter(string passenger_name)
        {   // 0->priest, 1->devil
            for (int i = 0; i < passengerPlaner.Length; i++)
            {
                if (passengerPlaner[i] != null && passengerPlaner[i].getName() == passenger_name)
                {
                    MyCharactorController charactorCtrl = passengerPlaner[i];
                    passengerPlaner[i] = null;
                    return charactorCtrl;
                }
            }
            Debug.Log("cant find passenger on coast: " + passenger_name);
            return null;
        }
        //分别得到岸上牧师和魔鬼的数量
        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->priest, 1->devil
                    count[0]++;
                }
                else
                {
                    count[1]++;
                }
            }
            return count;
        }

        public void reset()
        {
            passengerPlaner = new MyCharactorController[6];
        }

    }
MycharacterController
public class MyCharactorController
    {
        GameObject character;
        Moveable moveable;
        int characterType;
        bool isOnBoat;
        CoastController coast;
        ClickGUI clickGUI;
        public  MyCharactorController(string type)
        {
            if (type == "priest")
            {
                character = Object.Instantiate(Resources.Load("prefab/priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
                characterType = 0;
            }
            else
            {
                character = Object.Instantiate(Resources.Load("prefab/devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
                characterType = 1;
            }
            moveable = 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)
        {
            moveable.setDestionation(destination);
        }

        public int getType()
        {   // 0->priest, 1->devil
            return characterType;
        }

        public string getName()
        {
            return character.name;
        }
          public bool getIsOnBoat()
        {
            return isOnBoat;
        }
        public void GetOnBoat(BoatController boatController)
        {
            //下岸
            coast.removeCharacter(character.name);
            coast = null;
            //上船
            moveToPosition(boatController.getEmptyPosition());
            character.transform.parent = boatController.getGameobj().transform;
            isOnBoat = true;
            boatController.addCharacterCtrl(this);

        }

        public void getOnCoast(CoastController coastController, BoatController boatController)
        {
            //下船
            character.transform.parent = null;
            if (boatController != null)
                boatController.removeCharacterCtrl(character.name);
            //上岸
            moveToPosition(coastController.getEmptyPosition());
            coast = coastController;
            coastController.addCharacter(this);
            isOnBoat = false;

        }
        public void reset()
        {
            moveable.reset();
            coast = (Director.getInstance().currentSceneController as FirstController).startCoast;
            getOnCoast(coast, null);
            setPosition(coast.getEmptyPosition());
        }
        public CoastController GetCoastController()
        {
            return coast;
        }

    }
FirstController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Com.MyGame;

public class FirstController : MonoBehaviour, SceneController, UserAction {

    Vector3 river_pos = new Vector3(0, 0, 0);
    UserGUI userGUI;
    public CoastController startCoast;
    public CoastController endCoast;
    public BoatController boat;
    private MyCharactorController[] characterControllers;
    void Awake()
    {
        Director director = Director.getInstance();
        director.currentSceneController = this;
        userGUI = gameObject.AddComponent<UserGUI>() as UserGUI;
        characterControllers = new MyCharactorController[6];
        loadResources();
    }

    public void loadResources()
    {
        GameObject river = Instantiate(Resources.Load("prefab/river", typeof(GameObject)), river_pos, Quaternion.identity, null) as GameObject;
        river.name = "river";
        startCoast = new CoastController("start");
        endCoast = new CoastController("end");
        boat = new BoatController();
        loadCharacters();
    }
    private void loadCharacters()
    {
        for (int i = 0; i < 3; ++i)
        {
            MyCharactorController ch = new MyCharactorController("priest");
            ch.setName("priest" + i);
            ch.setPosition(startCoast.getEmptyPosition());
            ch.getOnCoast(startCoast, null);
            characterControllers[i] = ch;
        }
        for (int i = 0; i < 3; ++i)
        {
            MyCharactorController cha = new MyCharactorController("devil");
            cha.setName("devil" + i);
            cha.setPosition(startCoast.getEmptyPosition());
            cha.getOnCoast(startCoast, null);
            characterControllers[i + 3] = cha;

        }
    }
    //用户点击船时,船移动
    public void moveBoat()
    {
        if (boat.isEmpty())
        {
            return;
        }
        boat.move();
        userGUI.status = check_game_over();
    }
    //检查量变牧师和魔鬼的数量
    int check_game_over()
    {
        int start_priest = 0;
        int start_devil = 0;
        int end_priest = 0;
        int end_devil = 0;
        int[] start_count = startCoast.getCharacterNum();
        start_priest += start_count[0];
        start_devil += start_count[1];
        int[] end_count = endCoast.getCharacterNum();
        end_priest += end_count[0];
        end_devil += end_count[1];
        if (end_devil + end_priest == 6)
        {
            return 2;//WIN
        }
        int[] boat_num = boat.getCharacterNum();
        if (boat.get_start_or_end() == -1)
        {
            end_priest += boat_num[0];
            end_devil += boat_num[1];
        }
        else
        {
            start_priest += boat_num[0];
            start_devil += boat_num[1];
        }

        if (start_priest < start_devil && start_priest > 0)
        {
            return 1;//LOSE
        }
        if (end_devil > end_priest && end_priest > 0)
        {
            return 1;
        }
        return 0;//NOT FINISH
    }

    public void restart()
    {
        boat.reset();
        startCoast.reset();
        endCoast.reset();
        for (int i = 0; i < characterControllers.Length; ++i)
        {
            characterControllers[i].reset();
        }
    }
    //点击角色时的角色的行为
    public void characterIsClicked(MyCharactorController characterCtrl)
    {
        if (characterCtrl.getIsOnBoat())
        {
            if (boat.get_start_or_end() == -1)
            {
                characterCtrl.getOnCoast(endCoast, boat);
            }
            else
            {
                characterCtrl.getOnCoast(startCoast, boat);
            }
        }
        else
        {
            CoastController whichCoast = characterCtrl.GetCoastController();
            if (boat.getEmptyIndex() == -1)
            {
                return;
            }
            if (boat.get_start_or_end() != whichCoast.getStartOrEnd() )
            {
                return;
            }
            characterCtrl.GetOnBoat(boat);
        }
        userGUI.status = check_game_over();
    }
}

USERGUI

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

public class UserGUI : MonoBehaviour
{
    private UserAction action;
    public int status = 0;
    GUIStyle style;
    GUIStyle buttonStyle;
    float time = 60;
    float second = 0;
    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()
    {
        //倒计时
        second += Time.deltaTime / 2;
        if (second >= 1)
        {
            time--;
            second = 0;
        }
        if (time > 0 && status == 0)
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), time.ToString(), style);

        }
        //判断游戏状态
        if (status == 1 || time <= 0)
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), "You Fail!", style);
            if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart", buttonStyle))
            {
                status = 0;
                time = 60;
                action.restart();
                second = 0;
            }
        }
        else if (status == 2)
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), "You win!", style);
            if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart", buttonStyle))
            {
                status = 0;
                time = 60;
                action.restart();
                second = 0;
            }
        }
    }
}

参考链接
如有雷同,纯属…….