用Unity3D实现简单的牧师与魔鬼游戏

用Unity3D实现简单的牧师与魔鬼游戏

项目地址

牧师与魔鬼游戏

完成效果图

用Unity3D实现简单的牧师与魔鬼游戏_第1张图片

实现心得

游戏所使用的是MVC模式开发。

遵循的动作表为:

动作 条件
开船 船上至少有一个角色(牧师或魔鬼)
牧师在左岸上船 船上至少有一个空位且在左岸,左岸至少有一个牧师,船在左岸
牧师在左岸下船 船上至少有一个牧师且在左岸
恶魔在左岸上船 船上至少有一个空位且在左岸,左岸至少有一个魔鬼,船在左岸
恶魔在左岸下船 船上至少有一个恶魔且在左岸
牧师在右岸上船 船上至少有一个空位且在右岸,右岸至少有一个牧师,船在右岸
牧师在右岸下船 船上至少有一个牧师且船在右岸
恶魔在右岸上船 船上至少有一个空位且在右岸,右岸至少有一个恶魔,船在右岸
恶魔在右岸下船 船上至少有一个恶魔且船在右岸

首先确定游戏界面组成部分:

用Unity3D实现简单的牧师与魔鬼游戏_第2张图片

由游戏界面组成部分可以设计模式开发结构的视图部分:

可以看到,游戏界面由五个部分组成:船、岸、角色、河流、功能提示框,所以首先可以创建船视图BoatView、岸视图ShoreView、角色视图RoleView、河流视图RiverView和功能视图FunctionView五个视图类,因为移动也需要在界面中展现,所以还可以创建一个移动视图MoveView,从而很简单地确定了MVC结构的视图部分:

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

namespace PriestsAndDevils {
    /* 船视图 */
    public class BoatView {
        private GameObject boat; // 创建船视图需要的游戏对象

        /* 创建船视图 */
        public BoatView() {
            boat = Object.Instantiate(Resources.Load ("Perfabs/Boat", typeof(GameObject)), new Vector3(2.5F, -1, 0), Quaternion.identity, null) as GameObject;
			boat.name = "Boat";
        }

        public GameObject GetGameObj() {
            return boat;
        }

        /* 为游戏对象添加移动控制器 */
        public MoveController AddMoveScript() {
            MoveController moveScript = boat.AddComponent(typeof(MoveController)) as MoveController;
            return moveScript;
        }

        /* 为游戏对象添加点击控制器 */
        public void AddClickScript() {
            boat.AddComponent(typeof(ClickController));
        }
    }

    /* 岸视图 */
    public class ShoreView {
        private GameObject shore; // 创建岸视图需要的游戏对象

        /* 根据岸的边侧创建游戏对象和视图 */
        public ShoreView(int side, string sideName) {
            shore = Object.Instantiate(Resources.Load("Perfabs/Shore", typeof(GameObject)), new Vector3(7 * side, -1, 0), Quaternion.identity, null) as GameObject;
            shore.name = sideName;
        }
    }

    /* 角色视图 */
    public class RoleView {
        private GameObject role; // 创建角色视图需要的游戏对象

