Learning Game Development

题图

This article is a record of my journey to learn Game Development and it will keep updating.

这里记载了我零基础学习游戏开发的过程。

我从 2017 年 1 月开始自学游戏开发,在此之前并无任何游戏开发经验,但有三年使用 Java 进行 Web 和大数据系统开发的经历,在本科期间也学习过 C、C++ 和 C# 编程课程。即便如此,游戏开发所涉及的庞大知识体系仍然给初学者带来了巨大挑战。面对全新的知识领域,我决定将每个学习阶段中的知识要点记录下来以供回顾。不过这篇文章更像是一个索引,因为一些篇幅过长的详细专题内容不会被收录到这篇文章中,而是以链接的形式存在。总之,我会持续记录和更新,看看这些有趣的知识将把我带向何方。: )


Phase 1:认识 Unity

  • 认识 Unity 编辑器
  • 了解 Unity 脚本
  • 制作 Hello World 程序
  • 了解 Unity 中的光照
  • 使用地形编辑器
  • 了解粒子系统

笔记:https://github.com/Caizc/cai-knowledge-base
参考:《VR开发实战》

Phase 2:三维空间的数学基础

空间数学的基本概念:

  • 点、矢量、标量
  • 笛卡尔坐标系(左手坐标系、右手坐标系)

矢量运算:

  • 矢量和标量的乘法、除法
  • 矢量的加法和减法
  • 矢量的模
  • 矢量的归一化
  • 矢量的投影
  • 矢量的点积
  • 矢量的叉积

三角函数

笔记:https://github.com/Caizc/cai-knowledge-base
参考:《VR开发实战》

Phase 3:VR Hello World

  • Vive 开发环境搭建:

    • 获取 SteamVR 插件
    • 将 SteamVR 插件导入 Unity
    • 认识 SteamVR 包
  • 实现一个最基本的 VR 场景

  • 了解常用的 SteamVR 组件:

    • 预制体 CameraRig
    • 手柄控制脚本 SteamVR_TrackedController.cs
    • 手柄射线脚本 SteamVR_LaserPointer.cs
  • 实现手柄与 VR 中物品的简单交互:拿、放、扔

  • 实现手柄发射器效果

笔记:https://github.com/Caizc/cai-knowledge-base
参考:《VR开发实战》

Phase 4:简单的解谜游戏

  • 游戏策划和场景搭建(直接使用素材)
  • 编写脚本并关联到游戏物品
  • 了解 Prefab 预制件的制作和使用
  • 调试游戏交互和运行效果

源码:https://github.com/Caizc/learn-puzzle
笔记:https://github.com/Caizc/cai-knowledge-base
参考:《VR开发实战》

Phase 5:第一人称射击游戏

  • 创建怪物:

    • 使用导航组件为怪物添加自动寻路
    • 创建动画控制器和添加行为控制逻辑
    • 添加怪物攻击逻辑
  • 添加游戏管理类

  • 实现玩家攻击逻辑:

    • 创建手枪动画控制器
    • 添加手枪开枪逻辑
  • 实现游戏重玩逻辑

  • 添加背景音乐与调节音量

要点:

  • 《VR开发实战》书中有很多刊误,有些步骤按照书中所写无法执行,需要变通,自己查阅资料和摸索得到正确的做法。
  • 原 Demo 中使用的一些 API ,在新版本的 Unity 中已被标记为「过时的」,在学习时需注意同步更新,使用最新的 API 进行脚本编程。
  • 学会如何在 Unity 和 MonoDevelop 上调试游戏程序和脚本,这是最重要的。

源码:https://github.com/Caizc/learn-fps
笔记:https://github.com/Caizc/cai-knowledge-base
参考:《VR开发实战》

Phase 6:旅游观光应用

  • 实现游客在虚拟空间中的移动传送
  • 添加多场景管理

    • 实现场景的动态加载和卸载
  • 实现通过手柄方向盘对场景中光照角度的控制

  • 区分场景中可传送面和不可传送面
  • 修改手柄射线为抛物线

未解决的 bug :

  • 使用 SteamVR 进行调试时,预制件 CameraRig 在编辑模式和 Play 模式下,Inspector 中的组件列表并不一致,这会导致运行时的效果与预期不一致,这个问题暂时还没有头绪,需要更深入地了解 Unity 引擎的运行机制才得以解答。
  • 基于以上问题的存在,为了调试效果,尝试在 Play 模式下动态调整 Camera(head) 为 Enable,勾选后发现 Console 中持续报错:Assertion failed on expression: 'IsMatrixValid (matrix)',且画面中手柄出现刷新卡顿,暂未找到解决方法。

要点:

  • 使用 Visual Studio 作为 Unity 中 C# 脚本的编辑器进行开发和调试,提高生产效率。
  • 安装 Visual Studio Tools for Unity,以便在 Visual Studio 中调试 C# 脚本。

源码:https://github.com/Caizc/learn-tourism
笔记:https://github.com/Caizc/cai-knowledge-base
参考:《VR开发实战》

Phase 7:将单机版的第一人称射击游戏改造成联机游戏

  • 了解 Unity 提供的多玩家在线工具 UNET ( Unity Networking ),及其两个类型的API:HighLevelAPI 和 LowLevelAPI

  • 理解 HLAPI 中的几个重要概念:

    • 主机、本地客户端、远程客户端、本地用户、权限
    • 主机与客户机之间的通信方式和数据同步方式
    • 如何产生联机物体
  • 网络管理组件 NetworkManager

    • 在场景中添加网络管理组件 NetworkManager 以及简单的联机控制 UI —— NetworkManagerHUD
    • 两个重要的属性:SpawnInfo 和 Registered Spawnable Prefabs
  • 添加玩家控制器来控制玩家的所有网络行为,使用网络组件的特有属性来实现主机和客户端之间的远程调用和数据同步

  • 重写游戏管理逻辑
  • 为怪物添加网络通信组件
  • 设置玩家的固定出生点

未解决的 bug :

  • 按照书中步骤配置好游戏对象以及控制脚本后,运行游戏后 Console 持续报错:"There are no audio listeners in the scene. Please ensure there is always one audio listener in the scene.",场景中缺少 AudioListener 组件。应为摄像机对象添加一个 AudioListener 组件。
  • 另外,游戏不能正常运行,貌似存在不少 bug。
  • 暂时搁置这个 demo,待更加深入地学习了 Unity 的运行机制后,再回过头来解决。

笔记:https://github.com/Caizc/cai-knowledge-base
参考:《VR开发实战》

Phase 8:了解 Cardboard 开发、Oculus 开发和 MR 视频拍摄

阅读 Cardboard 开发、Oculus 开发和 MR 视频拍摄的入门知识,由于无相应硬件设备,暂时不做更深入的了解。

参考:《VR开发实战》

Phase 9:实现键鼠操控玩家对象在 3D 空间中自由移动

  • 分解要构建的项目所需的步骤
  • 创建游戏场景:

    • 创建地板、外墙和内墙
    • 设置场景中的灯光
  • 编写用鼠标控制玩家角色视野的脚本 MouseLook.cs

    • 使用 Input.GetAxis("Mouse X") 获取鼠标的水平坐标值,实现玩家视野的水平旋转
    • 使用 Input.GetAxis("Mouse Y") 获取鼠标的竖直坐标值,实现玩家视野的竖直旋转
    • 实现玩家视野的同时水平和竖直旋转
  • 编写用键盘控制玩家角色移动的脚本 FPSInput.cs

    • 使用 Input.GetAxis("Horizontal")Input.GetAxis("Vertical") 来获取键盘的水平和竖直方向的值,从而实现玩家的前后左右移动
    • 运用 Time.deltaTime 来避免玩家移动速率的「帧率依赖」,统一所有计算机上的移动速率
    • 使用 Character Controller 组件来实现碰撞检测
    • 给玩家对象增加重力,以保证其不会在空间中到处乱飞

要点:

  • 在 3D 坐标空间中,使用坐标位置和向量来表达位置和移动,理解「左手坐标系」和「右手坐标系」的区别。
  • 区分「本地坐标」「全局坐标」
  • 在 Unity 中,我们一般选择「米」作为坐标系中的长度单位,但这不是强制的,只要你在整个场景中保持使用一样的单位即可。
  • 使用 Character Controller 组件来让游戏对象表现得像一个玩家角色。
  • Input 类有一系列方法用于处理输入设备(例如鼠标、键盘),使用 float Input.GetAxis(string axisName); 来获取相应坐标轴上的值,坐标轴的名称和设置可以在 Unity 菜单 Edit > Project Settings > Input 中找到。
  • 了解欧拉角(Euler angle)四元数(Quaternion)的概念。
  • 在脚本的类声明之前添加 RequireComponent() 方法来确保脚本附加了其他需要的组件;添加 AddComponentMenu() 方法以在 Inspector 底部的 Add Component 组件菜单中添加一个自定义的组件链接。

