牧师与魔鬼(牧师与魔鬼的对话视频)
牧师与魔鬼
0.前言
本博客是中山大学2021学年3D游戏编程与设计课程的第二次课程作业。实现游戏“牧师与魔鬼”,要求使用MVC架构。有许多博客文章与这项任务有关,但大多数都比较简短。即使看完了,还是很难对整个作业的实施过程有一个清晰的认识。所以希望在实施过程中根据自己的理解写一篇详细的博客,为以后有需要的弟弟妹妹们提供一些帮助。感谢这位前辈(https://blog.csdn.net/weixin _ 43867940/文章/详情/108818301)的博客。没有他的启发,我不可能完成这项任务。
设计
总体文档框架如下
1.1资源的设计
这里的资源设计指的是预制件的设计。每种类型的游戏实体,只需要一个预制件,所以在这个游戏中,需要四个预制件:船、岸、牧师、恶魔。为了简单起见,预制部件可以用立方体、球体和其他可以在Unity中直接创建的对象来代替。然后为每个预制件创建一个材质,将材质拖拽到对应的预制件上,最简单的资源创建工作就完成了。预制部件放置在资源/预置文件夹中,材料放置在资源/预置文件夹中。
1.2代码设计
根据工作需求,代码设计需要使用MVC架构。即需要实现模型-视图-控制器的分离。
模型:在游戏中,所有的实体都可以看作模型,可以是具体的,也可以是抽象的。其中,具体的模型有:船、岸、牧师、魔鬼。抽象模型是:点击和移动。模型只关注实体本身最基本的属性和方法,没有考虑它与其他模型的交互。比如一个船模,它有两个基本特征:可以创建,可以点击。至于船是怎么载客的,被点击后船做什么,这都不是模型这个层面应该关心的问题。再比如“动”的抽象模型。它最基本的属性和方法是:移动的速度和移动的目的地。至于它想动谁,并不是模型本身的惯性。在“点击”模型中,它只关注谁被点击了,而不关注点击触发的事件是什么。另外,根据我自己的理解,我还加入了一个“位置”模型,这个模型只是提前记录了游戏过程中所有可能的位置。
控制器:控制器用于控制模型的行为。因此,理论上,模型的每个副本都会有一个对应的控制器(位置模型除外)。例如,船模型将有一个BoatController,它将控制所有以船为目标的操作,例如当单击船时处理事件,向船添加一名乘客,以及向船减少一名乘客。必须注意的是,直接控制模型的控制器之间没有耦合关系,也就是说,BoatController只能知道目前船上有多少乘客,而不知道他们是谁(这些信息应该由每个RoleController保存)。而且因为移动的船要同时移动船上所有的乘客,BoatController不能直接移动乘客,所以实际移动船的逻辑不能直接写在BoatController里(下面应该写在哪里)。移动模型会有一个MoveController,控制谁要移动,负责判断当前是否有模型在移动。
此外,还有一类非常重要的控制者,即场景控制者和导演。游戏就像一场戏剧。剧中会有多个场景,一个导演贯穿所有场景。导演控制器必须写成singleton模式,这样可以保证所有控制器都属于同一个剧。每个场景都会有一个单独的场景控制器,但是在这个游戏中,只有一个FirstController。场景控制器管理场景中的所有模型控制器,并实现它们的综合行为。就这个游戏而言,FirstController将生成并管理:一个BoatController、两个LandController、六个RoleController和一个MoveController。这个场景中所有的综合行为都将在这个场景控制器中实现。例如:移动一艘船涉及到船和乘客的同时移动;流动角色涉及三个方面:离岸、登船和人的状态的改变;判断当前游戏是否成功。
最后,以I开头的控制器都是接口,用来规范同类型控制器应该具有的行为,比如IObjectContoller应该被所有模型的控制器继承,IScenceController应该被所有场景的控制器继承,IUserAction负责与视图通信的接口。
视图:这里的视图是GUI,它控制GUI上所有与3D游戏实体无关的组件。比如:标题,重启按钮,游戏成功或失败时的提示。
3.工作流程
为了进一步说明代码的工作原理,我将以游戏行为“先点击一个牧师登船,再点击船移动,游戏判定失败”为例介绍代码的工作流程。执行IFirstController中的唤醒功能和UserGUI中的启动功能。前者初始化第一个场景中的所有3D对象,后者初始化2D GUI以点击一个牧师。click事件由一个牧师模型(Role.cs)的Click组件(Click.cs)中的OnMouseDown函数捕获,Click事件传递给对应的RoleController.cs(具体传递机制请参考RoleController.cs中的CreatModel函数)。RoleController.cs调用DealClick函数来解决Click事件。但是上面已经解释过了,RoleController.cs并不能真正解决这个点击事件,所以DealClick的做法是通过唯一导演找到管理他的场景控制者,让场景控制者来解决这个问题。(请注意,场景控制者可以直接找到他管理的模型控制者,但模型控制者只能通过导演找到其上级场景控制者。)场景控制器调用指定的函数(本例中是MoveRole函数),调动所有相关的模型控制器,真正处理click事件。
再次点击船,此时的响应逻辑和上面一模一样。船模型中的click事件会一步一步的传递给场景控制器,click事件会被场景控制器的MoveBoat函数处理。执行MoveBoat功能时,会检查当前游戏状态,判断游戏失败。那么场景控制器中的gameState变量将被设置为FAILED,并且不再允许它处理click事件。UserGUI中的OnGUI函数会一直监听场景控制器中gameState的值,当它发现这个值已经变得失效时,就会画出文字“youfailed”。
4.资源
4.1操作模式
在Unity中创建新的3D项目,并用以下链接中提供的Assert文件夹替换项目的Assert文件夹。将Assert/Scripts/UserGUI拖动到MainCamera创建一个新的空GameObject,将FirstController拖动到GameObject,然后单击Run。
4.2代码
https://gitee . com/GallonC/unity home work/tree/master/home work 2/Assets
4.3视频
https://www.bilibili.com/video/BV1634y1m75A? SPM _ id _ from = 333 . 999 . 0 . 0