        /* 根据角色类型和序号创建游戏对象和视图 */
        public RoleView(string roleName, int number) {
            role = Object.Instantiate(Resources.Load ("Perfabs/" + roleName, typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
            role.name = roleName + number;
        }

        /* 为游戏对象添加移动控制器 */
        public MoveController AddMoveScript() {
            MoveController moveScript = role.AddComponent(typeof(MoveController)) as MoveController;
            return moveScript;
        }

        /* 为游戏对象添加点击控制器 */
        public ClickController AddClickScript() {
            return role.AddComponent(typeof(ClickController)) as ClickController;
        }

        /* 获取对象名字 */
        public string GetName() {
            return role.name;
        }

        /* 设置角色父对象 */
        public void SetRoleParentTrans(Transform trans) {
            role.transform.parent = trans;
        }

        /* 设置角色位置 */
        public void SetPos(Vector3 pos) {
            role.transform.position = pos;
        }
    }

    /* 移动视图 */
    public class MoveView : MonoBehaviour {
        /* 在界面上根据移动位置和速度呈现物体移动效果 */
        public void MoveTo(Vector3 pos, int speed) {
            transform.position = Vector3.MoveTowards(transform.position, pos, speed * Time.deltaTime);
        }
    }

    /* 河流视图 */
    public class RiverView {
        private GameObject river; // 创建河流视图需要的游戏对象

        /* 创建河流视图 */
        public RiverView() {
            river = Object.Instantiate(Resources.Load ("Perfabs/River", typeof(GameObject)), new Vector3(0, -1.5F, 0), Quaternion.identity, null) as GameObject;
			river.name = "River";
        }
    }

    /* 功能视图 */
    public class FunctionView : MonoBehaviour {
        private string tipContent; // 提示框显示内容

        public FunctionView() {
            tipContent = "Click and Play the Game";
        }

        public void SetTipContent(string tip) {
            tipContent = tip;
        }

        /* 初始化提示框 */
        public void InitFunctionView() {
            tipContent = "Click and Play the Game";
        }

        void ShowResetButton() {
            GUIStyle resetStyle = new GUIStyle("button");
			resetStyle.fontSize = 20;

            // 按下重置按钮,游戏被初始化
			if (GUI.Button(new Rect(30, 20, 70, 30), "Reset", resetStyle)) {
				SingleObject.getInstance().GetMainController().InitGame();
			}
        }

        void ShowTip(string tipContent) {
            GUIStyle tipStyle = new GUIStyle();

            tipStyle.fontSize = 20;
            GUI.Label(new Rect(Screen.width - 140, 20, 100, 50), "Priest: Yellow\nDevil: Red", tipStyle);

			tipStyle.fontSize = 30;
			tipStyle.alignment = TextAnchor.MiddleCenter;

            // 展示提示内容
			GUI.Label(new Rect(Screen.width / 2 - 50, 70, 100, 50), tipContent, tipStyle);
        }

        void OnGUI() {
            ShowTip(tipContent); // 展现提示框
            ShowResetButton(); // 展现重置按钮
		}

    }
}

由视图部分可以接着设计模型部分和控制器部分:

对于模型部分,因为河流基本没有相关逻辑,功能提示框的逻辑与整个游戏有关,所以首先想到,只为船Boat、岸Shore、角色Role创建三个模型,另外两个部分可以直接创建视图,移动视图也可以创建一个模型,从而使具体的游戏对象可以挂载一个移动控制部分进行移动,所以MVC的模型部分,一共可以创建BoatModelShoreModelRoleModelMoveModel四个模型类:

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

namespace PriestsAndDevils {
    /* 船模型 */
    public class BoatModel {
        private int side;  // 岸边方向,-1表示船在左岸即终点岸,1表示船在右岸即出发岸
        private string [] roleOnBoat = new string [2]{"", ""};  // 船上的角色,要不为牧师或魔鬼,要不为空

        public BoatModel(int side) {
            this.side = side;
        }

        public void SetSide(int side) {
            this.side = side;
        }

        public int GetSide() {
            return side;
        }

        public void SetRoleOnBoat(string [] roles) {
            roleOnBoat = roles;
        }

        public string [] GetRoleOnBoat() {
            return roleOnBoat;
        }
    }

    /* 岸模型 */
    public class ShoreModel {
		private int side;  // 岸边方向,-1表示左岸即终点岸,1表示右岸即出发岸
        private string [] roleOnShore = new string [6]{"", "", "", "", "", ""};  // 岸上的角色,要不为牧师或魔鬼,要不为空

        public ShoreModel(int side) {
            this.side = side;
        }

        public void SetSide(int side) {
            this.side = side;
        }

        public int GetSide() {
            return side;
        }

        public void SetRoleOnShore(string [] roles) {
            roleOnShore = roles;
        }

        public string [] GetRoleOnShore() {
            return roleOnShore;
        }
    }

    /* 角色模型 */
    public class RoleModel {
		private int roleType;  // 角色类型,0表示牧师,1表示魔鬼
        private bool isOnBoat;  // 角色是否在船上

        public RoleModel(int type) {
            roleType = type;
        }

        public void SetRoleType(int type) {
            roleType = type;
        }