源码:https://github.com/Caizc/learn-fps-play-with-km
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 10:为 FPS 游戏实现玩家射击和具有简单 AI 的敌人

  • 通过射线射击
  • 脚本化反应的目标
  • 基本漫游 AI
  • 产生敌人预设
  • 通过实例化对象进行射击

源码:https://github.com/Caizc/learn-fps-play-with-km
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 11:为 FPS 游戏开发图形

  • 了解美术资源
  • 构建基础 3D 场景:白盒
  • 使用 2D 图像给场景贴图
  • 使用贴图图像产生天空视觉效果
  • 使用自定义 3D 模型
  • 使用粒子系统创建效果

源码:https://github.com/Caizc/learn-fps-play-with-km
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 12:2D 记忆力游戏

  • 设置 2D 图像
  • 构建卡片对象并让它响应单击
  • 显示不同的卡片图像
  • 实现匹配和匹配得分
  • 重启按钮

源码:https://github.com/Caizc/learn-memory-game-2d
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 13:为 3D 游戏创建 2D 的 GUI

  • 认识 Unity 的 GUI 系统
  • 创建用于界面的画布
  • 通过锚点定位 UI 元素
  • 为 UI 添加交互(按钮、滑动条等)
  • 从 UI 广播和侦听事件

源码:https://github.com/Caizc/learn-fps-play-with-km
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 14:实现第三人称 3D 游戏中玩家的移动和动画

  • 将角色模型导入到场景
  • 实现摄像机控制,以观察角色
  • 编写脚本,让玩家能够在地面上跑
  • 添加能够跳跃、移动的脚本
  • 基于移动来播放模型的动画

源码:https://github.com/Caizc/learn-third-person-view-game
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 15:在游戏中添加交互设施和物品

  • 编写脚本,让玩家控制门的开关(通过按键或者进入触发区域)
  • 创建响应物理碰撞的箱子
  • 创建可收集的物品供玩家存储在仓库中
  • 管理仓库数据和游戏状态
  • 装备及使用仓库中的物品

源码:https://github.com/Caizc/learn-third-person-view-game
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 16:将游戏连接到互联网

  • 修改天空盒的 shader 来实现从晴天向阴天过渡的效果
  • 从互联网服务获取实时的天气数据
  • 解析 XML 或 JSON 格式的天气数据并应用于场景中的天空效果
  • 从互联网下载图像并应用在场景中的物品上
  • 将游戏中的数据发送到服务器上

源码:https://github.com/Caizc/learn-online-game
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 17:为游戏添加音效和音乐

  • 导入音效,并调整导入设置
  • 将 2D 音效用于 UI,3D 音效用于场景
  • 编写音频管理器控制场景中的音量
  • 优化背景音乐的加载
  • 分别控制音乐和音效的音量
  • 实现不同背景音乐切换时的淡入淡出效果

源码:https://github.com/Caizc/learn-fps-play-with-km
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 18:简单但功能完整的 RPG 游戏

整合其他项目所学的内容,创建一个功能完整的角色扮演游戏,游戏主要功能如下:

  • 一个俯视的玩家视角,通过鼠标点击来控制角色移动
  • 通过鼠标点击来操作的机关设施
  • 散落在场景中的、可收集的装备物品
  • 管理玩家装备的 GUI
  • 在场景中随意游荡并可以攻击玩家角色的敌人
  • 可以保存游戏和恢复游戏进度
  • 游戏具有多个关卡,需要依次通关来完成游戏
  • 合理的、可拓展的代码架构

源码:https://github.com/Caizc/learn-rpg
参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 19:针对不同的游戏平台构建游戏

  • 为桌面平台 Windows 和 Mac 构建游戏
  • 为 Web 构建游戏
  • 为移动平台 iOS 和 Android 构建游戏

参考:《Unity in Action: Multiplatform Game Development in C# with Unity 5》

Phase 20:Roll a Ball

  • 使用键盘控制小球的前后左右自由移动
  • 实现第三人称视角,摄像头以固定角度跟随小球移动
  • 创建可收集的物品,在被小球碰撞到时消失并累计得分
  • 创建 UI,显示玩家得分和提示信息

要点:

  • 在脚本中,使用 COMMAND + ' / CTRL + ' 快捷键来快速查看指定对象的 Scripting Reference。
  • 保持玩家对象和摄像头的距离不变,就可以实现一个简单的第三人称视角。编写脚本来保持它们之间的固定距离,而不是将摄像头作为玩家对象的子对象,因为连结在一起的父子对象会同步旋转信息,这会导致玩家对象旋转时带动摄像头一起翻滚,这不是我们想要的效果。
  • 勾选 Collider 的 IsTrigger 属性,将碰撞体变成碰撞触发器,并且使用 OnTriggerEnter 事件函数来响应碰撞。
  • 使用 Tag 标签来区分可收集和不可收集的物体对象。
  • Unity 的物理引擎不允许两个碰撞体互相重叠,它通过计算物体的速度和旋转来确定碰撞体是静态还是动态的。静态的物体不响应碰撞,而动态的物体则会受到碰撞的影响。
  • 在场景中,静态物体不可移动,而动态物体可以移动。物理引擎为了优化性能,会缓存静态碰撞体的信息,而不用在游戏的每一帧中重新计算。但如果我们改变了静态碰撞体的位置、旋转等,物理引擎会重新计算以刷新缓存,反而更消耗性能资源。可以为游戏对象添加 Rigidbody 组件,同时拥有 Collider 组件和 Rigidbody 组件的游戏对象会被视为动态的,而不会影响静态碰撞体的缓存。
  • 拥有 Rigidbody 的组件默认会受重力的影响而往下坠,可以去除勾选 Use Gravity 属性,但该物体仍会受到力的影响,更好的做法是勾选 Is Kinematic 属性来声明它是一个运动的刚体物体,这样它就可以移动并且不受力的影响了。

参考:Roll-a-Ball tutorial - Unity-Learn-Tutorials

Phase 21:Space Shooter(项目设置,初始化场景设置和玩家对象)

Space Shooter 是一款街机风格的竖版射击游戏,玩家可以发射激光炮弹摧毁前方滚落的陨石和狡猾的敌人。

Space Shooter 游戏试玩链接:
http://caizicong.com/timecapsule/gamesdir/space_shooter/index.html
(在 PC/MAC 上使用 Chrome、Safari 或 Firefox 浏览器打开运行,游戏文件大小 9M,加载过程需 1-3 分钟,Have Fun!)

  • Project settings:项目设置为 Web 平台应用(在 Unity 5.5.2 中应选择目标平台为 WebGL),屏幕分辨率为 600*900。
  • Player GameObject:为玩家对象添加自定义网格的 Mesh Collider,设置为 Is Trigger,添加 Rigidbody 组件,添加粒子系统 prefab。
  • Camera:设置主摄像头为正交投影,调整其高度、旋转及视野范围,以适应本竖版游戏画面。
  • Lighting:自定义场景中的光照设施,配置主光源、辅助光源和边缘光源,以丰富游戏对象的光照效果。
  • Background:设置游戏的背景图片,调整图片材质的 Shader 类型。

源码:https://github.com/Caizc/game-space-shooter
参考:Space Shooter tutorial - Unity-Learn-Tutorials

Phase 22:Space Shooter(玩家移动、攻击,游戏边界、障碍物,粒子系统,游戏控制器)

  • 获取键盘输入,控制玩家对象的移动,限制游戏对象的活动区域
  • 创建玩家武器,自定义 Collider 适应激光束的大小,编写脚本实现发射效果
  • 创建游戏边界,销毁离开边界的游戏对象
  • 创建游戏中的障碍,增加游戏难度
  • 添加粒子系统,打造爆炸特效
  • 添加游戏控制脚本,控制障碍物的生成,为玩家增加挑战

要点:

  • FixedUpdate()
    当处理具有 Rigidbody 组件的游戏对象或需要考虑物理计算时,必须在 FixedUpdate() 方法中编写逻辑,而不是在 Update() 方法中。

  • Random.insideUnitSphere
    Random 包中的该属性返回一个半径为 1 的球体中的一个随机点,也就是返回一个 Vector3 类型的值,其中 x,y,z 的值分别为 0~1 之间的一个随机浮点数。

  • Rigidbody 组件的角速度 angularVelocity,会受到 Angular Drag 属性的影响。添加 Rigidbody 组件时,Angular Drag 属性有一个大于零的默认值,因此组件的角速度会在它的影响下越来越小。

  • Destroy() 方法真正执行销毁的时机:首先标记需要销毁的对象,然后在每一帧的最后再统一执行销毁。

  • Quaternion.identity
    该变量表示「不旋转」或「没有旋转」。

