牧师与魔鬼Priests and Devils--unity小游戏

2023-10-25 04:50

本文主要是介绍牧师与魔鬼Priests and Devils--unity小游戏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、游戏介绍:

Priests and Devils

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!

牧师与魔鬼
牧师与魔鬼是一款益智游戏,你将帮助牧师和魔鬼在限定时间内过河。河的一边有三个牧师和三个魔鬼。他们都想去河的对岸,但只有一条船,这条船每次只能载两个人。而且必须有一个人把船从一边开到另一边。在flash游戏中,你可以点击它们来移动它们,点击go按钮将船移动到另一个方向。如果在河的两边,牧师的人数比魔鬼多,他们就会被杀死,游戏也就结束了。你可以用很多方法来尝试。让所有的牧师活下去!好运!

二、效果演示

牧师与魔鬼(Priests and Devils)--unity小游戏

三、具体实现过程

1. MVC模式介绍

2. UML图

为了辅助表达游戏对象的设计,在这里通过UML图画出主要的对象以及它们之间的关系:

在这里给出上述个对象的简介:

IUserAction:作为动作接口,定义了四个函数,分别是移动船只(MoveBoat)、移动角色(MoveRole)、检查游戏是否结束(Check)、重新开始游戏(Restart)。

ISceneController:作为场景接口,定义了两个函数,分别是加载资源(LoadResource)、销毁资源(destroyResource)。

FirstController:作为整个游戏的主要控制器,实现了动作接口IUserAction和场景接口ISceneController,同时含有一个Awake()函数用于游戏的初始化、一个Update()函数对剩余时间的实时更新(用作倒计时功能)。

UserGUI:作为用户交互界面,用于显示游戏文字信息并创建按钮等部件与玩家交互。同时,UserGUI将当前的场景传递给IUserAction用于执行控制动作。

SSDirector:作为整个游戏的“导演”,用于把握全局、控制场景。

GameModels:与游戏有关的各对象以及它们的控制器,用于定义、创建对象以及执行对它们的控制。由于对象太多,不在此一一列出。

3. 游戏中提及的事物(Objects)

游戏中提及的事物有:

牧师Priests:游戏可操纵对象,需要在玩家的帮助下过河,并且不被魔鬼杀死

魔鬼Devils:游戏可操纵对象,干扰牧师过河

船Boat:游戏可操纵对象,负责搭载牧师或者魔鬼过河

河流River:不可操作对象,用作环境背景的构建

河岸Shore:不可操作对象,用作环境背景的构建

背景backGround:不可操作对象,用作环境背景的构建

以上游戏对象都已做成预制,并且在游戏中通过代码动态生成:

4. 玩家动作表(规则表) 

玩家动作表
玩家动作动作效果动作限制
点击牧师或魔鬼(统称为角色)点击岸上的角色将上船;点击船上的角色将上岸船上满员后角色将无法再上船
点击船只或“移动船只”按钮船只移动到河岸的另一侧船上至多搭载两名角色;船上没角色时无法移动
点击“重新开始”按钮游戏将重新开始,角色和船只复位

5. 文件组织形式

