本章将介绍Unity的核心概念,在游戏开发中必然用到的知识和基本操作,一些脚本的基础知识。
场景
概念: 包含了游戏环境、角色、UI元素,相当于一个独立的关卡
新建场景: 右键创造,默认带有一个主摄像机和一个方向光源
保存场景: ctrl+S
游戏物体
概念: 作为组件的容器,至少含有Transform
组件,还含有名称、标签、层级
组件
常见组件:变换组件、刚体组件、碰撞体组件、摄像机组件、粒子系统组件、音频组件.......
组件可以添加、删除、复位、粘贴、改变顺序
测试组件参数: 进入运行状态,修改组件参数,退出运行状态后组件参数会被重置
变换组件
三个属性,分别表示 位置、选择、缩放
父子关系:子物体的变换组件的参数是相对父物体的值,子物体会严格随着父物体一起移动、选择、缩放
游戏物体与变换组件是一一对应的,在脚本中父子关系的操作是在变换组件上进行的
父物体的非等比缩放可能会产生bug,粒子系统不受缩放系数影响
为父物体添加子物体时,可以先将父物体位置设为(0,0,0),这时子物体局部坐标系与世界坐标系重合,方便指定位置,添加完毕后再将父物体放到原位
脚本操作
概念:脚本相当于一个新的组件,其公共变量也会显示在编辑器中
创建脚本: 右键Create>C# Script,脚本的类名和文件名必须相同,若脚本要挂载在游戏物体上,则必须继承MonoBehaviour类
变量:脚本中定义的共有变量会显示在编辑器中,变量名称建议用驼峰式,而其对应的编辑器属性会自动变成首字母大写,且单词间有空格的形式
访问脚本挂载物体上的组件:
通过GetComponent函数
来访问组件/脚本
// 语法,调用该函数就会返回指定的组件
GetComponent<组件名/脚本名>()
// 例子:访问刚体
Rigidbody rb = GetComponent<Rigidbody>();
// 操作刚体
rb.mass = 10f;
rb.AddForce(Vector3.up * 10f);
访问非脚本挂载物体上的物体/组件:设置公共变量,再通过编辑器拖拽
// 游戏物体
public GameObject player;
// 组件
public Transform playerTransform;
拖拽的话用游戏物体拖拽到公共的组件属性上也可以,只要该游戏物体有这个组件就行。
访问一系列物体/组件
访问单个物体/组件通过拖拽比较方便,但是访问多个的就不方便了,解决方法如下:
-
访问子物体
通过父物体的脚本可以遍历子物体。
transform
是一个‘数组’,保存着所有子物体的信息。
public Transform[] waypoints;
void Start()
{
waypoints = new Transform[transform.childCount];
int i = 0;
// 遍历所有子物体
foreach(Transform t in transform)
{
waypoints[i++] = t;
}
}
- 通过标签和名称访问
// 名称查找
变量 = GameObject.Find("游戏物体的名称");
// 标签查找
变量 = GameObject.FindWithTag("标签名");
变量数组 = GameObject.FindGameObjectsWithTag("标签名");
事件函数
1. Update
每一帧渲染之前调用,每一帧的时间不固定,帧与帧之前的时间为Time.deltaTime
,单位为秒,所以物体移动的速度(m/s)需要乘以上述帧间隔时间才是路程。
2. FixedUpdate
每一次物理更新时调用,物体系统必须以固定的间隔时间工作,间隔时间Time.fixedDeltaTime
默认为0.02,可以在编辑器调整该值,适当调小可以避免模型穿透(前一帧两物体未碰撞,但由于速度太快,下一帧就过去了,也没撞上)
3. LateUpdate
每一帧渲染之后调用
4. Start
初始化函数,在更新函数之前调用一次
5. Awake
在Start调用之前就调用完毕
创建物体:Instantiate(xxx,位置,旋转)
,xxx可以是预制体/游戏物体,一般定义公共变量并拖拽赋值而成
销毁物体:Destroy(xxx, 延时时间)
,xxx同上,如果要销毁自身,则xxx为gameObject,切不可为this,因为这仅是销毁该脚本,游戏物体还存在
查看是否未激活:通过gameObject.activeInHierarchy
返回的布尔值判断
脚本的生命周期
脚本继承自MonoBehavior类
的都有生命周期
详见书本P59
标签
Tag直接在检视视图就可以设置与创建,作用如下:
- 通过标签查找物体,
GameObject.FindWithTag("xxx")
- 根据标签判断碰撞体的类型,进而触发对应的代码,
碰撞体.tag == "xxx"
静态物体
每个游戏物体的检视视图面板,在名称的右边都有一个Static
单选框
将游戏物体设置为静态的,即不会移动的,可以优化渲染,引擎会将静态物体看成一个整体,进行批量渲染。
还可以启用和禁用其子系统以便优化层级
作用:让摄像机只渲染某些层级的物体;让灯光只照亮某些层级的物体;碰撞检测和射线检测时只让某些物体发生碰撞。
新建和设置:在检视视图就可以手动设置
渲染部分物体:利用摄像机组件的剔除遮罩
选择性地射线检测
利用二进制的位操作思想,详见书本P65
预制体
预制体就是一个模板物体,根据其创建的物体都不是独立的,改变模板物体即可改变所有相关联的物体,方便用在创建大量相同的游戏物体上。每个物体还可以重载一些组件和参数。
创建预制体:右键Create>Prefab,再将场景中的游戏物体直接拖到空白的预制体上,也可以不先创建空白的,直接拖到文件夹中。
通过游戏物体实例修改预制体
与预制体相关联的游戏物体都有三个按钮:选择Select
、重置Reset
、应用Applay
。
点击选择:工程窗口高亮显示该预制体
点击重置:将修改后的参数重置回预制体本身的参数
点击应用:将修改后的参数应用在预制体上,即预制体被改变了
代码中实例化预制体
Instantiate(预制体,位置,旋转);
保存工程的注意事项
保存场景:Ctrl+S
保存工程:File>Save Project
工程数据和场景数据是独立的,需要分别保存,工程数据包括:工程设置、发布设置、资源的修改
输入
虚拟输入轴:用来屏蔽不同设备之间的差异,方便开发者兼容不同的设备
虚拟输入轴可以添加也可以删除,对每一个虚拟输入轴都可以自由添加映射的操作,如默认创建的竖直输入轴,键盘的W、S键和遥感的上下都被映射到该轴上。
脚本处理输入
脚本可以通过虚拟轴的名称从而读取对应的输入状态。
float value = Input.GetAxis("Horizontal"); // 范围-1到1
float value = Input.GetKey("a");
编辑与添加虚拟输入轴
在Edit>Project Settings>Input中,具体面板上属性的说明详见书本P75
移动设备的输入
详见书本P77
多点触摸Input.touches
获取鼠标/移动设备触摸的位置Input.mousePosition
加速度计Input.acceleration
,当移动设备移动时,加速度计会用一个三维变量持续报告当前加速度的值
方向与旋转的表示方法
欧拉角
简单,直观,三个数分别表示绕X轴、Y轴、Z轴旋转的角度
问题:万向节锁定,当中层圈旋转90度,内层圈和中层圈就重合了,这样外层圈和内层圈控制的方向是相同或相反的,即有两个轴不再独立控制物体的旋转了
四元数
不直观,无法超过180度,有四个数x、y、z、w
好处无万向节锁定
在Unity引擎中,检视面板用的是欧拉角,实际场景中用的是改良版的四元数
用代码控制物体旋转transform.Rotate(x,y,z)
一些坑详见书本P83
灯光
创建灯光:右键>Light>灯光类型选项
灯光种类
- 点光源(类似灯泡、蜡烛)
- 探照灯(圆锥形,类似手电筒、探照灯)
- 方向光(光纤平行,光强不会减弱,无穷远处发出,类似太阳光)
- 区域光源(光线从矩形表明均匀向四周发射)
- 发光材质
- 环境光(无需创建,为整个场景提供照明,在Window>Lighting>Setting中设置)
摄像机
属性说明详见书本P92
关键几个属性的说明:
- 剪切面:能拍摄到的最近和最远的范围,一个四棱台的形状
- 剔除遮罩:设置渲染哪些层级的游戏物体
- 视图矩形:XY表示坐标,WH表示宽高,范围都是0-1,可以将多个摄像机拍摄到的画面放在一个屏幕上
- 渲染贴图:将拍摄到的画面渲染到一张贴图上,类似画中画、镜子的效果