源码:https://github.com/Caizc/game-space-shooter
参考:Space Shooter tutorial - Unity-Learn-Tutorials

Phase 23:Space Shooter(音效和背景音乐,玩家得分和游戏重玩,构建游戏)

  • 添加游戏中的音效和背景音乐,并调整音量,营造游戏氛围
  • 记录玩家的得分,使用 GUI 显示得分
  • 编写游戏结束和重玩的逻辑
  • 编译并打包游戏应用,将其部署到 Web 平台上供玩家试玩

要点:

  • 对象实例的本质是,某一特定场景内的对象拷贝;而 prefab 的本质是,对象的模板,可以在任意场景中多次实例化。因此,无法在 prefab 中保存某一特定场景内的对象实例的引用(因为对象实例无法脱离特定场景独立存在)。为了解决这个问题,可以在 prefab 被实例化了之后,在脚本中通过 GameObject.FindWithTag ("tagname") 来动态获得当前场景内的对象实例引用。

源码:https://github.com/Caizc/game-space-shooter
参考:Space Shooter tutorial - Unity-Learn-Tutorials

Phase 24:Space Shooter(滚动背景,不同类型的障碍物,简单 AI 的敌人)

  • 将游戏的固定背景修改为滚动背景,使用 Mathf 类中的 float Repeat(float t, float length) 方法实现,该方法 Loops the value t, so that it is never larger than length and never smaller than 0.
  • 添加不同 artwork 的 asteroid,将逻辑组件附加在单独的 parent GameObject 上,而将 artwork 作为其 child GameObject,可以在扩展或更换游戏对象的 artwork 时,不需要重新处理其逻辑组件,是应该谨记的最佳实践
  • 添加会主动攻击玩家的敌人,增加游戏难度。敌人会在躲避模式和追踪玩家并攻击模式之间随机选择

要点:

  • 在需要实现简单的重复执行逻辑时,例如让敌人以固定间隔持续开火,可以使用 InvokeRepeating() 方法替代 coruotine 来简单实现。

    public void InvokeRepeating(string methodName, float time, float repeatRate);
    Invokes the method methodName in time seconds, then repeatedly every repeatRate seconds.

  • Mathf 的两个实用方法:

    • public static float Sign(float f);
      Return value is 1 when f is positive or zero, -1 when f is negative.

    • public static float MoveTowards(float current, float target, float maxDelta);
      Moves a value current towards target.

  • 一切问题都是数学问题。掌握好空间几何和线性代数等数学知识,在实现游戏中的动态逻辑时,会得心应手,否则将寸步难行。

源码:https://github.com/Caizc/game-space-shooter
参考:Space Shooter tutorial - Unity-Learn-Tutorials

Phase 25:Survival Shooter(环境设置,玩家角色,摄像头,敌人)

Survival Shooter 是一款第三人称生存射击游戏。游戏的玩家主角是一个身陷噩梦的小男孩,发现自己房间中所有的布偶都变成了行尸走肉,它们蜂拥着追逐玩家,玩家只有通过一把能够发射激光的玩具枪击退一波又一波的敌人,让自己生存下去。

Survival Shooter 游戏试玩链接:
http://caizicong.com/timecapsule/gamesdir/survival_shooter/index.html
(在 PC/MAC 上使用 Chrome、Safari 或 Firefox 浏览器打开运行,游戏文件大小 49M,加载过程需 5-10 分钟,Have Fun!)

  • Environment setup

    • Environment: 设置关卡场景
    • Layer: 设置场景的 Layer 为 Shootable
    • Background Music: 添加游戏背景音乐
    • Lighting: 添加场景灯光
    • Floor: 使用 Quad 来制作平整的地面,并移除其 Mesh Render,设置其 Layer 为 Floor
  • Player Character

    • Animator Controller: 制作玩家对象的动画状态机,添加动画控制器
    • Rigidbody: 添加刚体组件
    • Capsule Collider: 添加自定义的碰撞体组件
    • Audio Source: 添加音频组件
    • Tag: 为玩家对象打上标签 Player
    • Scripting: 编写脚本 PlayerMovement,实现玩家角色的移动、转向和动画
    • Player Prefab: 将玩家对象保存为 Prefab
  • Camera setup

    • Position and Rotation: 修改摄像机的位置和转向
    • Background color: 修改摄像机背景为纯黑
    • Projection: 修改摄像机的投影类型为正交投影 Orthographic
    • Size: 设置摄像机画面尺寸
    • Scripting: 编写脚本 CameraFollow,实现摄像机跟随玩家移动
  • Creating Enemy

    • Animator Controller: 制作敌人对象的动画状态机,添加动画控制器
    • Rigidbody: 添加刚体组件
    • Collider: 添加实现物理模拟的 Capsule Collider,以及作为触发器的 Sphere Collider
    • Partical System: 添加被激光击中的粒子特效
    • Audio Source: 添加音频组件
    • Layer: 设置其 Layer 为 Shootable
    • Navigation and NavMeshAgent: 设置导航寻路组件,并烘焙 Bake 导航网格 NevMesh,为 Enemy 添加 NevMeshAgent 组件
    • Scripting: 编写脚本 EnemyMovement,使用 NavMeshAgent.SetDestination 实现敌人对玩家的自动跟随
    • Enemy Prefab: 将敌人对象保存为 Prefab

要点:

  • Animator Controller
    注意,在状态转换 Transition 中勾选了 Has Exit Time,则该状态转换动画播放完毕后会停止不动,对于类似 Move 行走动画需要循环播放的转换,需确保该 Property 不被勾选。

    Apply Root Motion
    Should we control the character’s position and rotation from the animation itself or from script.
    如果勾选该属性,则模型动画中角色的位移和旋转都会被应用到场景中,例如模型在动画中向前跨了一米,则场景中的角色也相应向前移动一米;如果不勾选该属性,则角色只会在原地播放动画,而真正的移动和转向则是由脚本控制。

  • Rigidbody
    Drag and Angular Drag
    分别设置空气阻力对刚体移动和旋转的影响,0 表示无影响,即有运动惯性;Infinity 表示无控制输入时则立即停止移动和旋转,即不考虑惯性。

    Constraints
    Freeze PositionFreeze Rotation 可以冻结不需要移动和旋转的轴。

  • Input.GetAxisRaw()
    public static float GetAxisRaw(string axisName);
    The value will be in the range -1…1 for keyboard and joystick input. Since input is not smoothed, keyboard input will always be either -1, 0 or 1.
    对比 Input.GetAxis(),该方法仅返回 -1/0/1 三个值,对于不关心输入的中间值的情况下,可以使用该方法。

  • Vector3.normalized
    When normalized, a vector keeps the same direction but its length is 1.0.
    编写玩家移动逻辑的时候,要把表示移动的向量规一化,避免不在坐标轴方向上的移动大于 1。

  • Camera.ScreenPointToRay()
    public Ray ScreenPointToRay(Vector3 position);
    Returns a ray going from camera through a screen point.
    返回从摄像机到屏幕上某一点的一条射线,在计算鼠标点击或指向场景的具体位置时使用。

  • Physics.Raycast()
    public static bool Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);
    从指定的原点和方向发射指定长度的射线,检测是否击中指定的 Layer 并返回击中点的信息。

  • Vector3.Lerp()
    public static Vector3 Lerp(Vector3 a, Vector3 b, float t);
    Interpolates between the vectors a and b by the interpolant t.
    Linearly interpolates between two vectors.
    线性插值算法,为向量的过渡作平滑处理。

  • Vector3.Slerp()
    public static Vector3 Slerp(Vector3 a, Vector3 b, float t);
    Spherically interpolates between two vectors.
    Interpolates between a and b by amount t.
    球形线性插值算法,同样为向量过渡作平滑处理。

  • 设置为 Static 的游戏对象,在各种 Bake 中将被视为静态对象。例如关卡中的环境物体设置为 Static 后,在烘焙 NavMesh 导航网格 和 Lightmaps 光照贴图时,将被视为静态对象。

  • NavMeshAgent.SetDestination()
    public bool SetDestination(Vector3 target);
    Sets or updates the destination thus triggering the calculation for a new path.
    设置 NavMeshAgent 组件对象的目标,以为其导航和规划路径进行移动,用于 AI 敌人自动搜寻玩家并跟随。

