Unity3D笔记(三)牧师和魔鬼的游戏

这次做的是一个魔鬼和牧师的游戏
游戏DEMO

Priests and Devils is a puzzle game in which you will help the
Priests and Devils to cross the river within the time limit.
There are 3 priests and 3 devils at one side of the river.
They all want to get to the other side of this river, but
there is only one boat and this boat can only carry two
persons each time. And there must be one person steering the
boat from one side to the other side. In the flash game, you
can click on them to move them and click the go button to move
the boat to the other direction. If the priests are out
numbered by the devils on either side of the river, they get
killed and the game is over. You can try it in many ways. Keep
all priests alive! Good luck!

源码可以直接到最后看


游戏的规则还是很简单,做游戏的第一步首先是列出游戏中对象:

  • 牧师
  • 魔鬼
  • 两岸

    之后再列出游戏中的行为表

行为条件
开船船上至少有一人
牧师上船船上有空位
魔鬼上船船上有空位
左边下船左边船上有乘客
右边下船右边船上有乘客
游戏胜利6人均抵达对岸
游戏失败一边的魔鬼人数大于牧师数

之后我们需要制作出游戏中的资源:
* 牧师
* 魔鬼
* 船
* 两岸

预制件
具体方式比较简单,就不多说了
本次的3D过河小游戏遵循了MVC架构,在架构中,有导演,场记。所有的场记拥有统一的接口,由导演控制各个场景的进入和退出,而场记负责管理自己所在场景的资源,对象,动作,模型等。

UML图如下:
UML

首先根据行为表定义UI应该批准用户完成什么操作:

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

public interface IUserAction {
    void reset();
    void drive();

    void priest_to_boat_at_begin();
    void devil_to_boat_at_begin();
    void priest_to_boat_at_end();
    void devil_to_boat_at_end();

    void left_off_boat();
    void right_off_boat();
}

这里意思大概是UI会向模型传递这些消息,然后模型中要实现这些操作,使得UI可以调用接口中的这些函数,完成UI和模型之间的通讯。
具体UI的实现布局由UserGUI实现,其拥有一个成员变量,IUserAction。

之后就开始完成GenGameObject, 这个类主要负责加载资源,管理游戏对象,负责游戏逻辑。
首先是使用c#的collection进行游戏对象的管理:

// Collections to manage gameobject
    private Queue<GameObject> priest_begin;
    private Queue<GameObject> priest_end;

    private Queue<GameObject> devil_begin;
    private Queue<GameObject> devil_end;

    private Queue<GameObject> onTheWay;

在每个位置分别用一个集合去管理游戏对象
由于定位这些游戏对象需要用到很多vector的位置信息,又弄了一堆变量存储位置信息:

private Vector3 land_begin_position;
    private Vector3 land_end_position;

    private Vector3 priest_begin_position;
    private Vector3 priest_end_position;

    private Vector3 devil_begin_position;
    private Vector3 devil_end_position;

    private Vector3 object_move;
    private Vector3 object_move_at_the_boat;

    private Vector3 on_ship_left_position;
    private Vector3 on_ship_right_positon;

    private Vector3 ship_begin_position;
    private Vector3 ship_end_position;

为了实现船的运动,添加枚举类型ship_state,update通过这个变量进行判断船是否应该运动,以达到运动的目的。

    private enum ship_location {
        begin, end, moving_to_begin, moving_to_end
    }
    private ship_location ship_info;

那么drive这个函数就很好实现了

public void drive() {
        if (onTheWay.Count > 0) {
            if (ship_info == ship_location.begin) {
                ship_info = ship_location.moving_to_end;
            } else if (ship_info == ship_location.end) {
                ship_info = ship_location.moving_to_begin;
            }
            game_state = State.moving;
        }
    }

同时为了实现船上的乘客和船一起运动,乘客上船时将其transform.parent设为船的transform,那么乘客就可以跟随船一起运动。

public void priest_to_boat_at_begin() {
        if (priest_begin.Count > 0 && onTheWay.Count < 2 && ship_info == ship_location.begin) {
            GameObject temp = priest_begin.Dequeue();
            temp.transform.parent = ship.transform;
            onTheWay.Enqueue(temp);
        }
    }

