3D游戏实战开发
- 3D 实体搭建
在游戏场景中搭建静态物体
- 相机,光照,天空盒
营造游戏场景中的氛围感
- 控制与碰撞
让实体在游戏场景中动起来
- 玩法逻辑与UI
为游戏注入玩法和规则的灵魂
3D 实体搭建
3D 实体
3D 游戏是由一个个具有形状的实体组成的。每个实体在空间中存在于特定的位置,有特定的姿态(旋转角度)
3D 实体的位姿态(Transform)
- 位置 Position(x,y,z),是一个三维向量坐标
- 旋转 Rotation(x,y,z),是一个三维向量坐标
- 缩放 Scale(x,y,z),是一个三维向量坐标
在Unity中,绝大部分情况下,是先缩放,后旋转,最后平移
3D 实体的创建
- 通过加载3D模型创建,如 fbx、gltf、obj
- 通过组合参数化的基本几何体创建
3D 实体的绘制
- 材质
- 颜色
- 纹理
敌机模型制作
预制体
- 将游戏对象保存在工程中,在需要的时候创建出来,这就是预制体(prefab)
- 预制体存储着一个游戏对象,包括游戏对象的所有组件以及其下的所有子游戏对象
相机,光照,天空盒
相机
- Clear Flag
- 背景颜色
- Culling Mask
- 投影(透视、正交)
透视投影(Perspective)与正交投影(Orthographic)
光照
- 类型:点光源、平行光、聚光灯、面积光
- 颜色
- 强度
- 阴影类型
天空盒
- 相机的清除标志设为“天空盒”
- 窗口——渲染——照明设置
- 环境——天空盒材质
控制与碰撞
为主角飞船添加控制逻辑
- 添加刚体组件
- Add Compontent > Physics > Rigidb
- Use Gravity 设置为 false,忽略重力的影响
- isKinematic 设置为 true,飞船通过脚本而非力影响运动属性
- 设置 Constraints,冻结Z轴位移以及X、Y、Z轴旋转
- 添加自定义脚本
- Add Component > New Scricp
MonoBehaviour 是一个基类,所有 Unity 脚本都派生自该类
- Start()
在首次调用任何 Update 方法之前在帧上调用 Start
- Update()
每帧调用 Update
- FixedUpdate()
用于物理计算且独立于帧率。具有物理系统的频率;每个固定帧率调用该函数
- LateUpdate()
LateUpdate 在每一次调用 Update 函数后调用
- OnGUI()
系统调用 OnGUI 来渲染和处理 GUI 事件。OnGUI 实现可以每帧调用多次(每个事件调用一次)
- OnDisable()
该函数在对象被禁用时调用。对象销毁时也会调用该函数
- OnEnable()
该函数在对象变为启用和激活状态时调用
Input.GetAxis()和输入管理器(InputManager)
- InputManager 是 Unity 设置输入响应方式的管理列表,它的设置在 Edit > Project Setting > Input 中
添加敌机:
-
为每架敌机预制体添加一个刚体
- 选中敌机预制体,在菜单栏执行 Component > Physics > Rigidbody
- 在新添加的刚体组件中,将 Use Gravity 设置为 false
- 将 isKinematic 设置为 true
- 打开 Constraints 旁边的三角形展开按钮,冻结Z轴的坐标和XYZ轴的旋转
-
建立敌机的脚本 Enemey.cs
-
为每架敌机预制体均添加脚本 Enemy.cs
随机生成敌机:
- 新建一个名为 Main 的 C# 脚本,绑定到 Main Camera 上
- 敌机与玩家控制的飞船没有碰撞效果
- 玩家控制的飞船会移出相机视野之外
- 敌机会不断添加进场景中,导致场景中的实体越来越多
设置标签、图层和物理规则:
游戏中存在不同类型的游戏对象,它们需要放置在不同的图层中,并与其他游戏对象发生不同的交互。
- 主角飞船(Hero):会与敌机、敌机炮弹、升级道具碰撞,但不会与主角飞船的炮弹相碰撞
- 主角飞船的炮弹(ProjectileHero):与敌机、敌机的炮弹相碰撞
- 敌机(Enemy):与主角飞船和主角飞船的炮弹相碰撞
- 敌机的炮弹(ProjectileEnemy):只与主角飞船相碰撞
- 升级道具(PowerUp):只与主角飞船相碰撞
标签和图层管理器:
- 菜单栏中执行 Edit > Project Settings > Tags and Layers 命令
- 打开 Tags 左侧的三角形展开按钮,单击标签下方的+符号并输入标签名称
- 单击 Layers 旁边的三角形展开按钮从 User Layer8 开始,依次输入图层名称
物理管理器:
- 菜单栏中执行 Edit > Project Settings > Physics 命令
为游戏对象指定合适的图层:
- 在层级面板中选中_Hero,然后在检视面板中从 Layer 下拉菜单中选择“Hero”选项,Unity 会询问是否将_Hero的子对象也指定到该图层上,选择“Yes,change children”选项
- 在检视面板中,在 Tag 下拉菜单中选择Hero选项,为_Hero设置标签,不需要修改_Hero子对象的标签
- 从项目面板中选择这5个敌机预设,设置图层为 Enemy,如果出现提示,同样选择“Yes,change children”选项
- 设置每个敌机预设的标签为Enemy,不需要修改它们的子对象的标签
敌机碰撞主角飞船:
- 为主角飞船和敌机添加碰撞体。球体碰撞检测的效率较高。我们将主角飞船与敌机飞船及其子组件上已有的碰撞盒去掉,每个对象的母体上 Add Component > Sphere Collider
添加碰撞代码:
- 在 Hero 类中添加 OnTriggerEnter函数
void OnTriggerEnter(Collider other) {
// 可以用下面这行代码在Console窗口输出碰撞对象的名称
// print("触发碰撞事件:"+other.gameObject.name)
if (other.tag == "Enemy") {
Destroy(this.gameObject);
}
复制代码
}
玩法逻辑与 UI
主角飞船增加射击功能:
-
新建一个新的预制体,命名为 PrijectileHero。模型为一个立方体,Position与Rotation均为[0,0,0],Scale为[0.25,1,0.5].保留默认的 Box Collider。Box Collider的Size.z设置为10
-
创建一个名为 Mat_ProjectileHero 的新材质,将着色器指定为 ProtoTools > UnlitAlpha,并将新材质应用到 ProjectileHero上
-
为 ProjectileHero 游戏对象添加一个新的刚体组件,设置如下:
- Use Gravity 为 false
- isKinematic 为 false
- Collision Detection 为 Continuous
- Constraints 冻结Z坐标与X、Y、Z旋转轴
-
Tag 和 Layer 均设置为 ProjectileHero
按下空格后实例化新炮弹:
为子弹添加碰撞事件:
屏幕右上角显示计分板:
-
在菜单栏中执行 GameObject > UI > Text 命令
- Canvas:匹配游戏面板尺寸的画布
- EvenSystem:用于运转按钮、滚动条等交互元素
-
选中Text对象,修改名称为Score
-
设置Text对象的Anchors、Pivot、Pose、Width、Height、Text、Font Style、Font Size、Color等显示属性
每次消灭敌机为玩家增加50分:
重新开始游戏:
- 主角飞船被消灭2秒后重新开始游戏:
知识点总结
- 学习在Unity中创建实体,给实体设置位姿、材质、刚体、脚本。学会配置相机、光、天空盒。
- 学习在Unity中创建实体,给实体设置位姿、材质、刚体、脚本。学会配置相机、光、天空盒。
- 理解实时游戏update的时序机制。
- 通过Input.GetAxis()与InputManager监听玩家输入。
- 用Instantiate()动态生成游戏实体实例。
- 从Camera.main获得相机参数,设置场景中的物体在画面中的位置。
- 利用标签、图层管理器区分实体种类并设置物理规则。
- 通过Collider组件与OnTrigger 函数添加碰撞事件。
- 通过图形用户界面(GUI)管理游戏界面。
推荐课程
- GAMES 101 - 现代计算机图形学入门-闫令琪(中级)
- GAMES 104 - 现代游戏引擎入门必修课-王希(高级)
- GAMES 2022 - 高质量实时渲染-闫令琪(技美专用)