源码:https://github.com/Caizc/game-survival-shooter
参考:Survival Shooter tutorial - Unity-Learn-Tutorials

Phase 26:Survival Shooter(玩家血量 GUI,敌人攻击和玩家攻击行为)

  • Health HUD

    • HUD Canvas: 添加 HUD(Head Up Display)画布,作为显示游戏状态数据的 GUI
    • Image and Slider: 添加红心图片和滚动条组件
  • Player Health

    • Scripting: 编写脚本 PlayerHealth 和 EnemyAttack,实现玩家血条和敌人攻击
  • Harming Enemies

    • Scripting: 编写脚本 EnemyHealth,实现敌人血量控制
    • Enemy Animaton Events setup: 设置 Enemy Animation 的 Events 属性,在 Death Clips 添加 StartSinking 功能链接
    • GunBarrelEnd setup: 为枪管口添加粒子特效和灯光效果,让开火更炫酷
    • Scripting: 编写脚本 PlayerShooting,实现激光射击效果,并对敌人造成伤害

要点:

  • Canvas 组件:Canvas Group
    为 Canvas 添加 Canvas Group 组件,使用其下的属性 InteractableBlocks Raycasts 控制 Canvas 下对象是否可交互、是否屏蔽射线。

  • UI 组件:Slider
    Property: Transition
    Properties that determine the way the control responds visually to user actions.
    该属性决定如何响应用户交互的可视化效果。

  • Color.Lerp()
    public static Color Lerp(Color a, Color b, float t);
    Linearly interpolates between colors a and b by t.
    颜色的线性插值算法,实现两个颜色之间的渐变效果。

  • Animation: Events -> Script: Function
    在动画模型(.fbx 文件后缀)的 Animations 标签中的 Events 属性下,提供了将动画时间轴连接到脚本中自定义函数的实现,该功能虽然隐蔽,但是很有用。比如实现玩家模型移动时脚步声的播放等。

  • Component.GetComponentInChildren()
    public Component GetComponentInChildren(Type t);
    Returns the component of Type type in the GameObject or any of its children using depth first search.

  • LineRenderer.SetPosition()
    public void SetPosition(int index, Vector3 position);
    Set the position of the vertex in the line.
    设置线条的顶点。结合各种数学函数,可以画出各种酷炫的线条效果。

  • LayerMask.GetMask()
    public static int GetMask(params string[] layerNames);
    Return the layer mask created from the layerNames.

  • Transform.Translate()
    public void Translate(Vector3 translation, Space relativeTo = Space.Self);
    Moves the transform in the direction and distance of translation.
    用于移动游戏对象的位置。

  • Particle System 粒子系统可以附加在一个 GameObject 的 child GameObject 上进行使用,也可以直接将其作为场景中某个 GameObject 的 Component 进行使用。

源码:https://github.com/Caizc/game-survival-shooter
参考:Survival Shooter tutorial - Unity-Learn-Tutorials

Phase 27:Survival Shooter(玩家得分,自动刷怪,游戏重玩)

  • Scoring Points

    • HUD Canvas: 为显示得分添加 Text UI 组件,自定义的 Font 字体,并添加字体阴影 Shadow 组件,注意勾选 Shadow 组件的属性 Use Graphic Alpha,表示使用 Text 组件的颜色 Color 的 Alpha 通道值,以确保当字体完全透明时,其阴影也随着消失
    • Scripting: 编写脚本 ScoreManager,统一管理游戏中的得分及其 UI 展示
    • Enemy Prefab: 将敌人对象保存为 Prefab
  • Spawning Enemies

    • Animation Clips and Animator Controller reuse: 在模型的 Animation Clips 和 Avatar 相同的情况下,重用 Animation Clips、Animator Controller 和 Avatar,制作新敌人角色,并保存为 Prefab
    • Animation Override Controller: 在模型的 Animation Clips 不相同,但状态机相同的情况下,可以使用 Animation Override Controller 来重写指定 Animator Controller 的 Clips 属性
    • Avatar: 为新的敌人角色的 Animator 组件替换合适的 Avatar
    • SpawnPoints setup: 在场景中设置敌人的诞生地点
    • Scripting: 编写脚本 EnemyManager,控制游戏过程中产生的敌人种类和刷出频率,调节不同种类敌人的生命值,保持游戏难度的平衡
  • Game Over

    • HUD Canvas: 添加当游戏结束时淡入屏幕的遮罩层 ScreenFader,以及游戏结束文本 GameOverText
    • Re-order UI elements in Hierarchy: 在层级视图中调整 UI 元素的显示顺序
    • Color: 使用 Text 组件的属性 Color 的 Alpha 通道值来制作文本的淡入淡出效果
    • Animation as a state machine to control everything: Animation 事实上是一个状态机,可以用它来控制所有游戏对象、组件、属性值的动态转换
    • Scripting: 编写脚本 GameOverManager,控制游戏结束时 GameOver UI 界面的展现以及重新加载游戏

要点:

  • Unity Mecanim 动画系统的各个 Asset(Animation Clips, Animator Controller and Avatar)是如何协同工作的:

  • Draw order of GUI elements

    UI elements in the Canvas are drawn in the same order they appear in the Hierarchy. The first child is drawn first, the second child next, and so on. If two UI elements overlap, the later one will appear on top of the earlier one.

    To change which element appear on top of other elements, simply reorder the elements in the Hierarchy by dragging them. The order can also be controlled from scripting by using these methods on the Transform component: SetAsFirstSibling, SetAsLastSibling, and SetSiblingIndex.

  • 当你在脚本中使用 SceneManager.LoadScene("sceneName"); 重新加载场景时,所有游戏对象都会被重置。如果你勾选了项目的 Lighting 设置为 Auto Build(这是默认设置),则场景重新加载时将漆黑一片。因此,需要去除勾选 Window -> Lighting -> Scene -> Auto Build,然后手动点击 Build,生成场景的光照贴图 Lightmaps。

    Uncheck Auto Build in Lighting setting, and manually Build the Lightmaps, so your lighting won’t be gone after you reloaded you level.

源码:https://github.com/Caizc/game-survival-shooter
参考:Survival Shooter tutorial - Unity-Learn-Tutorials

Phase 28:Survival Shooter(Audio Mixer)

  • Auido Mixer

    • MasterMixer and SoundEffects
    • Route the Audio Source Output to Audio Mixer Groups(Player, Gunshots, Enemies, BGM)
    • Apply a Low Pass Filter to music track and also a Duck Volume Effect to music so that it gets quieter when sound effects play: Use Signal Flow to control the volume effects
  • Snapshot

    • Snapshot (Unpaused and Paused): 创建两个快照 Snapshot,分别为游戏进行和暂停时的音频设置
    • Menu Canvas: 添加游戏暂停菜单的 UI 画布,实现游戏背景音乐和音效音量的图形化展现
    • Scripting: 编写脚本 MenuManager,实现游戏暂停时音频快照的切换
  • Exposed Parameters

    • Scripting: 编写脚本 MixVolumes,连接 Menu Canvas 中的 UI 组件和 Audio Mix 中暴露的音频参数,实现使用暂停菜单控制游戏音量的效果

要点:

  • Time.timeScale
    public static float timeScale;
    The scale at which the time is passing. This can be used for slow motion effects.
    When timeScale is 1.0 the time is passing as fast as realtime. When timeScale is 0.5 the time is passing 2x slower than realtime.
    When timeScale is set to zero the game is basically paused if all your functions are frame rate independent.
    该变量可以用来控制游戏中所有运动的速率,包括快进、暂停和慢动作。

  • AudioMixerSnapshot.TransitionTo()
    public void TransitionTo(float timeToReach);
    Performs an interpolated transition towards this snapshot over the time interval specified.
    该方法用于切换游戏中的音频快照 Snapshot。

  • AudioMixer.SetFloat()
    public bool SetFloat(string name, float value);
    Sets the value of the exposed parameter specified. When a parameter is exposed, it is not controlled by mixer snapshots and can therefore only be changed via this function.
    该方法用于控制 Snapshot 暴露的参数的值。

源码:https://github.com/Caizc/game-survival-shooter
参考:Survival Shooter tutorial - Unity-Learn-Tutorials

Phase 29:Game Development Principles

《游戏开发原理》总体来说是一本适合初学者入门游戏开发的概论,内容涵盖了游戏开发的方方面面,但也注定其对于所有主题都是点到即止。对于帮助初学者建立起对游戏开发的整体认知的目的已经达到了,所以还算是本好书。但是原作让不走心的翻译毁了八成,强烈怀疑译者用的 Google 翻译翻成的中文译本就拿来出版。

