Lab5:“魔鬼与牧师“小游戏的实现

        "魔鬼与牧师"是个很经典的小游戏,也被扩展成牧羊人等多个版本,但核心玩法都大致相同:

规则简述:有三个牧师与三个魔鬼,要多次乘坐一艘核载2人的小船过河。但在任意一边的河岸上,若魔鬼的数量大于牧师的数量,则魔鬼就会伺机动手伤害牧师。你的任务就是在保证牧师不受伤害的情况下,顺利将所有人送到对岸。

元素提取:牧师*3,魔鬼*3,小河,船,河岸*2

玩家的动作以及条件:

        1. 选择牧师或魔鬼上/下船

        2.开船到对岸。(每次开船都判定游戏是否失败)

一,制造各个物体的模型

        为求简明,将各个物体的模型设计列成下表:
 

物体尺寸颜色
牧师*31*1*2青色
魔鬼*31.2*1.2*2.5红色
1*1*1淡黄色
河水3*3*1蓝色
河岸*25*5*3绿色

二,编写项目代码

接下来,分别对于用户交互界面、模型初始化和全局控制进行代码编写:

用户交互界面:

1.先在Start函数中定义action,同时初始化变量sign用于记录游戏状态(如胜利/失败/游戏开始):

private IUserAction action;
public int sign = 0;
void Start()
    {
        action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
    }

2.再在OnGUI函数中定义游戏的字体大小和不同状态:

void OnGUI()
    {
        GUIStyle text_style;
        GUIStyle button_style;

        text_style = new GUIStyle()
        {
            fontSize = 30
        };
        button_style = new GUIStyle("button")
        {
            fontSize = 15
        };

        // 如果还没开始则显示规则
        if (sign == 0)
        {
            //点击开始按钮,状态从未开始变为已开始
            if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 4, 100, 50), "Start", button_style))
            {
                sign = 1;
            }
            else//未点击开始按钮时,在主界面展示游戏规则
            {
                GUI.Label(new Rect(Screen.width / 2 - 85, 110, 200, 50), "玩法:点击牧师、恶魔、船移动");
                GUI.Label(new Rect(Screen.width / 2 - 85, 130, 250, 50), "胜利:让全部牧师和恶魔都渡河");
                GUI.Label(new Rect(Screen.width / 2 - 85, 150, 250, 50), "失败:任意一边的恶魔数量多于牧师");
            }
        }
        // sign == 2表示失败, sign == 3表示成功
        else if (sign == 2)
        {
            GUI.Label(new Rect(Screen.width / 2 - 60, Screen.height / 2 - 120, 100, 50), "Gameover!", text_style);
            if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 3, 100, 50), "Restart", button_style))
            {
                action.Restart();
                sign = 1;
            }
        }
        else if (sign == 3)
        {
            GUI.Label(new Rect(Screen.width / 2 - 60, Screen.height / 2 - 120, 100, 50), "You Win!", text_style);
            if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 4, 100, 50), "Restart", button_style))
            {
                action.Restart();
                sign = 1;
            }
        }
    }

模型初始化:

先创建一个名为mygame的命名空间,再在其中添加接口和不同模型的类。

命名空间中的接口:

public interface ISceneController                      //加载场景
    {
        void LoadResources();
    }
public interface IUserAction                           //用户互动会发生的事件
    {
        void MoveBoat();                                   //移动船
        void Restart();                                    //重新开始
        void MoveRole(RoleModel role);                     //移动角色
        int Check();                                       //检测游戏结束
    }

命名空间中的各个模型对应的类:

//Software School Director
    public class SSDirector : System.Object
    {
        private static SSDirector _instance;
        public ISceneController CurrentScenceController { get; set; }
        public static SSDirector GetInstance()
        {
            if (_instance == null)
            {
                _instance = new SSDirector();
            }
            return _instance;
        }
    }

    public class LandModel
    {
        GameObject land;                                //陆地对象
        Vector3[] positions;                            //用向量保存每个角色放在陆地上的位置
        int land_sign;                                  //到达陆地标志为-1,开始陆地标志为1,方便计算失败条件
        RoleModel[] roles = new RoleModel[6];           //陆地上角色的座位

        public LandModel(string land_mark)
        {
            //设置好空位,供人物的放置
            positions = new Vector3[] {new Vector3(7, 2, 30), new Vector3(9, 2, 30), new Vector3(11, 2, 30),
                new Vector3(13, 2, 30), new Vector3(15, 2, 30), new Vector3(17, 2, 30)};
            if (land_mark == "start")//是起始那边的岸
            {
                land = Object.Instantiate(Resources.Load("Prefabs/Land", typeof(GameObject)), new Vector3(12, 0, 30), Quaternion.identity) as GameObject;
                land_sign = 1;
            }
            else//是对岸
            {
                land = Object.Instantiate(Resources.Load("Prefabs/Land", typeof(GameObject)), new Vector3(-12, 0, 30), Quaternion.identity) as GameObject;
                land_sign = -1;
            }
        }

        // 得到陆地上最靠边的空位置下标
        public int GetEmptyNumber()
        {
            for (int i = 0; i < roles.Length; i++)
            {
                if (roles[i] == null)
                    return i;
            }
            return -1;
        }

        public int GetLandSign()
        {
            return land_sign;
        }

        // 得到陆地上空位置
        public Vector3 GetEmptyPosition()
        {
            Vector3 pos = positions[GetEmptyNumber()];
            // 因为两个陆地是x坐标对称,这样能转化得到负轴的位置
            pos.x = land_sign * pos.x;
            return pos;
        }

        public void AddRole(RoleModel role)
        {
            roles[GetEmptyNumber()] = role;
        }

        //离开陆地
        public RoleModel DeleteRoleByName(string role_name)
        {
            for (int i = 0; i < roles.Length; i++)
            {
                if (roles[i] != null && roles[i].GetName() == role_name)
                {
                    RoleModel role = roles[i];
                    roles[i] = null;
                    return role;
                }
            }
            return null;
        }

        public int[] GetRoleNum()
        {
            //count[0]是牧师数,count[1]是魔鬼数
            int[] count = { 0, 0 };
            for (int i = 0; i < roles.Length; i++)
            {
                if (roles[i] != null)
                {
                    if (roles[i].GetSign() == 0)
                        count[0]++;
                    else
                        count[1]++;
                }
            }
            return count;
        }

        public void Reset()
        {
            roles = new RoleModel[6];
        }
    }

    public class BoatModel
    {
        GameObject boat;
        Vector3[] start_empty_pos;                                    // 船在开始陆地的空位位置
        Vector3[] end_empty_pos;                                      // 船在结束陆地的空位位置
        Move move;
        Click click;
        int boat_sign = 1;                                            // 船在开始还是结束陆地(1-开始、-1-结束)
        RoleModel[] roles = new RoleModel[2];                         // 船上的角色

        public BoatModel()
        {
            boat = Object.Instantiate(Resources.Load("Prefabs/Boat", typeof(GameObject)), new Vector3(4, 0, 30), Quaternion.identity) as GameObject;
            boat.name = "boat";
            move = boat.AddComponent(typeof(Move)) as Move;
            click = boat.AddComponent(typeof(Click)) as Click;
            click.SetBoat(this);
            // 注意船在开始岸和结束岸的位置应该是相反的(如本文实现即固定船的右边为第一个空位,船的左边为第二个空位)
            start_empty_pos = new Vector3[] { new Vector3(5, 1, 30), new Vector3(3, 1, 30) };
            end_empty_pos = new Vector3[] { new Vector3(-3, 1, 30), new Vector3(-5, 1, 30) };
        }

        public bool IsEmpty()//用于判断船能不能出发
        {
            for (int i = 0; i < roles.Length; i++)
            {
                if (roles[i] != null)
                    return false;
            }
            return true;
        }

        public void BoatMove()
        {
            if (boat_sign == -1)
            {
                move.MovePosition(new Vector3(4, 0, 30));
                boat_sign = 1;
            }
            else
            {
                move.MovePosition(new Vector3(-4, 0, 30));
                boat_sign = -1;
            }
        }

        public int GetBoatSign()
        {
            return boat_sign;
        }

        public RoleModel DeleteRoleByName(string role_name)
        {
            for (int i = 0; i < roles.Length; i++)
            {
                if (roles[i] != null && roles[i].GetName() == role_name)
                {
                    RoleModel role = roles[i];
                    roles[i] = null;
                    return role;
                }
            }
            return null;
        }

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

        public Vector3 GetEmptyPosition()
        {
            Vector3 pos;
            if (boat_sign == -1)
                pos = end_empty_pos[GetEmptyNumber()];
            else
                pos = start_empty_pos[GetEmptyNumber()];
            return pos;
        }

        public void AddRole(RoleModel role)
        {
            roles[GetEmptyNumber()] = role;
        }

        public GameObject GetBoat()
        {
            return boat;
        }

        public int[] GetRoleNum()
        {
            int[] count = { 0, 0 };
            for (int i = 0; i < roles.Length; i++)
            {
                if (roles[i] == null)
                    continue;
                if (roles[i].GetSign() == 0)
                    count[0]++;
                else
                    count[1]++;
            }
            return count;
        }

        public void Reset()
        {
            if (boat_sign == -1)
                BoatMove();
            roles = new RoleModel[2];
        }
    }

    public class RoleModel
    {
        GameObject role;
        int role_sign;             //0为牧师,1为恶魔
        Click click;
        bool on_boat;              //是否在船上       
        Move move;
        LandModel land_model = (SSDirector.GetInstance().CurrentScenceController as Controllor).start_land;

        public RoleModel(string role_name)
        {
            if (role_name == "priest")
            {
                role = Object.Instantiate(Resources.Load("Prefabs/Priest", typeof(GameObject)), Vector3.zero, Quaternion.Euler(0, -90, 0)) as GameObject;
                role_sign = 0;
            }
            else
            {
                role = Object.Instantiate(Resources.Load("Prefabs/Devil", typeof(GameObject)), Vector3.zero, Quaternion.Euler(0, -90, 0)) as GameObject;
                role_sign = 1;
            }
            move = role.AddComponent(typeof(Move)) as Move;
            click = role.AddComponent(typeof(Click)) as Click;
            click.SetRole(this);//click的作用对象是本身
        }

        public int GetSign()
        {
            return role_sign;
        }
        public LandModel GetLandModel()
        {
            return land_model;
        }
        public string GetName()
        {
            return role.name;
        }
        public bool IsOnBoat()
        {
            return on_boat;
        }
        public void SetName(string name)
        {
            role.name = name;
        }
        public void SetPosition(Vector3 pos)
        {
            role.transform.position = pos;
        }

        public void Move(Vector3 vec)
        {
            move.MovePosition(vec);
        }

        public void GoLand(LandModel land)
        {
            role.transform.parent = null;
            land_model = land;
            on_boat = false;
        }

        public void GoBoat(BoatModel boat)
        {
            role.transform.parent = boat.GetBoat().transform;
            land_model = null;
            on_boat = true;
        }

        public void Reset()
        {
            land_model = (SSDirector.GetInstance().CurrentScenceController as Controllor).start_land;
            GoLand(land_model);
            SetPosition(land_model.GetEmptyPosition());
            land_model.AddRole(this);
        }
    }

    public class Move : MonoBehaviour
    {
        float move_speed = 100;                   //移动速度
        int move_sign = 0;                        //0是不动,1水平移动,2竖直移动
        Vector3 end_pos;
        Vector3 middle_pos;

        void Update()
        {
            if (move_sign == 1)//水平移动
            {
                transform.position = Vector3.MoveTowards(transform.position, middle_pos, move_speed * Time.deltaTime);
                if (transform.position == middle_pos)
                    move_sign = 2;//竖直移动
            }
            else if (move_sign == 2)//竖直移动
            {
                transform.position = Vector3.MoveTowards(transform.position, end_pos, move_speed * Time.deltaTime);
                if (transform.position == end_pos)
                    move_sign = 0;//不动
            }
        }
        public void MovePosition(Vector3 position)
        {
            end_pos = position;
            if (position.y == transform.position.y)
            {       //y相同,船只需要水平移动        
                move_sign = 2;
            }
            else if (position.y < transform.position.y)
            {       //目的地的y更低,涉及到垂直方向下降,角色从陆地到船
                middle_pos = new Vector3(position.x, transform.position.y, position.z);
                move_sign = 1;
            }
            else
            {       //目的地的y更高,涉及到垂直方向上升,角色从船到陆地
                middle_pos = new Vector3(transform.position.x, position.y, position.z);
                move_sign = 1;
            }
        }
    }

    public class Click : MonoBehaviour
    {
        IUserAction action;
        RoleModel role = null;
        BoatModel boat = null;
        public void SetRole(RoleModel role)//如果点击的是角色
        {
            this.role = role;
        }
        public void SetBoat(BoatModel boat)//如果点击的是船
        {
            this.boat = boat;
        }
        void Start()
        {
            action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
        }
        void OnMouseDown()
        {
            if (boat == null && role == null)
                return;
            if (boat != null)//船上有人
                action.MoveBoat();
            else if (role != null)//点的是角色
                action.MoveRole(role);
        }
    }
}

