3D游戏编程与设计作业04

Source
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/xxiangyusb/article/details/101618889

基本操作演练

  • 下载Fantasy Skybox FREE, 构建自己的游戏场景
    由于个人主观觉得Fantasy Skybox FREE不是特别好看,故而以下改用资源Wispy Skybox:在Asset Store中找到资源Wispy SkyboxDownloadImport。导入完成之后,直接将Wispy Skybox目录下的Materials中的一个Skybox(自己选一个喜欢的)拖到场景中赋值
    skybox
  • 写一个简单的总结,总结游戏对象的使用
    Unity中的所有实体都属于游戏对象(GameObject),其主要操作有如下几种:
    • 创建游戏对象

      • 方法一:菜单->GameObject->{3D Object(Cube, Sphere, Capsule, ……),Camera摄像机,Light光源,……}
      • 方法二:使用GameObject.CreatPrimitive()函数来创建Unity自带的模型
        GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Sphere);	
        
      • 方法三:使用Instantiate()函数进行游戏物体的实例化
        boat = Object.Instantiate(Resources.Load("Boat", typeof(GameObject)), boat_position, Quaternion.identity) as GameObject;
        
    • 获取游戏对象

      • 方法一:在代码里声明public类型GameObject,在Inspector属性栏中指定游戏对象(往往是直接将预制Prefab拖过去赋值)
      • 方法二:通过对象名称获取对象,例如obj = GameObject.Find("Sphere");
    • 为游戏对象添加组件和修改组件

      新创建的游戏对象本身仅仅具有很少的一部分属性,为了让它具备一些其他的功能,就必须给它添加游戏组件;
      添加游戏组件的时候,需要使用AddComponent()方法,而删除某一个组件的时候需要使用Object.Destory()的方法;
      需要删除的游戏对象或组件,如果删除的是一个对象,则该对象的所有组件将一并被删除;

      例如,

      move = boat.AddComponent(typeof(Move)) as Move;
      click = boat.AddComponent(typeof(Click)) as Click;
      
    • 克隆游戏对象

      • 首先,在场景中创建被克隆对象
      • 在主摄像机上挂在复制脚本——使用Instantiate()方法实例化游戏对象
      • 在脚本的Inspector中指定被克隆对象(脚本中将其设为public)

编程实践-牧师与魔鬼小游戏进阶版(动作分离版)

程序要求

  • 使用 C# 集合类型 有效组织对象

  • 整个游戏仅主摄像机和一个Empty对象,其他对象必须代码动态生成!!! 。 整个游戏不许出现Find游戏对象,SendMessage这类突破程序结构的通讯耦合语句。违背本条准则,不给分

  • 请使用课件架构图编程,不接受非MVC结构程序

  • 注意细节,例如:船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件!

  • 2019新要求:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束

  • 代码改进:

    • 设计裁判类(提取出Check方法)
  • 动作分离:

    • 把每个需要移动的游戏对象的移动方法提取出来,建立一个动作管理器来管理不同的移动方法。
    • 对于上一个版本,每一个可移动的游戏对象的组件都有一个Move脚本,当游戏对象需要移动时候,游戏对象自己调用Move脚本中的方法让自己移动。而动作分离版,则剥夺了游戏对象自己调用动作的能力,建立一个动作管理器,通过场景控制器(在我的代码设计中是Controller)把需要移动的游戏对象传递给动作管理器,让动作管理器去移动游戏对象。
    • 当动作很多或是需要做同样动作的游戏对象很多的时候,使用动作管理器可以让动作很容易管理,也提高了代码复用性。

游戏场景制作

资源下载:

  • Devil animated character【恶魔】
  • Toony Tiny RTS Demo【牧师】
  • Lowpoly Paper Boat【船】
  • Rounded Blocks【河岸】
  • Wispy Skybox【天空(背景)】