全书目录如下:

  • 游戏与游戏设计
  • 游戏软件开发
  • 游戏编程
  • 游戏数学
  • 图形、像素和色彩
  • 网格、操纵和动画
  • 照明与补光
  • 声音与音乐
  • 特殊效果与后期处理
  • 分销、发布和营销

笔记:https://github.com/Caizc/cai-knowledge-base
参考:《Game Development Principles》(《游戏开发原理》)

Phase 30:Zombie Toys for PC&Web

Zombie Toys 是 Survival Shooter 游戏的延伸,增加了新的游戏玩法,丰富了游戏体验,优化了视觉效果。新增特性包括:

  • 增加了新的玩家角色,可选择男孩或女孩角色进行游戏
  • 丰富了武器系统,有四种类型的武器可供选择进行攻击(按 Tab 键进行切换),对敌人造成不同效果的伤害
  • 增加了盟友,玩家在积累到一定的分数之后,可以召唤出一只绵羊,借此暂时吸引敌人远离玩家
  • 更多敌人种类,它们有不同的生命值,可以对玩家造成不同程度的伤害
  • 更为酷炫的视觉特效,丰富的武器特效和环境灯光效果,让游戏更加精致有趣

Zombie Toys 游戏试玩链接:
http://caizicong.com/timecapsule/gamesdir/zombie_toys/index.html
(在 PC/MAC 上使用 Chrome、Safari 或 Firefox 浏览器打开运行,游戏文件大小 52M,加载过程需 5-10 分钟,Have Fun!)

Zombie Toys PC 版下载地址:
http://blog-1252720346.cosgz.myqcloud.com/games/zombietoys/ZombieToys-by-ZicongCai.zip
(下载解压后直接运行 ZombieToys.exe 即可,游戏文件大小 70M,Have Fun!)

要点:

  • Light 组件 > Cookie 属性中设置了窗格图片之后,可以投射出阳光照进窗台的效果。
  • Light Probes 灯光探测器组件可以为场景中移动的游戏物体提供光照,模拟实时光照效果。
  • Reflection Probe 反射探测器组件可以优化场景中物体对光照的反射效果。
  • 关于 Unity 中的光照技术 可以阅读这篇文章
  • 该项目中最难实现的部分是武器特效,需要拆解每种武器的视觉效果和攻击逻辑,运用空间数学和物理知识逐步实现。
  • 在脚本中将默认方法 Start() 的返回类型声明为 IEnumerator,可以直接将其当作自启动的协程方法来使用。
  • 在脚本的属性前使用 [HeaderAttribute("Property Name")] 可以在 Inspector 中显示自定义的属性列表名称。
  • 在脚本中使用 [HideInInspector] 可以将声明为 public 的属性对 Inspector 隐藏。

  • Reset() 方法
    Reset is called when the user hits the Reset button in the Inspector’s context menu or when adding the component the first time. This function is only called in editor mode. Reset is most commonly used to give good default values in the inspector.

  • OnEnable() 方法
    This function is called when the object becomes enabled and active.

  • Unity 中并未在编辑器中默认实现 AnimationCurve 属性值的 Copy/Paste 功能,可以在项目中添加一个自定义脚本 CurvePropertyDrawer.cs 来实现便捷的 Curve 值的拷贝和粘贴,脚本来自 Unity Forums

源码:https://github.com/Caizc/game-zombie-toys
参考:Unity Certified Developer Courseware - Unity Asset Store

Phase 31:游戏系统设计指南

「三思而后行」,在游戏制作的工业流程中,策划设计处于编码实现的上游,全面地了解一个游戏从策划、到实现、再到发布运营的全过程,有利于建立起对游戏开发工作的全局视野,把握游戏开发工作中的核心要点。

技术是服务于业务的,底层技术实现的是上层业务逻辑。因此,对于游戏开发人员来说,了解游戏策划相关的知识,有利于在设计游戏系统技术架构时,充分考虑到游戏业务逻辑的多样性和不稳定性对底层技术所带来的挑战;有利于启发游戏开发人员对于系统稳定性与可扩展性的思考。

腾讯 GAD 编撰的这本《创世学说:游戏系统设计指南》,面向新手游戏系统策划人员,通俗易懂地讲述了游戏系统相关的知识。阅读这本书,有助于读者实现从游戏玩家视角到游戏制作人员视角的转变,建立起对游戏工业流水线的基础认知。

笔记:https://github.com/Caizc/cai-knowledge-base
参考:《创世学说:游戏系统设计指南》

Phase 32:2D 手机游戏

《Unity 5.x 创造 2D 手机游戏》以一个简单的 2D 横版跑酷游戏为例,带领读者一步步构建自己的第一个 2D 游戏项目,适合刚入门手机游戏开发的新手。

虽说作为贯穿全书的游戏项目较为简单,但麻雀虽小五脏俱全,其中涉及到的游戏编程思想在实际项目开发中也很重要,特别是游戏内存的使用优化、对象池、代理模式等知识,对于优化游戏性能、提高游戏代码架构的可扩展性有很大帮助。

同时,书中也存在一些纰漏,阅读时需注意:

  • 在讲解编码逻辑时,代码片段的编排方式有待优化,对于前文已经阐述过的代码,可以采用省略号来代替,只给出正在讨论的增量部分的代码,而不是每次都贴出一个完整的类,这样容易让读者找不着重点,占太多页面篇幅,又多了几分凑页数的嫌疑。
  • 书中仅前 5 章给出了源码素材的下载链接,后续章节则只能依靠读者仔细阅读书中内容来完成代码编写。而且书中提供的代码有些地方也无法执行成功,需读者自行修正变通。
  • 在下文中也给出了本人完成并调试通过的项目源码链接,仅供参考。使用的 Unity 版本为 5.6.0f3。

要点:

  • Sprite Editor

    使用 Sprite Editor 裁切 Sprite 动作序列图,裁切后的多张小图 Unity 只会把它们当作一个游戏对象,这样可以有效的地节省游戏运行内存,且游戏角色的动作可以被统一管理。

  • Pixel Per Unit

    Unity 中每一个单元格的像素大小。

  • Sprite 的 Filter Mode

    设置 Sprite 的 Filter ModePoint,对于像素资源来说,Point 模式可以让它看上去更加干净、清晰。

  • Sprite Packer

    在制作游戏的过程中,会使用到许多独立的图片,每一张图片都会占用 GPU 的一部分显存,为了优化游戏性能,通常会把这些零碎的图片打包在一起,这样不仅可以节省系统内存,也可以使得读取图片的速度成倍加快。
    选中所有需要打包的图片,在 Inspector 中为它们输入相同的 Packing Tag,然后打开 Sprite Packer 点击 Pack 来打包图片。
    在游戏运行时,我们可以打开 Game 视图中的 Statistics 面板,观察动态变化着的 BatchesSaved by batching,来验证 Sprite 打包后对性能的优化效果。

  • Orthographic Camera Size

    • 为了使游戏能够自动适配所有手机的屏幕分辨率,需要根据不同的设备分辨率动态调整正交摄像机的对焦距离,也就是动态调整 Orthographic Camera Size 值,确保游戏所有的场景元素都在摄像机的观察范围之内。

    • 计算方法:
      对于一个 800*640 像素且像素单元比(Pixel to Units)为 100 像素的场景来说,它的比例值就是场景高度 640 像素除以单元比例 100 像素等于 6.4。
      因为一般设置正交摄像机处于场景的正中央位置,所以它的 Size 值是比例值 6.4 的一半 3.2

  • 主流的手机屏幕分辨率

    iPhone 的主流屏幕比例以 16:9 为主,Android 的主流屏幕比例以 16:10 为主。(未经严格考证)

  • Shader - Mobile Particles 简介

    • Alpha Blended:主要用于实现半透明效果,类似于玻璃
    • Additive:相加混合模式
    • Multiply:相乘混合模式
    • Vertexlit Blended:渲染路径模式
  • Map、Texture、Material 的区别

    • Map (贴图)包含另一种含义,就是「映射」。其功能就是把 Texture 通过 UV 坐标映射到 3D 物体表面。Map 包含 Texture 以外的很多信息。
    • Texture (纹理)是最基本的数据输入单位,游戏领域基本上使用的都是位图。
    • Material(材质)是一个数据集,主要功能就是给渲染器提供数据和光照算法。Map 就是其中数据的一部分,根据用途不同,Map 也会被分成不同的类型。
  • 图像尺寸运算原理

    所有美术图片资源的内存技术都是 2 的指数次幂,所以材质一般都是 16、32、64、128 这样的尺寸,这样可以大幅提升图像的运算效率,节约系统消耗。

  • 在脚本中获取游戏对象的 Material 中的 Texture 引用

    在游戏对象的脚本中使用 GetComponent<Renderer>().material.GetTexture("_MainTex") 才可以获取到默认纹理的引用。

  • Material 中 Texture 的 Offset 属性

    通过动态修改游戏对象 Material 中 Texture 的 Offset 属性,可以实现纹理图片的循环播放效果。

  • Update() 和 FixedUpdate() 的区别

    • Update() 会在每次渲染新的一帧时被调用,它受当前渲染的物体,更确切地说是三角形的数量影响,时快时慢。帧率变化,Update() 被调用的时间间隔就会发生变化。
    • FixedUpdate() 则不受帧率变化的影响,它是以固定的时间间隔被调用的。在 Edit -> Project Setting -> Time 中,通过 Fixed Timestep 值来设置。
  • Rigidbody 的 Interpolate 属性

    设置 Interpolate 可以优化帧频抖动,使得刚体运动更加平滑。其渲染方式有以下两种:

    • Interpolate(内插值):基于上一帧的变换来平滑本帧变换
    • Extrapolate(外插值):基于下一帧的预估变换来平滑本帧变换
  • Destroy()

    在游戏中不要频繁地销毁与新建对象,而是重置对象的状态,不断地复用已经创建的游戏对象,这样做的好处是可以保证游戏的内存波动平稳,会减少游戏卡顿、死机、跳出的现象。

  • 对象池

    所有对象池都遵循着同样的工作原理——在对象池中寻找已经创建但是没有被使用的对象并激活它,如果没有可以用于被激活的对象,则重新创建一个新的对象。
    不是所有的对象池都是动态容量的,但在多数项目中都会设置对象池的容量,这样对程序的性能影响就可以在控制范围内。

  • GetComponent() 的限制