全局控制:

此处需要为所有需要变化的模型(包括船、人和站人的两岸)创建相应的变量,并且利用这些变量定义好在mygame中声明过的所有函数(需要在代码开头加上using mygame)。

public class Controllor : MonoBehaviour, ISceneController, IUserAction
{
    public LandModel start_land;            //开始陆地
    public LandModel end_land;              //结束陆地
    public BoatModel boat;                  //船
    private RoleModel[] roles;              //角色
    UserGUI user_gui;

    void Start()
    {
        SSDirector director = SSDirector.GetInstance();
        director.CurrentScenceController = this;
        user_gui = gameObject.AddComponent<UserGUI>() as UserGUI;
        LoadResources();//初始化所有模型
    }

    public void LoadResources()
    {             //创建水,陆地,角色,船
        GameObject water = Instantiate(Resources.Load("Prefabs/Water", typeof(GameObject)), new Vector3(0, -1, 30), Quaternion.identity) as GameObject;
        water.name = "water";
        start_land = new LandModel("start");
        end_land = new LandModel("end");
        boat = new BoatModel();
        roles = new RoleModel[6];

        for (int i = 0; i < 3; i++)
        {
            RoleModel role = new RoleModel("priest");
            role.SetName("priest" + i);
            role.SetPosition(start_land.GetEmptyPosition());//找陆地的空位放牧师
            role.GoLand(start_land);
            start_land.AddRole(role);
            roles[i] = role;//记录在roles[]中
        }

        for (int i = 0; i < 3; i++)
        {
            RoleModel role = new RoleModel("devil");
            role.SetName("devil" + i);
            role.SetPosition(start_land.GetEmptyPosition());
            role.GoLand(start_land);
            start_land.AddRole(role);
            roles[i + 3] = role;
        }
    }

