Unity3D 牧师与魔鬼

使用MVC结构编程
首先,我针对这个游戏总结了UML图,明确了我需要哪些函数,哪些类来支持游戏的实现:

这里写图片描述
GameModel
从图上我们可以看出我们需要4个GameModel,分别是:

  • moveable:用于控制角色和船的移动。
  • CoastController:用于控制与河岸有关的动作,比如角色上下岸,船的离开和停靠。
  • MyCharacterController:用于控制6个角色的动作,比如上船,上岸等。
  • BoatController:用于控制船的运动以及角色的上下船绑定。

对于moveable类,这个类只有三个函数,UpDate函数用于控制船的运动,在类的变量中我添加了一个中间量保证船是在河中运动而不出现能够穿过河岸的bug。

public class moveable: MonoBehaviour
    {
        readonly float move_speed = 20;
        private int move_to_where;//0->not move, 1->to middle, 2->to destination
        private Vector3 dest;
        private Vector3 middle;
        public static int cn_move = 0;//0->can move, 1->cant move

        void Update(){
            if (cn_move == 1)
                return;
            else{
                if(move_to_where == 1){
                    transform.position = Vector3.MoveTowards(transform.position, middle, move_speed*Time.deltaTime);
                    if (transform.position == middle)
                        move_to_where = 2;
                }
                else if(move_to_where == 2){
                    transform.position = Vector3.MoveTowards(transform.position, dest, move_speed*Time.deltaTime);
                    if (transform.position == dest)
                        move_to_where = 0;
                }
            }
        }

        public void SetDestination(Vector3 _dest){
            if (cn_move == 1)
                return;
            else{
                middle = _dest;
                dest = _dest;
                if (_dest.y < transform.position.y) {
                    middle.y = transform.position.y;
                } else {
                    middle.x = transform.position.x;
                }
                move_to_where = 1;
            }
        }
        public void reset(){
            if (cn_move == 1)
                return;
            else{
                move_to_where = 0;
            }
        }
    }

而在MyCharacterController类中,我们就要关注6个角色与船,河岸的联系,特别是在船移动时我们要保证角色是和船绑定的,以此来保证船和角色一起移动。代码太长就不展示了。。。
对于CoastController类和BoatController类,我将他们定义成容器使用,用于容纳角色。因此,我们需要给给这两个类分配容纳角色的空间的数组属性。同时还要判断是否有空间放置角色,最后是要能够返回牧师与魔鬼各自的数量用于判断游戏是否能够结束。同上,代码太长,加上我懒,就不放上去了。

View
我设定视图框架为与用户交互的界面,其中包括游戏场景,游戏对象的生成,游戏的重开,暂停,继续等功能。包括了ClickGUI和UserGUI两个类。

  • UserGUI类:设置游戏的重开,暂停及继续游戏的按钮。
  • ClickGUI类:设置游戏对象的点击事件。
    private UserAction action;
    private GUIStyle MyStyle;
    private GUIStyle MyButtonStyle;
    public int if_win_or_not;

    void Start(){
        action = Director.get_Instance ().curren as UserAction;

        MyStyle = new GUIStyle ();
        MyStyle.fontSize = 40;
        MyStyle.normal.textColor = new Color (255f, 0, 0);
        MyStyle.alignment = TextAnchor.MiddleCenter;

        MyButtonStyle = new GUIStyle ("button");
        MyButtonStyle.fontSize = 30;
    }
    void reStart(){
        if (GUI.Button (new Rect (Screen.width/2-Screen.width/8, Screen.height/2+100, 150, 50), "Restart", MyButtonStyle)) {
            if_win_or_not = 0;
            action.restart ();
            moveable.cn_move = 0;
        }
    }
    void IsPause(){
        if (GUI.Button (new Rect (Screen.width / 2 - 350, Screen.height / 2 + 100, 150, 50), "Pause", MyButtonStyle)) {
            if (moveable.cn_move == 0) {
                action.pause ();
                moveable.cn_move = 1;
            } 
        } else if (GUI.Button (new Rect (Screen.width-Screen.width/2, Screen.height / 2 + 100, 150, 50), "Continue", MyButtonStyle)) {
            if (moveable.cn_move == 1) {
                action.Coninu();
                moveable.cn_move = 0;
            }
        }
    }
    void OnGUI(){
        IsPause ();
        reStart ();
        if(moveable.cn_move == 1)
            GUI.Label (new Rect (Screen.width/2-Screen.width/8, 50, 100, 50), "Pausing", MyStyle);
        if (if_win_or_not == -1) {
            GUI.Label (new Rect (Screen.width/2-Screen.width/8, 50, 100, 50), "Game Over!!!", MyStyle);
            IsPause ();
            reStart ();
        } else if (if_win_or_not == 1) {
            GUI.Label (new Rect (Screen.width/2-Screen.width/8, 50, 100, 50), "You Win!!!", MyStyle);
            IsPause ();
            reStart ();
        }
    }
    UserAction action;
    MyCharacterController character;

    public void setController(MyCharacterController tem){
        character = tem;
    }
    void Start(){
        action = Director.get_Instance ().curren as UserAction;
    }
    void OnMouseDown(){
        if (gameObject.name == "boat") {
            action.moveboat ();
        } else {
            action.isClickChar (character);
        }
    }

Controller
重头戏来了,对于整个游戏,我们需要一个场记用于指挥所有角色的行为,与用户的交互等,这个类就是MySceneController。但我们立刻就会想到有了场记我们还缺少导演的。于是我们有Director类实现单例模式,在游戏开始时仅创建一个实例,这个实例控制着游戏的运行,暂停,互动。

        private static Director _instance;
        public SceneController curren{ get; set;}
        public static Director get_Instance(){
            if (_instance == null)
            {
                _instance = new Director();
            }
            return _instance;
        }

最后,哎,终于来到结尾了。我们需要实现两个接口:SceneController,这个接口是由导演控制用来实现游戏场景的加载,在这里我并没有把暂停游戏和继续游戏的函数放在这个接口,原因在于我觉得这是属于用户交互的方面,应该用在门面模式(Fasade):外部与一个子系统的通信必须通过一个统一的门面(Facade)对象进行。
这个门面模式用了UserAction接口中,MySceneController类必须实现UserAction接口的函数才能与用户通信,控制用户与游戏的互动。

说实话,这是我用Unity和C#第一次做面向对象设计,感觉像是大一的C++实训,那个时候也有实例,啊,感觉这个星期又经历了一次实训啊。
放成品图:
这里写图片描述
还是要感谢一波师兄的博客,完美的指导!!!!
最后放上鄙人的github:wangld,有需自取。

猜你喜欢

转载自blog.csdn.net/hellowangld/article/details/79805000