        public int GetRoleType() {
            return roleType;
        }

        public void SetIsOnBoat(bool isOnBoat) {
            this.isOnBoat = isOnBoat;
        }

        public bool GetIsOnBoat() {
            return isOnBoat;
        }
    }

    /* 移动模型 */
    public class MoveModel {
        private static int speed = 25;  // 移动速度
        private Vector3 middlePos;  // 移动的中间位置,因为角色以折线形式运动,所以会有一个先移动到的中间位置
        private Vector3 endPos;  // 移动的最后位置,角色或船最后移动到的位置
		private int state;	// 移动状态,0表示没有移动,1表示正在移动到中间位置,2表示正在从中间位置移动到最后位置

        public int GetSpeed() {
            return speed;
        }

        public void SetMiddlePos(Vector3 pos) {
            middlePos = pos;
        }

        public Vector3 GetMiddlePos() {
            return middlePos;
        }
        
        public void SetEndPos(Vector3 pos) {
            endPos = pos;
        }

        public Vector3 GetEndPos() {
            return endPos;
        }

        public void SetState(int state) {
            this.state = state;
        }

        public int GetState() {
            return state;
        }
    }
}

对应的控制类也要创建,所以首先创建BoatControllerShoreControllerRoleControllerMoveController这四个控制类,和模型部分以及视图部分相对应,由于游戏对象需要点击才能移动,所以也创建一个ClickController类,处理点击事件,整个游戏可以创建一个主控制类MainController,其中包含所有具体的游戏角色对应的控制器,比如船控制器、左岸控制器、右岸控制器、以及所有角色的6个控制器,最后创建一个单例对象类SingleObject,里面有唯一一个主控制类,用来使其它类能获取主控制类的唯一实例从而向主控制类传递信息,完成相应逻辑:

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

namespace PriestsAndDevils {
	/* 河岸岸边,左岸终点岸为-1,右岸出发岸为1 */
	enum ShoreSide {
		Left = -1, Right = 1
	};

	/* 移动状态,0表示没有移动,1表示正在移动到中间位置,2表示正在从中间位置移动到最后位置 */
	enum MoveState {
		Not_Moving = 0, Moving_To_Middle = 1, Moving_To_End = 2
	};

	/* 角色类型,0为牧师,1为魔鬼 */
	enum RoleType {
		NoRole = -1, Priest = 0, Devil = 1
	};

	/* 游戏状态,0为正在进行,1为游戏失败,2为游戏成功 */
	enum GameState {
		Playing = 0, Lose = 1, Win = 2
	};

	/* 移动控制器,一般会挂载到某个具体的游戏对象上 */
	public class MoveController : MonoBehaviour {
		private MoveModel moveModel;
		private MoveView moveView;

		void Start() {
			moveModel = new MoveModel();
			moveView = gameObject.AddComponent() as MoveView;
		}

		void Update() {
			int state = moveModel.GetState();
			Vector3 middlePos = moveModel.GetMiddlePos();
			Vector3 endPos = moveModel.GetEndPos();
			int speed = moveModel.GetSpeed();
			// 根据速度、中间位置、终点位置、移动状态来移动物体
			Move(state, middlePos, endPos, speed);
		}

		/* 初始化控制器,设置物体不移动 */
		public void InitMoveController() {
			moveModel.SetState((int)MoveState.Not_Moving);
		}

		/* 根据速度、中间位置、终点位置、移动状态来移动物体 */
		public void Move(int state, Vector3 middlePos, Vector3 endPos, int speed) {
			switch (state) {
				case (int)MoveState.Not_Moving:
					break;
				case (int)MoveState.Moving_To_Middle:
					// 移动物体到中间位置,移动到后,设置物体的下一个状态为移动到终点
					moveView.MoveTo(middlePos, speed);
					if (transform.position == middlePos) {
						moveModel.SetState((int)MoveState.Moving_To_End);
					}
					break;
				case (int)MoveState.Moving_To_End:
					// 移动物体到终点位置,移动到后,设置物体的下一个状态为不移动
					moveView.MoveTo(endPos, speed);
					if (transform.position == endPos) {
						moveModel.SetState((int)MoveState.Not_Moving);
					}
					break;
			}
		}