还有类似的函数实现魔鬼和牧师从各个方向上船。这样就实现了基本的运动。
这里写图片描述

为了实现模型向UI通讯,添加枚举量

    public enum State {
        win, over, moving, normal
    }
    public State game_state;

再添加check函数,每帧都check一次,如果游戏结束,或者游戏胜利,立刻通知UI结束游戏。

private void check() {
        count_num_of_object();
        // check at the begin
        if (num_of_devil_at_the_begin > num_of_priest_at_the_begin && num_of_priest_at_the_begin != 0) {
            game_state = State.over;
        } else if (num_of_devil_at_the_end > num_of_priest_at_the_end && num_of_priest_at_the_end != 0) {
            game_state = State.over;
        } else if (num_of_devil_at_the_end + num_of_priest_at_the_end == 6) {
            game_state = State.win;
        }
    }

最后上一波完整代码

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 bool running { get; set; }

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

ISceneController.cs

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

public interface ISceneController {
    void LoadResources ();
    // void Pause ();
    // void Resume ();
}

IUserAction.cs

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

public interface IUserAction {
    void reset();
    void drive();

    void priest_to_boat_at_begin();
    void devil_to_boat_at_begin();
    void priest_to_boat_at_end();
    void devil_to_boat_at_end();

    void left_off_boat();
    void right_off_boat();
}

GenGameObjects.cs

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

// generator gameobject in each frame

public class GenGameObjects : MonoBehaviour, ISceneController, IUserAction {

    // Collections to manage gameobject
    private Queue<GameObject> priest_begin;
    private Queue<GameObject> priest_end;

    private Queue<GameObject> devil_begin;
    private Queue<GameObject> devil_end;

    private Queue<GameObject> onTheWay;
    private GameObject ship;
    private enum ship_location {
        begin, end, moving_to_begin, moving_to_end
    }
    private ship_location ship_info;
    // the number of object in each side
    private int num_of_priest_at_the_begin;
    private int num_of_priest_at_the_end;
    private int num_of_devil_at_the_begin;
    private int num_of_devil_at_the_end;
    // position information
    private Vector3 land_begin_position;
    private Vector3 land_end_position;

    private Vector3 priest_begin_position;
    private Vector3 priest_end_position;

    private Vector3 devil_begin_position;
    private Vector3 devil_end_position;

    private Vector3 object_move;
    private Vector3 object_move_at_the_boat;

    private Vector3 on_ship_left_position;
    private Vector3 on_ship_right_positon;

    private Vector3 ship_begin_position;
    private Vector3 ship_end_position;

    public enum State
    {
        win, over, moving, normal
    }
    public State game_state;
    // Use this for initialization
    void Start () {

    }
    // Update is called once per frame
    void Update () {
        setPostion(priest_begin, priest_begin_position, object_move);
        setPostion(devil_begin, devil_begin_position, object_move);
        setPostion(priest_end, priest_end_position, object_move);
        setPostion(devil_end, devil_end_position, object_move);
        setPostion(onTheWay, on_ship_left_position, object_move_at_the_boat);

        if (ship_info == ship_location.moving_to_end) {
            float step = 3 * Time.deltaTime;
            ship.transform.position = Vector3.MoveTowards(ship.transform.position, ship_end_position, step);
            if (ship.transform.position == ship_end_position){
                ship_info = ship_location.end;
                game_state = State.normal;  
            }
        } else if (ship_info == ship_location.moving_to_begin) {
            float step = 3 * Time.deltaTime;
            ship.transform.position = Vector3.MoveTowards(ship.transform.position, ship_begin_position, step);
            if (ship.transform.position == ship_begin_position) {
                ship_info = ship_location.begin;
                game_state = State.normal;
            }
        } else {
            check();
        }
    }