场景布置:

  • Skybox:参见前面所述
  • 静态游戏对象:主摄像机和空对象MAIN(挂载脚本Click.cs,UseGUI.cs,Controller.csSSActionManger.cs
  • 预制:BoatBankPriest(添加组件Animator),Devil(添加组件Animator),如下图所示:
    AssetPrefabs
    事实上,为了让代码动态创建对象,我们需要将这些预制放在Resources目录下。
    此外,为了使BoatPriestDevil正常响应点击事件,需要“外包”一个游戏对象,例如对于Priest,用一个Capsule作为其父亲,将资源商店中下载的预制作为Capsule的子对象;并且为了达到较好的显示效果,设置一定的位置偏移和大小比例,这里我采用的是:
    对于Priest:
GameObject Position Scale
Capsule (0,0,0) (3,3,3)
从资源商店中下载的预制对象(TT_RTS_Demo_Character) (0,-1.2,0) (2,2,2)

对于Devil:

GameObject Position Scale
Capsule (0,0,0) (1.5,3,1.5)
从资源商店中下载的预制对象(devil@idle) (0.2,-1.4,0) (5,2.5,5)

对于Boat:

GameObject Position Scale
Cylinder (0,0,0) (15,1,8)
从资源商店中下载的预制对象(black_perl) (0,-1,0) (0.001,0.01,0.002)
  • 游戏预制对象坐标和大小设置
GameObject Position Rotation Scale
Main Camera (0,1,0) (0,0,0) (1,1,1)
MAIN (0,0,0) (0,0,0) (1,1,1)
Boat (-10,0,40) (0,10,0) (0.01,0.01,0.01)
BoatSeat1 (-6,1,40) / /
BoatSeat2 (-14,1,40) / /
Bank (-45,0,70) (0,0,0) (40,5,60)
Priest / (0,90,0) (3,3,3)
Devil / (0,90,0) (1.5,3,1.5)
PBankPosition1 (-28,3,70) / /
PBankPosition2 (-37,3,70) / /
PBankPosition3 (-46,3,70) / /
DBankPosition1 (-30,3,40) / /
DBankPosition2 (-26,3,40) / /
DBankPosition3 (-22,3,40) / /

动态生成代码时,另一侧的坐标只要做x坐标的对称运算即可

游戏场景预制结果:
游戏场景预制结果图

代码设计

MVC架构——界面人机交互程序设计的一种架构模式
将程序分为三部分:

  • 模型
    • RoleModel.cs
    • BoatModel.cs
    • BankModel.cs
    • PlayAnimation.cs
  • 控制器
    • SSDirector.cs
    • ISceneController.cs
    • IUserAction.cs
    • SSAction.cs
    • SSMoveToAction.cs
    • SequenceAction.cs
    • ISSActionCallback.cs
    • SSActionManager.cs
    • Controller.cs
    • Click.cs
    • MySceneActionManger.cs
    • Judge.cs
  • 界面
    • UserGUI.cs

与上一版本的比较:

  • 去除了Move.cs脚本
  • 用户动作列表中去除了Check(),而把这一动作交给Judge实现
  • 增加了Judge.cs脚本和相应的动作管理的脚本

模型

模型主要是描述数据对象及关系

  • RoleModel.cs:牧师/魔鬼角色模型
  • BoatModel.cs:船模型(记录了船的位置、船上的空位位置)
  • BankModel.cs:河岸模型(记录了河岸位置、河岸上的空位位置)
  • ISceneController:场记(接口)
    其职责大致如下:
    • 管理本次场景所有的游戏对象
    • 协调游戏对象(预制件级别)之间的通讯
    • 响应外部输入事件
    • 管理本场次的规则(裁判)
    • 各种杂务
      这里,我们用一个LoadResources()方法实现这些职责:
    public interface ISceneController //场记接口
    {
       void LoadResources();
    }
    
  • IUserAction:用户动作“列表”(接口)
    • 移动船
    • 移动角色
    • 重新开始
    public interface IUserAction //用户动作“列表”接口
    {
        void MoveBoat();//移动船
        void MoveRole(RoleModel role);//移动角色
        void Restart();//重新开始
    }
    
  • SSDirector:导演(单实例模式)
    创建SSDirector类,其职责大致如下:
    - 获取当前游戏的场景
    - 控制场景运行、切换、入栈与出栈
    - 暂停、恢复、退出
    - 管理游戏全局状态
    - 设定游戏的配置
    - 设定游戏全局视图
  • SSAction:动作基类
    SSAction是所有动作的基类。它集成了ScriptableObjects,代表SSAction不需要绑定GameObject对象,且受Unity引擎场景管理
  • SSMoveToAction.cs:移动动作实现(以speed的速度向target目的地移动)
  • SequenceAction.cs:组合动作实现
    SequenceAction继承了ISSActionCallback,因为组合动作是每一个动作的顺序完成,它管理这一连串动作中的每一个小的动作,所以当小的动作完成的时候,也要发消息告诉它,然后它得到消息后去处理下一个动作。SequenceAction也继承了SSAction,因为成个组合动作也需要游戏对象,也需要标识是否摧毁,也会有一个组合动作的管理者的接口,组成动作也是动作的子类,只不过是让具体的动作组合起来做罢了。
  • ISSActionCallback.cs:动作事件接口
    作为动作和动作管理者的接口(组合动作也可以是动作管理者),动作管理者继承这个接口,并且实现接口的方法。当动作完成时,动作会调用这个接口,发送消息通知动作管理者对象动作已完成,然后管理者会对下一个动作进行处理。
  • SSActionManager.cs:动作管理基类
    管理SequenceActionSSAction,可以给它们传递游戏对象,让游戏对象做动作或是一连串的动作,控制动作的切换。SActionManager继承了ISSActionCallback接口,通过这个接口,当动作做完或是连续的动作做完时候会告诉SSActionManager,然后SSActionManager去决定如何执行下一个动作。
  • Click.cs:点击事件脚本(挂载在MAIN对象上即可)
  • MySceneActionManger.cs:移动动作管理实现
    船的移动是一个SSMoveToAction动作就可以实现,而角色的移动需要两个SSMoveToAction动作组合(先垂直后水平移动或先水平后垂直移动)。然后设置当前场景控制器的动作管理者为MySceneActionManger,这样场景控制器就可以调用动作管理器的方法实现不同游戏对象的移动了。
    • Judge.cs:裁判类,当游戏达到结束条件时,通知场景控制器游戏结束
      设置当前场景控制器的裁判为Judge(传入当前场景控制器的boat构建Judge),将IUserAction中的Check()方法迁移至本类中,其中的start_land上的牧师与魔鬼个数,end_land上的牧师与魔鬼个数由场景控制器调用本类的Check()方法时传入即可。

控制器

  • Controller.cs:接受用户事件,控制上述各模型的变化(挂载在MAIN对象下)

用户界面

  • UserGUI:显示模型,将人机交互事件交给控制器处理
    • 处理鼠标点击事件(交给控制器Controller
    • 渲染GUI(OnGUI()),接收事件

详细代码参考我的github仓库:代码传送门

游戏演示视频

前往b站查看游戏演示视频

参考资料

前辈的博客
前辈的博客
前辈的博客