		/* 设置物体要移动到的位置 */
		public void SetMovePos(Vector3 pos) {
			// 设置物体开始往中间位置移动
			moveModel.SetState((int)MoveState.Moving_To_Middle);
			// 设置最终移动位置为pos
			moveModel.SetEndPos(pos);
			// 如果要移动到的位置的y坐标等于物体所在位置的y坐标,说明是船在移动
			if (pos.y == transform.position.y) {
				// 船沿直线运动,所以中间位置也可设为最终位置
				moveModel.SetMiddlePos(pos);
				// 移动状态可以直接设为往终点移动
				moveModel.SetState((int)MoveState.Moving_To_End);
			}
			// 如果要移动到的位置的y坐标小于物体所在位置的y坐标,说明是物体从岸上移动到船上
			else if (pos.y < transform.position.y) {
				moveModel.SetMiddlePos(new Vector3(pos.x, transform.position.y, pos.z));
			}
			// 如果要移动到的位置的y坐标小于物体所在位置的y坐标,说明是物体从船上移动到岸上
			else {
				moveModel.SetMiddlePos(new Vector3(transform.position.x, pos.y, pos.z));
			}
		}
	}

	/* 点击控制器,一般会挂载在某个游戏对象上 */
	public class ClickController : MonoBehaviour {
		RoleController clickRole; // 如果点击的是角色,那么记录点击的对象的控制器

		public void SetClickRole(RoleController role) {
			clickRole = role;
		}

		void OnMouseDown() {
			// 如果鼠标点击的对象是船,那么移动船只
			if (gameObject.name == "Boat") {
				SingleObject.getInstance().GetMainController().MoveBoat();
			} else {
				// 如果鼠标点击的对象是角色,那么移动对象
				SingleObject.getInstance().GetMainController().MoveRole(clickRole);
			}
		}
	}

	public class BoatController {
		// 船在左岸时装载角色的位置
		private static Vector3 [] leftLoadPos = new Vector3 [] {new Vector3(-3, -0.5F, 0), new Vector3(-2, -0.5F, 0)};
		// 船在右岸时装载角色的位置
		private static Vector3 [] rightLoadPos =  new Vector3 [] {new Vector3(2, -0.5F, 0), new Vector3(3, -0.5F, 0)};

		private BoatModel boatModel; // 船模型
		private BoatView boatView; // 船视图
		private MoveController moveController; // 移动控制器

		public BoatController() {
			// 船开始在右岸
			boatModel = new BoatModel((int)ShoreSide.Right);
			boatView = new BoatView();
			// 添加移动控制器
			moveController = boatView.AddMoveScript();
			// 添加点击控制器
			boatView.AddClickScript();
		}

		/* 初始化船控制器 */
		public void InitBoatController() {
			// 先初始化移动控制器
			moveController.InitMoveController();
			// 获取船在哪边
			int side = boatModel.GetSide();
			// 在左边的话,船会回到右边去
			if (side == (int)ShoreSide.Left) {
				MoveToOtherSide();
			}
			// 初始化船模型没有乘客
			boatModel.SetRoleOnBoat(new string [2]{"", ""});
		}

		// 获取船所在的岸边
		public int GetBoatSide() {
			return boatModel.GetSide();
		}

		// 获取船的游戏对象
		public GameObject GetBoat() {
			return boatView.GetGameObj();
		}

		/* 将船移动到岸的另一侧 */
		public void MoveToOtherSide() {
			// 先获取船在哪边
			int side = boatModel.GetSide();
			// 移动船到另一边
			if (side == (int)ShoreSide.Left) {
				moveController.SetMovePos(new Vector3(2.5F, -1, 0));
				boatModel.SetSide((int)ShoreSide.Right);
			} else {
				moveController.SetMovePos(new Vector3(-2.5F, -1, 0));
				boatModel.SetSide((int)ShoreSide.Left);
			}
		}

		/* 获取船上空闲的位置 */
		public int GetFreeIndex() {
			string [] roleOnBoat = boatModel.GetRoleOnBoat();
			// 按照顺序获取船上空闲的位置
			if (roleOnBoat[0] == "") {
				return 0;
			} else if (roleOnBoat[1] == "") {
				return 1;
			} else {
				// 如果没有空闲位置返回-1
				return -1;
			}
		}