Assets文件夹内,有三个子文件夹,分别是Resources(用于存储游戏资源的预制)、Scenes(存储游戏场景)、Scripts(用于存储C#脚本)。

对于Resources文件夹,其中的三个子文件夹分别存储模型的材质(Materials)、模型的预制(Prefabs)、图像纹理(Textures)。在这里不做展开。

对于Scenes文件夹,用于存储游戏的场景:

其中,整个游戏只有一个主摄像机(Main Camera)、灯光(Directional Light)和一个空对象(Empty),符合项目要求。

对于Script文件夹,则采用了MVC模式对文件进行划分:

其中Models文件夹存储游戏对象及空间关系,对应于MVC模式中的Model;Views文件夹存储玩家交互界面,即GUI,对应于MVC模式中的View;Controllers文件夹存储控制对象,用于执行游戏对象以及整个游戏的控制事件,对应于MVC模式中的Controller。

6. 代码介绍

(1)模型(Model)部分

正如上文所言,该游戏一共有六个对象,因为单独为它们创建一个类文件。其中“牧师”和“魔鬼”可以被归并成一类,即角色Role类。同时,模型部分还含有一个Position类用于存储各个对象处在游戏场景中的位置。

                                                                  背景BackGround

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BackGround
{public GameObject bg;public BackGround(Vector3 position) {bg = GameObject.Instantiate(Resources.Load("Prefabs/backGround", typeof(GameObject))) as GameObject;bg.transform.localScale = new Vector3(6,1,1.5f);bg.transform.position = position;bg.name = "backGround";}
}

                                                                  河流River

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class River
{public GameObject river;public River(Vector3 position) {river = GameObject.Instantiate(Resources.Load("Prefabs/River", typeof(GameObject))) as GameObject;river.transform.localScale = new Vector3(8,2.5f,2);river.transform.rotation = Quaternion.Euler(0, 0, 180);river.transform.position = position;river.name = "River";}
}

由于背景和河流都是不可操纵对象,因此只有一个初始化函数。

                                                                  河岸Shore

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Shore 
{public GameObject shore;public int priestCount, devilCount;  // 牧师和魔鬼的数量public Shore (Vector3 position){shore = GameObject.Instantiate(Resources.Load("Prefabs/Shore", typeof(GameObject))) as GameObject;shore.transform.localScale = new Vector3(8,4.8f,2); shore.transform.position = position;priestCount = devilCount = 0;           // 初始牧师和魔鬼数量为0}}

河岸具有两个变量priestCount, devilCount用于存储牧师和魔鬼的数量,在后续控制部分有用。

                                                                  船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; //船上的牧师和恶魔数量public Boat(Vector3 position) {boat = GameObject.Instantiate(Resources.Load("Prefabs/Boat", typeof(GameObject))) as GameObject;boat.name = "Boat";boat.transform.position = position;boat.transform.localScale = new Vector3(2.8f,0.4f,2);   roles = new Role[2];        // 船上最多两个角色isRight = false;         // 初始在左岸priestCount = devilCount = 0;      // 初始牧师和魔鬼数量为0boat.AddComponent<BoxCollider>();              // 添加碰撞组件boat.AddComponent<Click>();                    // 添加点击组件 }
}

除了能够记录牧师与魔鬼的数量,船还需要添加碰撞组件和点击组件。

                                                                  角色Role

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Role 
{public GameObject role;//model对应的游戏对象public bool isPriest;  // 是否是牧师public bool inBoat;  // 是否在船上public bool onRight;  // 是否在右岸public int id;  // 角色编号public Role (Vector3 position, bool isPriest, int id) {this.isPriest = isPriest;this.id = id;            // 角色编号onRight = false;         // 初始在左岸inBoat = false;          // 初始不在船上role = GameObject.Instantiate(Resources.Load("Prefabs/" + (isPriest ? "Priests" : "Devils"), typeof(GameObject))) as GameObject;role.transform.rotation = Quaternion.Euler(0, -90, 0);        // 设置角色朝向// role.transform.localScale = new Vector3(1,1.2f,1);            // 设置角色大小role.transform.position = position;role.name = "role" + id;role.AddComponent<Click>();                         // 添加点击组件role.AddComponent<BoxCollider>();                   // 添加碰撞组件}
}

角色也有碰撞组件和点击组件。

                                                                  位置Position

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Position  //存储所有对象的位置
{//固定位置(世界坐标)public static Vector3 left_shore = new Vector3(-8,-3,0);   // 左岸的位置public static Vector3 right_shore = new Vector3(8,-3,0);   // 右岸的位置public static Vector3 river = new Vector3(0,-3,0);         // 河流的位置public static Vector3 background = new Vector3(0,1,3);        // 背景的位置public static Vector3 left_boat = new Vector3(-2.3f,-1.75f,-0.4f);  // 左船的位置public static Vector3 right_boat = new Vector3(2.4f, -1.75f, -0.4f); // 右船的位置//角色相对于岸边的位置(相对坐标),每一边有6个位置,因此用数组存储public static Vector3[] role_shore = new Vector3[] {new Vector3(0.35f,0.77f,0), new Vector3(0.21f,0.77f,0), new Vector3(0.07f,0.77f,0), new Vector3(-0.07f,0.77f,0), new Vector3(-0.21f,0.77f,0), new Vector3(-0.35f,0.77f,0)};//角色相对于船的位置(相对坐标),船上有2个位置,因此用数组存储public static Vector3[] role_boat = new Vector3[] {new Vector3(0.2f,3,0), new Vector3(-0.2f,3,0)};}

记录各个对象的位置,其中船有两个位置、河岸的左边和右边各有六个位置,因此都使用数组来存储,并且使用相对位置。

(2)视图(View)部分

        视图部分只含有一个文件,即UserGUI。定义了文字显示的样式,同时创建了两个按钮“重新开始”、“移动船只”用于与玩家交互。

                                                                UserGUI

using System.Collections;
using System.Collections.Generic;
using UnityEngine;// 具体类型:UserGUI
public class UserGUI : MonoBehaviour  // 用户交互界面
{private IUserAction userAction;    // 通过接口与游戏逻辑界面交互public string gameMessage ;          // 传入想要显示的游戏信息,例如:游戏结束public int time;                 // 游戏结束倒计时GUIStyle style1, style2;void Start(){time = 60;userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction;style1 = new GUIStyle();       // 设置信息显示的样式style1.normal.textColor = Color.yellow;style1.fontSize = 30;style2 = new GUIStyle();   // 设置标题的样式style2.normal.textColor = Color.black;style2.fontSize = 50;}void OnGUI() {   GUI.Label(new Rect(Screen.width / 2 -150, 0, 50, 200), "牧 师 与 魔 鬼", style2);  // 显示游戏标题GUI.Label(new Rect(250, 100, 50, 200), gameMessage, style1);        // 显示游戏是否结束GUI.Label(new Rect(0,0,100,50), "倒计时: " + time, style1);if(GUI.Button(new Rect(0, 50, 100, 50), "重新开始")) {   // 重新开始游戏time = 60;gameMessage = "";userAction.Restart();}if(GUI.Button(new Rect(0,100,100,50),"移动船只")){      // 点击GO按钮,船开始移动userAction.MoveBoat();}userAction.Check();}
}

(3)控制器(Controller)部分

                                          点击组件Click 和 点击动作接口ClickAction

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Click : MonoBehaviour
{ClickAction clickAction;public void setClickAction(ClickAction clickAction) {      // 设置点击事件this.clickAction = clickAction;}void OnMouseDown() {          // 当鼠标按下时clickAction.DealClick();  // 处理点击事件}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public interface ClickAction
{void DealClick();    // 处理点击事件
}

创建了一个点击组件Click用于处理点击事件。同时创建了一个点击动作接口ClickAction来处理点击事件,对于船和角色,只需要实现ClickAction接口的DealClick函数就可以完成点击事件。

                                          移动动作MoveAction 和 移动控制器MoveCtrl

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MoveAction : MonoBehaviour
{public bool isMoving = false;        // 是否正在移动public float speed = 5;         // 移动速度public Vector3 destination;   // 目的地的位置(坐标)public Vector3 mid_destination;   // 中间的位置void Update()      // 一直执行移动操作,直到到达目的地{if (transform.localPosition == destination) {     // 如果已经到达目的地isMoving = false;     // 停止移动return;}isMoving = true;     // 标记为正在移动if (transform.localPosition.x != destination.x  && transform.localPosition.y != destination.y) {        // 如果x坐标和y坐标与目的地的x、y坐标都不相等,就先向中间位置移动transform.localPosition = Vector3.MoveTowards(transform.localPosition, mid_destination, speed * Time.deltaTime);}else {          // 如果x坐标或者y坐标与目的地的x、y坐标相等,就直接向目的地直线移动transform.localPosition = Vector3.MoveTowards(transform.localPosition, destination, speed * Time.deltaTime);}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MoveCtrl 
{GameObject moveObject;      // 需要移动的对象public bool GetIsMoving() {    // 判断是否正在移动return (moveObject != null && moveObject.GetComponent<MoveAction>().isMoving == true);   // 如果正在移动,返回true}public void SetMove(Vector3 destination, GameObject moveObject) {   // 设置移动的目的地和移动的对象MoveAction test;this.moveObject = moveObject;if (!moveObject.TryGetComponent<MoveAction>(out test)) {     // 如果对象没有Move组件,就添加Move组件moveObject.AddComponent<MoveAction>();    // 添加Move组件}this.moveObject.GetComponent<MoveAction>().destination = destination;     // 设置目的地if (this.moveObject.transform.localPosition.y > destination.y) {    // 如果目的地的y坐标小于当前位置的y坐标,说明从岸上到船上,就先水平移动到中间位置,然后再竖直移动到目的地this.moveObject.GetComponent<MoveAction>().mid_destination = new Vector3(destination.x, this.moveObject.transform.localPosition.y, destination.z);}else {   // 如果目的地的y坐标大于当前位置的y坐标,说明是从船上到岸上,那么就先竖直向上移动到中间位置,然后再水平移动到目的地this.moveObject.GetComponent<MoveAction>().mid_destination = new Vector3(this.moveObject.transform.localPosition.x, destination.y, destination.z);}}
}

移动动作MoveAction内update函数重复执行移动的操作,直到目标到达指定的位置,由于河岸和船有高度差,为了防止碰撞,因此采用折线移动的方式。而移动控制器MoveCtrl则设置了移动的中间位置和目的地。

                                                               船控制器BoatCtrl

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BoatCtrl : ClickAction   // 船可以被点击,所以实现ClickAction接口 
{Boat boatModel;     // 船的对象IUserAction userAction;public BoatCtrl() {userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction;     // 获取当前场景的场记}public void CreateBoat(Vector3 position) {   // 创建船,传入船的位置if (boatModel != null) {Object.DestroyImmediate(boatModel.boat);       // 如果已经有船了,就销毁}boatModel = new Boat(position);    boatModel.boat.GetComponent<Click>().setClickAction(this);      // 设置船的点击事件}public Boat GetBoatModel() {     // 获取船return boatModel;}//点击岸上的角色,将角色从岸上移动到船上,返回接下来角色应该到达的位置public Vector3 AddRole(Role roleModel) {int index = -1;   // 船上的位置索引if (boatModel.roles[0] == null) index = 0;      // 如果船上的第一个位置为空,就将角色放在第一个位置else if (boatModel.roles[1] == null) index = 1; // 如果船上的第二个位置为空,就将角色放在第二个位置else return roleModel.role.transform.localPosition;  // 如果船上的两个位置都不为空,角色无法上船,就返回角色当前的位置boatModel.roles[index] = roleModel;   // 将角色放在船上的第index个位置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;}}}public bool isEmpty(){   // 判断船是否没载客return (boatModel.roles[0] == null && boatModel.roles[1] == null) ? true : false;}public void DealClick() {       // 对于ClickAction的具体实现,点击船if (!isEmpty()) {  // 只有船上有角色时才能点击船,才能移动userAction.MoveBoat();             // 移动船}}
}

船控制器实现了创建船对象、获取船、添加角色(上船)和移出角色(下船)的函数; 同时实现了ClickAction接口, 用于处理点击船只的事件并执行IUserAction的移动船只函数。详细编写见上代码及注释。

                                                          角色控制器RoleCtrl

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RoleCtrl : ClickAction       // 角色可以被点击,所以实现ClickAction接口 
{Role roleModel;IUserAction userAction;public RoleCtrl() {userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction;   // 获取当前场景的场记}public void CreateRole(Vector3 position, bool isPriest, int id) {     // 创建角色,传入角色的位置,是否是牧师,角色的idif (roleModel != null) {Object.DestroyImmediate(roleModel.role);    // 如果已经有角色了,就销毁}roleModel = new Role(position, isPriest, id);     // 创建角色,传入角色的位置,是否是牧师,角色的idroleModel.role.GetComponent<Click>().setClickAction(this);      // 设置角色的点击事件}public Role GetRoleModel() {      // 获取角色return roleModel;}public void DealClick() {          // 对于ClickAction的具体实现,点击角色userAction.MoveRole(roleModel);}
}

角色控制器实现了创建角色,获取角色的操作,同时实现了ClickAction接口, 用于处理点击角色的事件并执行IUserAction的移动角色函数。详细代码见上。

                                                          河岸控制器ShoreCtrl

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ShoreCtrl
{Shore shoreModel;public void CreateShore(Vector3 position) {  // 创建河岸if (shoreModel == null) {shoreModel = new Shore(position);}}public Shore GetShore() {  // 获取河岸return shoreModel;}//将角色添加到岸上,返回角色在岸上的相对坐标public Vector3 AddRole(Role roleModel) {roleModel.role.transform.parent = shoreModel.shore.transform;    // 将当前角色的相对坐标设置为岸的坐标roleModel.inBoat = false;if (roleModel.isPriest) shoreModel.priestCount++;  // 当前角色是牧师, 牧师数量加一else shoreModel.devilCount++;                      // 当前角色是魔鬼, 魔鬼数量加一return Position.role_shore[roleModel.id];          // 返回当前角色在岸上的相对坐标}//将角色从岸上移除public void RemoveRole(Role roleModel) {if (roleModel.isPriest) shoreModel.priestCount--;     // 当前角色是牧师else shoreModel.devilCount--;       // 当前角色是魔鬼}
}

河岸控制器实现了创建河岸、获取河岸、添加角色(上岸)和移出角色(离开河岸)的函数;

                    导演SSDirector,场景接口ISceneController,动作接口IUserAction

using System.Collections;
using System.Collections.Generic;
using UnityEngine;// 导演把握全局,控制场景和游戏流程
public class SSDirector : System.Object  // 它继承至 C# 根对象,所以不会受 Unity 引擎管理,也不要加载
{private static SSDirector _instance;  public ISceneController CurrentSceneController {get; set;}public static SSDirector GetInstance() {  if (_instance == null) {_instance = new SSDirector();}return _instance;}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;// 场记
// 抽象类型(角色):ISceneController
public interface ISceneController
{void LoadResources();   // 加载资源void destroyResourse(); // 销毁资源
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;// 吃瓜群众
// 抽象类型(角色):IUserAction
// 游戏逻辑与用户界面之间交互的门面
public interface IUserAction {          //用户动作接口void MoveBoat();              //移动船void MoveRole(Role roleModel);    //移动角色void Check();                 //检查游戏是否结束void Restart();               //重新开始游戏
}

实现的内容在上文UML图部分已经给出。

                                                       主要控制器FirstController    

using System.Collections;
using System.Collections.Generic;
using UnityEngine;// 场记
// 具体类型:FirstController
public class FirstController : MonoBehaviour, ISceneController, IUserAction {  // 对于ISceneController和IUserAction接口的具体实现ShoreCtrl leftShoreController, rightShoreController;     // 左岸和右岸的控制器River river;     // 河流对象BackGround backGround;      // 背景对象BoatCtrl boatController;    // 船的控制器RoleCtrl[] roleControllers;   // 人物的控制器,每个人物分别对应一个控制器MoveCtrl moveController;   // 移动的控制器bool isRunning;   // 游戏是否在运行float time;   // 游戏倒计时SSDirector director;   // 导演实例public void LoadResources() {     // 对于ISceneController的具体实现,加载资源,初始化游戏//角色部分roleroleControllers = new RoleCtrl[6];      // 数组存储6个角色/控制器for (int i = 0; i < 6; ++i) {roleControllers[i] = new RoleCtrl();roleControllers[i].CreateRole(Position.role_shore[i], i < 3 ? true : false, i);   // 创建角色,传入角色在左岸的位置,依次填写角色的id,前三个是牧师,后三个是魔鬼}//河岸部分ShoreleftShoreController = new ShoreCtrl();leftShoreController.CreateShore(Position.left_shore);leftShoreController.GetShore().shore.name = "left_shore";rightShoreController = new ShoreCtrl();rightShoreController.CreateShore(Position.right_shore);rightShoreController.GetShore().shore.name = "right_shore";//将人物添加并定位至左岸  foreach (RoleCtrl roleController in roleControllers)      // 遍历角色控制器{roleController.GetRoleModel().role.transform.localPosition = leftShoreController.AddRole(roleController.GetRoleModel());  // 设置每个角色在左岸的位置}//船部分boatboatController = new BoatCtrl();boatController.CreateBoat(Position.left_boat);//河流对象riverriver = new River(Position.river);//背景对象backGroundbackGround = new BackGround(Position.background);//移动控制器moveController = new MoveCtrl();isRunning = true;    // 游戏开始运行time = 60;        // 游戏倒计时60s}public void destroyResourse(){Object.DestroyImmediate(leftShoreController.GetShore().shore);Object.DestroyImmediate(rightShoreController.GetShore().shore);Object.DestroyImmediate(boatController.GetBoatModel().boat);Object.DestroyImmediate(river.river);Object.DestroyImmediate(backGround.bg);foreach (RoleCtrl roleController in roleControllers) {Object.DestroyImmediate(roleController.GetRoleModel().role);}}public void MoveBoat() {    // 对于IUserAction的具体实现,移动船if (isRunning == false || moveController.GetIsMoving() || boatController.isEmpty()) 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) {  // 对于IUserAction的具体实现,移动角色if (isRunning == false || moveController.GetIsMoving()) return;      // 如果游戏结束或者船正在移动,那么无法再移动角色if (roleModel.inBoat) {            // 如果角色在船上if (boatController.GetBoatModel().isRight) {   // 如果船在右岸moveController.SetMove(rightShoreController.AddRole(roleModel), roleModel.role);  // 设置角色的目的地为右岸}else {    // 如果船在左岸moveController.SetMove(leftShoreController.AddRole(roleModel), roleModel.role);  // 设置角色的目的地为左岸}roleModel.onRight = boatController.GetBoatModel().isRight;  // 每次移动之后,角色的方向取决于船的方向boatController.RemoveRole(roleModel);   // 将角色从船上移动到岸上}else {     // 如果角色在岸上if (boatController.GetBoatModel().isRight == roleModel.onRight) {  // 如果角色和船在同一边if (roleModel.onRight) {  // 如果角色在右岸rightShoreController.RemoveRole(roleModel);  // 将角色从右岸移动到船上}else {  // 如果角色在左岸leftShoreController.RemoveRole(roleModel);   // 将角色从左岸移动到船上}moveController.SetMove(boatController.AddRole(roleModel), roleModel.role);  // 设置角色的目的地为船上}}}public void Check() {            // 对于IUserAction的具体实现,检查游戏是否结束if (isRunning == false) return;     // 如果游戏结束,那么无需再检查this.gameObject.GetComponent<UserGUI>().gameMessage = "";    // 游戏信息置空if (rightShoreController.GetShore().priestCount == 3) {  // 如果右岸的牧师数量为3,游戏胜利this.gameObject.GetComponent<UserGUI>().gameMessage = "牧师成功过岸,游戏胜利!";  // 显示游戏胜利信息isRunning = false;   // 游戏结束}else {int leftPriestCount, rightPriestCount, leftDevilCount, rightDevilCount;  // 两岸和船上的牧师和魔鬼数量leftPriestCount = leftShoreController.GetShore().priestCount + (boatController.GetBoatModel().isRight ? 0 : boatController.GetBoatModel().priestCount);rightPriestCount = rightShoreController.GetShore().priestCount + (boatController.GetBoatModel().isRight ? boatController.GetBoatModel().priestCount : 0);leftDevilCount = leftShoreController.GetShore().devilCount + (boatController.GetBoatModel().isRight ? 0 : boatController.GetBoatModel().devilCount);rightDevilCount = rightShoreController.GetShore().devilCount + (boatController.GetBoatModel().isRight ? boatController.GetBoatModel().devilCount : 0);if ((leftPriestCount != 0 && leftPriestCount < leftDevilCount) || (rightPriestCount != 0 && rightPriestCount < rightDevilCount)) {  // 如果任意一边的牧师数量不为0且小于魔鬼数量,游戏失败this.gameObject.GetComponent<UserGUI>().gameMessage = "牧师被魔鬼杀死,游戏结束!";isRunning = false;}}}public void Restart(){       // 重新开始游戏director.CurrentSceneController.destroyResourse();director.CurrentSceneController.LoadResources();}void Awake() {    // 唤醒函数,在游戏开始前调用一次director = SSDirector.GetInstance();   // 获取导演实例director.CurrentSceneController = this;   // 设置当前场景控制器为本对象director.CurrentSceneController.LoadResources();   // 调用上面的LoadResources()函数,加载资源,初始化游戏this.gameObject.AddComponent<UserGUI>();   // 添加UserGUI脚本,显示用户交互界面}void Update() {      // 每帧调用一次if (isRunning) {time -= Time.deltaTime;     // 游戏剩余时间减少this.gameObject.GetComponent<UserGUI>().time = (int)time;   // 设置游戏剩余时间if (time <= 0) {this.gameObject.GetComponent<UserGUI>().time = 0;   // 游戏剩余时间为0this.gameObject.GetComponent<UserGUI>().gameMessage = "时间到! 游戏失败!"; // 显示游戏结束信息isRunning = false;  // 游戏结束}}}
}

实现了场景接口ISceneController的加载/删除资源函数,其中在LoadResource中动态加载游戏对象,destroyResource用于销毁游戏对象,以便实现重新开始游戏的功能。

实现了动作接口IUserAction的移动船只、移动角色、检查游戏是否结束、重新开始的函数,其中限制了玩家在船只和角色在移动的过程中无法对其进行操作;检查游戏是否结束只需要检查在某一侧的魔鬼数量是否超过玩家数量即可;重新开始的函数只需要调用destroyResource函数销毁游戏对象,再用LoadResource重新加载即可。

在Awake()函数内定义了导演对象并对其进行赋值;在Update()函数内实现游戏倒计时和游戏信息播报的操作。具体实现见代码及注释。

7. 游戏视频

牧师与魔鬼(Priests and Devils)--unity小游戏_单机游戏热门视频

8. 代码链接

ZhouXSh/unity-3D (gitee.com)

这篇关于牧师与魔鬼Priests and Devils--unity小游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(4)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)-CSDN博客  这节就是真正的存储数据了   理清一下思路: 1.存储路径并检查 //2进制文件类存储private static string Data_Binary_Pa

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统,并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址(Addressable)时,您可以使用该资源的地址从任何地方加载它。无论资源是在本地应用程序中可用还是存储在远程内容分发网络上,Addressable 系统都会定位并返回该资源。 您

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj

在Unity环境中使用UTF-8编码

为什么要讨论这个问题         为了避免乱码和更好的跨平台         我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本,默认是看不到在VS中的编码格式,下面我介绍一种简单快

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(2) (*****生成数据结构类的方式特别有趣****)-CSDN博客 做完了数据结构类,该做一个存储类了,也就是生成一个字典类(只是声明)  实现和上一节的数据结构类的方式大同小异,所

【Unity小技巧】URP管线遮挡高亮效果

前言 在URP渲染管线环境下实现物体遮挡高亮显示效果,效果如下: Unity URP遮挡高亮 实现步骤 创建层级,为需要显示高亮效果的物体添加层级,比如Player 创建一个材质球,也就是高亮效果显示的材质球找到Universal Renderer Data Assets 4.在Assets上添加两个Render Objects组件 第一个做如下三处设置 指定遮挡层级指

【Unity面经】实习篇:面试官常问的一百个面试题

👨‍💻个人主页:@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 专栏交流🧧🟥Unity100个实战基础✨🎁🟦 Unity100个精华一记✨🎁🟩 Unity50个demo案例教程✨🎁🟨 Unity100个精华细节BUG✨🎁🟨 Unity100个面试题✨🎁 文章