Unity3D完全自学教程(第2章 开始Unity游戏开发)

3,012 阅读8分钟

本章将介绍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;

拖拽的话用游戏物体拖拽到公共的组件属性上也可以,只要该游戏物体有这个组件就行。

访问一系列物体/组件

访问单个物体/组件通过拖拽比较方便,但是访问多个的就不方便了,解决方法如下:

  1. 访问子物体

    通过父物体的脚本可以遍历子物体。transform是一个‘数组’,保存着所有子物体的信息。

public Transform[] waypoints;

void Start()
{
    waypoints = new Transform[transform.childCount];
    int i = 0;
    // 遍历所有子物体
    foreach(Transform t in transform)
    {
        waypoints[i++] = t;
    }
}
  1. 通过标签和名称访问
// 名称查找
变量 = 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直接在检视视图就可以设置与创建,作用如下:

  1. 通过标签查找物体,GameObject.FindWithTag("xxx")
  2. 根据标签判断碰撞体的类型,进而触发对应的代码,碰撞体.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,可以将多个摄像机拍摄到的画面放在一个屏幕上
  • 渲染贴图:将拍摄到的画面渲染到一张贴图上,类似画中画、镜子的效果