		/* 判断船上是否没有角色 */
		public bool IsNoRole() {
			string [] roleOnBoat = boatModel.GetRoleOnBoat();
			if (roleOnBoat[0] != "" || roleOnBoat[1] != "") {
				return false;
			} else {
				return true;
			}
		}

		/* 根据船靠岸的方向获取对应方向里船上空闲位置 */
		public Vector3 GetFreePosition() {
			string [] roleOnBoat = boatModel.GetRoleOnBoat();
			int freeIndex = GetFreeIndex();
			// 如果没有空闲位置,直接返回
			if (freeIndex < 0) {
				return new Vector3(-1, -1, -1);
			}
			int side = boatModel.GetSide();

			if (side == (int)ShoreSide.Left) {
				return leftLoadPos[freeIndex];
			} else {
				return rightLoadPos[freeIndex];
			}
		}

		/* 将角色放到船上 */
		public void PutRoleOnBoat(string roleName) {
			// 获取船上空闲位置
			int freeIndex = GetFreeIndex();
			// 如果没有空闲位置,直接返回
			if (freeIndex < 0) {
				return;
			}
			// 利用角色名字将角色放入船
			string [] roleOnBoat = boatModel.GetRoleOnBoat();
			roleOnBoat[freeIndex] = roleName;
		}

		/* 将角色离开船上 */
		public void PutRoleOffBoat(string roleName) {
			string [] roleOnBoat = boatModel.GetRoleOnBoat();
			// 如果船的相应位置有角色名字,将其置为空
			for (int i = 0; i < roleOnBoat.Length; i++) {
				if (roleOnBoat[i] == roleName) {
					roleOnBoat [i] = "";
					break;
				}
			}
		}

		/* 获取船上的各个角色的数目 */
		public int[] GetRoleCountOnBoat() {
			int [] count = {0, 0};
			string [] roleOnBoat = boatModel.GetRoleOnBoat();
			for (int i = 0; i < roleOnBoat.Length; i++) {
				string roleName = roleOnBoat[i];
				if (roleName == "") continue;
				if (roleName.Remove(roleName.Length - 1, 1) == "Priest") {
					count[0]++;
				} else {
					count[1]++;
				}
			}
			return count;
		}
	}

	/* 岸控制器 */
	public class ShoreController {
		private ShoreModel shoreModel; // 岸模型
		private ShoreView shoreView; // 岸视图

		// 右岸上可以放角色的位置,左岸的位置只需将坐标的x数值乘-1即可
		private static Vector3 [] positions = new Vector3[] {new Vector3(4.5F, 0.25F, 0), new Vector3(5.5F, 0.25F, 0), new Vector3(6.5F, 0.25F, 0), 
				new Vector3(7.5F, 0.25F, 0), new Vector3(8.5F, 0.25F, 0), new Vector3(9.5F, 0.25F, 0)};

		public ShoreController(int side, string sideName) {
			shoreModel = new ShoreModel(side);
			shoreView = new ShoreView(side, sideName);
		}

		public void InitShoreController() {
			shoreModel.SetRoleOnShore(new string [6]{"", "", "", "", "", ""});
		}

		/* 获取岸边 */
		public int GetShoreSide() {
			return shoreModel.GetSide();
		}

		/* 获取岸上空闲的位置 */
		public int GetFreeIndex() {
			string [] roleOnShore = shoreModel.GetRoleOnShore();
			for (int i = 0; i < roleOnShore.Length; i++) {
				if (roleOnShore[i] == "") {
					return i;
				}
			}
			// 如果没有空闲位置返回-1
			return -1;
		}

		/* 根据船靠岸的方向获取对应方向里岸上空闲位置 */
		public Vector3 GetFreePosition() {
			int freeIndex = GetFreeIndex();
			// 如果没有空闲位置,直接返回
			if (freeIndex < 0) {
				return new Vector3(-1, -1, -1);
			}
			Vector3 pos = positions[freeIndex];
			// 坐标的x数值乘1是右边岸上的位置,乘-1即可得到左边岸上的位置
			pos.x *= shoreModel.GetSide();
			return pos;
		}