    public void MoveBoat()
    {                 //移动船
        if (boat.IsEmpty() || user_gui.sign != 1)
            return;
        boat.BoatMove();//船非空、游戏开始且未结束时才能移动
    }

    public void MoveRole(RoleModel role)
    {   // 游戏开始且未结束时才能移动角色
        if (user_gui.sign != 1)
            return;

        if (role.IsOnBoat())
        {               // 游戏对象在船上
            LandModel land;
            if (boat.GetBoatSign() == -1)
                land = end_land;
            else
                land = start_land;
            boat.DeleteRoleByName(role.GetName());
            role.Move(land.GetEmptyPosition());//模型的移动
            role.GoLand(land);//此处仅仅是修改参数
            land.AddRole(role);
        }
        else
        {                           // 游戏对象不在船上
            LandModel land = role.GetLandModel();
            // 船没有空位,也不是船停靠的陆地,就不上船
            if (boat.GetEmptyNumber() == -1 || land.GetLandSign() != boat.GetBoatSign())
                return;

            land.DeleteRoleByName(role.GetName());//陆地不再记录该对象
            role.Move(boat.GetEmptyPosition());//对象的实体移动到船上
            role.GoBoat(boat);
            boat.AddRole(role);//船记录该对象
        }

        user_gui.sign = Check();
        if (user_gui.sign == 2)
        {
            Restart();
        }
    }

    public void Restart()
    {
        start_land.Reset();
        end_land.Reset();
        boat.Reset();
        for (int i = 0; i < roles.Length; i++)
        {
            roles[i].Reset();
        }
    }

    public int Check()
    {
        int start_priest = (start_land.GetRoleNum())[0];
        int start_devil = (start_land.GetRoleNum())[1];
        int end_priest = (end_land.GetRoleNum())[0];
        int end_devil = (end_land.GetRoleNum())[1];

        if (end_priest + end_devil == 6)        // 获胜
            return 3;

        int[] boat_role_num = boat.GetRoleNum();
        if (boat.GetBoatSign() == 1)
        {          // 在开始岸和船上的角色
            start_priest += boat_role_num[0];
            start_devil += boat_role_num[1];
        }
        else
        {                                // 在结束岸和船上的角色
            end_priest += boat_role_num[0];
            end_devil += boat_role_num[1];
        }

        if ((start_priest > 0 && start_priest < start_devil) || (end_priest > 0 && end_priest < end_devil))
        { //失败
            return 2;
        }

        return 1;                               //未完成
    }
}

三,运行效果

视频链接如下:

魔鬼与牧师_哔哩哔哩_bilibili

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值