GetComponent() 方法无法获取抽象类的引用,所以只能通过手动遍历游戏对象的组件列表来获取抽象类的引用。

https://docs.unity3d.com/Manual/UICanvas.html

  • Canvas 的参数 Pixel Perfect

    Pixel Perfect(像素完美)指的是一个 UI 素材本身的像素对应屏幕上的一个像素的情况,在这种情况下,UI 素材映射到屏幕上时没有任何拉伸和压缩,显示效果非常清晰、完美。

  • Canvas Scaler 组件

    为了适应不同的分辨率,我们可能会允许适当的 UI 整体性缩放,外加一些尽可能少的布局微调,这样就能达到一个比较理想的效果。

https://docs.unity3d.com/Manual/script-CanvasScaler.html

  • TimeSpan 函数

    TimeSpan 表示一个时间间隔值,包含了许多属性与方法,用于访问或处理每一个 TimeSpan 返回值。

    • FromDays:返回用天数计算的 TimeSpan 值
    • FromHours:返回用小时计算的 TimeSpan 值
    • FromMinutes:返回用分钟计算的 TimeSpan 值
    • FromSeconds:返回用秒计算的 TimeSpan 值
    • FromMilliSeconds:返回用毫秒计算的 TimeSpan 值

Standard TimeSpan Format Strings - Microsoft Docs.aspx)

  • PlayerPrefs

    使用 PlayerPrefs 可以实现简单的数据本地化存储,基于 key-value 的读写操作。

https://docs.unity3d.com/ScriptReference/PlayerPrefs.html

  • 在 string 中使用 HTML 标签
    通过在 string 中使用 <color="red">some text</color> 来灵活实现 UI Text 文本的标红效果。

源码:https://github.com/Caizc/game-endless-running
笔记:https://github.com/Caizc/cai-knowledge-base
参考:《Unity 5.x 创造 2D 手机游戏》

Phase 33:Programming in Lua

Lua 作为一种可嵌入,轻量快速,功能强大的脚本语言,在游戏开发领域主要用于功能原型的快速开发、嵌入其他语言框架以提供灵活的可扩展性,或者作为游戏热更新能力的有力工具。

要点:

笔记:https://github.com/Caizc/cai-knowledge-base
参考:《Programming in Lua》

Phase 34:CraftStudio 游戏引擎

CraftStudio 是一款非常小巧的游戏引擎(安装包大小仅 11M),免费,支持主机平台的 2D/3D 游戏制作。用它来制作像素风格的小游戏特别得心应手,从建模、动画、场景、脚本到音频一应俱全,最具特色的是其提供了多人在线实时协作的支持。采用可视化拖拽式脚本编程和 Lua 脚本编程,易于上手。如此小巧精炼的一款引擎,让开发者回归到游戏开发的核心本质,值得一试。

CraftStudio 工作界面:

可视化拖拽式脚本编程:

Lua 脚本编程:

游戏效果:

CraftStudio 的下载页面:https://sparklinlabs.itch.io/craftstudio
CraftStudio 的 Wiki 页面:https://bitbucket.org/sparklinlabs/craftstud.io/wiki/Home

Phase 35:Programming in CSharp

全面快速地回顾了 C# 语言的全部内容,期待在项目实战中持续深入。

笔记:https://github.com/Caizc/cai-knowledge-base
参考:C# 教程 - RUNOOB.COM

Phase 36:Mobile & Touch

  • 获取移动设备的输入

    • 多点触控:
      使用 Touch 结构用来描述手指触控,通过它可以访问到触控的相关信息。
      使用 Input.touchesInput.touchCount 可以实现多点触控输入。

    • 加速度传感器:
      使用 Input.acceleration 获得加速传感器的相关信息。

    • 缩放:
      利用设备的多点触控功能,通过处理 Input.touches 数组中的触控输入,相应的调整 Camera 的参数,可以实现双指操纵画面缩放的功能。

      输入 Input - Unity 圣典

  • 移动游戏的构建与调试

    • 构建和调试 iOS 游戏:
    1. 添加 Apple ID 到 Xcode
    2. 将 Unity 项目切换为 iOS 平台,并且设置好 Bundle Identifier
    3. 使用 Unity 构建一个 Xcode 项目
    4. 使用 Xcode 构建可运行的项目到 iOS 设备上
    5. 在 iOS 设备上测试游戏,在 Xcode 的控制台观察 debug 信息

      Building your Unity game to an iOS device for testing

    • 构建和调试 Android 游戏:
    1. 安装 JDK 和 Android SDK Tools
    2. 将 Unity 项目切换为 Android 平台,并且设置好 Bundle Identifier,设置好 Android SDK 安装路径
    3. 将 Android 设备设置为「开发者模式」
    4. 使用 Unity 构建一个 Android .apk 安装包
    5. 在 Android 设备上测试游戏,使用 Android Device Monitor 观察 debug 信息

      Building your Unity game to an Android device for testing

  • 移动游戏性能调优

    //TODO: 未完成学习
    Unite Europe 2016 - Optimizing Mobile Applications

参考:Mobile & Touch - Unity Tutorials

Phase 37:2D Roguelike

// TODO: 项目编码未完成

2D Roguelike

  • Player and Enemy animations
  • Create the tile prefabs
  • Scripting BoardManger
  • Scripting GameManager
  • Moving objects
  • Create destructible walls
  • Player Animator Controller
  • Player Script
  • Enemy Script
  • Enemy Animator Controller
  • UI
  • Audio
  • Mobile Control

要点:

  • Mathf.Epsilon

  • Has Exit Time

    Exit Time is a special transition that doesn’t rely on a parameter. Instead, it relies on the normalized time of the state. Check to make the transition happen at the specific time specified in Exit Time.

  • Exit Time

    If Has Exit Time is checked, this value represents the exact time at which the transition can take effect. This is represented in normalized time (for example, an exit time of 0.75 means that on the first frame where 75% of the animation has played, the Exit Time condition is true). On the next frame, the condition is false.

    For looped animations, transitions with exit times smaller than 1 are evaluated every loop, so you can use this to time your transition with the proper timing in the animation every loop.

    Transitions with an Exit Time greater than 1 are evaluated only once, so they can be used to exit at a specific time after a fixed number of loops. For example, a transition with an exit time of 3.5 are evaluated once, after three and a half loops.

  • Sorting Layer

  • Order in Layer

源码:https://github.com/Caizc/game-2d-roguelike
参考:2D Roguelike tutorial - Unity Tutorials

Phase 38:Useful Stuff in Unity Scripting

