系列文章
Unity编程基础0——GameObject
GameObject用来表示任何可以存在于Scene(场景)中的事物。更具体的内容参考GameObject API参考和GameObject 用户手册,简单的来说,一个场景由若干个GameObject组成,GameObject就是场景搭建用到的“砖块”,
下面从几个方面介绍一下GameObject:
- 场景状态属性
- 活动状态
- 静态状态
- 标签和层
- 添加和移除组件
- 访问(自身)组件
- 访问其他GameObject上的组件
- 在Inspector中使用变量链接到GameObject
- 查找子GameObject
- 发送和广播消息
- 按名称或标签查找GameObject
- 创建和销毁GameObject
- 原始对象
场景状态属性
所谓场景状态属性,指的是所有GameObject在Inspector顶部共享的一组与场景中GameObject状态相关的控件。通常在GameObject的Inspector视图下组件列表顶部展示,通常可设置GameObject的活动状态、名称、静态状态、标签(Tag)、层(Layer),如果是预制件(Prefab),还会显示Prefab特有的操作相关的面板,如打开、选择及重写设置。
活动状态
GameObject 在默认情况下处于活动状态,但可以停用,这会关闭附加到 GameObject 的所有组件。这通常意味着它将变得不可见,不会接收任何正常回调或事件,例如 Update
或 FixedUpdate
。
改变活动状态的方式有两种:
- 通过勾选Inspector顶部
GameObject
名称的复选框来改变状态; - 通过调用
GameObject
的SetActive
函数来控制状态;
活动状态的读取:
- 通过
GameObject.activeSelf
读取当前的活动状态(这个名字有点歧义,看着像将自己变为活动状态); - 通过
GameObject.activeInHierarchy
读取在场景中实际意义上的活动状态(因为如果自己本身是活动的,但其父GameObject是非活动的,那么它仍然是非活动的),也就是说GameObject
的实际活动状态由GameObject.activeInHierarchy
获取得到。
静态状态
之所以GameObject需要支持静态状态设置,是因为Unity的一些系统(如全局光照、遮挡、批处理、导航和反射指针)依赖于静态状态。感觉这个有点类似于变成中的静态变量,好处是可以全局访问和设置,坏处估计就是会导致内存消耗上的增加,所以这个如非必要应该还是要少将GameObject设置为静态状态。
通过GameObjectUtility.SetStaticEditorFlags
设置静态属性,通过GameObject.isStatic
查询是否被设置为静态属性。
静态属性设置代码示例:
1 | using UnityEngine; |
上面的flags支持以下值:
属性: | 功能: |
---|---|
Nothing | 对于任何系统,都不将游戏对象包含在预计算中。 |
Everything | 对于下方的任何系统,都将游戏对象包含在预计算中。 |
Contribute GI | When you enable this property, Unity includes the target Mesh Renderer in global illumination calculations. These calculations take place while precomputing lighting data at bake time. The ContributeGI property exposes the ReceiveGI property. The ContributeGI property only takes effect if you enable a global illumination setting such as Baked Global Illumination or Enlighten Realtime Global Illumination for the target Scene. A Unity Blog post about static lighting with Light Probes provides guidance for using this flag. For additional context, see this tutorial for setting up the Built-in Render Pipeline and lighting in Unity. |
Occluder Static | 在遮挡剔除系统中,将游戏对象标记为静态遮挡物。有关更多信息,请参阅有关遮挡剔除系统的文档。 |
Occludee Static | 在遮挡剔除系统中,将游戏对象标记为静态被遮挡物。有关更多信息,请参阅有关遮挡剔除系统的文档。 |
Batching Static | 将游戏对象的网格与其他符合条件的网格组合起来,有可能降低运行时渲染成本。有关更多信息,请参阅有关静态批处理的文档。 |
Navigation Static | 在预计算导航数据时包含游戏对象。有关更多信息,请参阅有关导航系统的文档。 |
Off Mesh Link Generation | 在预计算导航数据时,尝试生成一个从该游戏对象开始的网格外链接。有关更多信息,请参阅有关自动构建网格外链接的文档。 |
Reflection Probe | 为 Type 属性设置为 Baked 的反射探针预计算数据时,包含此游戏对象。有关更多信息,请参阅有关反射探针的文档。 |
标签和层
标签 (Tag) 提供一种在场景中标记和识别 GameObject 类型的方式,而层 (Layer) 提供一种类似但不同的方式在某些内置操作(例如渲染或物理碰撞)中包括或排除 GameObject 组。
标签可以通过GameObject.tag
来设置,层通过GameObject.layer
来设置,通过GameObject.CompareTag
来判断游戏对象是否被打伤了某个Tag,该方法没有任何额外的内存消耗。
添加和移除组件
可以通过脚本在运行时动态的添加和移除组件,也可以通过脚本动态的禁用或启用脚本组件和某些内置组件而不销毁他们。
- 添加组件,GameObject.AddComponent;
- 移除组件,没有相应的RemoveComponent方法,而是直接调用静态的Destroy函数,然后将要移除的组件传递进去。
- 禁用或启用脚本组件,通过Behavior.enabled属性来进行设置。
访问组件
可以通过GameObject的GetComponent方法来获取附加到该GameObject的的组件,比如获取GameObject上的刚体组件:
1 | void Start () |
注意:一个GameObject中添加了多个脚本,如果要在某个脚本中访问另一个脚本,同样可以通过GetComponent方法来获取,这个时候传递的是脚本的类名(即要获取脚本组件的名称)。
如果尝试获取一个尚未添加到GameObject的组件,GetComponent将会返回null,所以在要修改获取到对象的任何值时,请确保这个组件已经添加到GameObject中了。
访问其他GameObject上的组件
GameObject可以独立运行,但更多的情况下它是与其他GameObject有联系的,需要访问其他GameObject上的组件,并更具条件通过脚本来改变组件的属性等。
在Inspector中使用变量链接到GameObject
在一个GameObject的脚本组件中持有另一个GameObject变量,这样在Inspector中就可以直接将GameObject拖动到这个变量上了,脚本就可以通过这个GameObject变量来完成对其组件的访问,代码示例如下:
1 | public class Chef : MonoBehaviour { |
显然,这里的stove没有进行任何判空,也就是说,如果没有在Inspector中给它赋值,那么运行到这个脚本时肯定就会报空指针了。所以这种情况的弊端就是必须在运行之前在Inspector中为这个变量赋值。同样的,如果在脚本中声明了一个public的组件,则可以将已经附加该组件的GameObject拖动到它上面。即可将上面的public GameObject stove
换成如下代码:
1 | public Transform playerTransform |
上面这种变量的方式要求在Unity编辑器中就将对象或者组件绑定好,如果需要在运行时找到并定位对象可以通过下面这两种方式。
查找子GameObject
将所有的同类型的子游戏对象都作为同一个游戏对象的子对象,然后通过这个这个作为父对象的对象来操作这些子对象。即使用具有父子关系的对象组来进行父对象对子对象的检索;常见的如寻路脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16using UnityEngine;
public class WaypointManager : MonoBehaviour {
public Transform[] waypoints;
void Start()
{
waypoints = new Transform[transform.childCount];
int i = 0;
foreach (Transform t in transform)
{
waypoints[i++] = t;
}
}
}当然还可以使用 Transform.Find 方法按名称查找特定子对象:
transform.Find("Frying Pan");
GameObject可以动态的添加和移除子游戏对象的功能在游戏中很有用,如游戏的背包系统(背包可以根据等级扩容,即改变可以容纳的子游戏对象的个数)。
发送和广播消息
上面讲到通过持有GameObject可以完成GameObject间的通信,这个前提是需要在运行之前就通过Inspector的拖拽来指定两个GameObject的联系,如果我们事先不能建立这个联系,如何在运行时完成对象间的通信呢?例如我们需要在游戏运行过程中查找与角色最近的对象、需要找到在场景加载后才创建的对象这些情形。这些情况下,可以在运行时查找引用并在GameObject之间发送消息。通常会用到以下三个方法:BroadcastMessage、SendMessage、SendMessageUpwards,点击链接可以查看这三个方法的具体说明。
- BroadcastMessage,发送消息给自身以及自身的子对象,并调用指定的方法;
- SendMessage,发送消息给该游戏对象本身,并调用指定的方法;
- SendMessageUpwards,发送消息给对象以及该对象的所有父对象,并调用指定的方法;
按名称或标签查找 GameObject
按名称查找:
1 | GameObject player; |
按Tag查找:
1 | GameObject chef; |
创建和销毁 GameObject
可以在项目运行期间创建和销毁 GameObject。在 Unity 中,可以使用 Instantiate 方法创建 GameObject。该方法可以生成现有对象的新副本。
有关如何实例化 GameObject 的完整说明和示例,请参阅在运行时实例化预制件。
还有一个 Destroy 方法,该方法将在帧更新完成后或(可选)短时间延迟后销毁对象:
1 | void OnCollisionEnter(Collision otherObj) { |
请注意,Destroy 函数可以在不影响游戏对象本身的情况下销毁个别组件。一个常见错误编写以下代码,假设它会销毁脚本所附加到的 GameObject
1 | Destroy(this); |
然而,由于 “this” 表示脚本而不是 GameObject,因此它实际上只会销毁调用它的脚本组件,而留下移除了脚本组件的 GameObject。
原始对象
通过 GameObject.CreatePrimitive可以在脚本中创建Unity内置原始对象的实例,可以使用的原始对象有:
- Sphere,球体;
- Capsule,胶囊体;
- Cylinder,圆柱体;
- Cube,正方体;
- Plane,平面
- Quad,四边形。
以上就是GameObject的基本介绍,具体的可以参考Unity官方文档的手册API和脚本API。
补充
Object,有两个,一个UnityEngine 命名空间下的Object,一个是.NET Framework 框架下的Object,前者是Unity编辑器所有可以应用类型的基类,后者是.NET 中所有类的基类(类似于Java的Object类),两者处于两个不同的命名空间,所以可以独立使用,并不存在冲突
Unity编程基础——MonoBehavior
MonoBehaviour 类是一个基类,所有 Unity 脚本都默认派生自该类。在Unity编辑器中新建脚本时,默认会继承该类。该类会提供一个脚本模板,包括基本的事件回调方法Start()
和Update()
。
协程
MonoBehavior类提供了启动、停止和管理协程的方法,协程是一种编写异步代码的方法,即在等待一段代码执行绘者一些actions执行完成时,同时支持其他代码的运行。
事件回调
MonoBehavior提供了众多的事件消息来帮助开发者在项目中根据当前的时机在正确的事件中进行代码的执行。下面列出的是一些常见的事件,更完全的事件消息,可以参考MonoBehavior编程脚本参考的Messages章节。
Start
,GameObject
首次加载在场景中或者GameObject
首次被实例化时调用(在首次调用任何Update方法前启用脚本时,在帧上调用Start);Update,在每一帧都调用,但每一帧的时间是不固定的;
FixedUpdate
,用于物理计算且独立于帧率消息,它具有物理系统的频率,每个固定帧率帧调用这个函数;LateUpdate,所有的Update函数调用后开始调用,一个GameObject可以绑定多个脚本,多个脚本就存在多个Update调用
Unity 后台主线程将 Update、LateUpdate制作成两个多线程 先去执行Update的线程,等Update 执行完毕后 在去执行LateUpdate线程。
OnBecameVisible
andOnBecameInvisible
,当游戏对象的渲染器进入或离开摄像机视图时调用;OnCollisionEnter
和OnTriggerEnter
,在发生物理碰撞或触发时调用;OnDestroy,在销毁对象是调用;
OnGUI,即时模式的GUI 更新,一帧中可能会调用多次,是一个开发时的工具,用于显示调试信息,不建议使用(太耗性能了)
OnEnabled,开启是调用
OnDisabled,关闭时调用
常用的函数
- CancelInvoke,取消函数调用
- Invoke,调用函数
- InvokeRepeating,按一定的频率调用传入的函数
- IsInvoking,当前是否正在调用该函数
- StartCoroutine,开启协程
- StopAllCoroutine,停止所有协程
- StopCoroutine,停止指定协程
Unity Puzzle Match Kit学习记录
添加背景
添加网格
添加元素
添加基本消除块元素;
添加空消除块元素
制作元素对应的 Prefab
消除块移动脚本
添加不同类型的消除块
网格的填充
网格的填充动画
添加障碍物
- 制作障碍物Prefab
障碍物周边填充
消除块匹配
直线匹配
L型和T型匹配
块消除
消除动画
消除逻辑
奖励元素
Unity官方教程6——Creative Core Animation
创建Animation步骤
Unity 编辑器Project 面板的Assets目录下,新建目录Animation(如果没有的话),选择该目录,右键->新建Animation,就可以创建 Animation;
选择场景中一个GameObject(或者 Prefab),步骤1中创建的 Animation 拖动到该GameObject(或者Prefab),这个时候Inspector 面板上就会新增一个Animator 组件;
双击Animator组件的Controller属性,就可以打开Animator面板,这里是一个状态机,可以根据不同状态进行不同动画的播放;
保持步骤2中的选择,打开Animation面板(Window->Animation,或者快捷键CMD+6),这里可以对创建的Animation进行编辑,可以添加需要进行动画的属性、添加关键帧、控制动画速度等;
Unity官方教程3——Creator Kit FPS学习记录
文章链接
- Unity官方教程0——系列内容汇总
- Unity官方教程1——Playground学习记录
- Unity官方教程2——Creator Kit Puzzle学习记录
- Unity官方教程3——Creator Kit FPS学习记录
- Unity官方教程4——Creator Kit RPG学习记录
- Unity官方教程5——Creator Kit Beginner Code学习记录
assetstore地址
该案例包含了第一人称射击游戏的全部内容,通过示例的形式,如何用Unity创建第一人称游戏。
迷你地图系统
通过Mesh Reader来进行迷你地图的限制。
武器系统
武器切换
武器添加
武器弹药
弹药拾取