		/* 将角色放到岸上 */
		public void PutRoleOnShore(string roleName) {
			// 获取岸上空闲位置
			int freeIndex = GetFreeIndex();
			// 如果没有空闲位置,直接返回
			if (freeIndex < 0) {
				return;
			}
			// 利用角色名字将角色放上岸
			string [] roleOnShore = shoreModel.GetRoleOnShore();
			roleOnShore[freeIndex] = roleName;
		}

		/* 将角色离开岸上 */
		public void PutRoleOffShore(string roleName) {	// 0->priest, 1->devil
			string [] roleOnShore = shoreModel.GetRoleOnShore();
			// 如果岸的相应位置有角色名字,将其置为空
			for (int i = 0; i < roleOnShore.Length; i++) {
				if (roleOnShore[i] == roleName) {
					roleOnShore [i] = "";
					break;
				}
			}
		}

		/* 获取岸上的各个角色的数目 */
		public int [] GetRoleCountOnShore() {
			int [] count = {0, 0};
			string [] roleOnShore = shoreModel.GetRoleOnShore();
			for (int i = 0; i < roleOnShore.Length; i++) {
				string roleName = roleOnShore[i];
				if (roleName == "") continue;
				if (roleName.Remove(roleName.Length - 1, 1) == "Priest") {
					count[0]++;
				} else {
					count[1]++;
				}
			}
			return count;
		}
	}

	/* 角色控制器 */
	public class RoleController {
		private RoleModel roleModel; // 角色模型
		private RoleView roleView; // 角色视图

		private MoveController moveController; // 移动控制器
		private ClickController clickController; // 点击控制器

		ShoreController shoreController; // 角色所在相应岸上的岸控制器

		public RoleController(int roleType, string roleName, int number) {
			roleModel = new RoleModel(roleType);
			roleView = new RoleView(roleName, number);
			
			// 添加移动控制器
			moveController = roleView.AddMoveScript();
			// 添加点击控制器,并记录点击对象的控制器
			clickController = roleView.AddClickScript();
			clickController.SetClickRole(this);
		}

		public void InitRoleController() {
			// 初始化移动控制器
			moveController.InitMoveController();
			// 获取主控制器中的右岸控制器
			shoreController = (SingleObject.getInstance().GetMainController() as MainController).GetRightShore();
			// 将角色放到右岸上
			PutRoleOnShore(shoreController);
			SetRolePos(shoreController.GetFreePosition());
			// 右岸控制器中关于角色的信息也被初始化
			shoreController.PutRoleOnShore(this.GetRoleViewName());
		}

		public ShoreController GetShoreController() {
			return shoreController;
		}

		/* 设置角色游戏对象位置 */
		public void SetRolePos(Vector3 pos) {
			roleView.SetPos(pos);
		}

		/* 将角色移动到指定位置 */
		public void MoveToPosition(Vector3 endPosination) {
			moveController.SetMovePos(endPosination);
		}

		/* 获取角色类型 */
		public int GetRoleModelType() {
			return roleModel.GetRoleType();
		}

		/* 获取角色游戏对象名字 */
		public string GetRoleViewName() {
			return roleView.GetName();
		}

		/* 获取游戏对象是否在船上 */
		public bool IsOnBoat() {
			return roleModel.GetIsOnBoat();
		}

		/* 将角色放到船上 */
		public void PutRoleOnBoat(BoatController boatController) {
			// 设置角色不在岸上,所以也没有岸控制器
			shoreController = null;
			// 设置角色在船上
			roleModel.SetIsOnBoat(true);
			// 设置角色游戏对象的父对象是船只
			roleView.SetRoleParentTrans(boatController.GetBoat().transform);
		}

		/* 将角色放到岸上 */
		public void PutRoleOnShore(ShoreController shoreController) {
			// 设置角色在相应岸上的岸控制器
			this.shoreController = shoreController;
			// 设置角色不在船上
			roleModel.SetIsOnBoat(false);
			// 设置角色游戏对象的父对象为没有
			roleView.SetRoleParentTrans(null);
		}
	}

