unity3D游戏——魔鬼与牧师(Devil and Priest)的MVC实现

2023-11-23 00:30

本文主要是介绍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实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/414022

相关文章

Python实现AVIF图片与其他图片格式间的批量转换

《Python实现AVIF图片与其他图片格式间的批量转换》这篇文章主要为大家详细介绍了如何使用Pillow库实现AVIF与其他格式的相互转换,即将AVIF转换为常见的格式,比如JPG或PNG,需要的小... 目录环境配置1.将单个 AVIF 图片转换为 JPG 和 PNG2.批量转换目录下所有 AVIF 图

Pydantic中model_validator的实现

《Pydantic中model_validator的实现》本文主要介绍了Pydantic中model_validator的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录引言基础知识创建 Pydantic 模型使用 model_validator 装饰器高级用法mo

AJAX请求上传下载进度监控实现方式

《AJAX请求上传下载进度监控实现方式》在日常Web开发中,AJAX(AsynchronousJavaScriptandXML)被广泛用于异步请求数据,而无需刷新整个页面,:本文主要介绍AJAX请... 目录1. 前言2. 基于XMLHttpRequest的进度监控2.1 基础版文件上传监控2.2 增强版多

Redis分片集群的实现

《Redis分片集群的实现》Redis分片集群是一种将Redis数据库分散到多个节点上的方式,以提供更高的性能和可伸缩性,本文主要介绍了Redis分片集群的实现,具有一定的参考价值,感兴趣的可以了解一... 目录1. Redis Cluster的核心概念哈希槽(Hash Slots)主从复制与故障转移2.

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

Docker镜像修改hosts及dockerfile修改hosts文件的实现方式

《Docker镜像修改hosts及dockerfile修改hosts文件的实现方式》:本文主要介绍Docker镜像修改hosts及dockerfile修改hosts文件的实现方式,具有很好的参考价... 目录docker镜像修改hosts及dockerfile修改hosts文件准备 dockerfile 文

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N