游戏介绍:
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!
玩家动作

玩家行为
演示视频:演示视频
项目链接:https://github.com/Huthanble/Unity_Pastor_and_Devil
先在Unity中创建好各个所需部件的预制
步骤:1、先在Unity中使用GameObject创建一个3D物体
2、创建一个Material,用来给物体进行表面的上色或者挂载皮肤
3、将Material拖到3D物体上面,就完成了初步的搭建
4、将创建好的物体保存到创建的文件夹Prefabs中

预制好的游戏物件

GUI中所有的Scripts
需要有一个标题,,一个提示输赢的文本框,以及界面的左上角需要一个记录游戏时间的计时器(防止玩家一直卡关,也可以增加解密游戏难度)

因此先绘制好各个文本框的位置,并在Start函数中实现文本框及文字的风格定义(设置字体大小或是颜色,具体代码就不放出来了)
在该类中,拥有一个记录时间的time公共属性和记录输赢提示的gamemessage公共属性,在之后的主控制类中可以对这两个变量进行更改以达成游戏界面的实时更新

Model中所有的Scripts
在这个部分中主要实现了两个类型的功能
包含各个对象的模块,每个模块中定义了对象的类
包含自定义的部件Click,用于处理点击事件
需要实现的对象大致有三种:角色(牧师或者魔鬼),船,河以及岸边(不能被点击)
前两种对象都要能够被玩家点击,而最后一种则不用,因此需要实现一个ClickAction来统一管控这些对象的点击行为

Click类主要是用来处理用户的点击事件,而其中的ClickAction则是一个需要被实现的接口,其中包含一个Dealclick函数,主要是用来存放点击之后的具体逻辑实现
因为船或是角色被点击之后需要处理的行为逻辑不同,因此具体的实现逻辑需要在这两个类中具体实现,并将创建的ClickAction对象传递到Click中,就能够实现动态的行为匹配
PS:虽然Click应该是关于动作逻辑的实现,应该放在之后的行为逻辑或者控制类中,但由于Click需要作为这些可点击对象的最根本组成组件,且还没有进行具体的实现,因此也作为了一种静态的属性(组件)看待

船的类
因为这个部分主要是实现对象的静态属性,所以各个对象的实现方法都几乎一致(除了可点击组件要添加Click之外),因此其他的模型代码就不过多赘述(具体实现可以查看github)
对于船类来讲,需要设定好它的位置,大小,id,并且在该游戏中,角色可以上船,船也可以在左右岸边进行移动,所以需要添加船上的牧师或者魔鬼的数量,以及是否在右岸的判断布尔值(之后进行移动的逻辑需要使用)

游戏中所有对象的位置存储
利用一个位置类来存储所有物体的初始全局静态位置变量,集中管理可以方便之后的调用或是更改

所有行为逻辑的Scripts
在这个部分主要实现了对于具体行为逻辑的实现,各个Action部分各司其职,形成小的组件进行组合,可以更易于管理
它们的关系如下(加上了之后的控制类,可以让关系显得更加完整)

UML图
(一)SSAction
SSAction是这个部分中最基础也是最顶层的一个类,单个动作类和组合动作类都继承自它,是一个不需要GameObject对象的ScriptableObject类,包含了一个回调函数的接口ISSActionCallback

ISSActionCallback接口主要用来实现消息的传递,当一个动作进行完毕之后,动作的执行类就可以向接口传递动作执行完成的消息,而完成之后的一些参数设置以及动作就交由顶层的这个动作管理器来处理,层次更低的执行类就只用管理动作之中的逻辑,而不用考虑之后的状态转移
(二)SSActionManager
SSActionManager是动作管理者基类,用于提供一些接口给予真正的动作管理类使用(即之后继承该类的CCActionManager)
RunAction函数用于运行一个动作,并将该动作添加到动作队列之中

运行动作
动作队列也是这个类的核心逻辑之一,主要用来管理等待被加入的动作队列waitingAdd和等待被删除的动作队列waitingDelete

在每一帧的Update函数中主要进行两个操作:
将waitingAdd中的动作保存到字典中
运行字典中的动作,将完成的动作加入到waitingDelete队列中
(三)CCMovetoAction
该类继承自SSAction,主要用来处理单个动作的移动,用到了Vector3.MoveTowards函数,提供一个起点,一个终点,以及移动的速度,就可以实现一个对象的移动

(四)CCSequenceAction
该类与CCMovetoAction要实现的目的相同,但不同点在于该类处理的动作是组合移动,例如牧师要从岸上到达船上,需要先将牧师向右平移,然后再向下平移,因此这一个完整的动作之中需要有两次移动
主要的实现逻辑在于对于每一个执行的单步运动,需要一直向下执行下一个动作,直到所有动作都执行完则再向它自己的动作管理器传递消息

(五)CCActionManager
该类实现了船只和角色的具体移动逻辑,通过调用CCSequenceAction(角色)和CCMovetoAction(船只),传入移动开始的位置,目标位置,以及移动的速度,就可以实现具体的移动过程

(六)SSDirector
该类作为游戏场景的导演,继承至 C# 根对象,所以不会受 Unity 引擎管理,也不要加载,实现对一个场景的所有基础资源的把控(FPS,场景切换,场景获取……),所以它需要和一个ISceneController接口(场记,该游戏中的场记是FirstController)进行关联,用来获取当前管理的场景


所有控制逻辑的Scripts
(一)对象控制(船,角色,两岸)
对这三种游戏对象来说,在游戏过程中需要控制它们的创建以及获取
对于船来说,需要实现将角色添加或是删除到船上的逻辑,对于两岸来说,也需要实现将角色添加或是删除在岸上的逻辑(实际上就是对角色的计数进行修改,以及目标,起点位置,具体的移动代码已经在CCActionManager中实现)
所以两者在实现方式上有异曲同工之妙,此处只演示船的实现代码(角色的控制甚至不需要更改这些参数,所以除了创建获取就没有其他需要实现的函数)

创建
通过获取初始位置来创建一个船的游戏对象,并为Click组件中的DealClick动态设置具体的函数(这个函数实现因对象的不同而不同)

移动到船
因为船上有两个空位,因此有一个长度为2的一维数组,用来表示是否有角色在船上,通过对0或1的设置就可以完成角色的上下船
最后通过全局变量对岸的位置和船的位置,来计算出角色移动时的起始位置和终点

移动上岸
同理,将角色从船上移动到岸上也是这个流程,而计算位置的那一部分则放在了SideController类中上岸的部分(与角色上船同理)
(二)裁判类
裁判类是用来实时判断游戏的胜负,专门做游戏状态的检测,在游戏结束时通过回调通知FirstController场记的类

具体的判断逻辑
(三)主场景控制
FirstController是所有控制类的主管,负责游戏的对象加载,具体的移动起点与终点的计算,游戏场景的启动等

裁判类的回调
因为将原本是在主场景控制中的check函数分离了出去,所以需要一个回调函数来实时接收裁判类传来的输赢信息,如果状态变换了的话,就需要Firstcontroller来负责之后的场景变化以及游戏停止

资源加载
以下是具体移动船和移动角色时的逻辑函数(用于确定移动的起点以及终点位置)

移动船

角色上岸

角色上船

零碎组件
除此之外,还有一些用于添加游戏组件以及实时更新游戏时间的函数,用于左上角UI显示的时间倒计时