本文主要是介绍unity3D游戏——魔鬼与牧师(Devil and Priest)的MVC实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
前言
游戏对象及其对应的预制体
天空盒的制作
定义玩家行为
UML设计图
基于MVC架构的脚本文件
Models
船(Boat)
点击事件(Click)以及接口点击事件的动作(ClickAction)
陆地(Land)
位置(Position)
河流(River)
角色(Role)
Controllers
船控制器(BoatControl)
陆地控制器(LandControl)
角色控制器(RoleControl)
游戏对象的移动(Move)
游戏对象的移动控制器(MoveControl)
用户的操作动作(IUserAction)
场景控制的接口(ISceneController)
导演类(SSDirector)
主导全场的核心控制器(FirstController)
View
用户交互类(UserGUI)
结语
前言
魔鬼与牧师(Devil and Priest)是一个经典的智力解谜游戏,玩家需要帮助三位魔鬼和三位牧师,将所有的牧师带过河,同时还要确保牧师不会被魔鬼吃掉。
下面是游戏的规则和背景故事:
规则:
1. 在河的一边有三个魔鬼、三个牧师以及一条船。
2. 船最多可以承载两个角色(牧师或魔鬼)且必须承载一个角色。
3. 当河的一侧的魔鬼数量大于牧师数量时,魔鬼会吃掉牧师。
4. 玩家需要将所有的牧师都安全地运送到另一侧的河岸,而且保证没有牧师被魔鬼吃掉。
背景故事:
有三个牧师和三个魔鬼被困在一座孤岛上,他们想要回到对岸。但是,这座孤岛上有一条河,河上只有一艘小船,而且这条河非常危险,同时魔鬼会吃掉牧师。他们需要找到一种方法使所有的牧师都能够安全地过河。
玩家需要根据规则,使用合适的策略移动牧师和魔鬼,以确保所有的牧师都能够安全地过河。游戏中的挑战在于玩家需要平衡每一步的移动,避免牧师被魔鬼吃掉,并且在规定的条件下完成游戏。
魔鬼与牧师游戏是一个简单而有趣的逻辑解谜游戏,可以帮助玩家锻炼思维能力和问题解决能力。
首先让我们看一下游戏的演示视频吧:
魔鬼与牧师游戏演示视频
游戏对象及其对应的预制体
我们需要创建魔鬼与牧师的预制体,同时还需要船、陆地、河流,虽然陆地有左岸、右岸的区分,但是我们可以使用同一个预制体来创建两个对象来实现。预制体的制作完全可以按照你自己的需要以及审美来实现,具体的制作预制体的过程请自行百度查阅。
天空盒的制作
为了给游戏提供一个氛围感,我们可以给游戏添加一个天空盒,这一个天空盒我们可以自己导入素材,也可以自行制作。接下来我将介绍如何导入天空盒素材:
- 下载天空盒相关项目,它会包含一些天空资源,例如:
- 使用 skybox 在 unity Store 中搜索
- 选择 Free Assets
- 选择 Fantasy Skybox FREE,然后 Add to my Assets
- 选择 Open in Unity
- 网页自动打开 Editor 的 Package manager
- 选择 Fantasy Skybox FREE 包,点 download 按钮
- 完成后 Import 导入。导入所有资源
- 最后,Fantasy Skybox FREE 目录出现在你的项目中
- CubeMaps(立方图和六面体贴图)
- Panoramics(全景图)
- Scenes(演示场景)
- 在导入素材后我们也可以自己定义天空盒材料
- Assets 上下文菜单 -> create -> Material 起名 mysky
- 在 Inspector 视图中选择 Shader 是上述三种之一,例如:skybox/6side
- 将对应纹理(texture)/图片拖入对应参数
- 而如果要将天空盒分配给您正在处理的场景,请执行以下操作:
- 从菜单栏中选择 Window > Rendering > Lighting Settings。
- 在随后出现的窗口中选择 Environment 选项卡。
- 将新的天空盒材质拖放到 Skybox Material 字段。
如图所示,在本游戏中,这是我所添加的天空盒,具体的天空盒实现以及选择完全按照你自己的想法实现即可。
定义玩家行为
行为 | 效果 | 备注 |
点击魔鬼或者牧师 | 魔鬼以及牧师在船和岸上来回切换 | 必须点击魔鬼或者牧师其中的一个人 |
点击船 | 船在左右岸上来回移动 | 船上至少有一个角色,最多两个角色 |
UML设计图
在本游戏的实现中,我们采用的是MVC分离版本,我们在实现游戏时,将会创建model、controller以及view的脚本文件,分别实现相对应的功能。同时最核心的脚本代码就是controller里面的firstcontroller,同时还有导演类、用户交互类以及GUI类等等,首先我们需要使用UML绘图工具为各个文件的交互关系设计UML图,如图所示:
基于MVC架构的脚本文件
Models
脚本文件主要实现了该游戏的主要几个游戏对象——魔鬼、牧师、船、陆地、河流,以及角色的位置确定、处理点击事件的声明等。下面是Mode各个脚本文件的具体实现:
船(Boat)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Boat
{ //创建一个船对象public GameObject boat;//创建一个数组存储在船上的对象public Role[] roles;//判断该船是否在岸的右边public bool isRight;//创建变量分别存储船上的牧师和魔鬼的数量public int priestCount, devilCount;//Boat的构造函数public Boat(Vector3 position) {//从资源中加载船的预制体,并且使用Instantiate方法创建该预制体的游戏对象boat = GameObject.Instantiate(Resources.Load("prefab/boat", typeof(GameObject))) as GameObject;//将该游戏对象命名为“boat”boat.name = "boat";//接受一个position的输入,将我们创建的船的对象的position设置为接收的positionboat.transform.position = position;//设置船对象在各个方向轴上的缩放比例boat.transform.localScale = new Vector3(2.8f,0.4f,2);//在船上创建一个拥有两个位置的数组roles = new Role[2];//初始化为船刚开始在岸的左边isRight = false;//初始化船上的牧师与魔鬼数量为0priestCount = devilCount = 0;//接受组件盒子碰撞以及点击事件boat.AddComponent<BoxCollider>();boat.AddComponent<Click>();}
}
点击事件(Click)以及接口点击事件的动作(ClickAction)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Click : MonoBehaviour
{//声明变量,用于存储点击事件的处理方法ClickAction clickAction;//用于接收点击事件的参数,并且将其传给类中定义的clickActionpublic void setClickAction(ClickAction clickAction) {this.clickAction = clickAction;}//unity的一个内置方法,可以将点击事件的处理方式委托给其它方法void OnMouseDown() {clickAction.DealClick();}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//提供一个接口
public interface ClickAction
{//提供一个函数来解决点击事件void DealClick();
}
陆地(Land)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Land
{//创建一个陆地的游戏对象public GameObject land;//创建变量用于存储在陆地上的牧师与魔鬼的数量public int priestCount, devilCount;//land的构造函数public Land (Vector3 position){//从资源中加载陆地的预制体,并且使用Instantiate方法创建该预制体的游戏对象land = GameObject.Instantiate(Resources.Load("prefab/land", typeof(GameObject))) as GameObject;//设置陆地的游戏对象在各个方向轴上的缩放比例大小land.transform.localScale = new Vector3(8,4.8f,2);//接受构造函数传进来的position//将陆地对象的position设置为传进来的positionland.transform.position = position;//初始化陆地上牧师和魔鬼的数量为0priestCount = devilCount = 0;}
}
位置(Position)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Position //存储所有对象的位置
{//游戏对象的固定位置(世界坐标)public static Vector3 left_land = new Vector3(-8,-3,0);public static Vector3 right_land = new Vector3(8,-3,0);public static Vector3 river = new Vector3(0,-4,0);public static Vector3 left_boat = new Vector3(-2.3f,-2.3f,-0.4f);public static Vector3 right_boat = new Vector3(2.4f, -2.3f, -0.4f);//角色相对于(在)岸边的位置(相对坐标)public static Vector3[] role_land = new Vector3[] {new Vector3(0.4f,0.77f,0), new Vector3(0.25f,0.77f,0), new Vector3(0.1f,0.77f,0), new Vector3(-0.05f,0.77f,0), new Vector3(-0.2f,0.77f,0), new Vector3(-0.35f,0.77f,0)};//角色相对于(在)船的位置(相对坐标)public static Vector3[] role_boat = new Vector3[] {new Vector3(0.2f,3,0), new Vector3(-0.2f,3,0)};}
河流(River)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class River
{//创建一个河流的游戏对象public GameObject river;//河流的构造函数,接受传进来的positionpublic River(Vector3 position) {//从资源中加载河流的预制体,并且使用Instantiate方法创建该预制体的游戏对象river = GameObject.Instantiate(Resources.Load("prefab/river", typeof(GameObject))) as GameObject;//命名河流游戏对象为riverriver.name = "river"; //设置河流在各个方向轴上的缩放比例river.transform.localScale = new Vector3(8,2.5f,2);//将河流的position设置为构造函数传进来的positionriver.transform.position = position;}
}
角色(Role)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Role
{ //创建一个角色的游戏对象public GameObject role;//创建一个变量判断该角色是否为牧师public bool isPriest;//创建一个变量判断角色是否在船上public bool inBoat;//创建一个变量判断角色是否在岸的右边public bool onRight;//创建一个变量来标记该角色是属于第几个对象public int id;//Role的构造函数,接受参数为position、isPriest以及idpublic Role (Vector3 position, bool isPriest, int id) {//绑定参数this.isPriest = isPriest;this.id = id;//初始化各个变量onRight = false;inBoat = false;//从资源中加载角色的预制体,并且使用Instantiate方法创建该预制体的游戏对象//判断传进来的参数isPriest是否为True,如果是,那么加载牧师的预制体//如果不是,就加载魔鬼的预制体role = GameObject.Instantiate(Resources.Load("prefab/" + (isPriest ? "priest" : "devil"), typeof(GameObject))) as GameObject;//重命名该游戏角色role.name = "role" + id; //设置角色在各个方向轴上的缩放比例大小role.transform.localScale = new Vector3(1,1.2f,1);//将角色的position设置为构造函数传进来的positionrole.transform.position = position;//接收组件盒子碰撞以及点击事件role.AddComponent<Click>();role.AddComponent<BoxCollider>();}
}
Controllers
这些脚本文件主要实现了一些控制器,用于控制船、角色、陆地、运动以及用户交互动作、场景更新控制等等,其中,最为核心的控制部分为FirstController,它协调了所有的控制器,是本游戏实现的主要大脑,负责游戏的生成和变化、以及判断游戏的状态等等。下面是Controllers各个部分的脚本文件的实现代码:
船控制器(BoatControl)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BoatControl : ClickAction
{//首先创建一个船的类对象Boat boatModel;//创建一个用户操作动作的类对象,用于用户进行操作IUserAction userAction;//构造函数public BoatControl() {//实现使用userAction变量来调用IuserAction接口的方法,处理船的控制逻辑userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction;}//定义一个函数,用来创建一个船的对象public void CreateBoat(Vector3 position) {//如果船的对象为空,那么就销毁该船的游戏对象if (boatModel != null) {Object.DestroyImmediate(boatModel.boat);}//新建一个船的游戏对象,并且赋给类中定义的对象boatModel = new Boat(position);//获取Click组件,调用setClickAction方法boatModel.boat.GetComponent<Click>().setClickAction(this);}//返回当前的船的游戏对象public Boat GetBoatModel() {return boatModel;}//将角色从岸上移动到船上,接收的对象为需要移动的角色对象,返回移动后的角色的位置坐标public Vector3 AddRole(Role roleModel) {//首先初始化index=-1,然后判断船上是否有空的位置可以容纳角色对象int index = -1;if (boatModel.roles[0] == null) index = 0;else if (boatModel.roles[1] == null) index = 1;//如果船上已经满员了,返回角色的原始的位置if (index == -1) return roleModel.role.transform.localPosition;//如果没有满员,将角色添加到船上的对应位置boatModel.roles[index] = roleModel;//同时更新角色是否在船上的标记变量roleModel.inBoat = true;//将角色挂载到船上,以后的位置变换为相对船而变换roleModel.role.transform.parent = boatModel.boat.transform;//更新船上的角色(牧师还是魔鬼)数量if (roleModel.isPriest) boatModel.priestCount++;else boatModel.devilCount++;//返回角色的变换后的位置信息return Position.role_boat[index];}//将角色从船上移动到岸上,接收参数为角色对象,表示需要移动的对象public void RemoveRole(Role roleModel) {//循环遍历船上的角色数组for (int i = 0; i < 2; ++i){//如果当前遍历的角色与需要移动的角色相同,那么就移动该角色到岸上if (boatModel.roles[i] == roleModel) {boatModel.roles[i] = null;//更新船上的角色(牧师还是魔鬼)数量if (roleModel.isPriest) boatModel.priestCount--;else boatModel.devilCount--;break;}}}//重写DealClick函数public void DealClick() {//如果船上的角色数组不为空,那么调用移动角色的函数if (boatModel.roles[0] != null || boatModel.roles[1] != null) {userAction.MoveBoat();}}
}
陆地控制器(LandControl)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LandControl
{//首先创建一个陆地的对象Land landModel;//用于创建陆地对象,接收参数为positionpublic void CreateLand(Vector3 position) {//如果陆地对象为空,那么创建一个新的陆地对象if (landModel == null) {landModel = new Land(position);}}//用于获取陆地的对象public Land GetLand() {return landModel;}//将角色添加到岸上,接收参数为一个角色对象,然后返回角色在岸上的相对坐标public Vector3 AddRole2Land(Role roleModel) {//将角色挂载到陆地对象上,那么以后的操作都是相对于陆地了roleModel.role.transform.parent = landModel.land.transform;//更新角色对象中的是否在船上的变量为falseroleModel.inBoat = false;//更新陆地上的角色(牧师还是魔鬼)数量if (roleModel.isPriest) landModel.priestCount++;else landModel.devilCount++;//返回角色移动后的位置信息return Position.role_land[roleModel.id];}//将角色从岸上移除public void RemoveRole(Role roleModel) {//更新陆地对象中,角色(牧师还是魔鬼)的数量if (roleModel.isPriest) landModel.priestCount--;else landModel.devilCount--;}
}
角色控制器(RoleControl)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//用于控制对象的动作
public class RoleControl : ClickAction
{//首先创建一个需要操作的角色对象Role roleModel;//创建一个用户操作动作的类对象,用于用户进行操作IUserAction userAction;//构造函数,实现使用userAction变量来调用IuserAction接口的方法,处理角色的控制逻辑public RoleControl() {userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction;}//用于创建一个新的角色对象,接收参数为位置信息,是否为牧师信息,以及角色的id号public void CreateRole(Vector3 position, bool isPriest, int id) {//如果角色对象不为空,那么将该对象销毁if (roleModel != null) {Object.DestroyImmediate(roleModel.role);}//创建一个新的角色对象roleModel = new Role(position, isPriest, id);//获取Click组件,调用setClickAction方法来处理roleModel.role.GetComponent<Click>().setClickAction(this);}//获取返回的角色对象public Role GetRoleModel() {return roleModel;}//重写DealClick函数,调用移动角色的函数public void DealClick() {userAction.MoveRole(roleModel);}
}
游戏对象的移动(Move)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//用于控制对象的移动行为
public class Move : MonoBehaviour
{//用于标记该对象是否正在移动public bool isMoving = false;//表示该对象的移动速度public float speed = 5;//表示移动的对象的目标位置public Vector3 destination;//表示了移动对象到目标位置的中间位置,主要是为了产生一个动态以及美观的移动public Vector3 mid_destination;// Update is called once per framevoid Update(){//如果对象的移动目标等于对象正在的位置上,那么表示没有移动if (transform.localPosition == destination) {isMoving = false;return;}//否则,就是角色正在移动isMoving = true;//如果移动目标的x与y位置与角色正在的位置不同,那么移动该角色到目标的中间位置if (transform.localPosition.x != destination.x && transform.localPosition.y != destination.y) {transform.localPosition = Vector3.MoveTowards(transform.localPosition, mid_destination, speed * Time.deltaTime);}//否则,表示角色已经到达了中间位置,需要将角色移动到目标位置else {transform.localPosition = Vector3.MoveTowards(transform.localPosition, destination, speed * Time.deltaTime);}}
}
游戏对象的移动控制器(MoveControl)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//用于控制移动的行为
public class MoveControl
{//创建一个游戏对象,表示需要移动的游戏对象GameObject moveObject;//用于获取移动状态public bool GetIsMoving() {//当需要移动的对象不为空,而且附加的Move组件的ismoving为True时,返回Truereturn (moveObject != null && moveObject.GetComponent<Move>().isMoving == true);}//用于设置移动目标和相关参数,接收参数为目标位置以及一个需要移动的对象public void SetMove(Vector3 destination, GameObject moveObject) {//创建一个Move对象,用于对象的移动实现Move model;//将参数绑定this.moveObject = moveObject;//如果moveObject没有附加Move组件,那么为该游戏对象附加一个Move组件if (!moveObject.TryGetComponent<Move>(out model)) {moveObject.AddComponent<Move>();}//绑定Move组件中的移动目标位置this.moveObject.GetComponent<Move>().destination = destination;//如果需要移动对象的目标y位置大于目标的y位置,就将一个新的Vector3对象赋给移动对象的Move组件中的中间位置信息//其中x和z为目标的x和z,而y为对象本地的yif (this.moveObject.transform.localPosition.y > destination.y) {this.moveObject.GetComponent<Move>().mid_destination = new Vector3(destination.x, this.moveObject.transform.localPosition.y, destination.z);}else {//将一个新的Vector3对象赋给Move组件中的中间位置//其中x为移动对象的本地的x,而y和z为目标的y和zthis.moveObject.GetComponent<Move>().mid_destination = new Vector3(this.moveObject.transform.localPosition.x, destination.y, destination.z);}}
}
用户的操作动作(IUserAction)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//创建一个接口,定义用户的一些基本操作方式
public interface IUserAction {//用于移动船void MoveBoat();//用于移动角色,将角色从岸上移动到船上//将角色从船上移动到岸上void MoveRole(Role roleModel);//检查游戏的状态与条件void Check();
}
场景控制的接口(ISceneController)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//用于场景控制的接口
//而其中的LoadResources方法用于加载场景所需的资源
public interface ISceneController
{void LoadResources();
}
导演类(SSDirector)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//实现了单例模式,保证了在整个游戏的过程中只有一个实例存在
//方便访问和管理游戏中的全局对象,协调游戏场景和场景控制器
public class SSDirector : System.Object
{//静态变量,用于保存类的唯一实例static SSDirector _instance;//用于获取和设置当前场景的控制器对象public ISceneController CurrentSceneController {get; set;}//用于获取类的实例public static SSDirector GetInstance() {if (_instance == null) {_instance = new SSDirector();}return _instance;}
}
主导全场的核心控制器(FirstController)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//这是本游戏的最核心的控制部分,协调所有游戏对象的实现与信息调试
public class FirstController : MonoBehaviour, ISceneController, IUserAction {//创建陆地的对象,为左岸以及右岸LandControl leftLandController, rightLandController;//创建河流对象River river;//创建一个船控制器,调用将角色移动到船上或者反过来的实现函数BoatControl boatController;//创建一个角色控制器数组,每一个角色对象都需要一个独立的控制器RoleControl[] roleControllers;//创建一个控制移动的控制器对象MoveControl moveController;//创建一个布尔变量,用于标记游戏是否在运行bool isRunning;//创建一个变量,标记游戏的剩余时间float time;//用于加载资源、初始化游戏场景public void LoadResources() {//初始化每一个角色控制器roleControllers = new RoleControl[6];for (int i = 0; i < 6; ++i) {roleControllers[i] = new RoleControl();roleControllers[i].CreateRole(Position.role_land[i], i < 3 ? true : false, i);}//初始化陆地控制器//创建左岸与右岸以及获取名字leftLandController = new LandControl();leftLandController.CreateLand(Position.left_land);leftLandController.GetLand().land.name = "left_land";rightLandController = new LandControl();rightLandController.CreateLand(Position.right_land);rightLandController.GetLand().land.name = "right_land";//初始化地将人物添加并定位到左岸 foreach (RoleControl roleController in roleControllers){roleController.GetRoleModel().role.transform.localPosition = leftLandController.AddRole2Land(roleController.GetRoleModel());}//初始化船控制器,创建一个船控制器即可boatController = new BoatControl();boatController.CreateBoat(Position.left_boat);//初始化一条河river = new River(Position.river);//初始化移动控制器moveController = new MoveControl();//更新游戏是否运行为TrueisRunning = true;//初始化游戏剩余时间为60秒time = 60;}//实现移动船的函数public void MoveBoat() {//如果游戏没有运行或者移动控制器里面的获取对象是否移动为False时,就直接返回,函数结束if (isRunning == false || moveController.GetIsMoving()) return;//检查船的当前位置,然后设置船需要移动的目标位置(左岸或者右岸)if (boatController.GetBoatModel().isRight) {moveController.SetMove(Position.left_boat, boatController.GetBoatModel().boat);}else {moveController.SetMove(Position.right_boat, boatController.GetBoatModel().boat);}//更新参数boatController.GetBoatModel().isRight = !boatController.GetBoatModel().isRight;}//实现角色的移动的函数public void MoveRole(Role roleModel) {//如果游戏没有运行或者移动控制器里面的获取对象是否移动为False时,就直接返回,函数结束if (isRunning == false || moveController.GetIsMoving()) return;//如果角色在船上,那么根据船的位置设定移动的方向if (roleModel.inBoat) {if (boatController.GetBoatModel().isRight) {moveController.SetMove(rightLandController.AddRole2Land(roleModel), roleModel.role);}else {moveController.SetMove(leftLandController.AddRole2Land(roleModel), roleModel.role);}//检查角色的位置与岸的位置是否匹配,如果匹配,那么移动角色到岸上roleModel.onRight = boatController.GetBoatModel().isRight;boatController.RemoveRole(roleModel);}//如果角色在岸上,检查角色所在岸和船所在岸是否匹配,如果匹配,就将角色移动到船上else {if (boatController.GetBoatModel().isRight == roleModel.onRight) {if (roleModel.onRight) {rightLandController.RemoveRole(roleModel);}else {leftLandController.RemoveRole(roleModel);}moveController.SetMove(boatController.AddRole(roleModel), roleModel.role);}}}//检查游戏的状态与判断游戏是否获胜或者失败public void Check() {//如果游戏没有运行,那么直接返回,函数结束if (isRunning == false) return;//通过组件获取userGui的组件,并且将游戏提示清空this.gameObject.GetComponent<UserGUI>().gameMessage = "";//如果右边岸上的牧师等于三个,那么游戏结束,并且将游戏提示设为“You Win!”//同时更新游戏是否运行的参数if (rightLandController.GetLand().priestCount == 3) {this.gameObject.GetComponent<UserGUI>().gameMessage = "You Win!";isRunning = false;}//否则,如果左岸上的牧师数量不为0且小于魔鬼数量//或者右岸上的牧师数量不为0且小于魔鬼数量//或者游戏时间结束了//判断游戏结束,同时更新游戏是否运行的参数else {int leftPriestCount, rightPriestCount, leftDevilCount, rightDevilCount;leftPriestCount = leftLandController.GetLand().priestCount + (boatController.GetBoatModel().isRight ? 0 : boatController.GetBoatModel().priestCount);rightPriestCount = rightLandController.GetLand().priestCount + (boatController.GetBoatModel().isRight ? boatController.GetBoatModel().priestCount : 0);leftDevilCount = leftLandController.GetLand().devilCount + (boatController.GetBoatModel().isRight ? 0 : boatController.GetBoatModel().devilCount);rightDevilCount = rightLandController.GetLand().devilCount + (boatController.GetBoatModel().isRight ? boatController.GetBoatModel().devilCount : 0);if (leftPriestCount != 0 && leftPriestCount < leftDevilCount || rightPriestCount != 0 && rightPriestCount < rightDevilCount) {this.gameObject.GetComponent<UserGUI>().gameMessage = "Game Over!";isRunning = false;}}}//用于初始化场景和游戏对象//SSDirector是一个单例模式的类,通过GetInstance()方法获取实例//然后将当前场景控制器设置为当前对象,以便其他部分可以通过SSDirector来访问和管理场景控制器。void Awake() {SSDirector.GetInstance().CurrentSceneController = this;LoadResources();//向当前对象(即当前脚本所在的对象)添加了一个 UserGUI 组件。this.gameObject.AddComponent<UserGUI>();}//更新函数void Update() {//如果游戏正在运行,不断更新游戏结束的时间if (isRunning) {time -= Time.deltaTime;//将游戏剩余时间赋给组件的time属性,用于在界面上显示游戏剩余时间this.gameObject.GetComponent<UserGUI>().time = (int)time;//如果时间小于等于0,那么判断游戏结束,同时更新参数if (time <= 0) {this.gameObject.GetComponent<UserGUI>().time = 0;this.gameObject.GetComponent<UserGUI>().gameMessage = "Game Over!";isRunning = false;}}}
}
View
在该部分View中,只有一个脚本文件,主要功能是实现与用户交互的GUI类,用于提示游戏失败与成功,同时倒计时游戏的剩余时间。下面是该脚本文件的具体代码实现:
用户交互类(UserGUI)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//负责显示游戏界面和一些文本信息
public class UserGUI : MonoBehaviour
{//创建一个用户交互的对象,用于引入用户交互的接口IUserAction userAction;//创建一个变量用于存储需要显示的信息public string gameMessage ;//创建一个变量,用于计算游戏的剩余时间public int time;//unity内置的类GUIStyle style, bigstyle;// Start is called before the first frame updatevoid Start(){//初始化一些样式,主要是字体显示的样式//初始化时间为60秒time = 60;//获取当前场景的用户操作接口,并且将其赋给userAction变量userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction;style = new GUIStyle();style.normal.textColor = Color.white;style.fontSize = 30;bigstyle = new GUIStyle();bigstyle.normal.textColor = Color.white;bigstyle.fontSize = 50;}// Update is called once per framevoid OnGUI() {//不断检查游戏状态userAction.Check();//绘制三个标签用户显示信息GUI.Label(new Rect(160, Screen.height * 0.1f, 50, 200), "Prieists and Devils", bigstyle);GUI.Label(new Rect(250, 100, 50, 200), gameMessage, style);GUI.Label(new Rect(0,0,100,50), "Time: " + time, style);}
}
结语
至此,该游戏——魔鬼与牧师的unity实现就介绍完毕了,在该游戏实现中,我们主要采用了MVC架构将游戏的各个部件拆分开来实现,同时有一个最核心的控制器来控制协调所有的控制器以及Model,还有一些用户交互动作的实现也分开在不同的类文件中,还有场景更新等等。我们将所有的代码实现划分到各个不同的文件中,以及采用MVC架构的目的主要是为了使得我们的代码更加清晰、有序以及可维护,使得我们的代码可扩展。
在下一个实验中,我们将会介绍该游戏——魔鬼与牧师的动作分离版本,即不再采用MVC架构,而是采用动作分离的方式来维护我们的脚本代码文件。
最后,让我们一起动起手来,实现属于我们自己的游戏吧!
完整代码的Github地址:Github代码地址
这篇关于unity3D游戏——魔鬼与牧师(Devil and Priest)的MVC实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!