	/* 主控制器 */
	public class MainController : MonoBehaviour {

		private BoatController boat; // 船控制器
		private ShoreController leftShore; // 左岸控制器
		private ShoreController rightShore; // 右岸控制器
		private RoleController [] roles; // 所有角色的控制器
		private RiverView riverView; // 河流视图
		private FunctionView functionView; // 功能视图,包括提示框,游戏信息等

		private int gameState = (int)GameState.Playing; // 游戏状态,0表示游戏正在进行,1表示游戏失败,2表示游戏成功

		void Awake() {
			// 设置单例对象中只有唯一一个主控制器
			SingleObject instance = SingleObject.getInstance();
			instance.SetMainController(this);
			// 展现游戏视图
			ShowView();
		}

		/* 通过创建控制器的方式初始化游戏视图 */
		public void ShowView() {
			boat = new BoatController();
			leftShore = new ShoreController((int)ShoreSide.Left, "LeftShore");
			rightShore = new ShoreController((int)ShoreSide.Right, "RightShore");
			roles = new RoleController[6];
			CreateRolesController();
			riverView = new RiverView();
			functionView = gameObject.AddComponent() as FunctionView;
		}

		/* 为每个角色创建控制器并初始化视图 */
		private void CreateRolesController() {
			for (int i = 0; i < 3; i++) {
				roles[i] = new RoleController(0, "Priest", i + 1);
				roles[i].SetRolePos(rightShore.GetFreePosition());
				roles[i].PutRoleOnShore(rightShore);
				rightShore.PutRoleOnShore(roles[i].GetRoleViewName());
			}

			for (int i = 0; i < 3; i++) {
				roles[i + 3] = new RoleController(1, "Devil", i + 1);
				roles[i + 3].SetRolePos(rightShore.GetFreePosition());
				roles[i + 3].PutRoleOnShore(rightShore);
				rightShore.PutRoleOnShore(roles[i + 3].GetRoleViewName());
			}
		}

		/* 通过初始化控制器和视图的方式初始化游戏 */
		public void InitGame() {
			boat.InitBoatController();
			leftShore.InitShoreController();
			rightShore.InitShoreController();
			for (int i = 0; i < roles.Length; i++) {
				roles[i].InitRoleController();
			}
			functionView.InitFunctionView();
			gameState = (int)GameState.Playing;
		}

		public ShoreController GetRightShore() {
			return rightShore;
		}

		/* 根据不同的游戏状态返回不同的提示框内容 */
		public string StateToTip(int gameState) {
			switch (gameState) {
				case (int)GameState.Playing:
					return "Click and Play the Game";
				case (int)GameState.Lose:
					return "You lose! Click \"Reset\" to play again!";
				case (int)GameState.Win:
					return "You win! Click \"Reset\" to play again!";
				default:
					return "";
			}
		}

		/* 通过船控制器来移动船只 */
		public void MoveBoat() {
			// 如果游戏状态不是正在进行,那么不移动
			if (gameState != (int)GameState.Playing) {
				return;
			}
			// 如果船上没有角色,不能开船
			if (boat.IsNoRole()) {
				return;
			} else {
				// 否则直接将船开到另一边
				boat.MoveToOtherSide();
			}
			// 检查游戏状态
			gameState = CheckGameState();
			// 显示相应提示
			functionView.SetTipContent(StateToTip(gameState));
		}