在学习和实践的过程中,同步将 Unity 游戏脚本编程中常用的 API 及实用技巧整理出来,并持续更新这些知识点,为打好基础以做到熟练运用引擎工具来构建游戏提供帮助。

笔记:https://github.com/Caizc/cai-knowledge-base

Phase 39:Design Patterns in Game Development

学习《设计模式与游戏完美开发》一书,学习如何将设计模式灵活应用于游戏编程中。

游戏系统架构和逻辑设计的过程中重要的几种 UML 图:

  • 类图
  • 状态图
  • 时序图

//TODO: 已完成前 6 篇(1-20 章)内容的学习,并绘制了系统类图,剩余章节待学习。

笔记:https://github.com/Caizc/cai-knowledge-base

Phase 40:Playmaker

简单了解了 Unity 的一款可视化编程插件 —— Playmaker,看完了官方的入门系列教程。

Playmaker 的核心是一个 Finite State Machine (FSM) 有限状态机,通过「事件驱动」实现状态跳转,同时添加了功能强大的 Action、Variable、Event 实体等,试图部分取代 Unity 传统脚本编程的功能,用易于上手且相对灵活的方式实现游戏的玩法逻辑。

对于将 Playmaker 应用于项目开发中的评价褒贬不一,不过对于游戏开发新手或者快速实现游戏原型来说,Playmaker 是便捷之选。而对于上了体量的商业项目来说,游戏系统规模膨胀以后,使用 Playmaker 的项目管理起来可能是个噩梦。

不过,对于 INSIDE 这款游戏使用了 Playmaker 进行制作还是挺让人惊讶的,不知道它对于这个插件的使用规模如何。

参考:
简单易用的可视化编程插件 Playmaker
Core Concepts of Playmaker
TUTORIALS - Playmaker
Should you use Playmaker in production?

Phase 41:Corgi Engine

Corgi Engine 是在 Unity 引擎中实现的一款 2D + 2.5D 平台游戏(Platformer)开发套件,其中包含了实现一款平台跳跃游戏所需的角色、道具、场景组件等,具有完善的文档、教程支持,其项目源码也非常规范整洁,值得研究学习。

Corgi Engine - the Best 2D + 2.5D Platformer Solution for Unity

为了方便查阅和加深理解,我索性将 Corgi Engine 的官方文档全部翻译为简体中文,详细信息可以访问这篇博客文章进行了解:

Unity 2D 平台游戏套件 Corgi Engine 中文文档

项目地址:Corgi Engine 简体中文文档

Phase 42:Mecanim 动画系统

我发现在使用 Unity 进行游戏开发的过程中,如果遇到不懂的地方,Unity Manual 往往是最权威的一份学习材料,第一时间阅读它来查找你想要的答案是最快捷的方式。

虽然之前做过的项目中多多少少用到了 Unity 的动画系统,但还是有很多地方一知半解,于是决心从官方手册开始全面学习 Unity 的动画系统 Mecanim

Animation - Unity Manual

学习如何实现角色的「换皮」,即重用角色的动画状态机、动画和骨骼,利用 Unity 的 Avatar 机制可以实现。

Mecanim 动画系统相关资料:

骨骼与动画重定向 - 知乎专栏
這樣條動作絕對錯不了! Animator系統介紹! - Youtube
免费的 3D 模型制作和下载网站 - mixamo

Phase 43:xLua & Google Protocol Buffers

  • 网络游戏「热更新」与 xLua 框架

    网络游戏,特别是移动端的网络游戏,「热更新」往往是刚需,因此在项目中引入与 Unity 官方脚本语言 C# 相结合的脚本编程框架迫在眉睫。

    Tencent 开源的 xLua 框架提供了 lua 与 C# 交互的桥梁,实现了在同一个项目中两种不同类型语言之间的相互调用,使项目既能得益于 C# 构建的稳定架构,又能获得 lua 提供的额外的灵活性。

    为了扩展项目的功能,还利用了 xLua 整合第三方 lua 扩展库的能力,增加了 lua-rapidjson、lepg 和 pbc 库的相应功能。

  • xLua 的相关资料:

    xLua - GitHub
    如何评价腾讯在Unity下的xLua(开源)热更方案?- 知乎

  • Google Protocol Buffers

    Protocol Buffers(或简称 Protobuf)是 Google 开源的结构化数据格式,类似于 XML 和 JSON,与语言和平台无关,因此通常被用于制定网络通信协议。Protobuf 还可用于定义数据结构、数据序列化存盘、充当配置文件等。

    Protocol Buffers - Google Developers

  • Protocol Buffers 的安装、编译和使用:

    从Protocol Buffers 到 gRPC
    Protocol Buffers 在游戏中的应用

  • xLua 整合 pbc 库

    为了在项目中使用 Protobuf 3 的新特性,需要将「云风」开源的 lua 版本的 Protobuf 实现 —— pbc 库整合到 xLua 框架中,为此费了不少功夫。

    在学习了 xLua 中如何实现 lua 代码和 C# 代码的相互调用,以及如何为 xLua 增加第三方 lua 库之后,最终测试通过了在 C# 中调用 lua 执行 Protobuf 协议数据的编码和解码(序列化和反序列化)。

    不过,最终测试结果还表明 pbc 库对于Protobuf 3 新特性仅部分支持,不支持的特性仍需使用 Protobuf 2 旧版本中的协议定义方法作为替代。

    另外遗憾的是「云风」似乎已经决定不再维护 pbc 开源项目的更新,想要通过 pbc 使用 Protobuf 新版本特性的话可能要另寻他法了。

    pbc - GitHub
    build_xlua_with_libs - GitHub

源码:xLua 整合 pbc 库项目(编/解码测试通过)
笔记:Google Protocol Buffers 的使用

Phase 44:网络编程

网络游戏离不开网络编程。

为了寻找适合中小型项目的网络通信模块,深入了解了几个基于 Socket 实现的网络通信框架,其中有出自书籍《Unity 5.x 3D 游戏开发技术详解与典型案例》和《Unity 3D 网络游戏实战》的简单案例,也有开源的应用于实际项目中的框架,如 cocosocketKBEngine

最后还学习了 Unity 自带的 UNet,以及成熟商用的网络引擎 Photon

各个框架简单的分析对比记录在这里:
Network Frameworks work with Unity

笔记:https://github.com/Caizc/cai-knowledge-base
参考:
《Unity 5.x 3D 游戏开发技术详解与典型案例》
《Unity 3D 网络游戏实战》

Phase 45:Java 服务端程序搭建

为了调试游戏客户端的网络模块,需要有相应的服务端程序来处理请求,为此又「重操旧业」,在腾讯云上搭建起了 Java Web Application

还顺便学习了 Docker 的搭建与使用,以及 Docker 中 MySQL 容器的使用。

另外还入手了 IntelliJ IDEA,可以说非常趁手,Java 程序开发效率倍增。随后学习使用 Maven 管理项目依赖,并用它顺利集成了 Protobuf 3,以便与游戏客户端联调网络通信协议功能。

源码:rapid-server
笔记:
CentOS 搭建 Docker 环境教程
Docker Tips

Phase 46:网络游戏同步技术

网络游戏的同步是一个大课题,目前应用较为广泛的同步方案主要有状态同步帧同步,以及两者混用的方式。当然,无论采用哪种方案,核心都是借鉴方案中实现游戏同步的思路,然后根据项目的需要来调整具体的实现细节,最终达到各客户端流畅一致的游戏体验。

Network Programming in Game Development 这份笔记中,记录了网络游戏同步相关的技术要点,以及几种主流方案的简要总结。

随后对 Lockstep Synchronization 帧锁定同步 方案进行了深入研究,总结了该方案的技术要点,绘制了算法的流程图(在 learn-photon-truesync 项目中可以找到)。最后对 Photon 网络引擎的 TrueSync 帧锁定同步组件进行深入剖析,解析源码并绘制了系统类图。

初步完成对网络游戏同步技术的调研后,接下来将尝试自己实现一个网络游戏同步框架(包括客户端和服务端程序)。

源码:learn-photon-truesync
笔记:
Network Programming in Game Development - 游戏开发中的网络编程
Lockstep Synchronization - 帧同步
参考:TRUESYNC - Photon

Phase 47:Unity Certified Developer Courseware

在 9 月份「开学季」官方促销时订阅了一年 Unity Plus,同时获得了 Unity 认证开发者课程为期 3 个月的访问权限,虽然之前已经深入学习过 Zombie Toys 项目的代码实现,但课程中除脚本编程以外的内容,还是非常值得学习的。