    void Awake() {
        land_begin_position.Set(-6, 0, 0);
        land_end_position.Set(6, 0, 0);

        priest_begin_position.Set(-8, 1, 0);
        priest_end_position.Set(4, 1, 0);

        devil_begin_position.Set(-5.3f, 1, 0);
        devil_end_position.Set(6.5f, 1, 0);

        object_move.Set(1, 0, 0);
        object_move_at_the_boat.Set(0.5f, 0, 0);
        on_ship_left_position.Set(-0.3f, 1.3f, 0);
        on_ship_right_positon.Set(0.3f, 1.5f, 0);

        ship_begin_position.Set(-1.7f, 0, 0);
        ship_end_position.Set(1.7f, 0, 0);

        priest_begin = new Queue<GameObject>();
        priest_end = new Queue<GameObject>();
        devil_begin = new Queue<GameObject>();
        devil_end = new Queue<GameObject>();
        onTheWay = new Queue<GameObject>();

        game_state = State.normal;

        ship_info = ship_location.begin;
        SSDirector director = SSDirector.getInstance();
        director.currentSceneController = this;
        director.currentSceneController.LoadResources();
    }

    public void LoadResources() {
        // generate gameobject from prefabs
        GameObject land_left = Instantiate(Resources.Load("Prefabs/land") as GameObject, land_begin_position, Quaternion.identity);
        GameObject land_right = Instantiate(Resources.Load("Prefabs/land") as GameObject, land_end_position, Quaternion.identity);
        ship = Instantiate(Resources.Load("Prefabs/ship") as GameObject, ship_begin_position, Quaternion.identity);
        for (int i = 0; i < 3; ++i) {
            devil_begin.Enqueue(Instantiate(Resources.Load("Prefabs/devil"), devil_begin_position + i * object_move, Quaternion.identity) as GameObject);
            priest_begin.Enqueue(Instantiate(Resources.Load("Prefabs/priest"), priest_begin_position + i * object_move, Quaternion.identity) as GameObject);
        }
        Debug.Log("in LoadResources");
        Debug.Log(priest_begin.Count);
    }
    // set object postion on each update()
    // implement it later
    void setPostion(Queue<GameObject> obj_queue, Vector3 first, Vector3 move) {
        GameObject[] temp = obj_queue.ToArray();
        for (int i = 0; i < temp.Length; ++i) {
            temp[i].transform.localPosition = first + i * move;
        }
    }
    // get on the boat
    public void priest_to_boat_at_begin() {
        if (priest_begin.Count > 0 && onTheWay.Count < 2 && ship_info == ship_location.begin) {
            GameObject temp = priest_begin.Dequeue();
            temp.transform.parent = ship.transform;
            onTheWay.Enqueue(temp);
        }
    }
    public void devil_to_boat_at_begin() {
        if (devil_begin.Count > 0 && onTheWay.Count < 2 && ship_info == ship_location.begin) {
            GameObject temp = devil_begin.Dequeue();
            temp.transform.parent = ship.transform;
            onTheWay.Enqueue(temp);
        }
    }
    public void priest_to_boat_at_end() {
        if (priest_end.Count > 0 && onTheWay.Count < 2 && ship_info == ship_location.end) {
            GameObject temp = priest_end.Dequeue();
            temp.transform.parent = ship.transform;
            onTheWay.Enqueue(temp);
        }
    }
    public void devil_to_boat_at_end() {
        if (devil_end.Count > 0 && onTheWay.Count < 2 && ship_info == ship_location.end) {
            GameObject temp = devil_end.Dequeue();
            temp.transform.parent = ship.transform;
            onTheWay.Enqueue(temp);
        }
    }
    // get off the boat
    public void left_off_boat() {
        if (onTheWay.Count > 0) {
            GameObject temp = onTheWay.Dequeue();
            getOffTheBoat(temp);
        }
    }

    public void right_off_boat() {
        if (onTheWay.Count > 1) {
            GameObject temp_1 = onTheWay.Dequeue();
            GameObject temp_2 = onTheWay.Dequeue();
            onTheWay.Enqueue(temp_1);
            getOffTheBoat(temp_2);
        }
    }
    public void drive() {
        if (onTheWay.Count > 0) {
            if (ship_info == ship_location.begin) {
                ship_info = ship_location.moving_to_end;
            } else if (ship_info == ship_location.end) {
                ship_info = ship_location.moving_to_begin;
            }
            game_state = State.moving;
        }
    }
    // private void getOnTheBoat(GameObject obj) {