		/* 通过角色控制器移动角色 */
		public void MoveRole(RoleController role) {
			// 如果游戏状态不是正在进行,那么不移动
			if (gameState != (int)GameState.Playing) {
				return;
			}
			// 如果角色在船上,那么要将角色放上相应岸边
			if (role.IsOnBoat()) {
				// 获取船所在的岸边
				ShoreController boatShore;
				if (boat.GetBoatSide() == (int)ShoreSide.Left) {
					boatShore = leftShore;
				} else {
					boatShore = rightShore;
				}

				/* 设置船和岸以及角色控制器以将角色放到岸上 */
				role.MoveToPosition(boatShore.GetFreePosition());
				boat.PutRoleOffBoat(role.GetRoleViewName());
				role.PutRoleOnShore(boatShore);
				boatShore.PutRoleOnShore(role.GetRoleViewName());
			} else {
				// 否则角色在岸上,要将角色放到船上,首先获取角色所在岸边的控制器
				ShoreController boatShore = role.GetShoreController();

				// 如果船上没有位置,或者船和物体所在的岸不是一边,直接返回
				if (boat.GetFreeIndex() == -1 || boatShore.GetShoreSide() != boat.GetBoatSide()) {
					return;
				}

				/* 设置船和岸以及角色控制器以将角色放到船上 */
				role.MoveToPosition(boat.GetFreePosition());
				boat.PutRoleOnBoat(role.GetRoleViewName());
				role.PutRoleOnBoat(boat);
				boatShore.PutRoleOffShore(role.GetRoleViewName());
			}
			// 检查游戏状态
			gameState = CheckGameState();
			// 显示相应提示
			functionView.SetTipContent(StateToTip(gameState));
		}

		/* 检查游戏状态 */
		public int CheckGameState() {
			// 初始化左边和右边的牧师和魔鬼数量都为0
			int leftPriest = 0, rightPriest = 0;
			int leftDevil = 0, rightDevil = 0;

			// 获取左岸和右岸以及船上的牧师和魔鬼的数量数组
			int [] leftShoreCount = leftShore.GetRoleCountOnShore();
			int [] rightShoreCount = rightShore.GetRoleCountOnShore();
			int [] boatCount = boat.GetRoleCountOnBoat();

			// 如果船在左边,那么左边的牧师和魔鬼的数量为左岸数量加上船里的数量
			if (boat.GetBoatSide() == (int)ShoreSide.Left) {
				leftPriest = leftShoreCount[0] + boatCount[0];
				leftDevil = leftShoreCount[1] + boatCount[1];
				rightPriest = rightShoreCount[0];
				rightDevil = rightShoreCount[1]; 
			} else {
				// 如果船在右边,那么右边的牧师和魔鬼的数量为右岸数量加上船里的数量
				leftPriest = leftShoreCount[0];
				leftDevil = leftShoreCount[1];
				rightPriest = rightShoreCount[0] + boatCount[0];
				rightDevil = rightShoreCount[1] + boatCount[1]; 
			}

			// 如果左边牧师加魔鬼的数量等于6且魔鬼全部上岸,那么游戏成功
			if (leftPriest + leftDevil == 6 && leftShoreCount[1] == 3) {
				return (int)GameState.Win;
			} else if (leftPriest < leftDevil && leftPriest > 0) {
				// 如果任意一边牧师的数量小于魔鬼的数量且有牧师,那么游戏失败
				return (int)GameState.Lose;
			} else if (rightPriest < rightDevil && rightPriest > 0) {
				return (int)GameState.Lose;
			} else {
				// 否则游戏正在进行中
				return (int)GameState.Playing;
			}
		}
	}

	/* 单例对象,里面有唯一一个主控制类 */
	public class SingleObject {
		private static SingleObject instance = new SingleObject();
		private MainController mainController;

		public static SingleObject getInstance() {
			return instance;
		}

		public void SetMainController(MainController controller) {
			mainController = controller;
		}

		public MainController GetMainController() {
			return mainController;
		}
	}
}

最后在主文件中,调用主控制器,进行游戏,就实现了一个基本的MVC模式:

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

namespace PriestsAndDevils {
	public class Main : MonoBehaviour {
        void Start() {
            // 创建主控制器
            MainController mainController = gameObject.AddComponent() as MainController;
        }
    }
}

总结: 所以在实现MVC模式时,可以先将最后的效果图画出来,创建相应的视图类,由视图类进行分析,设计相应的模型类和控制器类,基本没有逻辑的可以不创建模型,直接在主控制类中实现相应视图或逻辑控制,最后实现MVC模式的开发。

游戏截图:

正在进行游戏:

用Unity3D实现简单的牧师与魔鬼游戏_第3张图片

游戏失败:

用Unity3D实现简单的牧师与魔鬼游戏_第4张图片

游戏成功:

用Unity3D实现简单的牧师与魔鬼游戏_第5张图片

你可能感兴趣的