课程中对于使用 Unity 进行游戏开发涉及的各方面技术都进行了讲解,虽然没办法探讨得很深入,但对于新手一览游戏开发全貌还是很有帮助的。藉着学习该课程的过程,我也对 Unity 的基础知识进行了查漏补缺,特别是 3D/2D 资源制作与导入、光照、导航寻路等模块,扫除了不少知识盲点,获益颇丰。

课程中的知识要点整理在了 Best Practices for Unity 这份文档中。

笔记:
Best Practices for Unity
https://github.com/Caizc/cai-knowledge-base
参考:Unity Certified Developer Courseware

Phase 48:Proton Engine 雏形

有了近一年来浅薄的积累,跃跃欲试想要搭建自己的首个网络游戏框架,作为业余实验创造的沙盒。

这个框架我将其取名为 Proton Engine 质子引擎,灵感源于 Photon Engine 光子引擎。计划中 Proton Engine 将作为一个通用的网络游戏开发框架,而开发网络游戏的核心就是要搞定网络同步,Proton Engine 整合了 Photon 的帧同步组件 TrueSync 作为其网络同步方案的基础。

但 Proton Engine 显然不是要成为第二个 Photon Engine,它并不专注于提供网络同步能力,而更像是一个工具箱,整合了各种通用的网络游戏开发组件,尽量做到模块化、组件化,轻耦合,随取随用,同时提供必要的文档指南,做到简单易用。

验证一个工具是否趁手的方法是,把它拿到实战中耍耍。于是,在实现了基本的网络同步底层后,我想在其基础上实现一个 2D 平台类的多人实时对战游戏。早期游戏的玩法并不重要,仅仅为了验证和完善底层的帧同步框架和网络通信框架,所以我打算使用 Corgi Engine 提供的组件快速搭建一个简单的 2D 平台游戏玩法,后续有精力再设计实现更丰富的玩法。

Proton Engine 包括了使用 Unity 构建和 C# 语言编写的客户端以及使用 Java 语言编写的服务端,主要模块有:

  • UI - 通用 UI 框架,目前仅具备简单的玩家登录界面和房间界面
  • GamePlay - 游戏玩法逻辑,目前仅将 Unity Tutorials 中的 SpaceShooter 移植过来作为简单的游戏玩法
  • Corgi Engine - 2D 游戏工具箱,目前仅整合了 Corgi Engine 的通用工具包
  • Framework - 通用系统工具箱,目前还未添加任何组件
  • RealSync - 帧同步框架,目前已将 TrueSync 从其原生依赖的 PUN 组件中剥离并整合进来(更多细节可参考这篇笔记的「TrueSync 从 PUN 基础上剥离」部分),但后续计划在摸清其实现原理细节后,自定制一个简化版作为替代
  • Networking - 网络通信框架(横跨客户端和服务端),目前是一个基于 TCP 和异步 Socket 的简易通信模块,后续计划会是一个支持 UDP/KCP 等更高效协议、高可靠、高并发的网络通信框架(类似 PUN)
  • Server - 服务端程序,目前参考的是《Unity 3D 网络游戏实战》一书中的范例实现,将其从 C# 实现修改为 Java 实现,并做了些许优化。计划中服务端程序并不是重点,仅为实现客户端的网络同步等提供必要的支持

目前 Proton Engine 客户端的最新版本是在 spaceshooter 分支上。服务端程序独立为 Proton Server 项目,使用 JDK 1.8 和 IntelliJ IDEA 工具进行开发。

Proton Engine 项目中还包含了详细的系统架构设计图(使用 Enterprise Architect 8.0 打开),还有逐步完善的使用手册 Proton Engine Manual

反正坑已经挖了,接下来只能慢慢填了。 : )

Phase 49:云服务器环境搭建

从 2016 年 10 月使用腾讯云至今,最低配置的云主机对于我来说也够用了,主要用途是托管个人博客网站,以及部署开发测试用的服务端程序。原本月租 74 元勉强可以接受,但「双 11」无意中发现同等配置有打 3 折的机器可抢(折扣价只是前 9 个月有效,过期恢复原价),就果断抢购了一台,接下来就是折腾将旧主机上的数据和程序迁移到新主机上。

原本打算籍此机会将整个网站服务 Docker 化,以后再迁移起来就轻松多了,还可以利用 Docker 将生产环境和测试环境分开,可谓一举多得。没成想真正实施起来,还是踩了很多雷,学习了相关文档资料后,解决了部分问题,但还是有力不能及的地方,只好先搁置,老实按照原来的方式搭好环境。

云服务器环境的搭建还算顺利,过程笔记在随后给出的链接中有。网站正常运行之后,就在 DNSPod 上将域名指向新的云主机 IP,接着就是漫长的「备案信息更改」之旅(因为云主机变了嘛)。为了备案能顺利通过,还得将所有文章撤下来,只在首页放一张风景图片,呵,真魔幻。等了近 1 个月才终于通过审核,说好的 10 个工作日内搞定呢?至此,个人博客才终于重见天日。

笔记:
CentOS 搭建 Docker 环境教程
CentOS 搭建 FTP 服务教程
CentOS 搭建 Git 服务教程
CentOS 搭建 Java Web 环境教程

Phase 50:Unity 游戏开发技术概览

游戏开发涉及的知识纷繁复杂,妄想通过一本教材的学习就熟知游戏开发的方方面面是不现实的。本着先勾勒出领域的知识全貌,再深入探索特定专题模块的原则,开始仔细学习《Unity 5.X 3D 游戏开发技术详解与典型案例》一书。

对比 Unity Manual,这本书的内容胜在通俗、操作性较强,有易于上手的案例加强理解,但在深度和权威性上有所欠缺。很多知识点都是点到即止,读者知其然、不知其所以然,想要真正掌握书中提及的知识点,还是需要继续深入学习和实践相应的专题内容。

求知之路漫漫,一步一脚印,切不可一蹴而就。

笔记:
https://github.com/Caizc/cai-knowledge-base
参考:
《Unity 5.X 3D 游戏开发技术详解与典型案例》

Phase 51:Scripting Tutorials

脚本编程能力对于构建丰富多样、灵活多变的游戏世界至关重要,有如绳网之于渔夫、弓矛之于猎人。为巩固脚本编程基础,以期在实现需求和解决问题时,能够熟练驾驭语言工具,仔细学习了 Unity 的 Scripting 系列教程,循例整理了学习笔记以供回顾。

笔记:
Scripting 教程学习笔记
参考:
Scripting - Unity Tutorials

Phase 52:性能优化

一款游戏「对性能友好」的理念,应该贯穿游戏的生命周期,从调研、策划、研发到运营,毕竟这是关乎游戏体验的重要组成部分。即便游戏趣味十足,却频繁卡顿闪退、耗电发热、流量杀手,玩家也会心生畏惧。

由于实际项目经历薄弱,性能优化一直都是我的知识盲区,要多学习实践。

笔记:
Optimization in Unity

Phase 53:游戏中的 AI

广义的人工智能是一门高深的学科,它是研究如何通过程序手段实现类人智能的技术。反观游戏中的人工智能则简单得多,它要做到的仅仅是使得游戏中的主体单位按照设定好的规则,根据环境或状态的变化,作出相应的行为响应,以增加游戏的趣味性。游戏中的 AI 不要求这些主体真正具备人类的独立思维能力,这样的决策过程对于我们来说是一个「黑盒」,我们需要的是一个可配置、可预测、可扩展的,有着确定输入和输出的行为调度系统

游戏中的 AI 可以理解为一个「状态机」,它从环境中获取其关注的信息,判断是否满足某个状态的准入条件,然后动态的更新自身的状态或触发相应的行为。

Unity 自带的 Animator 组件实质就是一个状态机,它不仅可用于控制角色动画的转换播放,结合脚本编程,它甚至可以控制游戏中的一切元素,让它们产生动态变化。

而对于知识量更为庞大的 AI 系统,「行为树」是一个更易于理解的表达方式。在当前的项目中,我们使用了 BT-Framework 作为 AI 系统框架,已经足以构建出灵活多变的角色行为系统了。

参考:
游戏 AI - 行为树 Part1:简介 - 知乎
游戏 AI - 行为树 Part2:框架 - 知乎
BT-Framework - GitHub
《Unity 人工智能游戏开发》

Phase 54:游戏设计与开发知识记录

坚持「Don’t Repeat Yourself!」原则,每天接触到的知识碎片也值得整理记录。

Phase 55

To be continued…


蔡子聪
2017-1-18


change log:

- 创建(2017-01-18)
- 更新(2017-10-25)
- 添加 Phase 48-54(2018-03-16)