    // }
    private void getOffTheBoat(GameObject obj) {
        obj.transform.parent = null;
        if (ship_info == ship_location.begin) {
            if (obj.tag == "devil") {
                devil_begin.Enqueue(obj);
            } else {
                priest_begin.Enqueue(obj);
            }
        } else {
            if (obj.tag == "devil" ) {
                devil_end.Enqueue(obj);
            } else {
                priest_end.Enqueue(obj);
            }
        }
    }

    void count_num_of_object() {
        num_of_devil_at_the_begin = devil_begin.Count;
        num_of_devil_at_the_end = devil_end.Count;
        num_of_priest_at_the_begin = priest_begin.Count;
        num_of_priest_at_the_end = priest_end.Count;
        GameObject[] temp = onTheWay.ToArray();
        foreach (GameObject item in onTheWay)
        {
            if (item.tag == "priest") {
                if (ship_info == ship_location.begin) {
                    num_of_priest_at_the_begin++;
                } else {
                    num_of_priest_at_the_end++;
                }
            } else {
                if (ship_info == ship_location.begin) {
                    num_of_devil_at_the_begin++;
                } else {
                    num_of_devil_at_the_end++;
                }
            }
        }
    }
    private void check() {
        count_num_of_object();
        // check at the begin
        if (num_of_devil_at_the_begin > num_of_priest_at_the_begin && num_of_priest_at_the_begin != 0) {
            game_state = State.over;
        } else if (num_of_devil_at_the_end > num_of_priest_at_the_end && num_of_priest_at_the_end != 0) {
            game_state = State.over;
        } else if (num_of_devil_at_the_end + num_of_priest_at_the_end == 6) {
            game_state = State.win;
        }
    }

    public void reset() {
        Application.LoadLevel(Application.loadedLevelName);  
        game_state = State.normal;
    }
}

UserGUI.cs:

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

public class UserGUI : MonoBehaviour {
    private IUserAction action;
    private GenGameObjects sc;
    private float button_width;
    private float button_height;
    private float button_y;
    private float move_x;

    // Use this for initialization
    void Start () {
        sc = SSDirector.getInstance().currentSceneController as GenGameObjects;
        action = SSDirector.getInstance ().currentSceneController as IUserAction;
        button_width = 100;
        button_height = 50;
        button_y = 80;
        move_x = 100;
        Debug.Log("UI start");
        Debug.Log(action);
    }

    void OnGUI() {
        // reset button
        if (GUI.Button(new Rect(350, button_y + 150, button_width, button_height), "Reset")) {
            action.reset();
        }
        if (sc.game_state == GenGameObjects.State.over) {
            GUI.Label(new Rect(350, button_y, button_width * 2, button_height * 2), "GameOver!");
            return;
        }
        if (sc.game_state == GenGameObjects.State.win) {
            GUI.Label(new Rect(350, button_y, button_width * 2, button_height * 2), "Win");
            return;
        }
        // priest to boat at the begin
       if (GUI.Button(new Rect(70, button_y, button_width, button_height), "On")) {
            action.priest_to_boat_at_begin();
            Debug.Log("button clicked");
       }
       // devil to boat at the begin
       if (GUI.Button(new Rect(70 + move_x, button_y, button_width, button_height), "On")) {
            action.devil_to_boat_at_begin();
       }
       // priest to boat at the end
       if (GUI.Button(new Rect(540, button_y, button_width, button_height), "On")) {
            action.priest_to_boat_at_end();
       }
       // devil to boat at the end
       if (GUI.Button(new Rect(540 + move_x, button_y, button_width, button_height), "On")) {
            action.devil_to_boat_at_end();
       }
       // left of boat off
       if (GUI.Button(new Rect(320, button_y, button_width, button_height), "Off")) {
            action.left_off_boat();
       }
       // right of boat off
       if (GUI.Button(new Rect(320 + move_x, button_y, button_width, button_height), "Off")) {
            action.right_off_boat();
       }
       // go button
       if (GUI.Button(new Rect(350, button_y - 80, button_width, button_height), "GO!")) {
            action.drive();
       }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值