Unity 安装

安装 Unity Hub

  1. 下载链接https://unity.cn/releases
  2. 选择长期支持版本进行下载
  3. 下载完成后进行安装
  4. 设置安装目录D:\Program Files\Unity\Unity Hub
  5. 开始安装

安装 Unity 编辑器

  1. 打开 Unity Hub在界面选择 installs
  2. 然后选择 Install Editor选择想要的版本
  3. 设置安装位置D:\Program Files\Unity\Editor
  4. 开始安装

获取许可证

许可证 → 添加 → 获取个人许可证

编辑器设置

中文界面

打开 Unity Hub → 安装 → 找到要汉化的 Unity 编辑器 → 点击齿轮图标 → 添加模块 → 选择语言包这里注意要把安装开发工具那个勾选取消掉 → 开始安装


打开项目 → Edit → Preferenges → Language → 简体中文 → 等待加载
如果没有直接更改为中文重启一下编辑器就好了

基本组件

项目文件夹

💗💗 每个项目的结构可能会有所不同具体取决于开发者的需求和个人偏好

文件夹描述
Assets 这是Unity项目的核心文件夹包含了所有可以被导入到Unity编辑器中的资源文件
这些资源包括但不限于模型纹理脚本音频文件等
在Assets文件夹内你可以根据需要进一步划分子文件夹来组织你的资源
Library 该文件夹包含了 Unity 生成的所有缓存文件和中间构建结果
这些文件不会被包含在最终的构建输出中但对编辑器来说非常重要
一般情况下你不应该手动修改这个文件夹里的内容
Logs 存放Unity编辑器的日志文件可以帮助开发者追踪错误或警告信息
Packages 用于存放Unity的包管理器所安装的各种包这些包可以是Unity官方提供的也可以是第三方开发者制作的
ProjectSettings 包含项目的各种设置文件比如渲染设置物理系统设置等这些文件控制着整个项目的全局行为
Temp 用于存放临时文件在构建过程中可能会用到
构建完成后这些文件可以被安全删除
UserSettings 用来存储与用户设置相关的配置文件的一个特殊文件夹
这些配置文件通常包含了用户的偏好设置游戏进度等非图形资源的数据
Plugins 用于放置平台特定的插件库文件例如.dll, .so, .dylib等
插件库可以扩展Unity的功能提供额外的能力
StreamingAssets 这个文件夹中的文件会被包含在最终的构建输出中并且可以在运行时访问
通常用于存放一些不希望被Unity资源加载系统处理的文件如文本文件配置文件等
Editor 如果你的项目包含了一些自定义编辑器工具或者脚本可以将它们放在这个文件夹下
只有在编辑器模式下这些脚本才会被编译和执行
Resources 用于存放那些需要通过Resources.Load方法加载的资源文件
通常用于存放一些小文件或者需要频繁访问的资源

面板视图

💗💗 Unity 编辑器各个面板视图的作用

/视图描述快捷键
Project 项目视图 显示项目中的所有文件和资源如脚本材质纹理模型等
可以通过双击文件将其导入到场景中
Ctrl + D 复制文件
Scene 场景视图 展示当前场景中的所有对象并允许在其中进行交互操作
可以移动相机视角选择物体调整物体位置等
W移动E旋转R缩放
Hierarchy 层级视图 列出场景中的所有 GameObject游戏对象并显示它们之间的层次关系
可以在此处创建新对象删除对象或改变它们的父子关系
Ctrl + D 复制选中的 GameObject
Inspector 检查器视图 显示当前选中 GameObject 的属性和组件
可以在这里修改对象的变换信息位置旋转缩放添加组件编辑组件属性等
Ctrl + Shift + I 选中 Inspector 视图
Console 控制台视图 显示编辑器和运行时的日志信息包括警告和错误消息 Ctrl + Shift + C 打开 Console 视图
Game 游戏视图 模拟游戏在目标平台上运行的情况 Ctrl + G 切换 Game View 的焦点
Animator 动画器视图 用于创建和编辑角色动画
可以创建动画状态机设置过渡条件等
可通过 Window > Animation 打开
Asset Store 资源商店 一个内置的市场可以在其中搜索和下载各种免费或付费的资源如模型材质脚本等 可通过 Window > Asset Store 打开
Package Manager 包管理器 管理 Unity 的附加包可以安装更新或卸载各种扩展包这些扩展包提供了额外的功能或工具 可通过 Window > Package Manager 打开
Profiler 分析器视图 用于性能分析可以帮助了解游戏中哪些部分最耗性能并找出优化方向 可通过 Window > Profiler 打开
Timeline 时间线视图 用于创建复杂的音频和视频剪辑适用于制作游戏内的过场动画或交互式叙事内容 可通过 Window > Timeline 打开
Scripting Definition Editor 脚本定义编辑器 用于定义和管理 C# 脚本的元数据如事件函数属性等 可通过 Window > Scripting Definition Editor 打开
Preferences 偏好设置 设置 Unity 编辑器的行为和外观例如界面颜色方案键盘布局等 可通过 Edit > Preferences 打开
Layouts 窗口布局 保存和加载编辑器的窗口布局以便快速切换至预先设置好的工作空间 可通过 Window > Layouts 打开
Scene Dock 场景停靠 允许将场景视图固定在一个位置即使在编辑器中导航其他窗口也不会改变场景视图的位置 可通过拖拽场景视图到编辑器边缘来启用

构建物体

Unity 支持创建多种基本形状Primitives如立方体球体圆柱体等

💗💗 创建示例

  1. 创建文件夹在 Project 视图Assets 文件夹中点击右键 → Create → Folder → 对文件夹进行命令
  2. 创建场景 点击进入新创建的文件夹中点击右键 → Create → Scene → 对场景进行命令
  3. 创建物体 点击进入新创建场景
    • 创建空对象在 Hierarchy 视图中点击右键 → Create Empty → 对空对象进行命令
    • 创建物体右键点击空对象 → 3D Object → 选择想要创建的基本形状比如 Cube, Sphere, Cylinder 等
    • 组合物体可以使用多个物体进行拼合拼合的物体就是一个对象

几何基本体

几何基本体Primitives是最简单的三维模型通常由Unity这样的游戏引擎内置提供它们可以快速创建场景的基础结构例如墙壁地面天空盒等

Primitives 物体 描述
Cube 立方体 可以作为墙壁或地面的基本形状
Sphere 球体 可以用作角色头部或者小行星等圆形物体
Cylinder 圆柱体 适合制作树木的树干或灯杆
Capsule 胶囊体 可以用来创建简单的角色模型或障碍物
Plane 平面 作为地面或者墙壁的表面
Quad 四边形 适合制作UI元素或贴图
Ring 环形 可以用作装饰性物体或特殊效果

其他物体

/ 物体 操作
Material 材质 在Assets文件夹内右键点击 → Create → Material → 为材质命名
Light 光源 在Hierarchy 视图中点击右键 → Light → 选择一种光源类型Directional Light, Point Light, Spot Light 等
Camera 摄像机 在 Hierarchy 视图中点击右键 → Camera
UI 按钮文本图像等 在 Hierarchy 视图中点击右键 -> UI -> 选择一个 UI 元素类型Button, Text, Image 等

坐标系

💗💗 局部坐标系

局部坐标系是指相对于父对象如果有的坐标系如果没有父对象则局部坐标系就是物体自己的坐标系

局部坐标系的原点通常是物体的中心位置也可以通过变换组件 (Transform) 来移动这个原点

物体的旋转和缩放也都是基于局部坐标系来进行的

  • X 轴通常表示物体的宽度方向即物体左右的方向
  • Y 轴通常表示物体的高度方向即物体上下方向
  • Z 轴通常表示物体的深度方向即物体前后方向

💗💗 世界坐标系

世界坐标系是全局坐标系它定义了场景中的绝对位置无论物体如何移动或旋转其世界坐标始终是相对于场景的原点通常是场景的左下角而言的

  • X 轴场景中的水平方向通常从左向右
  • Y 轴垂直向上通常表示高度方向
  • Z 轴指向屏幕外的方向即从屏幕向后

💗💗 局部与世界坐标系的区别

  • 位置物体的局部位置 (transform.localPosition) 是相对于其父物体的位置而物体的世界位置 (transform.position 或 transform.position) 是相对于世界原点的位置
  • 旋转物体的局部旋转 (transform.localRotation) 是相对于其父物体的旋转而物体的世界旋转 (transform.rotation) 是相对于世界坐标系的旋转
  • 缩放物体的局部缩放 (transform.localScale) 是相对于其父物体的缩放而物体的世界缩放 (transform.lossyScale) 是考虑了父物体缩放后的最终比例

属性和组件

Inspector 面板是 Unity 编辑器中非常重要的一个组成部分它允许你查看和编辑当前选中 GameObject 的属性和组件每一种不同的物体都有其对应的属性可以通过修改属性改变物体的位置和外观

视图调整

视图操作快捷键

  • 平移视图默认情况下按住鼠标中键或同时按下左键和右键并拖动鼠标可以平移视图
  • 缩放视图滚动鼠标滚轮可以缩放视图
  • 旋转视图按住 Alt 键并拖动鼠标可以旋转视图
  • 切换视图类型
    • 透视视图Perspective按 P 键
    • 正交视图Orthographic按 O 键

工具切换快捷键

  • 选择工具Select Tool按 Q 键这是默认的选择工具用于选择和移动游戏对象
  • 平移工具Move Tool 按 W 键用于在场景视图中移动游戏对象
  • 旋转工具Rotate Tool按 E 键用于在场景视图中旋转游戏对象
  • 缩放工具Scale Tool 按 R 键用于在场景视图中缩放游戏对象
  • 调整工具Rect Tool 按 T 键用于在场景视图中调整游戏对象
  • 整合Transform Tool 按 Y 键上述的功能的整合体工具

其他常用快捷键

  • 捕捉模式按 . 键可以启用捕捉模式使游戏对象的移动旋转和缩放更加精确
  • 取消捕捉模式再次按 . 键可以取消捕捉模式
  • 全选游戏对象按 Ctrl + AWindows/Linux或 Cmd + AMac可以全选场景中的所有游戏对象
  • 保存场景按 Ctrl + SWindows/Linux或 Cmd + SMac可以保存当前场景

脚本函数

生命周期

生命周期 函数 描述
初始化阶段 Awake 此方法在任何初始化之前调用
OnEnable 当脚本启用时调用如果脚本在场景启动时就是启用状态则 OnEnable 会在 Awake 方法之后立即调用
Start 在脚本第一次被启用并且所有依赖组件加载完成之后调用这是初始化脚本的常用位置
更新阶段 OnAnimatorIK 如果脚本绑定了一个 Animator 组件那么在每次 Update 之前会调用此方法用于设置动画 IK 和身体遮挡
OnAnimatorMove 如果脚本绑定了一个 Animator 组件那么在每次 Physics.Update 之前会调用此方法用于改变动画的状态
FixedUpdate 在每一帧的固定时间间隔内调用通常用于物理模拟因为它保证了固定的更新率即使帧速率变化也不会影响物理效果
Update 在每一帧调用通常用于处理用户输入移动物体等
LateUpdate 在所有 Update 方法完成后调用常用于调整相机位置或执行其他依赖于 Update 方法中已更新的数据的操作
OnPostRender 在渲染每一帧之后调用通常用于绘制额外的图形或调试信息
清理阶段 OnDisable 在脚本被禁用时调用
OnDestroy 在脚本被销毁前调用通常是用于释放资源

💗💗 注意事项 💗💗

1. Update 和 FixedUpdate 的调用顺序取决于 Unity 的版本以及是否启用了 Deterministic Update Order

2. Awake 总是在 Start 之前调用但 Start 不一定会在 FixedUpdate 或 Update 之前调用

3. OnEnable 和 OnDisable 可以用来处理脚本启用和禁用时的事件比如注册或取消注册事件监听器

4. OnAnimatorIK 和 OnAnimatorMove 只在绑定 Animator 组件时才有效

常见的类

在 Unity 中有许多类构成了游戏开发的核心部分这些类主要集中在 UnityEngine 命名空间中并且它们为创建游戏对象处理物理动画等提供了必要的功能

功能 类名 描述
对象管理 Object 一切的基础类所有 Unity 中的对象都继承自这个类
GameObject 表示场景中的一个实体可以附加组件
Component GameObject 的组件负责处理特定的功能如渲染或物理
渲染 Renderer 负责渲染的基类
MeshRenderer 渲染网格模型
SpriteRenderer 渲染精灵图
SkinnedMeshRenderer 渲染蒙皮网格模型
变换 Transform 控制 GameObject 的位置旋转和缩放
物理 Rigidbody 提供刚体物理模拟
Rigidbody2D 2D 刚体物理模拟
Collider 物理碰撞器基类
BoxCollider
SphereCollider
CapsuleCollider
不同形状的碰撞器
BoxCollider2D
CircleCollider2D
EdgeCollider2D
2D 碰撞器
动画 Animator 控制角色动画状态机
Animation 较旧的动画系统用于控制动画剪辑的播放
用户界面 (UI) Canvas UI 元素的容器
Image
Text
Button
Slider
Toggle
UI 控件
输入 Input 获取用户输入的静态类
Touch 触摸输入的信息
音频 AudioSource 播放音频剪辑
AudioClip 包含音频数据的类
时间 Time 访问时间相关数据的静态类
场景管理 SceneManager 加载卸载和切换场景
Scene 表示一个 Unity 场景
资源管理 Resources 加载资源文件
AssetBundle 加载外部资源包
网络 NetworkManager 管理网络连接和玩家
NetworkIdentity 标记具有网络同步功能的游戏对象

这些类只是 Unity 提供的一部分核心类实际上还有很多其他类和功能可用于更复杂的任务如果你需要了解更详细的列表或者具体的类信息可以查阅 Unity 官方文档

GameObject 类

GameObject 类是场景中最基本的构建单元每个可见的对象或可交互的元素都是一个GameObject实例包括3D模型灯光摄像机等GameObject类本身并不具备任何功能它的作用主要是作为组件的容器

基本属性

属性 描述
name GameObject的名字用于在编辑器中识别
transform GameObject的位置旋转和缩放信息通过Transform组件来表示
activeSelf 返回或设置GameObject是否处于激活状态
tag 用于快速分类GameObject比如PlayerEnemy
layer GameObject所在的图层可以用来控制渲染顺序或者碰撞检测

基本方法

方法 描述
AddComponent() 向GameObject添加指定类型的组件
GetComponent() 获取GameObject上指定类型的组件
GetComponents() 获取GameObject上所有指定类型的组件
SetActive(bool value) 设置GameObject是否激活
CompareTag(string tag) 检查GameObject的标签是否与给定的字符串匹配
Find(string name) 查找具有指定名称的第一个 GameObject
CreatePrimitive(PrimitiveType primitiveType) 创建一个具有指定类型的原始几何体 GameObject
Instantiate 克隆一个 GameObject并设置其位置旋转和父物体
Destroy(GameObject obj) 销毁指定的 GameObject可选延迟销毁

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
public class AddComponentExample : MonoBehaviour
{
void Start()
{
// 添加一个 MeshRenderer 组件到 GameObject
MeshRenderer renderer = gameObject.AddComponent<MeshRenderer>();

// 设置 MeshRenderer 的材质
renderer.material = new Material(Shader.Find("Standard"));

Debug.Log("MeshRenderer 组件已添加.");
}
}

public class GetComponentExample : MonoBehaviour
{
void Start()
{
// 获取 GameObject 上的 MeshRenderer 组件
MeshRenderer renderer = GetComponent<MeshRenderer>();

if (renderer != null)
{
Debug.Log("找到了 MeshRenderer 组件.");
}
else
{
Debug.Log("没有找到 MeshRenderer 组件.");
}
}
}

public class GetComponentsExample : MonoBehaviour
{
void Start()
{
// 获取 GameObject 上的所有 MeshRenderer 组件
MeshRenderer[] renderers = GetComponents<MeshRenderer>();

foreach (MeshRenderer renderer in renderers)
{
Debug.Log("找到了一个 MeshRenderer 组件.");
}
}
}

public class SetActiveExample : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
gameObject.SetActive(!gameObject.activeSelf);
}
}
}

public class CompareTagExample : MonoBehaviour
{
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
Debug.Log("Player 进入了触发区域!");
}
}
}

public class CreatePrimitiveExample : MonoBehaviour
{
void Start()
{
// 创建一个球体 GameObject
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.position = new Vector3(10f, 0f, 0f);
}
}

public class FindGameObjectExample : MonoBehaviour
{
void Start()
{
// 使用 GameObject.Find 查找名为 "Player" 的 GameObject
GameObject player = GameObject.Find("Player");

if (player != null)
{
Debug.Log("找到了 Player: " + player.name);
}
else
{
Debug.Log("没有找到名为 'Player' 的 GameObject");
}
}
}

public class InstantiateExample : MonoBehaviour
{
public GameObject prefab; // 需要在 Inspector 中指定预制件

void Start()
{
// 克隆一个预制件 GameObject并设置其位置和旋转
GameObject clone = GameObject.Instantiate(prefab, new Vector3(5f, 0f, 0f), Quaternion.identity, transform);
}
}

public class DestroyExample : MonoBehaviour
{
public GameObject target; // 需要在 Inspector 中指定目标 GameObject

void Start()
{
// 销毁指定的 GameObject延迟 2 秒后执行
GameObject.Destroy(target, 2f);
}
}

Transform 类

Transform类是所有游戏对象的核心组件之一它负责管理游戏对象的位置旋转和缩放等属性Transform组件对于场景中的物体定位至关重要并且它是每个游戏对象默认就有的一个组件

基本属性

属性 描述
Position 代表了游戏对象在世界空间中的位置坐标是一个Vector3类型的变量
Rotation 代表了游戏对象的旋转角度通常以四元数(Quaternion)形式表示也可以通过 .eulerAngles 属性转换为欧拉角表示
Scale 代表了游戏对象的缩放比例也是一个Vector3类型用于控制对象的大小

常用方法

方法 描述
Translate 将游戏对象沿指定方向移动指定的距离
Rotate 使游戏对象绕指定轴旋转指定的角度
Scale : 改变游戏对象的尺寸
LookAt 使游戏对象面向指定的目标或世界坐标
SetParent 设置此游戏对象的父级如果worldPositionStays设置为true则保持当前的世界位置不变
GetParent 返回当前游戏对象的父Transform
Find 在子对象中寻找具有指定名称的游戏对象
InverseTransformPoint 将世界坐标转换为本地坐标
TransformPoint 将本地坐标转换为世界坐标
InverseTransformDirection 从世界坐标系转换到本地坐标系
TransformDirection 从本地坐标系转换到世界坐标系
InverseTransformDirection 从源物体坐标系到目标物体坐标系的变换

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//获取Transform组件
public class MoveObject : MonoBehaviour
{
public float speed = 1.0f; // 移动速度

Transform transform;

void Start()
{
transform = GameObject.Find("ObjectName").transform;
}
}

//移动物体
public class MoveObject : MonoBehaviour
{
public float speed = 1.0f; // 移动速度

void Update()
{
Transform transform = GameObject.Find("ObjectName").transform;
// 每帧向右移动
transform.Translate(new Vector3(speed * Time.deltaTime, 0, 0));
}
}

//旋转物体
public class RotateObject : MonoBehaviour
{
public float rotationSpeed = 360.0f; // 每秒旋转360度

void Update()
{
// 绕Y轴旋转
transform.Rotate(new Vector3(0, rotationSpeed * Time.deltaTime, 0));
}
}

//改变物体大小
public class ScaleObject : MonoBehaviour
{
private Vector3 originalScale;
private float timeElapsed = 0f;
private float duration = 2f; // 缩放持续时间

void Start()
{
originalScale = transform.localScale;
}

void Update()
{
timeElapsed += Time.deltaTime;

// 缩放从1.0到1.5再回到1.0
float scaleFactor = Mathf.Lerp(1.0f, 1.5f, (timeElapsed % duration) / duration);
transform.localScale = originalScale * scaleFactor;
}
}

//面向目标
public class LookAtCursor : MonoBehaviour
{
void Update()
{
// 获取鼠标在屏幕上的位置
Vector3 mousePos = Input.mousePosition;

// 把鼠标位置从屏幕坐标转换成世界坐标
mousePos.z = Camera.main.nearClipPlane;
Vector3 worldMousePos = Camera.main.ScreenToWorldPoint(mousePos);

// 计算物体应该面向的方向
Vector3 direction = worldMousePos - transform.position;

// 使物体面向该方向
transform.LookAt(new Vector3(worldMousePos.x, transform.position.y, worldMousePos.z));
}
}

//获取和设置父对象
public class ParentingExample : MonoBehaviour
{
public Transform newParent; // 新的父物体

void Start()
{
// 获取当前物体的父物体
Debug.Log("Current parent: " + transform.parent);

// 设置新的父物体
transform.SetParent(newParent);

// 检查新的父物体
Debug.Log("New parent: " + transform.parent);
}
}

//查找子物体
public class FindChildExample : MonoBehaviour
{
void Start()
{
// 查找名为"ChildObject"的子物体
Transform childTransform = transform.Find("ChildObject");

if (childTransform != null)
{
Debug.Log("Found child object: " + childTransform.name);
}
else
{
Debug.Log("Child object not found.");
}
}
}

//本地和世界坐标转换
public class CoordinateConversionExample : MonoBehaviour
{
public Transform targetTransform; // 目标物体

void Update()
{
// 将世界坐标转换为本地坐标
Vector3 localPosition = transform.InverseTransformPoint(targetTransform.position);
Debug.Log("Local position of target: " + localPosition);

// 将本地坐标转换为世界坐标
Vector3 worldPosition = transform.TransformPoint(localPosition);
Debug.Log("World position of target: " + worldPosition);
}
}

//本地和世界坐标系转换
public class VectorTransformationExample : MonoBehaviour
{
public Transform targetTransform; // 目标物体

void Update()
{
// 从世界坐标系转换到本地坐标系
Vector3 inverseDirection = transform.InverseTransformDirection(worldDirection);
Debug.Log("Inverse direction: " + inverseDirection);

// 从本地坐标系转换到世界坐标系
Vector3 localDirection = new Vector3(1, 0, 0);
Vector3 worldDirection = transform.TransformDirection(localDirection);
Debug.Log("World direction: " + worldDirection);
}
}

//从一个物体的坐标系变换到另一个物体的坐标系
public class VectorTransformationBetweenObjects : MonoBehaviour
{
public Transform sourceTransform; // 源物体
public Transform targetTransform; // 目标物体

void Update()
{
// 从源物体坐标系到目标物体坐标系的变换
Vector3 sourceDirection = new Vector3(1, 0, 0);
Vector3 targetDirection = targetTransform.InverseTransformDirection(
sourceTransform.TransformDirection(sourceDirection));
Debug.Log("Target direction: " + targetDirection);
}
}

Vector3 类

在Unity中Vector3 类是一个非常重要的结构体用于表示三维空间中的点或向量这个类定义了几个常量属性和方法可以帮助你在游戏中处理各种与位置方向和速度有关的操作

基本结构

Vector3 是一个结构体struct它包含三个浮点数成员x, y 和 z分别对应三维空间中的三个坐标轴

常用属性

属性 描述
zero 返回一个新的 Vector3(0, 0, 0)
one 返回一个新的 Vector3(1, 1, 1)
up 返回一个新的 Vector3(0, 1, 0)表示正y轴方向
down 返回一个新的 Vector3(0, -1, 0)表示负y轴方向
left 返回一个新的 Vector3(-1, 0, 0)表示负x轴方向
right 返回一个新的 Vector3(1, 0, 0)表示正x轴方向
forward 返回一个新的 Vector3(0, 0, 1)表示正z轴方向
back 返回一个新的 Vector3(0, 0, -1)表示负z轴方向
magnitude 返回向量的长度
sqrMagnitude 返回向量长度的平方通常用于性能敏感的场合避免开方运算
normalized 返回单位向量即原向量的方向但长度为1
x, y, z 分别获取或设置向量的 x, y, z 坐标值

常用方法

方法 描述
Normalize 将当前向量归一化为单位向量
Scale 按照提供的向量对当前向量进行缩放
Set 设置向量的 x, y, z 值
Dot 计算与另一个向量的点积
Cross 计算与另一个向量的叉积
Distance 计算与另一个向量的距离
Lerp 在两个向量之间进行线性插值
MoveTowards 向目标向量移动最大移动距离受限制
Reflect 根据法线反射一个向量
Angle 计算与另一个向量之间的角度
Min 返回一个新向量其各分量为当前向量与另一个向量相应分量的较小值
Max 返回一个新向量其各分量为当前向量与另一个向量相应分量的较大值

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//Normalize将一个向量归一化即保持方向不变但长度变为1
Vector3 vector = new Vector3(3, 4, 0);
Vector3 normalizedVector = vector.normalized;
Debug.Log("Normalized Vector: " + normalizedVector);
// 输出: Normalized Vector: (0.6, 0.8, 0)

//Scale按比例缩放向量
Vector3 vector = new Vector3(1, 2, 3);
Vector3 scale = new Vector3(2, 2, 2);
vector.Scale(scale);
Debug.Log("Scaled Vector: " + vector);
// 输出: Scaled Vector: (2, 4, 6)

//Set设置向量的 x, y, z 值
Vector3 vector = new Vector3();
vector.Set(1, 2, 3);
Debug.Log("Set Vector: " + vector);
// 输出: Set Vector: (1, 2, 3)

//Dot计算两个向量的点积
Vector3 vector1 = new Vector3(1, 2, 3);
Vector3 vector2 = new Vector3(4, 5, 6);
float dotProduct = Vector3.Dot(vector1, vector2);
Debug.Log("Dot Product: " + dotProduct);
// 输出: Dot Product: 32

//Cross计算两个向量的叉积
Vector3 vector1 = new Vector3(1, 0, 0);
Vector3 vector2 = new Vector3(0, 1, 0);
Vector3 crossProduct = Vector3.Cross(vector1, vector2);
Debug.Log("Cross Product: " + crossProduct);
// 输出: Cross Product: (0, 0, 1)

//Distance计算两个向量之间的距离
Vector3 vector1 = new Vector3(1, 2, 3);
Vector3 vector2 = new Vector3(4, 5, 6);
float distance = Vector3.Distance(vector1, vector2);
Debug.Log("Distance: " + distance);
// 输出: Distance: 5.196152

//Lerp在线性插值两个向量之间
Vector3 from = new Vector3(0, 0, 0);
Vector3 to = new Vector3(10, 10, 10);
float t = 0.5f; // 插值因子
Vector3 interpolated = Vector3.Lerp(from, to, t);
Debug.Log("Interpolated Vector: " + interpolated);
// 输出: Interpolated Vector: (5, 5, 5)

//MoveTowards向目标向量移动最大移动距离受限制
Vector3 current = new Vector3(0, 0, 0);
Vector3 target = new Vector3(10, 10, 10);
float maxDistanceDelta = 5; // 最大移动距离
Vector3 moved = Vector3.MoveTowards(current, target, maxDistanceDelta);
Debug.Log("Moved Vector: " + moved);
// 输出: Moved Vector: (5, 5, 5)

//Reflect根据法线反射一个向量
Vector3 inDirection = new Vector3(1, -1, 0);
Vector3 normal = new Vector3(0, 1, 0);
Vector3 reflected = Vector3.Reflect(inDirection, normal);
Debug.Log("Reflected Vector: " + reflected);
// 输出: Reflected Vector: (1, 1, 0)

//Angle计算两个向量之间的角度
Vector3 vector1 = new Vector3(1, 0, 0);
Vector3 vector2 = new Vector3(0, 1, 0);
float angle = Vector3.Angle(vector1, vector2);
Debug.Log("Angle: " + angle);
// 输出: Angle: 90

//Min() 和 Max()返回一个新向量其各分量为当前向量与另一个向量相应分量的最小值或最大值
Vector3 vector1 = new Vector3(1, 2, 3);
Vector3 vector2 = new Vector3(4, 1, 2);
Vector3 minVector = Vector3.Min(vector1, vector2);
Vector3 maxVector = Vector3.Max(vector1, vector2);
Debug.Log("Min Vector: " + minVector);
Debug.Log("Max Vector: " + maxVector);
// 输出: Min Vector: (1, 1, 2)
// 输出: Max Vector: (4, 2, 3)

游戏应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//Normalize当你需要确保一个方向向量的长度为1时通常用于计算方向而非大小
void Update()
{
Vector3 direction = Input.mousePosition -
Camera.main.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2));
direction.Normalize();
transform.forward = direction;
}

//Scale当你需要按比例缩放一个物体的位置时
void ScalePosition()
{
Vector3 currentPosition = transform.position;
Vector3 scale = new Vector3(2, 2, 2);
currentPosition.Scale(scale);
transform.position = currentPosition;
}

//Set当你需要快速设置一个物体的位置时
void SetPosition()
{
Vector3 newPosition = new Vector3();
newPosition.Set(10, 20, 30);
transform.position = newPosition;
}

//Dot当你需要检测两个物体是否面向彼此时
bool IsFacingTarget(Transform target)
{
Vector3 directionToTarget = target.position - transform.position;
directionToTarget.Normalize();
float dotProduct = Vector3.Dot(transform.forward, directionToTarget);
return dotProduct > 0.9f; // 如果两个向量几乎相同则认为是面向目标
}

//Cross当你需要计算两个向量之间的法线时
Vector3 CalculateNormal(Vector3 a, Vector3 b, Vector3 c)
{
Vector3 ab = b - a;
Vector3 ac = c - a;
return Vector3.Cross(ab, ac).normalized;
}

//Distance当你需要检测玩家与目标之间的距离时
void CheckDistanceToPlayer(Transform player)
{
float distanceToPlayer = Vector3.Distance(player.position, transform.position);
if (distanceToPlayer < 10.0f)
{
// 接近玩家
}
}

//Lerp当你需要平滑地移动一个物体到目标位置时
void SmoothlyMoveToTarget(Transform target, float speed)
{
Vector3 newPos = Vector3.Lerp(transform.position, target.position, Time.deltaTime * speed);
transform.position = newPos;
}

//MoveTowards当你需要限制物体移动的最大速度时
void MoveTowardsTarget(Transform target, float maxSpeed)
{
Vector3 newPos = Vector3.MoveTowards(transform.position, target.position, maxSpeed * Time.deltaTime);
transform.position = newPos;
}

//Reflect当你需要模拟光线反射时
void ReflectLightRay(Vector3 rayDirection, Vector3 surfaceNormal)
{
Vector3 reflectedRay = Vector3.Reflect(rayDirection, surfaceNormal);
// 使用 reflectedRay 绘制光线
}

//Angle当你需要计算两个物体之间的角度时
float CalculateAngleBetweenPlayers(Transform player1, Transform player2)
{
Vector3 direction = player2.position - player1.position;
return Vector3.Angle(player1.forward, direction);
}

//Min() 和 Max()当你需要限制物体的位置范围时
void LimitPosition(Vector3 minLimit, Vector3 maxLimit)
{
Vector3 clampedPosition = Vector3.Clamp(transform.position, minLimit, maxLimit);
transform.position = clampedPosition;
}

Time 类

在Unity中Time类提供了一系列的属性和方法来帮助开发者处理游戏中的时间相关功能这对于实现游戏逻辑动画物理模拟等非常重要

常用属性

属性 描述
deltaTime 表示从上一帧到当前帧的时间差以秒为单位
time 从游戏开始到现在经过的总时间以秒为单位
timeSinceLevelLoad 自从加载当前场景以来经过的时间以秒为单位
fixedDeltaTime 固定时间步长即从上一个固定更新到当前固定更新之间的时间差
fixedTime 自游戏启动以来经过的总时间按照固定时间步长计算
fixedUpdatePeriod 物理引擎和其他固定更新系统的更新频率
unscaledTime 从游戏开始到现在经过的真实时间不受时间缩放的影响
unscaledDeltaTime 上一帧和当前帧之间的真实时间差同样不受时间缩放的影响
unscaledTimeSinceLevelLoad 自从加载当前场景以来的真实时间
timeScale 游戏世界时间的缩放系数默认值为1.0表示正常速度
smoothDeltaTime 返回一个平滑的deltaTime值可以避免突然的时间跳跃影响游戏体验
captureFramerate 获取目标帧率
targetFramerate 设置目标帧率

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//使用 Time.deltaTime 控制物体平滑移动
public class SmoothMovement : MonoBehaviour
{
public float speed = 5f; // 物体移动速度

void Update()
{
// 使用Time.deltaTime确保速度一致
transform.Translate(Vector3.right * speed * Time.deltaTime);
}
}

//使用 Time.time 计算游戏中的经过时间
public class GameTimer : MonoBehaviour
{
private float startTime;

void Start()
{
// 在游戏开始时记录开始时间
startTime = Time.time;
}

void Update()
{
// 计算已经过去的时间
float elapsedTime = Time.time - startTime;

Debug.Log("Elapsed Time: " + elapsedTime);
}
}

//使用 Time.timeSinceLevelLoad 计算场景加载后的经过时间
public class LevelTimer : MonoBehaviour
{
void Update()
{
// 计算自从加载当前场景以来经过的时间
float timeSinceLevelLoad = Time.timeSinceLevelLoad;

Debug.Log("Time Since Level Load: " + timeSinceLevelLoad);
}
}

//使用 Time.fixedDeltaTime 更新物理系统
public class PhysicsUpdateExample : MonoBehaviour
{
public Rigidbody rb;

void FixedUpdate()
{
// 使用Time.fixedDeltaTime更新物理系统
rb.AddForce(Vector3.up * 5f * Time.fixedDeltaTime);
}
}

//使用 Time.fixedTime 计算基于固定时间间隔的时间
public class FixedTimeExample : MonoBehaviour
{
private float startTime;

void Start()
{
// 在游戏开始时记录开始时间
startTime = Time.fixedTime;
}

void FixedUpdate()
{
// 计算已经过去的时间
float elapsedTime = Time.fixedTime - startTime;

Debug.Log("Fixed Elapsed Time: " + elapsedTime);
}
}

//使用 Time.fixedUpdatePeriod 获取固定更新周期
public class FixedUpdatePeriodExample : MonoBehaviour
{
void Start()
{
// 输出固定更新周期
Debug.Log("Fixed Update Period: " + Time.fixedUpdatePeriod);
}
}

//使用 Time.unscaledTime 记录真实时间
public class RealTimeTracker : MonoBehaviour
{
private float startTime;
private float totalPlayTime;

void Start()
{
startTime = Time.unscaledTime;
}

void Update()
{
// 计算从游戏开始以来的真实时间
totalPlayTime = Time.unscaledTime - startTime;

Debug.Log("Total Play Time: " + totalPlayTime);
}
}

//使用 Time.unscaledDeltaTime 计算真实时间差
public class UnscaledDeltaTimeExample : MonoBehaviour
{
private float elapsedTime = 0f;

void Update()
{
elapsedTime += Time.unscaledDeltaTime;

if (elapsedTime > 5f)
{
// 每隔5秒执行一次
Debug.Log("Unscaled Delta Time Elapsed: " + elapsedTime);
elapsedTime = 0f;
}
}
}

//使用 Time.unscaledTimeSinceLevelLoad 计算真实时间差
public class UnscaledTimeSinceLevelLoadExample : MonoBehaviour
{
private float startTime;

void Start()
{
startTime = Time.unscaledTimeSinceLevelLoad;
}

void Update()
{
// 计算自从加载当前场景以来的真实时间
float timeSinceLevelLoad = Time.unscaledTimeSinceLevelLoad - startTime;

Debug.Log("Unscaled Time Since Level Load: " + timeSinceLevelLoad);
}
}

//使用 Time.timeScale 实现慢动作效果
public class SlowMotion : MonoBehaviour
{
public float normalSpeed = 1f; // 正常时间速度
public float slowMotionSpeed = 0.2f; // 慢动作速度
private bool isSlowMotion = false;

void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// 切换慢动作状态
isSlowMotion = !isSlowMotion;
Time.timeScale = isSlowMotion ? slowMotionSpeed : normalSpeed;
}
}
}

//使用 Time.smoothDeltaTime 平滑化时间测量
public class SmoothTimeExample : MonoBehaviour
{
private float elapsedTime = 0f;

void Update()
{
elapsedTime += Time.smoothDeltaTime;

if (elapsedTime > 5f)
{
// 每隔5秒执行一次
Debug.Log("Smooth Delta Time Elapsed: " + elapsedTime);
elapsedTime = 0f;
}
}
}

游戏应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//Time.smoothDeltaTime是一个平滑处理的deltaTime值它可以避免由于deltaTime的突然变化而导致的游戏逻辑中的不稳定行为这个值通常被用来做更平滑的时间测量
public class SmoothDeltaTimeExample : MonoBehaviour
{
private float elapsedTime = 0f;

void Update()
{
elapsedTime += Time.smoothDeltaTime;

if (elapsedTime > 5f)
{
// 每隔5秒执行一次
Debug.Log("Smooth Delta Time Elapsed: " + elapsedTime);
elapsedTime = 0f;
}
}
}

//监控游戏的帧率可以使用Time.captureFramerate属性
public class FramerateMonitor : MonoBehaviour
{
void OnGUI()
{
GUI.Label(new Rect(10, 10, 200, 20), "Current Framerate: " + Time.captureFramerate);
}
}

//Time.captureFramerate是当前捕捉到的帧率而QualitySettings.vSyncCount可以用来设置是否启用垂直同步以及目标帧率垂直同步V-Sync是一种技术可以使游戏帧率与显示器的刷新率同步从而减少屏幕撕裂现象在Unity中你可以通过设置QualitySettings.vSyncCount来控制是否启用垂直同步并间接地设置目标帧率
public class FramerateControl : MonoBehaviour
{
private const int TARGET_FRAMERATE = 60; // 目标帧率为60 FPS

void Start()
{
SetTargetFramerate(TARGET_FRAMERATE);
}

private void SetTargetFramerate(int targetFramerate)
{
// 设置垂直同步0表示禁用1表示启用并锁定帧率为目标帧率
QualitySettings.vSyncCount = targetFramerate <= 30 ? 1 : 0;

// 如果启用垂直同步则实际帧率等于显示器刷新率
if (QualitySettings.vSyncCount == 1)
{
Application.targetFrameRate = targetFramerate;
}
else
{
// 否则我们可以通过设置targetFramerate来限制最大帧率
Application.targetFrameRate = targetFramerate;
}
}

void OnGUI()
{
GUI.Label(new Rect(10, 10, 200, 20), "Current Framerate: " + Time.captureFramerate);
}
}

//设置目标帧率并在游戏运行时显示当前帧率
public class TargetFramerateExample : MonoBehaviour
{
[SerializeField] private int targetFramerate = 60; // 目标帧率为60 FPS
private GUIStyle labelStyle;

private void Start()
{
// 设置目标帧率
SetTargetFramerate(targetFramerate);

// 初始化GUI样式
labelStyle = new GUIStyle();
labelStyle.fontSize = 20;
labelStyle.normal.textColor = Color.white;
}

private void SetTargetFramerate(int targetFramerate)
{
// 如果目标帧率大于30关闭垂直同步
// 如果目标帧率为30或更低开启垂直同步
QualitySettings.vSyncCount = targetFramerate <= 30 ? 1 : 0;

// 设置目标帧率
Application.targetFrameRate = targetFramerate;
}

private void OnGUI()
{
// 显示当前帧率
GUI.Label(new Rect(10, 10, 200, 20), $"Current Framerate: {Time.captureFramerate}", labelStyle);
}
}

Input类

在Unity中Input类是一个非常重要的工具用于处理来自用户的输入包括键盘鼠标和游戏手柄等这个类提供了许多静态属性和方法来检测这些输入设备的状态

基本概念

  • AxesUnity中的轴用来表示连续输入比如移动或旋转轴值范围通常在-1到+1之间
  • 按键Keys表示特定键是否被按下例如Input.GetKey(KeyCode.A)检查A键是否被持续按下
  • 触发Triggers表示键或按钮从未按下变为按下的瞬间
  • 释放Releases表示键或按钮从按下变为未按下的瞬间

常用属性

属性 描述
Input.anyKeyDown 返回一个布尔值表示是否有任何键被按下
Input.mousePosition 返回一个Vector3表示鼠标在屏幕上的位置
Input.mouseScrollDelta 返回一个Vector2表示鼠标滚轮的滚动量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//这个属性返回一个布尔值表示是否有任何键被按下
void Update()
{
if (Input.anyKeyDown)
{
Debug.Log("Any key is down.");
}
}

//这个属性返回一个Vector3表示鼠标在屏幕上的位置
void Update()
{
Vector3 mousePos = Input.mousePosition;
Debug.Log("Mouse Position: " + mousePos);
}

//这个属性返回一个Vector2表示鼠标滚轮的滚动量
void Update()
{
Vector2 scrollDelta = Input.mouseScrollDelta;
if (scrollDelta.y != 0)
{
Debug.Log("Mouse Scroll Delta: " + scrollDelta);
}
}

键盘输入

方法 描述
Input.GetKeyDown(KeyCode) 检查键是否被按下
Input.GetKey(KeyCode) 检查键是否被持续按下
Input.GetKeyUp(KeyCode) 检查键是否被释放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//这个方法检查键是否被按下
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("Space key was pressed.");
}
}

//这个方法检查键是否被持续按下
void Update()
{
if (Input.GetKey(KeyCode.W))
{
Debug.Log("W key is being held down.");
}
}

//这个方法检查键是否被释放
void Update()
{
if (Input.GetKeyUp(KeyCode.Escape))
{
Debug.Log("Escape key was released.");
}
}

鼠标输入

方法 描述
Input.GetMouseButtonDown(int button) 检查鼠标按钮是否被按下
Input.GetMouseButton(int button) 检查鼠标按钮是否被持续按下
Input.GetMouseButtonUp(int button) 检查鼠标按钮是否被释放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//这个方法检查鼠标按钮是否被按下
void Update()
{
if (Input.GetMouseButtonDown(0)) // 0 represents the left mouse button.
{
Debug.Log("Left mouse button was pressed.");
}
}

//这个方法检查鼠标按钮是否被持续按下
void Update()
{
if (Input.GetMouseButton(1)) // 1 represents the right mouse button.
{
Debug.Log("Right mouse button is being held down.");
}
}

//这个方法检查鼠标按钮是否被释放
void Update()
{
if (Input.GetMouseButtonUp(2)) // 2 represents the middle mouse button.
{
Debug.Log("Middle mouse button was released.");
}
}

轴输入

方法 描述
Input.GetAxis(string axisName) 获取指定轴的值
Input.GetAxisRaw(string axisName) 获取指定轴的原始值不经过平滑处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//这个方法获取指定轴的值
void Update()
{
float horizontal = Input.GetAxis("Horizontal");
if (horizontal != 0)
{
Debug.Log("Horizontal axis value: " + horizontal);
}
}

//这个方法获取指定轴的原始值不经过平滑处理
void Update()
{
int verticalRaw = (int)Input.GetAxisRaw("Vertical");
if (verticalRaw != 0)
{
Debug.Log("Vertical axis raw value: " + verticalRaw);
}
}

GUI 类

GUI 类是 Unity 引擎中的一个核心组件它允许开发者直接在屏幕上绘制用户界面元素如按钮滑块文本框等GUI 类属于 UnityEngine 命名空间并且它的功能主要通过 OnGUI 回调函数在运行时进行绘制尽管 OnGUI 方法在 Unity 中仍然可用但现代 Unity 开发更倾向于使用 UGUI用户图形界面系统因为它提供了更好的性能和更丰富的功能集

常用属性

属性 描述
color 设置或获取当前 GUI 的绘制颜色
contentColor 设置或获取 GUI 内容的颜色
depth 设置或获取 GUI 绘制的深度
具有较高深度值的对象会覆盖较低深度值的对象
enabled 设置或获取 GUI 控件是否响应用户输入
skin 设置或获取当前 GUI 皮肤
cursor 设置或获取鼠标光标的样式

常用方法

方法 描述
Button 创建一个按钮可以检测用户是否点击了该按钮
Label 创建一个标签用于显示文本
TextField 创建一个文本输入框允许用户输入文本
PasswordField 创建一个密码输入框输入的字符会被隐藏
Slider 创建一个滑动条允许用户通过拖动来选择一个值
HorizontalSlider 创建一个水平滑动条
Toggle 创建一个开关按钮用户可以选择开启或关闭
Box 创建一个带有边框的矩形区域
DrawTexture 在屏幕上绘制一个纹理
DrawTextureWithTexCoords 使用指定的 UV 坐标绘制纹理

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using UnityEngine;

public class GUIExamples : MonoBehaviour
{
// 文本输入框的状态
private string textInput = "初始文本";

// 切换按钮的状态
private bool toggleState = false;

// 滑动条的值
private float sliderValue = 0.5f;

// 水平滑动条的值
private float horizontalSliderValue = 0.5f;

// 选择框的选项
private int dropdownSelection = 0;
private string[] dropdownOptions = new string[] { "选项 A", "选项 B", "选项 C" };

// 用于绘制纹理的路径
private Texture2D textureToDraw;

void Start()
{
// 加载一个纹理这里假设你有一个名为 "MyTexture" 的资源
textureToDraw = Resources.Load<Texture2D>("Textures/MyTexture");
}

void OnGUI()
{
// 文本输入框
textInput = GUI.TextField(new Rect(10, 10, 150, 30), textInput);

// 按钮
if (GUI.Button(new Rect(10, 50, 100, 30), "点击我"))
{
Debug.Log("按钮被点击");
}

// 标签
GUI.Label(new Rect(10, 90, 150, 30), "这是标签文本");

// 滑动条
sliderValue = GUI.VerticalSlider(new Rect(10, 130, 30, 100), sliderValue, 0.0f, 1.0f);

// 水平滑动条
horizontalSliderValue = GUI.HorizontalSlider(new Rect(50, 130, 150, 30), horizontalSliderValue, 0.0f, 1.0f);

// 切换按钮
toggleState = GUI.Toggle(new Rect(10, 240, 100, 30), toggleState, "切换");

// 下拉列表
dropdownSelection = GUI.SelectionGrid(new Rect(10, 280, 150, 30), dropdownSelection, dropdownOptions, 1);

// 绘制纹理
if (textureToDraw != null)
{
GUI.DrawTexture(new Rect(10, 320, textureToDraw.width, textureToDraw.height), textureToDraw);
}
}
}

游戏应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//世界坐标转平面坐标并使用GUI在人物头上添加一个血条
using UnityEngine;
using UnityEngine.UI;

public class HealthBarExample : MonoBehaviour
{
public Transform target; // 目标角色的Transform
public float health; // 角色的生命值
public float maxHealth; // 角色的最大生命值

private void Start()
{
// 初始化最大生命值
maxHealth = health;
}

private Vector3 ScreenPosition(Transform transform)
{
// 将世界坐标转换为屏幕坐标
Vector3 screenPos = Camera.main.WorldToScreenPoint(transform.position);
return screenPos;
}

private void OnGUI()
{
if (target == null)
return;

Vector3 screenPos = ScreenPosition(target);

// 确保角色在屏幕内
if (screenPos.z > 0)
{
// 计算血条的位置和大小
Rect healthBarRect = new Rect(screenPos.x - 25, Screen.height - screenPos.y + 25, 50, 5); // 血条宽度高度等可以根据需要调整

// 绘制背景
GUI.Box(healthBarRect, "");

// 计算血条的填充比例
float fillRatio = Mathf.Clamp01(health / maxHealth);

// 绘制实际的血条长度
Rect fillRect = new Rect(healthBarRect.x, healthBarRect.y, healthBarRect.width * fillRatio, healthBarRect.height);
GUI.Box(fillRect, "");
}
}
}

SceneManager 类

SceneManager 类是 Unity 引擎中的一个核心组件用于管理和操作场景在 Unity 中游戏是由一个或多个场景组成的而 SceneManager 提供了加载卸载切换场景等功能

枚举类

LoadSceneMode 枚举定义了场景加载的不同模式

属性 描述
Single 新场景将替换当前正在运行的场景
Additive 新场景将被添加到当前正在运行的场景列表中
AdditiveWithUnloading 新场景被加载后旧场景会被卸载

常用方法

方法 描述
LoadScene 同步加载场景
LoadSceneAsync(string sceneName) 异步加载指定名称的场景
LoadSceneAsync(int sceneBuildIndex) 异步加载指定构建索引的场景
LoadSceneAsync(Scene scene, LoadSceneMode mode) 按照指定模式异步加载场景对象
UnloadSceneAsync(int sceneBuildIndex) 异步卸载指定构建索引的场景
UnloadSceneAsync(Scene scene) 异步卸载指定的场景对象
GetActiveScene 获取当前活动的场景
GetSceneByBuildIndex 通过构建索引来获取场景
GetSceneByName 通过场景名称来获取场景
MoveGameObjectToScene 将游戏对象移动到指定的场景中

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//LoadScene (同步加载)
//同步加载场景是最简单的方式但会阻塞主线程不适合加载大型场景
public class SceneLoader : MonoBehaviour
{
public void LoadScene(string sceneName)
{
SceneManager.LoadScene(sceneName);
}
}

//LoadSceneAsync (异步加载)
//异步加载场景不会阻塞主线程适用于加载大型场景
public class AsyncSceneLoader : MonoBehaviour
{
public void LoadSceneAsync(string sceneName)
{
SceneManager.LoadSceneAsync(sceneName);
}

// 如果你需要知道加载进度可以这样实现
public void LoadSceneWithProgress(string sceneName)
{
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);

while (!asyncLoad.isDone)
{
float progress = asyncLoad.progress;
Debug.Log("Loading progress: " + progress);
}
}
}

//UnloadSceneAsync (异步卸载)
//异步卸载场景同样不会阻塞主线程
public class AsyncSceneUnloader : MonoBehaviour
{
public void UnloadSceneAsync(string sceneName)
{
Scene scene = SceneManager.GetSceneByName(sceneName);
if (scene != null && scene.isLoaded)
{
SceneManager.UnloadSceneAsync(scene);
}
}
}

//GetActiveScene (获取当前活动场景)
public class ActiveSceneGetter : MonoBehaviour
{
private void Start()
{
Scene activeScene = SceneManager.GetActiveScene();
Debug.Log("Current active scene is: " + activeScene.name);
}
}

//GetSceneByBuildIndex (通过构建索引获取场景)
public class SceneByBuildIndex : MonoBehaviour
{
public void GetSceneByIndex(int index)
{
Scene scene = SceneManager.GetSceneByBuildIndex(index);
Debug.Log("Scene at build index " + index + " is named: " + scene.name);
}
}

//GetSceneByName (通过场景名称获取场景)
public class SceneByName : MonoBehaviour
{
public void GetSceneByName(string sceneName)
{
Scene scene = SceneManager.GetSceneByName(sceneName);
Debug.Log("Scene with name " + sceneName + " has state: " + scene.state);
}
}

//MoveGameObjectToScene (将 GameObject 移动到场景)
//将一个 GameObject 移动到另一个场景中
public class GameObjectMover : MonoBehaviour
{
public GameObject gameObjectToMove;
public string targetSceneName;

public void MoveGameObjectToScene()
{
Scene targetScene = SceneManager.GetSceneByName(targetSceneName);
if (targetScene.isLoaded)
{
SceneManager.MoveGameObjectToScene(gameObjectToLoad, targetScene);
}
else
{
Debug.LogError("Target scene is not loaded.");
}
}
}

//LoadSceneMode (枚举示例)
//使用 LoadSceneMode 枚举来指定加载场景的模式
public class SceneLoaderWithMode : MonoBehaviour
{
public void LoadSceneAdditive(string sceneName)
{
SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
}
}

游戏应用

💗💗 游戏关卡切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//在游戏中玩家完成一个关卡后通常需要切换到下一个关卡
public class LevelSwitcher : MonoBehaviour
{
public void NextLevel(string nextLevelName)
{
SceneManager.LoadScene(nextLevelName);
}
}

// 在某个事件触发器中调用 NextLevel 方法
public class LevelCompletedTrigger : MonoBehaviour
{
public string nextLevelName;

private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
LevelSwitcher levelSwitcher = FindObjectOfType<LevelSwitcher>();
if (levelSwitcher != null)
{
levelSwitcher.NextLevel(nextLevelName);
}
}
}
}

💗💗 游戏菜单系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//许多游戏都有一个主菜单玩家可以从那里开始游戏进入设置或退出游戏
public class MenuManager : MonoBehaviour
{
public void LoadMainMenu()
{
SceneManager.LoadScene("MainMenu");
}

public void LoadSettingsMenu()
{
SceneManager.LoadScene("SettingsMenu", LoadSceneMode.Additive);
}

public void QuitGame()
{
Application.Quit();
}
}

💗💗 复活点和检查点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//在某些游戏中玩家在死亡后可以从最近的复活点或检查点重新开始
public class PlayerRespawner : MonoBehaviour
{
public string respawnSceneName;

private void OnEnable()
{
// 假设有一个事件系统来通知玩家死亡
PlayerDeathEventSystem.PlayerDied += OnPlayerDied;
}

private void OnDisable()
{
PlayerDeathEventSystem.PlayerDied -= OnPlayerDied;
}

private void OnPlayerDied()
{
SceneManager.LoadScene(respawnSceneName);
}
}

💗💗 动态加载内容

1
2
3
4
5
6
7
8
9
10
11
12
13
//对于大型开放世界游戏可能需要在游戏运行时动态加载新的区域或任务
public class DynamicContentLoader : MonoBehaviour
{
public void LoadNewArea(string areaName)
{
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(areaName, LoadSceneMode.Additive);

asyncLoad.completed += (AsyncOperation op) =>
{
Debug.Log("New area loaded successfully!");
};
}
}

💗💗 资源管理与优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//在多场景游戏中有时候我们需要在切换场景时卸载不再需要的场景以节省内存
public class ResourceOptimizer : MonoBehaviour
{
public void UnloadUnusedScenes()
{
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
if (scene.name != "ActiveScene" && scene.isLoaded)
{
SceneManager.UnloadSceneAsync(scene);
}
}
}
}

💗💗 场景之间的数据传递

1
2
3
4
5
6
7
8
9
//在切换场景时可能需要传递一些状态信息或玩家数据可以通过 PlayerPrefs 或者全局脚本来实现这一点
public class DataPasser : MonoBehaviour
{
public void PassDataToNextScene()
{
PlayerPrefs.SetInt("Score", 100); // 保存玩家分数
SceneManager.LoadScene("NextLevel");
}
}

AudioSource 类

AudioSource 是 Unity 引擎中用于播放音频的基础组件在 Unity 中如果你想要在场景中播放声音无论是背景音乐音效还是语音都需要将 AudioSource 组件添加到游戏对象上

基本属性

属性 描述
Mute 是否静音
Volume 音量大小范围从 0 到 1其中 1 表示原始音量
Pitch 音调可以改变声音的高低通常用来模拟远离或靠近的声音效果
Playback Mode 播放模式
Looping循环播放
Play Once只播放一次
Stop停止播放
Clip 指定要播放的音频剪辑

属性示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//Mute在游戏菜单中提供一个按钮当玩家点击时可以静音所有游戏声音
public void ToggleMute()
{
audioSource.mute = !audioSource.mute;
}

//Volume在游戏设置中提供一个滑块来调整游戏的整体音量
public void SetVolume(float volume)
{
audioSource.volume = volume;
}

//Pitch根据游戏角色的速度来改变脚步声的音调
public void UpdatePitch(float speedFactor)
{
audioSource.pitch = 1.0f + speedFactor; // 假设速度因子为正数代表更快的速度
}

//Playback Mode设置背景音乐为循环播放
void Start()
{
audioSource.loop = true; // 启用循环
audioSource.clip = backgroundMusicClip;
audioSource.Play();
}

//Clip在角色跳跃时播放跳跃音效
public void PlayJumpSound()
{
audioSource.clip = jumpSoundClip;
audioSource.Play();
}

常用方法

方法 描述
Play 开始播放音频
Stop 停止播放音频
Pause 暂停播放音频
UnPause 取消暂停状态继续播放音频

方法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//Play当游戏开始时播放主题曲
void Start()
{
audioSource.clip = themeSongClip;
audioSource.Play();
}

//Stop当游戏暂停时停止所有音效
public void OnGamePaused()
{
audioSource.Stop();
}

//Pause当玩家进入菜单时暂停背景音乐
public void EnterMenu()
{
audioSource.Pause();
}

//UnPause当玩家返回游戏时恢复背景音乐播放
public void ReturnToGame()
{
audioSource.UnPause();
}

其他属性

属性 描述
Pan Stereo 立体声的左右平衡可以在-1全左到1全右之间设置
Bypass Effects 是否绕过音频效果
Bypass Listener Effects 是否绕过监听器效果
Bypass Reverb Zones 是否绕过混响区域
Doppler Level 多普勒效应的强度影响音调变化
Rolloff Mode 声音衰减模式决定了声音如何随着距离的增加而减弱
Min Distance 和 Max Distance 定义了声音从开始减弱的距离最小距离和完全不可听的距离最大距离

其他属性示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//Pan Stereo根据声音来源的方向调整立体声的左右平衡
public void SetPanStereo(float panValue)
{
audioSource.panStereo = panValue;
}

//Bypass Effects在某些情况下关闭所有的音频效果处理
public void DisableEffects()
{
audioSource.bypassEffects = true;
}

//Bypass Listener Effects在特殊场景下忽略监听器上的所有效果
public void IgnoreListenerEffects()
{
audioSource.bypassListenerEffects = true;
}

//Bypass Reverb Zones在特定情况下忽略混响区域的效果
public void IgnoreReverbZones()
{
audioSource.bypassReverbZones = true;
}

//Doppler Level根据移动物体的速度调整多普勒效应
public void SetDopplerLevel(float level)
{
audioSource.dopplerLevel = level;
}

//Rolloff Mode根据声音源和监听器之间的距离改变声音的衰减模式
public void SetRolloffMode(AudioRolloffMode mode)
{
audioSource.rolloffMode = mode;
}

//Min Distance 和 Max Distance设置声音开始衰减的距离以及完全消失的最大距离
public void SetMinMaxDistance(float min, float max)
{
audioSource.minDistance = min;
audioSource.maxDistance = max;
}

游戏应用

💗💗 背景音乐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//背景音乐通常是游戏中持续播放的一种音频类型它有助于营造游戏氛围
public class BackgroundMusic : MonoBehaviour
{
public AudioClip musicClip;

private AudioSource audioSource;

void Start()
{
audioSource = GetComponent<AudioSource>();
audioSource.clip = musicClip;
audioSource.loop = true; // 循环播放
audioSource.Play();
}
}

💗💗 角色动作音效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//当角色跳跃时播放一个特定的音效
public class PlayerController : MonoBehaviour
{
public AudioClip jumpSound;
private AudioSource audioSource;

void Start()
{
audioSource = GetComponent<AudioSource>();
}

public void Jump()
{
if (audioSource && jumpSound)
{
audioSource.clip = jumpSound;
audioSource.Play();
}
}
}

💗💗 UI 交互音效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//在游戏菜单或UI元素上添加音效反馈如按钮点击音效
public class UIButtonSound : MonoBehaviour
{
public AudioClip clickSound;
private AudioSource audioSource;

void Start()
{
audioSource = GetComponent<AudioSource>();
}

public void OnButtonClick()
{
if (audioSource && clickSound)
{
audioSource.clip = clickSound;
audioSource.Play();
}
}
}

💗💗 动态音调变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//根据游戏状态改变音调例如敌人接近时声音变高
public class EnemySound : MonoBehaviour
{
public AudioClip enemySound;
private AudioSource audioSource;

void Start()
{
audioSource = GetComponent<AudioSource>();
audioSource.clip = enemySound;
}

public void UpdatePitch(float distanceFactor)
{
if (audioSource.isPlaying)
{
audioSource.pitch = Mathf.Lerp(0.8f, 1.2f, distanceFactor); // 根据距离因素线性插值音调
}
}
}

💗💗 3D 音效定位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//根据声音源的位置调整音效使其听起来像是来自特定方向
public class SoundEmitter : MonoBehaviour
{
public AudioClip soundClip;
private AudioSource audioSource;

void Start()
{
audioSource = GetComponent<AudioSource>();
audioSource.spatialBlend = 1f; // 设置为3D音效
audioSource.clip = soundClip;
}

public void PlaySound()
{
audioSource.Play();
}
}

💗💗 动态音量调整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//根据游戏环境调整音量例如角色进入室内时降低背景音量
public class IndoorSound : MonoBehaviour
{
public AudioClip indoorAmbience;
private AudioSource audioSource;

void Start()
{
audioSource = GetComponent<AudioSource>();
audioSource.clip = indoorAmbience;
audioSource.Play();
}

public void AdjustVolume(bool isIndoor)
{
audioSource.volume = isIndoor ? 0.5f : 1.0f;
}
}

💗💗 多个音频源管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//对于复杂的游戏可能需要管理多个 AudioSource例如背景音乐环境音效角色音效等
public class AudioManager : MonoBehaviour
{
public AudioSource bgmSource;
public AudioSource sfxSource;

public void PlayBGM(AudioClip clip)
{
bgmSource.clip = clip;
bgmSource.Play();
}

public void PlaySFX(AudioClip clip)
{
sfxSource.clip = clip;
sfxSource.Play();
}
}

Rigidbody 类

Rigidbody 是 Unity 中的一个重要组件它为游戏对象提供了物理特性通过给游戏对象添加 Rigidbody 组件你可以让这个对象受到重力的影响与其他带有 Rigidbody 的对象发生碰撞并且可以使用力和扭矩来改变其运动状态

ForceMode 枚举

属性 描述
Force 应用线性力
Acceleration 应用加速度
Impulse 应用瞬时冲量
VelocityChange 直接改变速度

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class ApplyForceOnJump : MonoBehaviour
{
public float jumpForce = 500f;
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
}
}

//将刚体的速度设置为特定值
public class SetVelocity : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void Update()
{
// 设置刚体的速度
rb.velocity = new Vector3(10, 0, 0); // 沿 X 轴移动
}
}

//AddTorque 方法用于向刚体施加扭矩使其绕轴旋转
public class RotateObject : MonoBehaviour
{
public float torqueStrength = 10f;
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void FixedUpdate()
{
// 向刚体施加扭矩使其绕 Y 轴旋转
rb.AddTorque(Vector3.up * torqueStrength);
}
}

主要属性

属性 描述
Mass 物体的质量影响物体如何响应外力的作用
Drag 一个系数决定了物体在空气中移动时受到的阻力大小
Angular Drag 一个系数决定了物体旋转时受到的阻力大小
Use Gravity 如果启用则该物体将受到重力的影响
Is Kinematic 如果启用则该物体不会受到物理引擎的影响可以被脚本精确控制
Constraints 可以限制物体沿某些轴移动或旋转
Sleep Mode 当物体静止一段时间后物理引擎会将其设置为休眠状态以节省计算资源

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//mass 属性用于设置或获取刚体的质量质量较大的物体需要更大的力才能达到相同的速度
public class ChangeMass : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
rb.mass = 20f; // 设置质量为 20 千克
}
}

//drag 属性用于设置或获取刚体在空气中的阻力系数较高的阻力系数会使物体减速更快
public class IncreaseAirResistance : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
rb.drag = 0.5f; // 增加空气阻力
}
}

//angularDrag 属性用于设置或获取刚体旋转时的阻力系数较高的角阻力系数会使物体停止旋转得更快
public class IncreaseAngularResistance : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
rb.angularDrag = 0.5f; // 增加角阻力
}
}

//useGravity 属性用于设置或获取刚体是否受到重力的影响
public class DisableGravity : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
rb.useGravity = false; // 不受重力影响
}
}

//isKinematic 属性用于设置或获取刚体是否为运动学对象运动学对象不受物理引擎的影响只能通过脚本精确控制
public class MakeKinematic : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
rb.isKinematic = true; // 设置为运动学对象
}
}

常用方法

方法 描述
AddForce 向刚体施加力
AddTorque 向刚体施加扭矩
MovePosition(Vector3 position) 在非物理模拟的情况下直接移动刚体的位置仅当 IsKinematic 为 true 时可用
MoveRotation(Quaternion rotation) 直接改变刚体的旋转仅当 IsKinematic 为 true 时可用
velocity 读取或设置刚体的速度
angularVelocity 读取或设置刚体的角速度

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//AddForce 方法用于向刚体施加力
public class JumpOnSpace : MonoBehaviour
{
public float jumpForce = 500f;
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
}
}

//AddTorque 方法用于向刚体施加扭矩
public class RotateObject : MonoBehaviour
{
public float torqueStrength = 10f;
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void FixedUpdate()
{
rb.AddTorque(Vector3.up * torqueStrength);
}
}

//MovePosition (移动位置) 方法用于在非物理模拟的情况下直接移动刚体的位置
public class TeleportObject : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void Update()
{
if (Input.GetKeyDown(KeyCode.T))
{
rb.MovePosition(transform.position + Vector3.forward * 5f);
}
}
}

//MoveRotation (移动旋转) 方法用于在非物理模拟的情况下直接改变刚体的旋转
public class RotateInstantly : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
Quaternion newRotation = Quaternion.Euler(0, 90, 0); // 绕 Y 轴旋转 90 度
rb.MoveRotation(newRotation);
}
}
}

//velocity 属性允许你读取或设置刚体的速度
public class SetVelocityExample : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void Update()
{
// 设置刚体的速度
rb.velocity = new Vector3(10, 0, 0); // 沿 X 轴移动
}
}

//angularVelocity 属性允许你读取或设置刚体的角速度
public class RotateObjectExample : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void Update()
{
// 设置刚体的角速度
rb.angularVelocity = new Vector3(0, 10, 0); // 绕 Y 轴旋转
}
}

游戏应用

💗💗 创建一个简单的弹跳球

  1. 在 Unity 中创建一个新的空场景
  2. 添加一个球体 GameObject (GameObject > 3D Object > Sphere)
  3. 为球体添加 Mesh Collider 和 Rigidbody 组件
  4. 在 Rigidbody 组件中设置 Mass 为 1.0Use Gravity 为勾选状态Is Kinematic 不勾选
  5. 创建一个 C# 脚本并附加到球体 GameObject 上这个脚本将控制球体的初始位置和速度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;

public class BouncingBall : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();

// 设置球体的初始位置
transform.position = new Vector3(0, 5, 0);

// 添加一个向下的力使球体开始下落
rb.AddForce(Vector3.down * 500, ForceMode.Force);
}
}

💗💗 创建一个简单的推箱子游戏

  1. 创建一个立方体 GameObject (GameObject > 3D Object > Cube)
  2. 为立方体添加 Box Collider 和 Rigidbody 组件
  3. 在 Rigidbody 组件中设置 Mass 为 1.0Use Gravity 为勾选状态Is Kinematic 不勾选
  4. 创建一个 C# 脚本并附加到箱子 GameObject 上这个脚本将控制玩家点击时箱子的移动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using UnityEngine;

public class PushableBox : MonoBehaviour
{
private Rigidbody rb;

void Start()
{
rb = GetComponent<Rigidbody>();
}

void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;

if (Physics.Raycast(ray, out hit))
{
if (hit.collider.gameObject == gameObject)
{
// 计算鼠标点击的方向
Vector3 direction = (hit.point - transform.position).normalized;

// 向该方向施加力
rb.AddForce(direction * 100, ForceMode.Impulse);
}
}
}
}
}

碰撞检测

基础概念

Collider碰撞器是 Unity 中用于碰撞检测的组件它们可以附加到 GameObject 上并定义了 GameObject 在物理世界中的形状例如 Box ColliderSphere Collider 和 Capsule Collider 等


Rigidbody刚体使 GameObject 具有物理属性如质量摩擦力等并允许它参与物理模拟没有 Rigidbody 的 GameObject 不会受到重力或碰撞的影响

回调函数

类型 回调函数 描述
碰撞回调函数 OnCollisionEnter 当两个带有 Rigidbody 的 GameObject 第一次碰撞时调用
OnCollisionStay 当两个带有 Rigidbody 的 GameObject 仍然接触时每一帧调用
OnCollisionExit 当两个带有 Rigidbody 的 GameObject 不再接触时调用
触发器回调函数 OnTriggerEnter 当另一个 Collider 进入触发器区域时调用
OnTriggerStay 当另一个 Collider 仍然在触发器区域内时每一帧调用
OnTriggerExit 当另一个 Collider 完全离开触发器区域时调用

碰撞检测

💗💗 回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//OnCollisionEnter当两个带有 Rigidbody 的 GameObject 发生碰撞时调用此函数
public class CollisionDetectionExample : MonoBehaviour
{
private void OnCollisionEnter(Collision collision)
{
Debug.Log("Collision detected with: " + collision.gameObject.name);
}
}

//OnTriggerEnter当另一个 Collider 进入触发器区域时调用此函数
public class TriggerDetectionExample : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
Debug.Log("Trigger entered by: " + other.gameObject.name);
}
}

💗💗 射线投射

射线投射是一个非常有用的碰撞检测技术它可以用来检测玩家的视线前方是否有障碍物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Physics.Raycast检查从一点向另一点发射的射线是否与任何 Collider 相交
public class RaycastExample : MonoBehaviour
{
private void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.forward, out hit, 10f))
{
Debug.Log("Hit object: " + hit.transform.name);
Debug.DrawRay(transform.position, transform.forward * 10f, Color.green);
}
else
{
Debug.DrawRay(transform.position, transform.forward * 10f, Color.red);
}
}
}

💗💗 形状投射

形状投射可以用来检测不同形状的对象是否与其他对象碰撞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Physics.SphereCast检测球体沿着指定方向移动是否会与其他 Collider 发生碰撞
public class SphereCastExample : MonoBehaviour
{
private void Update()
{
RaycastHit hit;
if (Physics.SphereCast(transform.position, 0.5f, transform.forward, out hit, 10f))
{
Debug.Log("Hit object: " + hit.transform.name);
Debug.DrawRay(transform.position, transform.forward * 10f, Color.green);
}
else
{
Debug.DrawRay(transform.position, transform.forward * 10f, Color.red);
}
}
}

💗💗 形状重叠

形状重叠可以用来检测一个形状是否与场景中的其他 Collider 重叠

1
2
3
4
5
6
7
8
9
10
11
12
//Physics.OverlapSphere检测指定位置的球体是否与场景中的 Collider 重叠
public class OverlapSphereExample : MonoBehaviour
{
private void Update()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, 1.0f);
foreach (var collider in colliders)
{
Debug.Log("Overlap with: " + collider.gameObject.name);
}
}
}

💗💗 触发器

触发器是一种特殊的 Collider当其他 Collider 进入或离开时不会产生碰撞但会触发回调函数

1
2
3
4
5
6
7
8
9
10
11
//将 Collider 的 isTrigger 属性设置为 true
public class TriggerSetupExample : MonoBehaviour
{
public Collider myCollider;

private void Start()
{
// 将 Collider 设置为触发器
myCollider.isTrigger = true;
}
}

游戏应用

💗💗 回调函数玩家与敌人的碰撞检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//假设我们有一个玩家角色和一些敌人角色我们希望在玩家触碰到敌人时减少玩家的生命值
using UnityEngine;

public class PlayerController : MonoBehaviour
{
public int health = 100;

private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
Debug.Log("Player collided with an enemy!");
health -= 10; // 减少生命值
if (health <= 0)
{
// 处理玩家死亡的情况
Debug.Log("Game Over!");
}
}
}
}

💗💗 射线投射射击游戏中的命中检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//假设正在制作一个第一人称射击游戏需要检测玩家的子弹是否击中了敌人
using UnityEngine;

public class BulletScript : MonoBehaviour
{
private void Update()
{
// 检测鼠标点击
if (Input.GetMouseButtonDown(0))
{
// 获取屏幕上的点击位置
Vector3 mousePosition = Input.mousePosition;
// 把屏幕坐标转换为世界坐标
Ray ray = Camera.main.ScreenPointToRay(mousePosition);
RaycastHit hit;

// 尝试投射射线
if (Physics.Raycast(ray, out hit, 100f))
{
if (hit.collider.CompareTag("Enemy"))
{
Debug.Log("Bullet hit an enemy at: " + hit.point);
// 处理命中逻辑
}
}
}
}
}

💗💗 形状重叠检测玩家是否踩到了地板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//假设我们需要检测玩家是否离开了地面这可以用来实现跳跃逻辑
using UnityEngine;

public class GroundCheck : MonoBehaviour
{
public Transform groundCheckPoint;
public float checkRadius = 0.2f;
public LayerMask whatIsGround;

private bool isGrounded;

private void Update()
{
isGrounded = Physics.OverlapSphere(groundCheckPoint.position, checkRadius, whatIsGround).Length > 0;

if (isGrounded)
{
Debug.Log("Player is on the ground.");
}
else
{
Debug.Log("Player is in the air.");
}
}
}

💗💗 形状投射检测玩家是否进入某个区域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//假设我们有一个特殊的区域当玩家进入这个区域时会发生某些事件
using UnityEngine;

public class AreaTrigger : MonoBehaviour
{
public LayerMask playerLayer;
private bool isInArea = false;

private void Update()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, 2f, playerLayer);

if (colliders.Length > 0 && !isInArea)
{
isInArea = true;
Debug.Log("Player entered the special area.");
}
else if (colliders.Length == 0 && isInArea)
{
isInArea = false;
Debug.Log("Player left the special area.");
}
}
}

💗💗 触发器假设我们有一扇门当玩家穿过门时门会关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using UnityEngine;

public class DoorController : MonoBehaviour
{
public GameObject door;

private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
Debug.Log("Player entered the door trigger.");
door.SetActive(false); // 关闭门
}
}
}

相机跟随

第一人称视角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//简单示例
void shijiao1() {
camera.transform.position = tank.position + tank.forward * -1 + new Vector3(0, 1, 0);
camera.transform.LookAt(SJ.transform);
}


//游戏应用
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FirstPersonCamera : MonoBehaviour
{
// 鼠标灵敏度
public float mouseSensitivity = 100.0f;

// 用于限制垂直旋转角度的变量
private float xRotation = 0.0f;

void Start()
{
// 锁定鼠标光标并禁用光标
Cursor.lockState = CursorLockMode.Locked;
}

void Update()
{
// 获取水平和垂直方向上的鼠标输入
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;

// 更新垂直旋转角度
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);

// 设置相机的垂直旋转
transform.localRotation = Quaternion.Euler(new Vector3(xRotation, 0, 0));

// 旋转玩家模型以匹配水平方向上的鼠标输入
transform.parent.Rotate(new Vector3(0, mouseX, 0));
}
}

第三人称视角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ThirdPersonCamera : MonoBehaviour
{
// 公开的Transform变量用于在编辑器中指定要跟随的目标
public Transform target; // 目标对象通常是玩家角色

// 相对于目标的位置偏移
// 这个偏移定义了相机相对于目标的位置例如(-3, 0, -5)意味着相机位于目标的左侧后方
public Vector3 offset = new Vector3(-3f, 0f, -5f);

// 平滑跟随的时间常数值越大跟随越平滑
public float smoothTime = 0.3f;

// 用于平滑跟随计算的累积速度
private Vector3 velocity = Vector3.zero;

void LateUpdate()
{
// 如果没有指定目标则不执行任何操作
if (target == null)
return;

// 计算目标位置
Vector3 targetPosition = target.position + offset;

// 使用平滑跟随函数平滑地移动相机到目标位置
Vector3 smoothedPosition = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);

// 设置相机的新位置
transform.position = smoothedPosition;

// 保持相机始终朝向目标
transform.LookAt(target);
}
}

鼠标行为

回调函数

函数 描述
OnMouseEnter 当鼠标进入一个 Collider 或 Canvas 的边界时被调用
OnMouseExit 当鼠标离开一个 Collider 或 Canvas 的边界时被调用
OnMouseDown 当鼠标按钮在一个 Collider 或 Canvas 上被按下时被调用
OnMouseUp 当鼠标按钮在一个 Collider 或 Canvas 上被释放时被调用
OnMouseDrag 当鼠标被按下并移动时被调用
OnMouseOver 只要鼠标在一个 Collider 或 Canvas 上方每一帧都会被调用
OnMouseUpAsButton 当鼠标按钮在一个 GUI 元素上被释放时被调用类似于点击按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using UnityEngine;

public class ClickableObject : MonoBehaviour
{
void OnMouseDown()
{
Debug.Log("GameObject was clicked!");
// 在这里添加点击时需要执行的代码
}

void OnMouseUp()
{
Debug.Log("Mouse button released on GameObject.");
// 在这里添加释放鼠标按钮时需要执行的代码
}

void OnMouseDrag()
{
Debug.Log("Dragging object with mouse.");
// 在这里添加拖拽时需要执行的代码
}

void OnMouseEnter()
{
Debug.Log("Mouse entered the GameObject.");
// 在这里添加鼠标进入时需要执行的代码
}

void OnMouseExit()
{
Debug.Log("Mouse exited the GameObject.");
// 在这里添加鼠标离开时需要执行的代码
}
}

事件触发器

事件 描述
OnPointerClick 当用户点击一个 UI 元素时触发
OnPointerEnter 当鼠标进入一个 UI 元素时触发
OnPointerExit 当鼠标离开一个 UI 元素时触发
1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using UnityEngine.EventSystems;

public class UIButtonHandler : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("Button was clicked!");
// 在这里添加点击按钮时需要执行的代码
}
}

鼠标输入

输入 描述
Input.mousePosition 返回屏幕坐标系中的鼠标位置以像素为单位其中 (0, 0) 通常表示屏幕左下角
Input.GetMouseButtonDown(int button) 检查鼠标按钮是否按下0左键1右键2中键
Input.GetMouseButton(int button) 检查鼠标按钮是否被持续按下0左键1右键2中键
Input.GetMouseButtonUp(int button) 检查鼠标按钮是否释放0左键1右键2中键
Input.GetAxis("Mouse ScrollWheel") 返回鼠标滚轮的滚动值正数表示向上滚动负数表示向下滚动
Input.GetAxis("Mouse X") 返回水平方向上的鼠标移动量
Input.GetAxis("Mouse Y") 分别垂直方向上的鼠标移动量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//获取鼠标位置
Vector3 mousePosition = Input.mousePosition;
Debug.Log("Mouse Position: " + mousePosition);

//检测鼠标按钮
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Left mouse button down.");
}

if (Input.GetMouseButton(1))
{
Debug.Log("Right mouse button held.");
}

if (Input.GetMouseButtonUp(2))
{
Debug.Log("Middle mouse button up.");
}

//获取鼠标滚动轮
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll > 0f)
{
Debug.Log("Scroll Up.");
}
else if (scroll < 0f)
{
Debug.Log("Scroll Down.");
}

//鼠标移动
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");

if (mouseX != 0f || mouseY != 0f)
{
Debug.Log("Mouse moved: (" + mouseX + ", " + mouseY + ")");
}

//射线投射射线投射是一种常用的技术用于检测鼠标点击的对象你可以使用 Camera.ScreenPointToRay 方法将屏幕坐标转换为世界空间中的射线然后使用 Physics.Raycast 来检测射线是否击中任何碰撞体
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
Debug.Log("Hit object: " + hit.transform.name);
// 在这里添加点击后的处理逻辑
}
}
}

对象池

对象池Object Pooling是一种设计模式通常用于游戏开发中以提高性能和减少内存分配的压力在Unity中对象池可以有效地管理预制体实例的创建销毁和重用尤其在处理大量短暂存在的游戏对象时非常有用比如子弹粒子效果或敌人等

基本原理

对象池维护了一个对象列表这些对象预先被创建并且在需要使用的时候从池中取出不需要时再放回池中而不是直接销毁这样做的好处是避免了频繁的创建和销毁对象所带来的性能开销因为创建新对象涉及到内存分配而销毁对象则可能涉及垃圾回收这两者都会对性能产生影响

实现步骤

  • 预生成对象在游戏开始时或者加载关卡时根据预期的需求预先创建一定数量的对象并将它们存储在一个列表中
  • 获取对象当游戏运行时需要某个对象时从池中取出一个对象并激活它如果池中没有可用的对象则可以根据需要创建新的对象
  • 释放对象当对象不再需要时将其放回池中而不是销毁这通常伴随着重置对象的状态以便于下次使用

实现方法

  • 单例模式通常会使用单例模式来管理对象池确保游戏中只有一个对象池实例
  • 使用队列或列表使用一个队列或列表来存储所有的空闲对象
  • 预制体和实例化使用Unity的GameObject预制体系统通过Instantiate方法来生成对象并通过SetActive(false)来隐藏对象而不销毁它当需要时再通过SetActive(true)激活它
  • 池化策略可以设定最大池大小动态调整池的大小或者使用不同的策略来决定何时释放对象回到池中

注意事项

  • 内存管理虽然对象池可以减少频繁的创建和销毁带来的开销但如果管理不当也可能导致内存泄漏例如忘记从池中移除不再使用的对象
  • 对象状态在将对象返回到池中之前确保对象的状态被正确地重置否则可能会导致意外的行为
  • 性能考虑对于非常短生命周期的对象如果创建销毁的成本较低可能不需要使用对象池

简单示例

创建一个子弹对象池并在需要时从池中获取子弹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//创建一个新的C#脚本命名为ObjectPoolManager并在你的主摄像机或者其他合适的游戏对象上挂载这个脚本
using System.Collections.Generic;
using UnityEngine;

public class ObjectPoolManager : MonoBehaviour
{
// 单例模式确保场景中只有一个对象池管理器实例
public static ObjectPoolManager Instance { get; private set; }

// 定义一个序列化的内部类用于配置不同类型的对象池
[System.Serializable]
public class Pool
{
// 标签用于区分不同类型的对象
public string tag;
// 对象的预制体
public GameObject prefab;
// 池子的大小即预先创建的对象数量
public int size;
}

// 在Inspector中配置的对象池列表
public List<Pool> pools;
// 存储对象池的字典键是标签值是队列
public Dictionary<string, Queue<GameObject>> poolDictionary = new Dictionary<string, Queue<GameObject>>();

private void Awake()
{
// 初始化单例
Instance = this;

// 遍历所有配置的对象池
foreach (Pool poolData in pools)
{
// 创建一个队列来存储空闲对象
Queue<GameObject> objectPool = new Queue<GameObject>();

// 预先创建指定数量的对象
for (int i = 0; i < poolData.size; i++)
{
GameObject obj = Instantiate(poolData.prefab);
obj.SetActive(false); // 创建后先不激活
objectPool.Enqueue(obj); // 将对象放入队列
}

// 将对象池加入到字典中
poolDictionary.Add(poolData.tag, objectPool);
}
}

// 从对象池中获取一个对象并设置其位置和旋转
public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
{
// 检查是否有对应的对象池
if (!poolDictionary.ContainsKey(tag))
{
Debug.LogError("Pool with tag " + tag + " not found.");
return null;
}

// 从队列中取出一个对象
GameObject obj = poolDictionary[tag].Dequeue();

// 设置对象的位置和旋转
obj.transform.position = position;
obj.transform.rotation = rotation;

// 激活对象
obj.SetActive(true);

// 添加回调当对象使用完成后将其归还给池子
obj.GetComponent<Bullet>().OnObjectUseComplete(OnObjectUseComplete);

return obj;
}

// 当对象使用完成后将其归还给池子
private void OnObjectUseComplete(GameObject obj)
{
obj.SetActive(false); // 关闭对象
string tag = obj.tag;
if (poolDictionary.ContainsKey(tag))
{
// 将对象放回队列
poolDictionary[tag].Enqueue(obj);
}
}
}

//为子弹预制体添加一个组件脚本例如命名为Bullet并实现OnObjectUseComplete方法来通知对象池管理器
using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour
{
// 子弹存活的时间
public float lifeTime = 2f;

// 当对象使用完成时调用的方法
public void OnObjectUseComplete(System.Action<GameObject> callback)
{
// 启动协程在子弹存活时间后调用回调函数
StartCoroutine(DestroyAfterLifetime(callback));
}

// 使子弹在一段时间后被销毁
private IEnumerator DestroyAfterLifetime(System.Action<GameObject> callback)
{
yield return new WaitForSeconds(lifeTime);
// 调用回调函数将对象归还给池子
callback?.Invoke(this.gameObject);
}
}

// 示例射击时调用对象池中的方法
void Shoot()
{
// 计算子弹的生成位置
Vector3 spawnPosition = transform.position + transform.forward * 1f;
Quaternion spawnRotation = transform.rotation;

// 从对象池中获取子弹并设置位置和旋转
ObjectPoolManager.Instance.SpawnFromPool("Bullet", spawnPosition, spawnRotation);
}

数学应用

Mathf 类

Mathf 是 Unity 中的一个静态类用于提供各种数学函数和常量主要用于处理浮点数这个类包含了许多常用数学运算的方法如三角函数指数函数取整函数等这些方法对于游戏开发中的各种计算非常有用

属性 / 方法 描述
Clamp(float value, float min, float max) 限制 value 在 min 和 max 之间
Clamp01(float value) 限制 value 在 0 和 1 之间
Abs(float f) 返回 f 的绝对值
Sign(float f) 返回 f 的符号-1, 0 或 1
Floor(float f) 返回不大于 f 的最大整数
Ceil(float f) 返回不小于 f 的最小整数
Round(float f) 将 f 四舍五入到最近的整数
SmoothStep(float edge0, float edge1, float x) 平滑地插值从 0 到 1 的范围
Lerp(float a, float b, float t) 线性插值从 a 到 b
Sin(float f) 返回 `f` 弧度的正弦值
Cos(float f) 返回 `f` 弧度的余弦值
Tan(float f) 返回 `f` 弧度的正切值
Acos(float f) 返回 `f` 的反余弦值
Asin(float f) 返回 `f` 的反正弦值
Atan(float f) 返回 `f` 的反正切值
Atan2(float y, float x) 根据坐标 `(x, y)` 返回角度
Pow(float f, float power) 返回 `f` 的 `power` 次幂
Sqrt(float f) 返回 `f` 的平方根
Log(float f) 返回 `f` 的自然对数
Log10(float f) 返回 `f` 的以 10 为底的对数
Mathf.PI π 的近似值
Mathf.Deg2Rad 用于将角度转换为弧度
Mathf.Rad2Deg 用于将弧度转换为角度
Mathf.Epsilon 一个非常小的正数用于比较浮点数时避免除零错误

角度与弧度

角度和弧度是处理旋转和方向时常用的两种单位角度通常用于直观地描述旋转量一个完整的圆周被分为360度弧度是另一种测量角的单位它是国际单位制SI中的标准单位一个完整的圆周包含2π约等于6.283185弧度

角度与弧度之间的转换

角度转弧度: 弧度 = π / 180 × 角度
弧度转角度: 角度 = 180 / π × 弧度

游戏应用

💗💗 使用角度和弧度来计算物体的旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//假设我们需要让一个物体沿着一个圆形路径移动我们可以使用弧度来计算物体的位置
using System.Collections;
using UnityEngine;

public class CirclePath : MonoBehaviour
{
public float radius = 1.0f; // 圆形路径的半径
public float speed = 1.0f; // 物体移动的速度

private float angleInRadians = 0.0f; // 初始角度以弧度表示

void Update()
{
// 更新物体的角度
angleInRadians += Time.deltaTime * speed;

// 将角度从弧度转换为位置坐标
Vector3 newPosition = new Vector3(radius * Mathf.Cos(angleInRadians), 0, radius * Mathf.Sin(angleInRadians));

// 移动物体
transform.position = newPosition;
}
}

💗💗 使用角度来设置物体的方向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//如果我们想要根据玩家的输入来控制一个角色的朝向我们可以使用角度
using UnityEngine;

public class PlayerRotation : MonoBehaviour
{
public float turnSpeed = 100.0f; // 转动速度

void Update()
{
float horizontalInput = Input.GetAxis("Horizontal"); // 获取水平轴输入
float verticalInput = Input.GetAxis("Vertical"); // 获取垂直轴输入

// 计算新的旋转角度
float angleInDegrees = Mathf.Atan2(verticalInput, horizontalInput) * Mathf.Rad2Deg;

// 应用旋转
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, angleInDegrees, 0), turnSpeed * Time.deltaTime);
}
}

💗💗 使用弧度来计算物体的旋转速度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//如果我们要根据物体的旋转角度来计算旋转速度我们可以使用弧度来进行计算
using UnityEngine;

public class RotateTowardsTarget : MonoBehaviour
{
public Transform target; // 目标物体
public float maxAngularSpeed = 100.0f; // 最大旋转速度

void Update()
{
// 计算物体与目标之间的角度差
float angleDifferenceInDegrees = Vector3.SignedAngle(transform.forward, target.position - transform.position, Vector3.up);

// 转换为弧度
float angleDifferenceInRadians = angleDifferenceInDegrees * Mathf.Deg2Rad;

// 计算所需的旋转速度
float angularSpeed = angleDifferenceInRadians / Time.deltaTime;

// 限制最大旋转速度
if (Mathf.Abs(angularSpeed) > maxAngularSpeed)
{
angularSpeed = Mathf.Sign(angularSpeed) * maxAngularSpeed;
}

// 应用旋转
transform.Rotate(Vector3.up, angularSpeed * Time.deltaTime * Mathf.Rad2Deg);
}
}

三角函数

三角函数是数学中一类重要的函数它们在几何物理工程学等领域都有广泛的应用最基本的三个三角函数是正弦sine余弦cosine和正切tangent

圆周定义

  • 正弦值 sin(θ)是该角对应的终边与单位圆交点的 y 坐标
  • 余弦值 cos(θ)是该角对应的终边与单位圆交点的 x 坐标
  • 正切值 tan(θ)定义为 sin(θ) / cos(θ)当 cos(θ) ≠ 0 时有效

直角三角形定义

对于直角三角形中的一个锐角 θ

  • 正弦 sin(θ)定义为对边与斜边的比值
  • 余弦 cos(θ)定义为邻边与斜边的比值
  • 正切 tan(θ)定义为对边与邻边的比值

游戏应用

在游戏开发中三角函数是非常重要的数学工具它们被广泛应用于许多方面以下是一些使用三角函数在 Unity 游戏开发中的典型应用场景

💗💗 物体移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//三角函数可以用来控制物体沿特定路径移动例如让物体沿着一个圆形路径移动
using UnityEngine;
using System;

public class CircularMotion : MonoBehaviour
{
public float radius = 5.0f;
public float speed = 1.0f;

private float angle = 0.0f;

void Update()
{
angle += Time.deltaTime * speed;

float x = Mathf.Cos(angle) * radius;
float y = Mathf.Sin(angle) * radius;

transform.position = new Vector3(x, 0, y);
}
}

💗💗 物体旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//使用三角函数可以帮助你实现平滑的旋转效果比如让一个对象面向另一个对象
using UnityEngine;

public class LookAtTarget : MonoBehaviour
{
public Transform target;

void Update()
{
Vector3 direction = (target.position - transform.position).normalized;
Quaternion lookRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Lerp(transform.rotation, lookRotation, Time.deltaTime * 5.0f);
}
}

💗💗 投影和光照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//在处理光照和投影时经常需要用到三角函数来计算光线与表面之间的角度
using UnityEngine;

public class LightProjection : MonoBehaviour
{
public Light lightSource;
public Material surfaceMaterial;

void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Vector3 lightDirection = lightSource.transform.forward;
float dotProduct = Vector3.Dot(lightDirection, transform.up);
surfaceMaterial.SetFloat("_LightAngle", dotProduct);

Graphics.Blit(source, destination, surfaceMaterial);
}
}

💗💗 摄像机摇摆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//使用三角函数可以使摄像机产生摇摆效果增加游戏的真实感
using UnityEngine;

public class CameraShake : MonoBehaviour
{
public float amplitude = 0.1f;
public float frequency = 2.0f;

void Update()
{
float time = Time.time;
float offsetX = Mathf.Sin(time * frequency) * amplitude;
float offsetZ = Mathf.Cos(time * frequency) * amplitude;

transform.position = new Vector3(offsetX, 0, offsetZ);
}
}

💗💗 物理模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//三角函数在物理引擎中用于计算力的方向和大小如重力摩擦力等
using UnityEngine;

public class PhysicsSimulation : MonoBehaviour
{
public Rigidbody rb;
public float gravityStrength = 9.81f;

void FixedUpdate()
{
Vector3 gravityDirection = new Vector3(0, -1, 0);
Vector3 gravityForce = gravityDirection * gravityStrength;
rb.AddForce(gravityForce, ForceMode.Acceleration);
}
}

💗💗 AI 行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//在 AI 控制的角色中三角函数可以用来计算角色朝向目标的角度从而决定其移动方向
using UnityEngine;

public class AIBehavior : MonoBehaviour
{
public Transform target;
public float maxSpeed = 5.0f;

void Update()
{
Vector3 direction = (target.position - transform.position).normalized;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, 0, angle);

Vector3 velocity = direction * maxSpeed;
transform.Translate(velocity * Time.deltaTime);
}
}

线性插值

线性插值Linear Interpolation, 简称 Lerp是一种在两个已知值之间找到一个未知值的数学方法在游戏开发和计算机图形学中线性插值常用于平滑地改变物体的位置旋转或缩放等属性在Unity中线性插值被广泛应用于动画移动物体以及平滑过渡效果Unity提供了多种Lerp函数来处理不同的数据类型如位置颜色向量等

基本概念

  • 起点 (startValue)起始值
  • 终点 (endValue)目标值
  • t时间参数通常是一个介于0到1之间的值表示从起点到终点的进度百分比当t=0时结果等于startValue当t=1时结果等于endValue

Lerp函数

Unity中提供了一系列Lerp函数来处理不同类型的数据
函数 描述
Vector3.Lerp 用于向量的线性插值
Color.Lerp 用于颜色的线性插值
Quaternion.Lerp 用于四元数旋转的线性插值
Mathf.Lerp 用于浮点数值的线性插值

游戏应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//使用Vector3.Lerp来平滑地移动一个游戏对象
using UnityEngine;

public class MoveObject : MonoBehaviour
{
public Vector3 startPosition;
public Vector3 endPosition;
public float duration = 2.0f;
private float startTime;

void Start()
{
startTime = Time.time;
}

void Update()
{
float t = (Time.time - startTime) / duration;
t = Mathf.Clamp01(t); // 确保t的值在0到1之间
transform.position = Vector3.Lerp(startPosition, endPosition, t);

if (t >= 1)
{
startTime = Time.time; // 重新设置开始时间
}
}
}

//使用Color.Lerp来平滑地改变物体的颜色
using UnityEngine;
using UnityEngine.UI;

public class ChangeColor : MonoBehaviour
{
public Color startColor;
public Color endColor;
public float duration = 2.0f;
private float startTime;

void Start()
{
startTime = Time.time;
GetComponent<Renderer>().material.color = startColor;
}

void Update()
{
float t = (Time.time - startTime) / duration;
t = Mathf.Clamp01(t); // 确保t的值在0到1之间
GetComponent<Renderer>().material.color = Color.Lerp(startColor, endColor, t);

if (t >= 1)
{
startTime = Time.time; // 重新设置开始时间
}
}
}

//使用Quaternion.Lerp来平滑地改变物体的旋转
using UnityEngine;

public class RotateObject : MonoBehaviour
{
public Quaternion startRotation;
public Quaternion endRotation;
public float duration = 2.0f;
private float startTime;

void Start()
{
startTime = Time.time;
transform.rotation = startRotation;
}

void Update()
{
float t = (Time.time - startTime) / duration;
t = Mathf.Clamp01(t); // 确保t的值在0到1之间
transform.rotation = Quaternion.Lerp(startRotation, endRotation, t);

if (t >= 1)
{
startTime = Time.time; // 重新设置开始时间
}
}
}

//使用Mathf.Lerp来平滑地改变一个数值例如生命值的显示
using UnityEngine;
using UnityEngine.UI;

public class ChangeHealthBar : MonoBehaviour
{
public float startHealth;
public float endHealth;
public float duration = 2.0f;
private float startTime;
private Slider healthSlider;

void Start()
{
startTime = Time.time;
healthSlider = GetComponent<Slider>();
healthSlider.value = startHealth;
}

void Update()
{
float t = (Time.time - startTime) / duration;
t = Mathf.Clamp01(t); // 确保t的值在0到1之间
float currentHealth = Mathf.Lerp(startHealth, endHealth, t);
healthSlider.value = currentHealth;

if (t >= 1)
{
startTime = Time.time; // 重新设置开始时间
}
}
}

向量

在数学中向量是一个非常重要的概念它不仅在几何学中有广泛的应用在物理学工程学以及计算机科学等领域也扮演着核心角色

基本定义

  1. 向量可以被看作是有方向和大小长度或模的量
  2. 在平面或空间中一个向量可以用一条带有箭头的线段来表示箭头指示向量的方向而线段的长度代表向量的大小

表示方法

  1. 在二维坐标系中向量可以通过一对坐标 (x, y) 来表示其中 x 和 y 分别是该向量沿 x 轴和 y 轴的分量
  2. 在三维坐标系中向量则通过三元组 (x, y, z) 来表示

向量的运算

💗💗 向量加法

如果有两个向量 \( \overrightarrow{A} = (A1, A2) \) 和 \( \overrightarrow{B} = (B1, B2) \) 那么它们的和 \( \overrightarrow{A} + \overrightarrow{B} \) 定义为 \( (A1 + B1, A2 + B2) \)

💗💗 向量减法

向量 \( \overrightarrow{A} - \overrightarrow{B} \) 可以理解为 \( \overrightarrow{A} + (-\overrightarrow{B}) \) 即\( \overrightarrow{B} \) 的相反向量与 \( \overrightarrow{A} \) 相加

💗💗 数乘

当一个向量与一个标量 \( K \) 乘时结果是一个新的向量其方向与原向量相同 \( (当 K > 0) \)相反 \( (当 K < 0) \)且长度是原向量长度的 \( |k| \)倍

💗💗 点乘内积

对于两个向量 \( \overrightarrow{A} \) 和 \( \overrightarrow{B} \)它们的内积定义为 \( \overrightarrow{A} · \overrightarrow{B} = |\overrightarrow{A}| × |\overrightarrow{B}| × cosθ \) 其中 \( θ \) 是两向量之间的夹角

内积也可以通过分量来计算如果 \( \overrightarrow{A} = (A1, A2) \) 和 \( \overrightarrow{B} = (B1, B2) \)那么 \( \overrightarrow{A} · \overrightarrow{B} = A1 × B1 + A2 × B2 \)

💗💗 叉乘外积

在三维空间中两个向量 \( \overrightarrow{A} \) 和 \( \overrightarrow{B} \) 的外积是一个新向量 \( \overrightarrow{C} \)其方向垂直于 \( \overrightarrow{A} \) 和 \( \overrightarrow{B} \) 所确定的平面并且满足右手定则

外积的大小等于这两个向量构成的平行四边形的面积

💗💗 向量的模

向量的模或长度定义为 \( \sqrt{x^2 + y^2} \) (在二维空间) 或 \( \sqrt{x^2 + y^2 + z^2} \) (在三维空间)

二维向量

在 Unity 中 Vector2 是一个二维向量通常用于2D游戏或3D游戏中的一些特定用途如纹理坐标

💗💗 常用属性

  • zero: 返回一个所有元素都为0的新向量
  • one: 返回一个所有元素都为1的新向量
  • up: 返回一个y值为1的新向量 (0, 1)
  • down: 返回一个y值为-1的新向量 (0, -1)
  • left: 返回一个x值为-1的新向量 (-1, 0)
  • right: 返回一个x值为1的新向量 (1, 0)
  • normalized: 返回一个单位长度的新向量
  • magnitude: 返回向量的长度
  • sqrMagnitude: 返回向量长度的平方

💗💗 常用方法

  • Normalize(): 将当前向量归一化
  • Scale(Vector2 scale): 按照scale参数缩放当前向量
  • Dot(Vector2 other): 计算与另一个向量的点积
  • Lerp(Vector2 a, Vector2 b, float t): 线性插值
  • MoveTowards(Vector2 current, Vector2 target, float maxDistanceDelta): 移动到目标位置但不会超过最大移动距离

💗💗 应用场景

  • 2D 游戏角色移动
    • 用于表示2D游戏角色的位置速度和加速度
    • 例如通过键盘输入控制角色在二维平面上移动
  • UI 元素定位
    • 用于确定 UI 元素如按钮文本框等在屏幕上的位置
    • 也可以用于表示 UI 元素的尺寸
  • 粒子系统
    • 用于定义粒子的初始速度方向和加速度
    • 例如在雨滴效果中每个雨滴的初始速度可以是一个 Vector2
  • 碰撞检测
    • 用于表示矩形碰撞体的大小和位置
    • 在2D游戏中碰撞检测经常使用 Vector2 来实现
  • 纹理坐标
    • 用于表示贴图在 UV 空间中的位置
    • 这对于2D精灵来说非常重要

💗💗 游戏应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//假设你正在制作一个2D平台游戏你需要控制一个角色在水平面上移动你可以使用 Vector2 来表示角色的位置和速度
using UnityEngine;

public class PlayerController2D : MonoBehaviour
{
public float speed = 5.0f;
private Vector2 velocity;

void Update()
{
// 从输入获取水平方向的速度
float horizontalInput = Input.GetAxis("Horizontal");

// 设置速度向量
velocity = new Vector2(horizontalInput * speed, 0);

// 更新角色的位置
transform.Translate(velocity * Time.deltaTime);
}
}

三维向量

在 Unity 中 Vector3 是一个三维向量最常用于3D游戏中

💗💗 常用属性

  • zero: 返回一个所有元素都为0的新向量
  • one: 返回一个所有元素都为1的新向量
  • up: 返回一个y值为1的新向量 (0, 1, 0)
  • down: 返回一个y值为-1的新向量 (0, -1, 0)
  • left: 返回一个x值为-1的新向量 (-1, 0, 0)
  • right: 返回一个x值为1的新向量 (1, 0, 0)
  • forward: 返回一个z值为1的新向量 (0, 0, 1)
  • back: 返回一个z值为-1的新向量 (0, 0, -1)
  • normalized: 返回一个单位长度的新向量
  • magnitude: 返回向量的长度
  • sqrMagnitude: 返回向量长度的平方

💗💗 常用方法

  • Normalize(): 将当前向量归一化
  • Scale(Vector3 scale): 按照scale参数缩放当前向量
  • Dot(Vector3 other): 计算与另一个向量的点积
  • Cross(Vector3 other): 计算与另一个向量的叉积
  • Lerp(Vector3 a, Vector3 b, float t): 线性插值
  • MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta): 移动到目标位置但不会超过最大移动距离

💗💗 应用场景

  • 3D 游戏对象的位置
    • 用于表示3D游戏世界中物体的位置
    • 例如一个玩家角色在游戏世界中的坐标
  • 物体的旋转
    • 用于表示3D物体绕某轴旋转的角度
    • 例如飞机绕X轴Y轴和Z轴的旋转角度
  • 光照方向
    • 用于表示光源的方向
    • 例如太阳光的方向可以用一个 Vector3 表示
  • 力的作用
    • 用于表示施加在物体上的力的方向和大小
    • 例如风力或爆炸力
  • 摄像机位置和方向
    • 用于表示摄像机的位置和朝向
    • 例如设置摄像机跟随玩家角色

💗💗 游戏应用

1
2
3
4
5
6
7
8
9
10
11
12
13
//假设你在做一个3D游戏需要让一个物体围绕它的中心旋转你可以使用 Vector3 来指定旋转轴和旋转速度
using UnityEngine;

public class RotateObject : MonoBehaviour
{
public Vector3 rotationSpeed = new Vector3(10, 20, 30); // 每秒旋转的角度

void Update()
{
// 使用Vector3来表示旋转轴
transform.Rotate(rotationSpeed * Time.deltaTime);
}
}

四维向量

在 Unity 中 Vector4 是一个四维向量通常用于表示颜色或者在某些图形计算中

💗💗 常用属性

  • zero: 返回一个所有元素都为0的新向量
  • one: 返回一个所有元素都为1的新向量
  • normalized: 返回一个单位长度的新向量
  • magnitude: 返回向量的长度
  • sqrMagnitude: 返回向量长度的平方

💗💗 常用方法

  • Normalize(): 将当前向量归一化
  • Scale(Vector4 scale): 按照scale参数缩放当前向量
  • Dot(Vector4 other): 计算与另一个向量的点积

💗💗 应用场景

  • 颜色表示
    • 用于表示带有透明度的颜色
    • 例如一个颜色值可以表示为 RGBA红绿蓝透明度格式
  • 变换矩阵
    • 用于表示4x4变换矩阵的一列或一行
    • 在图形编程中Vector4 经常用于表示矩阵向量乘法的结果
  • 光线追踪
    • 用于表示光线的方向和起点
    • 在光线追踪算法中Vector4 可以表示光线的方向和起点
  • 光照颜色
    • 用于表示光源的颜色和强度
    • 例如一个光源的颜色和强度可以通过 Vector4 表示
  • 投影平面
    • 用于表示投影平面的法线和平面方程
    • 在某些图形算法中Vector4 可以表示一个平面的方程

💗💗 游戏应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//虽然 Vector4 不常用作物理或空间向量但它可以用来表示颜色其中每个分量代表红绿蓝透明度RGBA例如在 Unity 中你可以使用 Vector4 来混合两种颜色
using UnityEngine;

public class ColorBlender : MonoBehaviour
{
public Color colorA = new Color(1, 0, 0, 1); // 红色
public Color colorB = new Color(0, 0, 1, 1); // 蓝色
public float blendFactor = 0.5f; // 混合因子

void OnGUI()
{
// 使用Vector4来混合颜色
Color blendedColor = Color.Lerp(colorA, colorB, blendFactor);

// 显示混合后的颜色
GUI.color = blendedColor;
GUI.Label(new Rect(10, 10, 100, 30), "Blended Color");
}
}

欧拉角

欧拉角是一种用于描述三维空间中刚体或坐标系相对于某个参考坐标系的旋转或姿态的方法具体来说欧拉角是指通过沿着三个互相垂直的轴进行连续旋转来达到特定姿态所需的三个角度这三个轴通常是固定的相对于参考坐标系或者是随刚体一起旋转的

在Unity中欧拉角Euler angles是一种表示物体三维空间旋转的方法它们由三个角度组成分别代表沿x轴y轴和z轴的旋转在不同的上下文中这些轴可以有不同的名称比如横滚roll俯仰pitch和偏航yaw

  • 横滚Roll绕着物体自身的X轴旋转通常与飞机的翻滚动作相对应
  • 俯仰Pitch绕着物体自身的Y轴旋转通常与飞机抬头或低头的动作相对应
  • 偏航Yaw绕着物体自身的Z轴旋转通常与飞机向左或向右转弯的动作相对应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using UnityEngine;

public class EulerRotationExample : MonoBehaviour
{
public float rollSpeed = 10.0f;
public float pitchSpeed = 10.0f;
public float yawSpeed = 10.0f;

private void Update()
{
// 横滚Roll - 绕 X 轴旋转
transform.Rotate(Vector3.right * rollSpeed * Time.deltaTime);

// 俯仰Pitch - 绕 Y 轴旋转
transform.Rotate(Vector3.up * pitchSpeed * Time.deltaTime);

// 偏航Yaw - 绕 Z 轴旋转
transform.Rotate(Vector3.forward * yawSpeed * Time.deltaTime);
}
}

物体旋转

  • 设置物体的旋转角度你可以直接使用 transform.eulerAngles 属性来设置物体的旋转角度
  • 获取物体的旋转角度同样地你可以读取 transform.eulerAngles 来得到物体当前的旋转角度
1
2
3
4
5
6
7
8
9
10
11
12
using UnityEngine;

public class RotateObject : MonoBehaviour
{
public Vector3 rotationAngles = new Vector3(0, 45, 0); // 绕X轴0度绕Y轴45度绕Z轴0度

void Start()
{
// 设置物体的旋转角度
transform.eulerAngles = rotationAngles;
}
}

动画和骨骼

  • 在动画中欧拉角常用于表示关节的旋转角度
  • 例如在角色动画中手臂或腿部的关节旋转通常会用欧拉角来表示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using UnityEngine;
using UnityEngine.AI;

public class CharacterController : MonoBehaviour
{
public Animator animator; // 指向角色的Animator组件
public NavMeshAgent agent; // 如果使用NavMesh导航

private void Update()
{
// 获取输入并更新Animator参数
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");

// 更新动画状态
animator.SetFloat("Speed", horizontalInput * horizontalInput + verticalInput * verticalInput);

// 如果使用NavMesh则更新速度
if (agent != null)
{
agent.speed = verticalInput * 5f; // 假设最大速度为5
}

// 如果角色正在移动我们可以更新它的朝向
if (verticalInput != 0 || horizontalInput != 0)
{
Vector3 moveDirection = new Vector3(horizontalInput, 0, verticalInput).normalized;
transform.rotation = Quaternion.LookRotation(moveDirection, Vector3.up);
}
}
}

摄像机控制

  • 第一人称视角你可以使用欧拉角来控制第一人称摄像机的俯仰和偏航
  • 第三人称视角同样适用于第三人称摄像机以保持摄像机相对于玩家角色的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;

public class FirstPersonCamera : MonoBehaviour
{
public float mouseSensitivity = 100.0f;
private float xRotation = 0.0f;

void Update()
{
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;

xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);

transform.localRotation = Quaternion.Euler(xRotation, 0, 0);
transform.Rotate(Vector3.up * mouseX);
}
}

物理模拟

  • 在物理引擎中欧拉角可用于初始化刚体的旋转角度
  • 例如你可以设置一个刚体的初始旋转角度以便它以特定的角度开始运动
1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;

public class RigidbodyInitialRotation : MonoBehaviour
{
public Vector3 initialRotation = new Vector3(0, 90, 0); // 初始旋转角度

void Start()
{
GetComponent<Rigidbody>().transform.eulerAngles = initialRotation;
}
}

用户界面

  • 当使用 Unity 的 UI 系统时欧拉角也可以用于调整 UI 元素的位置和方向
  • 例如你可能需要根据用户的输入动态旋转 UI 元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using UnityEngine;
using UnityEngine.UI;

public class RotatableUIButton : MonoBehaviour
{
public float rotationSpeed = 10.0f; // 旋转速度
public bool rotateOnHover = true; // 是否在鼠标悬停时旋转
public bool rotateOnClick = true; // 是否在点击时旋转

private Image buttonImage; // Button的Image组件

private void Start()
{
buttonImage = GetComponent<Image>();
}

private void Update()
{
// 鼠标悬停时旋转
if (rotateOnHover && IsPointerOverGameObject())
{
transform.Rotate(new Vector3(0, 0, rotationSpeed) * Time.deltaTime);
}

// 鼠标点击时旋转
if (rotateOnClick && Input.GetMouseButtonDown(0))
{
if (IsPointerOverGameObject())
{
transform.Rotate(new Vector3(0, 0, 180)); // 旋转180度
}
}
}

private bool IsPointerOverGameObject()
{
PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
foreach (RaycastResult result in results)
{
if (result.gameObject == gameObject)
{
return true;
}
}
return false;
}
}

游戏逻辑

  • 在游戏逻辑中欧拉角可以用来表示物体的方向比如炮塔指向敌人的方向
  • 例如你可以计算出两个物体之间的相对角度并使用欧拉角来调整炮塔的方向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;

public class TurretRotation : MonoBehaviour
{
public Transform target;
public float rotationSpeed = 10.0f;

void Update()
{
if (target)
{
Vector3 direction = target.position - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Lerp(transform.rotation, lookRotation, rotationSpeed * Time.deltaTime);
}
}
}

四元数

四元数Quaternion是一种用于表示物体旋转的数学结构相比于使用欧拉角Euler angles四元数提供了更高效更稳定的方式来处理旋转问题尤其是在涉及多个轴的旋转和插值时

四元数由四个部分组成一个实部和三个虚部一般形式为 q = w + xi + yj + zk其中 w 是实部而 x, y, 和 z 分别对应于三个虚部

在Unity中四元数是通过 Quaternion 类来表示的Quaternion 类提供了一系列方法和属性来帮助你操作四元数 通常用来表示物体的旋转其形式简化为 (x, y, z, w)其中 (x, y, z) 表示旋转轴的方向w 表示旋转角度的余弦值的一半

常用属性

属性 描述
identity 表示没有旋转的四元数
eulerAngles 获取或设置四元数对应的欧拉角

常用方法

方法 描述
Quaternion.LookRotation(Vector3 forward, Vector3 upwards) 返回一个指向指定方向的四元数
Quaternion.FromToRotation(Vector3 fromDir, Vector3 toDir) 从一个方向向另一个方向旋转
Quaternion.Euler(float x, float y, float z) 根据欧拉角创建四元数
Quaternion.Slerp(Quaternion a, Quaternion b, float t) 使用球面线性插值来平滑地从一个四元数插值到另一个四元数
Quaternion.Lerp(Quaternion a, Quaternion b, float t) 线性插值两个四元数
Quaternion.Inverse(Quaternion rotation) 计算四元数的逆
Quaternion.Normalize(Quaternion q) 归一化四元数

应用场景

  • 旋转物体
    • 使用 transform.rotation = new Quaternion(x, y, z, w); 可以直接设置物体的旋转
    • 使用 transform.Rotate(Vector3 axis, float angle, Space relativeTo) 可以绕着某个轴旋转一定的角度
  • 插值
    • 在动画和游戏逻辑中经常需要使用 Quaternion.Slerp 或 Quaternion.Lerp 来实现平滑的旋转效果
  • 组合旋转
    • 可以使用 * 运算符来组合两个旋转例如 Quaternion result = q1 * q2;

游戏应用

💗💗 使用四元数实现物体绕轴旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using UnityEngine;

public class RotateObject : MonoBehaviour
{
public float rotationSpeed = 45.0f;

private void Update()
{
// 创建一个四元数表示绕Y轴旋转的角度
Quaternion rotation = Quaternion.Euler(0, rotationSpeed * Time.deltaTime, 0);

// 将这个旋转应用到物体上
transform.rotation = transform.rotation * rotation;
}
}

💗💗 使用四元数实现物体旋转的平滑过渡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using UnityEngine;

public class SmoothRotation : MonoBehaviour
{
public Transform target;
public float smoothTime = 0.3f;
private Quaternion targetRotation;
private float timeSinceLastUpdate = 0.0f;

private void Start()
{
targetRotation = target.rotation;
}

private void Update()
{
if (target != null)
{
targetRotation = target.rotation;

// 使用Lerp实现线性插值
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, smoothTime * Time.deltaTime);

// 或者使用Slerp实现球面线性插值
// transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smoothTime * Time.deltaTime);
}
}
}

💗💗 使用四元数实现物体看向目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using UnityEngine;

public class LookAtTarget : MonoBehaviour
{
public Transform target;

private void Update()
{
if (target != null)
{
// 让物体面向目标位置
Quaternion lookRotation = Quaternion.LookRotation(target.position - transform.position);
transform.rotation = lookRotation;
}
}
}

💗💗 使用四元数实现随机旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using UnityEngine;

public class RandomRotation : MonoBehaviour
{
public float maxAngle = 10.0f;

private void Update()
{
// 生成一个随机的旋转轴
Vector3 randomAxis = Random.insideUnitSphere;

// 生成一个随机的旋转角度
float randomAngle = Random.Range(-maxAngle, maxAngle);

// 创建一个四元数来表示这个随机旋转
Quaternion randomRotation = Quaternion.AngleAxis(randomAngle, randomAxis);

// 应用这个随机旋转
transform.rotation = transform.rotation * randomRotation;
}
}

加速度

加速度是速度的变化率通常表示为单位时间内速度的改变量

使用Rigidbody组件

如果你正在使用Unity的物理引擎可以给游戏对象添加一个Rigidbody组件并通过施加力Force来改变其运动状态

1
2
3
4
5
6
7
8
9
// 在脚本中给刚体施加力
void FixedUpdate()
{
// 获取刚体组件
Rigidbody rb = GetComponent<Rigidbody>();
// 施加力
Vector3 force = new Vector3(10f, 0f, 0f); // 水平方向上的力
rb.AddForce(force, ForceMode.Force);
}

不使用Rigidbody组件

如果你不使用物理引擎而是手动控制物体的位置和速度可以在Update或FixedUpdate方法中手动计算加速度并更新速度和位置

1
2
3
4
5
6
7
8
9
10
// 手动管理加速度和速度
float speed = 0f; // 当前速度
float acceleration = 10f; // 加速度
float deltaTime = Time.deltaTime; // 自上次调用以来经过的时间

void Update()
{
speed += acceleration * deltaTime; // 更新速度
transform.Translate(Vector3.forward * speed * deltaTime); // 更新位置
}

万有引力

在Unity中模拟万有引力可以用于创建更加真实的游戏场景特别是在涉及天体物理学行星运动或者任何需要模拟重力影响的场景时

  • 定义引力常数在Unity中我们可能不需要使用真实的万有引力常数而是可以根据游戏的需求调整这个值
  • 定义物体的质量为每个游戏对象分配一个质量属性
  • 计算引力编写一个函数来计算两个物体之间的引力大小
  • 施加引力使用计算出的引力来施加力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using UnityEngine;

public class GravitySimulation : MonoBehaviour
{
public float gravityConstant = 1f; // 引力常数可以调整以适应游戏需求
public float mass; // 物体的质量

void FixedUpdate()
{
// 获取所有带有GravitySimulation脚本的游戏对象
GravitySimulation[] simulations = FindObjectsOfType<GravitySimulation>();

foreach (GravitySimulation other in simulations)
{
if (other != this)
{
ApplyGravitationalForce(other);
}
}
}

void ApplyGravitationalForce(GravitySimulation other)
{
Vector3 direction = other.transform.position - transform.position;
float distance = direction.magnitude; // 两物体间的距离
float forceMagnitude = gravityConstant * mass * other.mass / (distance * distance); // 引力大小
Vector3 force = direction.normalized * forceMagnitude; // 单位方向向量乘以力的大小

// 应用力
Rigidbody myRigidbody = GetComponent<Rigidbody>();
Rigidbody otherRigidbody = other.GetComponent<Rigidbody>();
myRigidbody.AddForce(-force, ForceMode.Force); // 向量方向相反
otherRigidbody.AddForce(force, ForceMode.Force);
}
}

动量定理

在Unity中动量定理是物理学中的一个重要概念它描述了力对物体动量变化的影响动量定理可以表述为作用在一个物体上的外力对该物体动量的变化率等于力的大小换句话说动量的变化等于力与时间的乘积即冲量在Unity中可以通过不同的方式来应用动量定理具体取决于你是在使用Unity的物理引擎还是自己手动控制物体的运动

使用Rigidbody组件

当你使用Unity的物理引擎时可以通过施加力来改变物体的动量Unity的Rigidbody组件允许你施加力并且它会根据动量定理自动更新物体的状态

1
2
3
4
5
6
7
8
9
10
// 在脚本中给刚体施加力
void FixedUpdate()
{
// 获取刚体组件
Rigidbody rb = GetComponent<Rigidbody>();

// 施加力
Vector3 force = new Vector3(10f, 0f, 0f); // 水平方向上的力
rb.AddForce(force, ForceMode.Force);
}

手动控制物体的动量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 手动管理动量
float mass = 1f; // 物体的质量
Vector3 momentum = Vector3.zero; // 初始动量

void Update()
{
// 计算力
Vector3 force = new Vector3(10f, 0f, 0f); // 水平方向上的力
float deltaTime = Time.deltaTime; // 自上次调用以来经过的时间

// 根据力和时间更新动量
momentum += force * deltaTime;

// 计算速度
Vector3 velocity = momentum / mass;

// 更新位置
transform.Translate(velocity * deltaTime);
}

高级组件

本地持久化

在Unity开发中有时候我们需要让游戏数据在关闭应用后仍然保留这就需要用到本地持久化存储技术

PlayerPrefs 类

PlayerPrefs 是Unity提供的一个简单的键值对存储系统非常适合存储一些轻量级的数据如玩家的分数设置选项等它会自动保存数据到用户的设备上并且会在下次启动游戏时自动加载这些数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//保存
PlayerPrefs.SetInt("PlayerScore", 100);
PlayerPrefs.Save(); // 确保数据被保存

//查询// 如果没有找到键则返回默认值0
int score = PlayerPrefs.GetInt("PlayerScore", 0);

//修改
PlayerPrefs.SetInt("PlayerScore", 150);
PlayerPrefs.Save();

//删除
PlayerPrefs.DeleteKey("PlayerScore");
PlayerPrefs.Save();

文件系统

Unity允许开发者直接访问设备的文件系统可以用来读写文件通过 System.IO 命名空间中的类如 FileStream, StreamReader, StreamWriter 等你可以创建读取更新和删除文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//保存
string filePath = Application.persistentDataPath + "/player_score.txt";
// 创建或打开文件以写入模式
using (StreamWriter writer = new StreamWriter(filePath, true))
{
writer.WriteLine("PlayerScore:100");
}

//查询
// 读取文件内容
if (File.Exists(filePath))
{
using (StreamReader reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.StartsWith("PlayerScore:"))
{
int score = int.Parse(line.Substring(11));
// score现在是文件中的玩家分数
}
}
}
}

//修改
// 清空文件并重新写入新的分数
File.WriteAllText(filePath, "PlayerScore:150");

//删除
if (File.Exists(filePath))
{
File.Delete(filePath);
}

JSON/XML

如果你需要存储结构化的数据可以考虑使用JSON或XML格式这两种格式易于解析并且可以通过序列化库如 Unity 自带的 System.Text.Json 或第三方库如 Json.NET轻松地将C#对象转换为JSON/XML字符串然后将其保存到文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//假设我们有一个简单的C#类来表示玩家信息
[System.Serializable]
public class PlayerData
{
public int Score;
}

void SavePlayerData(PlayerData data)
{
string json = JsonUtility.ToJson(data);
File.WriteAllText(Application.persistentDataPath + "/playerdata.json", json);
}

PlayerData LoadPlayerData()
{
string path = Application.persistentDataPath + "/playerdata.json";
if (File.Exists(path))
{
string json = File.ReadAllText(path);
return JsonUtility.FromJson<PlayerData>(json);
}
return null;
}

//保存
PlayerData playerData = new PlayerData { Score = 100 };
SavePlayerData(playerData);

//查询
PlayerData playerData = LoadPlayerData();
if (playerData != null)
{
int score = playerData.Score;
}

//修改
PlayerData playerData = LoadPlayerData();
if (playerData != null)
{
playerData.Score = 150;
SavePlayerData(playerData);
}

//删除
string path = Application.persistentDataPath + "/playerdata.json";
if (File.Exists(path))
{
File.Delete(path);
}

SQLite

对于更复杂的应用程序可能需要使用数据库来存储数据SQLite是一个轻量级的数据库非常适合用于移动设备和桌面应用程序Unity支持SQLite并且有许多插件可以帮助你在Unity中集成SQLite数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//需要安装一个SQLite库例如 SQLiteForUnity3D

//保存 // 假设你已经有了一个SQLite数据库连接
using (IDbConnection conn = new SQLiteConnection("Data Source=" + databasePath))
{
conn.Open();
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = "INSERT INTO Players (Score) VALUES (@score)";
cmd.Parameters.Add(new SQLiteParameter("@score", 100));
cmd.ExecuteNonQuery();
}

//查询
using (IDbConnection conn = new SQLiteConnection("Data Source=" + databasePath))
{
conn.Open();
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT Score FROM Players WHERE ID = @id";
cmd.Parameters.Add(new SQLiteParameter("@id", 1)); // 假设这是唯一的ID
IDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
int score = reader.GetInt32(0);
}
}

//修改
using (IDbConnection conn = new SQLiteConnection("Data Source=" + databasePath))
{
conn.Open();
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = "UPDATE Players SET Score = @score WHERE ID = @id";
cmd.Parameters.Add(new SQLiteParameter("@score", 150));
cmd.Parameters.Add(new SQLiteParameter("@id", 1)); // 假设这是唯一的ID
cmd.ExecuteNonQuery();
}

//删除
using (IDbConnection conn = new SQLiteConnection("Data Source=" + databasePath))
{
conn.Open();
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = "DELETE FROM Players WHERE ID = @id";
cmd.Parameters.Add(new SQLiteParameter("@id", 1)); // 假设这是唯一的ID
cmd.ExecuteNonQuery();
}

资源加载

Unity 是一个非常流行的游戏开发引擎它提供了丰富的功能来帮助开发者创建交互式内容和游戏在 Unity 中资源管理是非常重要的一个方面因为游戏中的各种元素如模型纹理声音等都需要被正确地加载和使用

Resources

Unity 提供了一个特殊的 Resources 文件夹用于存储在运行时需要访问的资产任何放置在这个文件夹内的资源都可以通过 Resources.Load 方法来加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;

public class LoadFromResources : MonoBehaviour
{
// 在 Start 函数中加载资源
void Start()
{
// 加载名为 "myPrefab" 的预制体
GameObject myPrefab = Resources.Load("Prefabs/myPrefab") as GameObject;

// 如果需要实例化预制体
if (myPrefab != null)
{
GameObject instance = Instantiate(myPrefab, Vector3.zero, Quaternion.identity);
}
}
}

AssetBundles

AssetBundles 是一组被打包在一起的资源它们可以被异步加载到游戏中这对于大型游戏特别有用因为它允许开发者按需加载资源从而减少初始加载时间和内存占用

  • 构建 AssetBundles首先你需要选择哪些资源应该被打包成 AssetBundles并构建这些包
  • 加载 AssetBundles你可以从本地磁盘或者远程服务器加载 AssetBundles
  • 卸载 AssetBundles不再需要时可以释放 AssetBundles 占用的内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using UnityEngine;
using UnityEngine.Networking;

public class LoadFromAssetBundles : MonoBehaviour
{
private const string _baseDownloadURL = "file:/path/to/your.assetbundle"; // 替换为实际路径或URL

// 在 Start 函数中加载 AssetBundle
void Start()
{
StartCoroutine(LoadAssetBundle());
}

// 异步加载 AssetBundle 并从中加载资源
IEnumerator LoadAssetBundle()
{
UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(_baseDownloadURL);

yield return www.SendWebRequest();

if (www.isNetworkError || www.isHttpError)
{
Debug.LogError("Error loading asset bundle: " + www.error);
}
else
{
// 获取 AssetBundle 对象
AssetBundle myAssetBundle = DownloadHandlerAssetBundle.GetContent(www);

// 从 AssetBundle 中加载资源
GameObject myObject = myAssetBundle.LoadAsset<GameObject>("assetNameInBundle");

// 如果需要实例化该对象
if (myObject != null)
{
GameObject instance = Instantiate(myObject, Vector3.zero, Quaternion.identity);
}

// 卸载 AssetBundle 以释放内存
myAssetBundle.Unload(false); // false 表示只卸载 AssetBundle不卸载其加载的资源
}
}
}

StreamingAssets

StreamingAssets 文件夹包含的内容在打包过程中不会被修改并且可以在运行时直接读取这个文件夹通常用来存放一些不需要 Unity 引擎解析的数据文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;
using System.IO;

public class LoadFromStreamingAssets : MonoBehaviour
{
// 在 Start 函数中加载文件
void Start()
{
// 获取 StreamingAssets 文件夹中的文件路径
string path = Application.streamingAssetsPath + "/example.txt";

// 读取文件内容
string fileContent = File.ReadAllText(path);

// 输出文件内容
Debug.Log("File content: " + fileContent);
}
}

灯光组件

Unity 是一个非常流行的跨平台游戏开发引擎它支持多种类型的灯光组件来帮助开发者创建丰富多样的光照效果在 Unity 中你可以添加不同种类的光源到场景中以模拟现实世界中的各种光照情况

  • Directional Light定向光
    • 定向光也被称为太阳光因为它模仿了太阳照射地球的效果所有从定向光源发出的光线都平行并且在整个场景中保持相同的方向这种光通常用来模拟远距离的大光源比如太阳
  • Point Light点光源
    • 点光源是从一个点向各个方向均匀发射光线的光源它们可以用来模拟像灯泡这样的小光源点光源的光照强度会随着距离增加而衰减
  • Spot Light聚光灯
    • 聚光灯有一个明确的方向并且在指定的角度范围内发射光线类似于手电筒或舞台上的聚光灯你可以设置聚光灯的锥角来控制光照的范围
  • Area Light区域光/面光源
    • 区域光在 Unity 中是指具有特定形状如矩形或圆形并从该形状表面发射光线的光源在实时渲染中Unity 使用称为烘焙的技术来模拟面积光源的效果因为直接计算面积光需要较高的计算资源

面板属性

属性 描述
Color 光源的颜色
Intensity 光源的强度
Range 光源影响的最大距离
Spot Angle 仅在转换为聚光灯时可见表示光束的角度
Spot Transition Angle 光束过渡角度定义光束边缘到完全衰减的过渡区域
Use Realtime Global Illumination 是否启用实时全局光照
Shadow Type 阴影类型
Shadow Bias 阴影偏移量
Shadow Normal Bias 用于减少自阴影现象的偏移值
Shadow Softness 阴影柔和度
Shadow Radius 用于计算阴影的采样半径
Shadow Resolution 阴影贴图的分辨率
Shadow Contrast Threshold 阴影对比度阈值
Draw Distance 绘制距离

相机组件

摄像机Camera组件是一个核心组件它负责捕捉场景中的图像并将其显示给用户

基本概念

  • 摄像机在 Unity 中代表一个观察者的眼睛它决定了用户看到的场景部分
  • 场景由多个游戏对象组成的虚拟世界
  • 视口屏幕上显示的最终渲染结果的区域

工作模式

  • Perspective透视投影模拟人眼视觉效果远处物体看起来更小
  • Orthographic正交投影所有物体大小一致不会随距离改变而变化适合 2D 游戏

基本属性

属性 描述
Clear Flags 清除标志用于设置背景颜色或使用天空盒等
Background 背景色当 Clear Flags 设置为 Solid Color 时摄像机会用这个颜色来填充背景
Field of View (FOV) 视野角度控制摄像机的视角宽度通常用于 3D 摄像机
Orthographic 正交投影如果启用则摄像机使用正交投影而不是透视投影适用于 2D 游戏
Orthographic Size 正交尺寸设置摄像机可见区域的高度在 2D 模式下有效
Near Clip Plane 近裁剪平面定义了离摄像机最近的可见点
Far Clip Plane 远裁剪平面定义了离摄像机最远的可见点
Depth 深度值决定了摄像机绘制顺序值越大摄像机越后绘制
Rendering Path 渲染路径决定了渲染物体的方式
Target Texture 目标纹理可将摄像机输出定向到一个纹理上这对于实现某些效果如 UI 镜头很有用

应用示例

💗💗 主视角与 UI 摄像机

在很多游戏中你需要一个摄像机来显示游戏的主要画面同时需要另一个摄像机来专门处理用户界面UI这样可以确保 UI 元素总是清晰地显示在屏幕的正确位置而不受主摄像机变换的影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 主摄像机
public class MainCameraController : MonoBehaviour
{
public Transform target; // 目标物体

void LateUpdate()
{
transform.position = new Vector3(target.position.x, target.position.y, -10); // 调整摄像机位置
}
}

// UI 摄像机在场景中单独设置
public class UICamera : MonoBehaviour
{
void Start()
{
Camera.main.targetTexture = new RenderTexture(Screen.width, Screen.height, 24);
GetComponent<Camera>().targetTexture = null;
}
}

💗💗 第三人称视角与过肩视角

在第三人称游戏中你可能希望有一个固定的过肩视角摄像机来展示玩家角色的行动同时可能还有一个自由移动的摄像机来提供更广阔的视角

1
2
3
4
5
6
7
8
9
10
11
public class OverTheShoulderCamera : MonoBehaviour
{
public Transform player; // 玩家角色
public Vector3 offset; // 摄像机偏移量

void LateUpdate()
{
transform.position = player.position + offset;
transform.LookAt(player);
}
}

💗💗 分屏多人游戏

在分屏多人游戏中你需要为每个玩家分配一个摄像机每个摄像机显示各自玩家的视角这可以通过调整摄像机的裁剪区域或者使用多个摄像机来实现

1
2
3
4
5
6
7
8
9
10
11
12
public class SplitScreenCamera : MonoBehaviour
{
public Camera player1Cam;
public Camera player2Cam;

void Start()
{
// 设置摄像机的视口区域
player1Cam.rect = new Rect(0, 0.5f, 1, 0.5f); // 占据下半屏幕
player2Cam.rect = new Rect(0, 0, 1, 0.5f); // 占据上半屏幕
}
}

💗💗 2D 与 3D 混合视角

在一些游戏中可能会结合 2D 与 3D 视角例如一个 3D 摄像机显示游戏世界而另一个 2D 摄像机则显示某个特定的 2D 场景或迷你地图

1
2
3
4
5
6
7
8
9
10
11
public class MixedDimensionCamera : MonoBehaviour
{
public Camera mainCam; // 3D 主摄像机
public Camera miniMapCam; // 2D 迷你地图摄像机

void Start()
{
mainCam.orthographic = false; // 设置为主摄像机使用透视投影
miniMapCam.orthographic = true; // 设置为迷你地图摄像机使用正交投影
}
}

💗💗 录像与回放

在一些竞技游戏中可能会使用额外的摄像机来录制游戏过程以便之后进行回放分析这些摄像机通常会记录整个场景或特定区域的视角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ReplayCamera : MonoBehaviour
{
public Camera replayCam;
private RenderTexture renderTexture;

void Start()
{
renderTexture = new RenderTexture(Screen.width, Screen.height, 24);
replayCam.targetTexture = renderTexture; // 将摄像机的输出定向到纹理
}

void Update()
{
// 记录摄像机视角
}
}

线渲染器

Line Renderer 是 Unity 中用于绘制一系列相连点的组件常用于创建轨迹路径或者任何需要线性图形的应用场景比如激光束绳索效果等

基本概念

  • Position Points: Line Renderer 通过一系列位置点来定义线条的形状这些点可以手动设置也可以在运行时通过脚本动态更改
  • Materials and Shaders: Line Renderer 支持使用材质和着色器来自定义线条的外观包括颜色宽度等属性
  • Start Width / End Width: 定义了线条起点和终点的宽度可以创建锥形或其他变化宽度的效果
  • Start Color / End Color: 设置线条起点和终点的颜色支持颜色渐变
  • Sorting Layer & Sorting Order: 对于2D线条可以设置排序层和排序顺序来控制线条的渲染层级

组件属性

属性 描述
Position Count 当前 Line Renderer 中的位置点数量
Use World Space 如果启用则位置点是在世界空间中定义的如果禁用则位置点相对于 Line Renderer 所属 GameObject 的局部空间
Width Curve 可以使用曲线来控制整个线条宽度的变化
Color Gradient 使用颜色渐变来定义线条的颜色变化
Align With Direction 启用后线条的方向将会对齐到其位置点之间的方向
Loop 如果启用则第一个位置点与最后一个位置点将被连接起来形成闭合的环
Optimize Start Frame 优化开始帧减少不必要的计算
Width Multiply 宽度乘数用于缩放线条宽度
Material 指定用于渲染线条的材质

应用示例

创建一个简单的Unity脚本示例展示如何使用 LineRenderer 来模拟玩家发射激光的效果在这个例子中当玩家点击鼠标时将从玩家的位置发射一条激光线直到鼠标释放为止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
using UnityEngine;

public class LaserEmitter : MonoBehaviour
{
// 引用LineRenderer组件
public LineRenderer lineRenderer;

// 激光的最大长度
public float maxLaserLength = 100f;

// 是否正在发射激光
private bool isFiring = false;

// 激光的起点位置
private Vector3 startLaserPosition;

// 上一个位置点
private Vector3 lastPosition;

void Start()
{
// 初始化LineRenderer
InitializeLineRenderer();
}

void Update()
{
// 开始发射激光
if (Input.GetMouseButtonDown(0))
{
isFiring = true;
startLaserPosition = transform.position;
lastPosition = startLaserPosition;
}

// 激光持续发射
if (isFiring)
{
// 获取鼠标在世界中的位置
Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mouseWorldPos.z = 0; // 假设我们在一个平面内发射激光

// 计算新的位置点
Vector3 newPosition = Vector3.Lerp(startLaserPosition, mouseWorldPos, 1f);

// 限制激光长度
if ((newPosition - startLaserPosition).magnitude > maxLaserLength)
{
newPosition = startLaserPosition + (newPosition - startLaserPosition).normalized * maxLaserLength;
}

// 添加新的位置点
AddPosition(newPosition);

// 如果鼠标按钮释放停止发射激光
if (Input.GetMouseButtonUp(0))
{
isFiring = false;
}
}
}

// 初始化LineRenderer
private void InitializeLineRenderer()
{
// 设置LineRenderer的材质
Material mat = new Material(Shader.Find("Sprites/Default"));
lineRenderer.material = mat;

// 设置LineRenderer的起始和结束宽度
lineRenderer.startWidth = 0.1f;
lineRenderer.endWidth = 0.1f;

// 设置LineRenderer的起始和结束颜色
lineRenderer.startColor = Color.red;
lineRenderer.endColor = Color.red;
}

// 添加新的位置点
private void AddPosition(Vector3 position)
{
int oldPositionCount = lineRenderer.positionCount;
lineRenderer.positionCount = oldPositionCount + 1;
lineRenderer.SetPosition(oldPositionCount, position);

// 记录当前位置以便下次调用
lastPosition = position;
}
}

轨道渲染器

Trail Renderer 是一个用于模拟连续轨迹或路径的组件常用于创建如粒子尾迹物体拖尾效果例如火箭推进器的火焰轨迹角色移动路径等视觉效果

组件介绍

当你在游戏中添加一个 GameObject并附上 Trail Renderer 组件后你可以通过调整各种属性来控制轨迹的表现形式

  • Position Count: 设置轨迹上记录的位置数量这决定了轨迹的长度
  • Time (在 Trail Renderer 的 Emission 模块中): 控制轨迹的持续时间和生命周期
  • Width Multiplier: 调整轨迹的宽度
  • Gradient: 用于定义轨迹的颜色渐变可以设置开始点中间点和结束点的颜色
  • Sorting Layer & Sorting Order: 这两个选项主要用于 2D 游戏中确定轨迹渲染层的顺序
  • Simulation Speed: 控制轨迹模拟的速度
  • Optimization: 提供了一些优化选项比如是否启用碰撞检测等

使用方法

  • 创建 Trail: 在 GameObject 上添加 Trail Renderer 组件
  • 配置属性: 根据需要调整 Trail 的颜色宽度位置计数等属性
  • 发射点控制: 如果你想要 Trail 跟随某个移动对象你需要在该对象上附加 Rigidbody 或其他移动机制并确保 Trail Renderer 正确地跟随它的运动
  • 动画与交互: 可以使用脚本来控制 Trail 的开启和关闭或者改变其属性值从而实现更复杂的动态效果

应用示例

💗💗 粒子尾迹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//假设你有一个飞行的物体比如一个小飞机你想让它留下尾迹
using UnityEngine;

public class ParticleTrail : MonoBehaviour
{
public TrailRenderer trailRenderer; // 引用 TrailRenderer 组件

private void Start()
{
// 关闭 Trail防止启动时立即显示
trailRenderer.emitting = false;
}

private void OnTriggerEnter(Collider other)
{
// 当物体进入某个触发器区域时开启尾迹
if (other.gameObject.CompareTag("TriggerArea"))
{
trailRenderer.emitting = true;
}
}

private void OnTriggerExit(Collider other)
{
// 当物体离开触发器区域时关闭尾迹
if (other.gameObject.CompareTag("TriggerArea"))
{
trailRenderer.emitting = false;
}
}
}

💗💗 物体拖尾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//为火箭推进器添加火焰轨迹
using UnityEngine;

public class RocketTrail : MonoBehaviour
{
public TrailRenderer trailRenderer; // 引用 TrailRenderer 组件
public float speedThreshold = 5f; // 火箭速度阈值低于此速度不显示尾迹

private Rigidbody rb;

private void Start()
{
rb = GetComponent<Rigidbody>();
// 初始状态关闭尾迹
trailRenderer.emitting = false;
}

private void Update()
{
// 检查火箭的速度
if (rb.velocity.magnitude > speedThreshold)
{
// 开启尾迹
trailRenderer.emitting = true;
}
else
{
// 关闭尾迹
trailRenderer.emitting = false;
}
}
}

💗💗 角色移动路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//玩家角色移动时显示一条路径
using UnityEngine;

public class PlayerMovementTrail : MonoBehaviour
{
public TrailRenderer trailRenderer; // 引用 TrailRenderer 组件

private void Start()
{
// 关闭 Trail直到玩家开始移动
trailRenderer.emitting = false;
}

private void Update()
{
// 检测玩家输入
if (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0)
{
// 玩家正在移动开启尾迹
trailRenderer.emitting = true;
}
else
{
// 玩家停止移动关闭尾迹
trailRenderer.emitting = false;
}
}
}

网格渲染器

Mesh Renderer是一个重要的组件它负责将网格Mesh渲染到场景中这个组件与Mesh Filter和网格资源一起工作来显示3D对象的外观

基本概念

  • Mesh Renderer 组件用于定义物体的表面如何被渲染它决定了模型的颜色纹理以及如何响应光照
  • Mesh Filter 组件关联一个网格数据到游戏对象网格数据定义了物体的几何形状
  • 网格本身是一个资源文件包含了顶点的位置信息以及其他细节如UV坐标等

使用方法

  1. 添加Mesh Renderer组件在Unity编辑器中创建一个空的游戏对象后你可以通过在Inspector面板中添加Mesh Renderer组件来开始渲染一个网格如果你想要渲染一个具体的形状比如一个立方体或者球体你也需要添加一个Mesh Filter组件并且给它分配一个相应的网格资源
  2. 材质MaterialMesh Renderer组件需要一个材质Material这是一个包含了渲染物体所需的所有信息的容器包括颜色贴图Textures着色器Shader材质决定了物体如何根据光照条件改变其外观
  3. 着色器Shader着色器是控制物体渲染方式的脚本它们可以非常简单例如仅使用颜色或基础纹理也可以非常复杂例如实现高级光照效果或物理渲染PBR材质中定义了要使用的着色器
  4. 阴影模式Shadows ModeMesh Renderer允许你配置物体是否接收阴影以及是否投射阴影这可以通过设置阴影模式选项来完成有多种选择如Casting ShadowsReceiving Shadows
  5. Layer层是一种组织和过滤场景中的物体的方法你可以为Mesh Renderer指定一个或多个层这对于光照碰撞检测和其他基于层的操作非常有用

应用示例

MeshRenderer组件可以让你动态地更改物体的外观例如改变颜色切换材质等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using UnityEngine;

public class ChangeMaterialExample : MonoBehaviour
{
// 定义一个材质变量用于存储新的材质
public Material newMaterial;

private MeshRenderer meshRenderer;

void Start()
{
// 在Start()方法中获取MeshRenderer组件
meshRenderer = GetComponent<MeshRenderer>();

if (meshRenderer == null)
{
Debug.LogError("MeshRenderer component not found on this game object.");
return;
}

// 检查是否指定了新的材质
if (newMaterial == null)
{
Debug.LogWarning("No new material assigned in the inspector.");
return;
}

// 更改游戏对象的材质
ChangeMaterial();
}

// 更改游戏对象的材质
void ChangeMaterial()
{
// 获取当前MeshRenderer使用的材质列表
Material[] currentMaterials = meshRenderer.materials;

// 遍历材质数组并替换每个材质
for (int i = 0; i < currentMaterials.Length; i++)
{
currentMaterials[i] = newMaterial;
}

// 将更新后的材质数组应用回MeshRenderer
meshRenderer.materials = currentMaterials;
}
}

协程

协程Coroutine是 Unity 中一种非常有用的功能它允许你编写可以暂停执行的函数并在稍后的时间点恢复执行协程在 Unity 中常用于实现动画延迟执行任务简单的时间间隔操作等

基本概念

  • 定义协程是一个可以暂停执行的方法当它被暂停时可以恢复执行其他代码然后在适当的时候恢复协程的执行
  • 声明协程方法必须返回 IEnumerator 类型并且通常以 IEnumerator 开头
  • 启动要启动一个协程你需要调用 StartCoroutine 方法并传递协程方法的引用
  • 停止你可以通过调用 StopCoroutine 方法来停止一个正在进行的协程
  • 生命周期协程在其所属的 MonoBehaviour 对象存活期间可以持续运行如果该对象被销毁则所有正在运行的协程也会被自动停止

yield 表达式

💗💗 yield return null;

暂停协程直到下一帧开始这可以用来实现每一帧执行一次的逻辑

1
2
3
4
5
6
7
8
IEnumerator ExampleCoroutine()
{
while (true)
{
Debug.Log("Current frame: " + Time.frameCount);
yield return null;
}
}

💗💗 yield return new WaitForEndOfFrame();

类似于 yield return null;但确保当前帧的所有渲染操作完成后再继续执行

1
2
3
4
5
6
7
8
IEnumerator ExampleCoroutine()
{
while (true)
{
Debug.Log("After rendering completes: " + Time.frameCount);
yield return new WaitForEndOfFrame();
}
}

💗💗 yield return new WaitForSeconds(float seconds);

让协程暂停指定的秒数这是最常见的暂停方式之一

1
2
3
4
5
6
7
8
9
IEnumerator ExampleCoroutine()
{
while (true)
{
Debug.Log("Waiting for 1 second...");
yield return new WaitForSeconds(1f);
Debug.Log("Done waiting.");
}
}

💗💗 yield return new WaitUntil(System.Func condition);

让协程暂停直到提供的委托函数返回 true

1
2
3
4
5
6
7
8
IEnumerator ExampleCoroutine()
{
while (true)
{
Debug.Log("Waiting for game object to reach position...");
yield return new WaitUntil(() => gameObject.transform.position.x >= 5f);
}
}

💗💗 yield return new WaitWhile(System.Func condition);

让协程暂停只要提供的委托函数返回 true

1
2
3
4
5
6
7
8
9
10
11
IEnumerator ExampleCoroutine()
{
bool isWaiting = true;
while (true)
{
Debug.Log("Waiting until condition becomes false...");
yield return new WaitWhile(() => isWaiting);
Debug.Log("Condition became false.");
isWaiting = true;
}
}

💗💗 yield return StartCoroutine(IEnumerator routine);

让当前协程等待另一个协程的完成

1
2
3
4
5
6
7
8
9
10
11
IEnumerator ExampleCoroutine()
{
yield return StartCoroutine(AnotherCoroutine());
}

IEnumerator AnotherCoroutine()
{
Debug.Log("Starting another coroutine...");
yield return new WaitForSeconds(2f);
Debug.Log("Another coroutine finished.");
}

💗💗 yield break;

立即退出当前协程

1
2
3
4
5
6
7
8
IEnumerator ExampleCoroutine()
{
if (!someCondition)
{
yield break;
}
Debug.Log("Continuing...");
}

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
// 启动协程
private void Start()
{
StartCoroutine(MyCoroutine());
}

// 定义协程
private IEnumerator MyCoroutine()
{
Debug.Log("协程开始");

// 等待 2 秒
yield return new WaitForSeconds(2f);
Debug.Log("等待 2 秒后");

// 等待直到某个条件满足
while (!IsSomeConditionTrue())
{
yield return null;
}
Debug.Log("条件满足");

// 等待下一帧
yield return new WaitForEndOfFrame();
Debug.Log("下一帧开始");

// 结束协程
yield break;
}

// 示例条件函数
private bool IsSomeConditionTrue()
{
// 这里可以是任何条件判断
return Input.GetKeyDown(KeyCode.Space);
}

// 停止协程
public void StopMyCoroutine()
{
StopCoroutine("MyCoroutine");
}
}

动画组件

Animation Clips

💗💗 概念

  • 定义动画剪辑是Unity中用于存储动画数据的一种资源类型它由一系列关键帧组成每个关键帧记录了一个时间点上的骨骼位置旋转和缩放信息
  • 创建可以在Unity的Animation Window中创建动画剪辑也可以直接导入已经存在的FBX或BVH文件
  • 编辑使用Unity的动画编辑器可以添加删除或修改关键帧调整曲线实现平滑的动画过渡

💗💗 应用场景

动画剪辑在游戏中为角色创建各种动作如行走跳跃攻击等

💗💗 操作步骤

  1. 创建一个新的Unity项目
  2. 导入你的角色模型并确保模型带有骨骼
  3. 打开Animation窗口 (Window > Animation > Animation)
  4. 在Hierarchy视图中选择你的角色模型
  5. 在Inspector视图中点击Create按钮创建新的动画剪辑
  6. 为动画剪辑命名例如 Jump
  7. 使用Timeline添加关键帧定义跳跃的起始最高点和落地的姿态
  8. 调整曲线以获得平滑过渡

Animator Controller

💗💗 概念

  • 定义Animator Controller是一个脚本用于管理动画状态机定义动画状态之间的过渡规则
  • 使用在Unity的Animator窗口中创建Animator Controller并将其分配给游戏对象通过拖拽动画剪辑到Animator Controller中可以创建动画状态
  • 状态机每个状态可以代表一个动画剪辑状态之间的过渡可以基于特定条件或参数的变化

💗💗 应用场景

控制角色在不同状态之间的转换例如从静止到行走再到攻击

💗💗 操作步骤

  1. 在Assets视图中右键点击选择 Create > Animator Controller
  2. 将新创建的Animator Controller拖到你的角色模型上
  3. 在Animator窗口 (Window > Animation > Animator) 中打开这个Animator Controller
  4. 添加状态例如 Idle, Walk, Attack
  5. 设置状态之间的过渡例如从 Idle 到 Walk 当速度大于某个阈值时触发
  6. 将相应的动画剪辑分配给每个状态

State Machines

💗💗 概念

  • 定义动画状态机是一种组织动画的方式它将动画划分为多个状态并定义了这些状态之间的转换规则
  • 工作原理每个状态通常对应于一个动画剪辑而状态间的转换则依赖于Animator Controller中的条件判断
  • 层次结构状态机可以有层次结构允许嵌套子状态机以便更好地管理和组织复杂的动画逻辑

💗💗 应用场景

动画状态机管理角色的动画状态例如从闲逛状态到战斗状态

💗💗 操作步骤

  1. 在Animator窗口中通过拖动状态来构建状态机
  2. 为状态机添加层次结构例如主状态机可以包含 Combat 和 NonCombat 子状态机
  3. 在每个子状态机内部再添加具体的动画状态如 Combat 下可以有 Attack, Defend 等

Parameters

💗💗 概念

  • 定义参数是用来控制动画状态机中状态切换的变量可以是布尔型整型或浮点型
  • 用途通过改变参数值可以触发状态机中的状态转换从而播放不同的动画序列
  • 动态更改可以在运行时通过脚本动态更改参数值以响应用户输入或游戏逻辑

💗💗 应用场景

控制角色动画的动态切换例如当玩家按下攻击键时切换到攻击动画

💗💗 操作步骤

  1. 在Animator窗口中点击 + 添加一个布尔参数 isAttacking
  2. 设置 Attack 状态只有当 isAttacking 为 true 时才进入
  3. 在脚本中当玩家按下攻击键时设置 animator.SetBool(“isAttacking”, true)

Blend Trees

💗💗 概念

  • 定义混合树是一种特殊的动画状态它允许将多个动画按照一定的权重混合在一起播放
  • 类型混合树可以是一维的也可以是二维的取决于需要混合的动画数量和维度
  • 应用常用于实现角色在不同速度下的移动动画或者是基于两个变量如水平和垂直速度的动画混合

💗💗 应用场景

混合树根据角色的速度混合不同的行走动画

💗💗 操作步骤

  1. 在Animator窗口中创建一个一维混合树状态
  2. 添加至少两个动画剪辑作为混合树的输入例如 Idle 和 Run
  3. 设置混合轴为 Speed并关联到角色的速度
  4. 在脚本中更新角色的速度参数

Avatar 和骨骼绑定

💗💗 概念

  • 定义Avatar是一个配置文件它定义了Unity如何将动画数据映射到特定角色的骨骼上
  • 作用确保动画数据与角色模型匹配即使角色模型来自不同的来源
  • 创建通过将模型导入Unity并标记骨骼关节来创建Avatar

💗💗 应用场景

确保不同来源的角色模型能够正确播放相同的动画

💗💗 操作步骤

  1. 导入新的角色模型
  2. 在Inspector视图中检查模型的 Humanoid 属性是否已正确映射
  3. 如果需要调整可以手动设置 Humanoid 骨骼映射

使用脚本控制动画

💗💗 概念

  • 定义通过编写C#脚本可以控制Animator Controller中的参数从而控制动画的播放
  • 示例可以检测玩家输入的方向键并相应地更改Animator Controller中的速度参数来播放行走或跑步动画

💗💗 应用场景

响应玩家输入来改变动画状态

💗💗 操作步骤

  1. 创建一个脚本附加到角色上
  2. 在脚本中根据玩家输入改变Animator参数例如 animator.SetFloat(“Speed”, speed);

Animation Events

💗💗 概念

  • 定义动画事件是在动画的特定时刻触发的事件可以用来执行代码
  • 设置在Unity的动画编辑器中为动画剪辑设置事件指定触发时间和要调用的方法
  • 用途用于同步动画与其他游戏元素如音效粒子效果等

💗💗 应用场景

动画事件在动画的特定时刻触发声音效果或粒子效果

💗💗 操作步骤

  1. 在Animation窗口中为动画剪辑添加一个动画事件
  2. 设置事件触发的时间点
  3. 在脚本中实现事件方法例如播放音效

Animation Notifications

💗💗 概念

  • 定义类似于动画事件但在动画剪辑播放期间发送消息而不是直接执行代码
  • 用途用于在动画播放的不同阶段通知其他系统执行某些操作

💗💗 应用场景

动画通知在动画播放期间发送消息例如通知UI系统显示伤害数值

💗💗 操作步骤

  1. 在Animation窗口中为动画剪辑添加一个动画通知
  2. 设置通知发送的时间点
  3. 在脚本中监听这些通知并执行相应逻辑

Inverse Kinematics

💗💗 概念

  • 定义IK允许你直接控制角色肢体的末端如手或脚即使这些肢体在动画剪辑中处于不同的姿势
  • 应用对于实现角色与环境互动非常重要例如让角色的手抓住物体或脚踏实地
  • 配置在Animator窗口中启用IK并指定IK目标和权重

💗💗 应用场景

动画IK让角色的手臂自然地抓取物品

💗💗 操作步骤

  1. 在Animator窗口中为相关状态启用IK
  2. 设置IK的目标例如角色的手部
  3. 在脚本中更新IK目标的位置使其靠近物品

自动寻路

Unity 中的自动寻路Pathfinding功能通常通过其内置的导航系统来实现这个系统允许你在场景中定义可行走区域并为AI角色计算出从一个点到另一个点的最佳路径

  • NavMesh Build Settings用于配置如何构建导航网格你可以指定哪些层是可行走的设置代理的高度半径等参数以及定义障碍物的跨越高度
  • NavMesh Obstacle可以添加到阻挡导航网格构建的对象上如移动的障碍物或敌对AI这样它们就不会影响到导航网格的构建而是作为动态障碍处理

💗💗 操作步骤

  1. 选择场景视图打开Unity编辑器选择你的场景
  2. 打开Navigation窗口在菜单栏选择 Window > AI > Navigation
  3. 配置Build Settings
    • 在Navigation窗口中点击 Settings 按钮
    • 在弹出的 NavMesh Build Settings 窗口中你可以设置哪些层是可以行走的Layers定义代理的尺寸Agent Radius, Agent Height
  4. 构建NavMesh
    • 回到Navigation窗口点击 Build 按钮来构建导航网格
    • 你可以看到场景中的物体表面变成了蓝色表示这是可行走区域
  • 这是一个组件你可以将其附加到需要进行自动寻路的GameObject上它负责处理移动逻辑比如速度加速度旋转方式等
  • Destination目标位置可以通过脚本或者Inspector面板来设定
  • Is On NavMesh确保代理在导航网格上如果不在则会试图找到最近的可行走区域
  • Obstacle Avoidance根据环境中的其他代理或动态障碍物来调整路径

💗💗 操作步骤

  1. 选择GameObject在Hierarchy视图中选择你需要自动寻路的游戏对象
  2. 添加NavMesh Agent组件在Inspector面板中点击 Add Component然后搜索 NavMesh Agent 并添加
  3. 配置Agent参数
    • 调整 Speed 来设置代理的最大移动速度
    • 设置 Acceleration 来控制加速和减速的速度
    • 使用 Angular Speed 来设置转向速度
    • 你可以设置目标位置 Destination这可以通过脚本来动态更改
  • Navigation Static标记物体为静态导航对象其变化不会影响到已经构建好的导航网格
  • Navigation Dynamic动态导航区域这些区域可以在运行时改变比如桥塌陷后的水面

💗💗 操作步骤

  1. 创建Navigation Static/Dynamic区域选择需要定义为导航区域的对象在Inspector面板中找到 Navigation 部分
  2. 设置Area Type选择合适的类型如 Walkable 表示可行走区域Not Walkable 表示不可行走区域

当两个NavMesh之间没有直接连接但是可以通过某些特定路径例如跳跃攀爬使用绳索等到达时可以使用此组件来创建这种非网格链接

💗💗 操作步骤

  1. 创建Off-Mesh Link选择需要创建链接的对象然后在Inspector中添加 OffMeshLink 组件
  2. 设置起始和结束位置通过 Position 属性来指定链接的起点和终点
  3. 配置属性可以设置 Travel Cost 来改变经过此链接的成本从而影响AI的选择

Path

寻路结果会被存储在一个Path对象中这个对象包含了从起点到终点的一系列点NavMesh Agent可以读取这个路径并跟随它

💗💗 操作步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using UnityEngine;
using UnityEngine.AI;

public class PathExample : MonoBehaviour
{
public Transform target; // 目标位置
private NavMeshAgent agent; // NavMesh代理

void Start()
{
agent = GetComponent<NavMeshAgent>();
if (agent != null)
{
// 设置目标
agent.destination = target.position;
}
}

void Update()
{
if (agent != null && agent.pathStatus == NavMeshPathStatus.PathComplete)
{
Debug.Log("路径已计算完成");
}
}
}

可以修改局部导航网格属性的对象比如降低某个区域的通行成本使得AI更倾向于或避免通过该区域

💗💗 操作步骤

  1. 添加NavMesh Modifier组件选择需要修改局部导航网格属性的对象添加 NavMeshModifierVolume 组件
  2. 配置属性
    • 设置 Shape 为 Box, Capsule, Sphere 或者 Cylinder
    • 调整 Size 和 Offset 来确定影响范围
    • 设置 Area 类型和 Cost Modifier 来改变代理经过该区域的成本

用户界面

在Unity中UI用户界面系统允许开发者创建交互式且美观的界面以增强游戏或应用的用户体验

画布

Canvas 组件是所有 UI 元素的容器它决定了 UI 元素在屏幕上的呈现方式画布是创建用户界面的基础所有的 UI 控件如按钮文本图像等都必须放置在画布之下

创建画布

  • 创建一个新的 Canvas在 Unity 编辑器的 Hierarchy 视图中选择 GameObject > UI > Canvas 来创建一个新的 UI 画布
  • 默认结构创建后Unity 会自动为你创建一个包含几个子物体的 Canvas 对象包括一个 Event System一个 Stencil如果启用了Pixel Perfect一个 Content Scale Factor用于 DPI 缩放以及一个 Rect Transform

主要属性

  • Render Mode: 画布的渲染模式决定了画布相对于相机的位置
    • Screen Space - Overlay: 默认模式将 UI 元素直接渲染到屏幕之上与场景中的 3D 物体无关
    • Screen Space - Camera: 将 UI 元素渲染到指定的摄像机上可以实现更高级的效果如 UI 元素跟随摄像机移动
    • World Space: 将 UI 元素作为世界空间中的对象渲染可以将 UI 元素放置在 3D 场景中的任意位置
  • Pixel Perfect: 如果勾选Unity 会尝试让 UI 在不同分辨率的屏幕上看起来相同大小对于需要精确像素的艺术风格来说非常有用
  • Root Canvas: 如果勾选则该画布将成为根画布通常情况下每个场景只有一个根画布它是所有其他画布的父级
  • Raycast Target: 如果启用那么该画布下的 UI 元素可以接收到用户输入如点击如果禁用则无法接收到用户输入
  • Additional Canvas Properties: 包括一些额外的属性如模糊效果透明度等

子物体

  • Event System: 处理用户输入事件如鼠标点击触摸屏输入等
  • Canvas Scaler: 控制 UI 元素在不同屏幕尺寸下的缩放
  • Graphic Raycaster: 用于检测用户输入并触发相应的 UI 事件
  • RectTransform Tool: 用于精确调整 UI 元素的位置和大小

Canvas Scaler 组件

Canvas Scaler 组件对于适配不同分辨率的屏幕非常重要它具有以下几种模式

  • Constant Pixel Size: 保持 UI 元素的像素大小一致
  • Scale With Screen Size: 根据屏幕宽度的比例缩放 UI 元素
  • Constant Physical Size: 保持 UI 元素的实际物理尺寸一致

Graphic Raycaster 组件

Graphic Raycaster 组件用于检测用户输入并将其传递给相应的 UI 元素你可以根据需要添加多个 Raycaster以支持不同的输入类型如鼠标触摸屏等

字体

这是最基本的UI组件之一用于在屏幕上显示文本当你在Hierarchy视图中创建一个UI Canvas并添加一个新的UI Text对象时就会自动附带Text组件在Inspector视图中你可以设置文本的内容字体大小颜色等属性

属性

  • Text: 显示的文本内容
  • Font Size: 文本字体的大小
  • Font Data: 设置使用的字体文件
  • Color: 文本的颜色
  • Alignment: 文本对齐方式如左对齐居中右对齐等
  • Line Spacing: 行间距
  • Rich Text: 如果启用此选项可以使用富文本标签来格式化文本例如加粗斜体等

字体设置

在Unity中字体是通过材质来定义的你可以使用Unity自带的字体也可以导入自己的TTF或OTF格式的字体文件一旦导入了字体可以在Font面板中调整字符间距行高和其他相关设置

字体材质

为了使用自定义字体你需要创建一个字体材质这通常是通过在Unity编辑器中将字体文件拖到项目窗口来完成的Unity会自动创建一个字体材质资源

富文本

启用了Text组件上的Rich Text选项那么可以在文本字符串中使用特殊的标签来改变文本的外观

  • <b> 和 </b> 可以用来加粗文本
  • <i> 和 </i> 可以用来斜体化文本
  • <color=#rrggbb> 可以用来改变文本的颜色
  • <size=值> 可以用来改变字体大小

文本描边

描边是一个可选的效果可以用来增加文本的可读性或者美感描边是在文本边缘添加的一种颜色效果通常与文本本身的颜色不同

文本溢出

当文本长度超过分配给其的区域时Unity提供了几种处理文本溢出的方式比如截断或换行

图片

在Unity中UI图片组件是用来显示图像的一个重要元素广泛应用于游戏和应用程序的用户界面设计中UI图片组件不仅限于静态图像展示还可以用于实现动态效果响应用户输入等

  • 创建Image对象在Hierarchy视图中创建一个UI Canvas后在Canvas下创建一个新的Image对象
  • 设置Source Image在Inspector视图中找到Image组件并设置其Source Image属性这里可以选择从项目资源中选择一个图像文件作为源图像

属性

  • Source Image: 指定要显示的图像可以选择SpriteTexture或者不指定任何图像None
  • Type: 图像的类型包括SimpleSlicedTiled和Filled四种模式
    • Simple: 最简单的显示模式图像按照原样显示
    • Sliced: 用于显示有边框和角落的图像可以设置四个角的像素宽度和高度来保持图像的拉伸不变形
    • Tiled: 重复显示图像常用于创建纹理效果
    • Filled: 显示填充效果可以设置填充的角度方向和填充比例
  • Color: 调整图像的颜色即使没有选择任何图像也可以通过这个属性显示纯色块
  • Fill Method: 当Type设置为Filled时有效可以选择从中心向外填充从左向右等不同的填充方法
  • Preserve Aspect: 如果勾选则在调整图像大小时会保持原始图像的宽高比
  • Raycast Target: 如果启用那么该图像可以接收用户的输入事件例如点击触摸等

Sprite Mask

除了基本的Image组件之外还可以使用Sprite Mask组件来创建剪裁效果Sprite Mask可以用来遮罩其子物体只有位于遮罩区域内的子物体才能显示出来

遮罩

遮罩组件Mask是一种用来限制其他 UI 元素可见范围的技术可以用来创建各种视觉效果如圆形菜单异形按钮等遮罩组件主要有两种形式Sprite Mask 和 Graphic Mask这两种遮罩组件都可以用来隐藏其子物体超出遮罩范围的部分从而实现特定形状的显示效果

Sprite Mask 组件

Sprite Mask 是一种遮罩组件它允许你使用 Sprite 来定义遮罩的形状Sprite Mask 组件通常用于创建具有复杂形状的 UI 元素如不规则形状的按钮图标等

创建 Sprite Mask

  1. 创建一个新的 GameObject在 Hierarchy 视图中创建一个新的空 GameObject
  2. 添加 Sprite Mask 组件选择 GameObject 并在 Components 菜单下添加 UI > Mask > Sprite Mask
  3. 设置 Sprite在 Inspector 视图中为 Sprite Mask 组件选择一个合适的 Sprite 作为遮罩的形状

属性

  • Source Image: 用于定义遮罩形状的 Sprite
  • Show Outer Contour: 如果勾选将显示遮罩的轮廓这对于调试遮罩效果很有帮助
  • Use Triggers: 如果勾选遮罩将使用触发器Trigger来定义其边界这意味着遮罩将影响其所有的子物体而不仅仅是直接的子物体

Graphic Mask 组件

Graphic Mask 是另一种类型的遮罩组件它允许你使用任何 Graphic 类型的对象如 ImageRaw Image 等来定义遮罩的形状Graphic Mask 可以用来创建更加灵活的遮罩效果

创建 Graphic Mask

  1. 创建一个新的 GameObject在 Hierarchy 视图中创建一个新的 GameObject
  2. 添加 Image 或其他 Graphic 组件为 GameObject 添加一个 Image 组件或其他类型的 Graphic 组件
  3. 添加 Graphic Mask 组件在 Components 菜单下添加 UI > Mask > Graphic Mask
  4. 设置 Graphic Mask在 Inspector 视图中可以设置 Graphic Mask 的属性

属性

  • Target Graphic: 选择一个 Graphic 组件作为遮罩的形状来源
  • Rect Mask: 如果勾选遮罩将基于目标 Graphic 的矩形边界来定义
  • Use Triggers: 同 Sprite Mask 的 Use Triggers 属性

使用示例

遮罩组件通常与 Canvas 的 Maskable Graphics 一起使用以达到最佳效果Maskable Graphics 是指那些支持遮罩的 UI 元素如 ImageTextMeshTextRawImage 等

创建一个圆形的按钮

  1. 创建一个 Image GameObject用于显示按钮的背景
  2. 创建一个 Sprite Mask GameObject用于定义按钮的形状
  3. 设置 Sprite Mask 的 Source Image选择一个圆形的 Sprite
  4. 将 Image GameObject 拖动到 Sprite Mask GameObject 下作为子物体这样 Image 就会被圆形的 Sprite Mask 限制在其范围内显示

按钮

Button 组件是一个UI元素用于响应用户的点击事件它可以绑定到脚本中的函数从而执行特定的操作

创建按钮

  1. 在Hierarchy视图中创建一个Canvas
  2. 在Canvas下创建一个新的Button对象
  3. 在Inspector视图中你会看到一个带有默认文本的Button对象它已经包含了Button组件

主要属性

  • On Click(): 这是一个事件列表可以在这里添加脚本的方法引用或其他脚本的函数当用户点击按钮时这些方法会被调用
  • Transition: 按钮状态变化的过渡方式默认是None表示没有过渡效果其他选项包括
    • Color Tint: 改变按钮颜色来显示不同的状态
    • Sprite Swap: 使用不同的Sprite来显示不同的状态
    • Animation: 使用Unity动画系统来播放动画
  • Colors: 定义按钮在不同状态下的颜色设置包括正常状态高亮状态按下状态和禁用状态
  • Sprites: 当Transition设置为Sprite Swap时这里可以指定不同状态下的Sprite
  • Animations: 当Transition设置为Animation时这里可以指定不同状态下的动画

添加事件

  • 在On Click()事件列表中你可以通过点击+按钮来添加一个新的事件条目
  • 选择Object来选择一个游戏对象并选择该对象上的一个脚本方法
  • 也可以直接添加预设的方法如Print Log用于测试按钮是否有效

高级用法

  • 动画: 可以为按钮添加动画效果使其在点击时产生动画反馈
  • 禁用按钮: 可以通过脚本或直接在Inspector中禁用按钮使其不可点击
  • 条件响应: 根据某些条件来决定按钮是否响应用户的点击

自定义样式

通过Sprite Mask和Sprite Shapes来创建复杂的按钮样式此外还可以通过脚本来动态地改变按钮的外观和行为

示例代码

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using UnityEngine.UI;

public class ButtonScript : MonoBehaviour
{
public void OnButtonClick()
{
Debug.Log("Button was clicked!");
// 执行你的逻辑例如改变场景播放声音等
}
}

选框

Toggle 组件是一个常用的 UI 元素通常用于表示二元状态的选择如开启/关闭是/否等Toggle 组件可以用来创建复选框开关等用户界面元素并且可以方便地与脚本结合来实现各种功能

创建 Toggle

  • 创建一个新的 Toggle在 Unity 编辑器的 Hierarchy 视图中选择 GameObject > UI > Toggle 来创建一个新的 Toggle 对象
  • 默认结构创建后Unity 会自动为你创建一个包含 Toggle Group如果之前已有 Toggle Group以及 Toggle Button 和 Toggle Label 的 Toggle 对象

主要属性

  • Is On: 初始状态下 Toggle 是否被激活默认值为 true表示初始状态为打开
  • Group: Toggle 所属的 Toggle Group如果 Toggle 是一个组的一部分那么一次只能有一个 Toggle 处于活动状态这对于实现单选按钮功能非常有用
  • On Value Changed: 一个回调函数列表当 Toggle 的状态发生改变时即从开启变为关闭或从关闭变为开启这些函数会被调用可以在这里添加脚本中的方法来响应 Toggle 状态的变化

Toggle Button

Toggle Button 是 Toggle 的图形表示当 Toggle 被激活或关闭时它的外观会发生变化Toggle Button 通常由一个 Image 组件构成可以通过设置不同的 Sprite 或者改变颜色来表示不同的状态

Toggle Label

Toggle Label 是 Toggle 旁边显示的文本标签用于描述 Toggle 的功能或用途Label 通常由一个 Text 组件构成可以设置文本的内容字体大小颜色等属性

Toggle Group

Toggle Group 是一个可选的组件用于将多个 Toggle 组件组合在一起使得它们之间可以互相排斥即一次只能有一个 Toggle 处于活动状态这对于创建单选按钮功能非常有用

创建 Toggle Group

  • 在 Hierarchy 视图中创建一个新的空 GameObject
  • 将此 GameObject 命名为 ToggleGroup
  • 为其添加 Toggle Group 组件
  • 将想要成为一组的 Toggle 对象拖拽到 ToggleGroup 下作为子物体
  • 在每个 Toggle 的 Toggle 组件中选择刚刚创建的 ToggleGroup 作为其 Group

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using UnityEngine;
using UnityEngine.UI;

public class ToggleExample : MonoBehaviour
{
public Toggle myToggle;

void Start()
{
// 注册事件监听器
myToggle.onValueChanged.AddListener(ToggleValueChanged);
}

void ToggleValueChanged(bool isOn)
{
if (isOn)
{
Debug.Log("Toggle is ON");
// 执行开启时的逻辑
}
else
{
Debug.Log("Toggle is OFF");
// 执行关闭时的逻辑
}
}
}

滑块

UI 滑块组件Slider是一个常用的用户界面元素用于表示数值的变化并允许用户通过拖动滑块来改变这些数值滑块组件可以用来控制音量调节亮度改变速度等多种场景

创建滑块

  1. 创建一个新的滑块在 Unity 编辑器的 Hierarchy 视图中选择 GameObject > UI > Slider 来创建一个新的滑块对象
  2. 默认结构创建后Unity 会自动为你创建一个包含滑块背景滑块手柄Fill Area及其内部的填充部分Fill Rect等组件的滑块对象

主要属性

  • Direction: 滑块的方向可以是水平Horizontal或垂直Vertical
  • Min Value: 滑块的最小值
  • Max Value: 滑块的最大值
  • Value: 当前滑块的值可以在 Inspector 中直接设置或通过脚本动态修改
  • Whole Numbers: 如果勾选滑块的值将只能取整数
  • Handle Slide: 如果勾选用户可以通过拖动手柄来改变滑块的值
  • On Value Changed: 一个回调函数列表当滑块的值发生变化时这些函数会被调用可以在这里添加脚本中的方法来响应滑块值的变化
  • Fill Area 和 Fill Rect滑块的 Fill Area 包含一个 Fill Rect用来显示滑块当前所处的位置Fill Rect 的大小和位置会根据滑块的当前值动态改变

滑块的子物体

  • Slider/Handle Rect: 用于表示滑块的手柄用户可以通过拖动它来改变滑块的值
  • Slider/Fill Rect: 代表滑块填充部分的矩形随着滑块值的变化而变化
  • Slider/Thumb: 滑块的手柄图标可以自定义其外观

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using UnityEngine.UI;

public class SliderExample : MonoBehaviour
{
public Slider mySlider;

void Start()
{
// 注册事件监听器
mySlider.onValueChanged.AddListener(SliderValueChanged);
}

void SliderValueChanged(float value)
{
Debug.Log("Slider value changed to: " + value);
// 执行滑块值变化时的逻辑
}
}

高级用法

  • 动态调整值可以在脚本中动态地调整滑块的值
  • 禁用滑块可以禁用滑块使其无法通过用户交互改变值
  • 自定义外观可以通过设置不同的 Sprite 或者改变颜色来自定义滑块的外观
  • 多滑块同步可以使用脚本来同步多个滑块的状态使它们的值保持一致

滚动条

UI 滚动条组件Scrollbar是一个常见的用户界面元素用于表示和控制数值的变化范围滚动条通常用于模拟长文档或列表的滚动行为让用户能够通过拖动滚动条来改变当前显示的内容

创建滚动条

  1. 创建一个新的滚动条在 Unity 编辑器的 Hierarchy 视图中选择 GameObject > UI > Scrollbar 来创建一个新的滚动条对象
  2. 默认结构创建后Unity 会自动为你创建一个包含滚动条背景滑块手柄等组件的滚动条对象

主要属性

  • Direction: 滚动条的方向可以是水平Horizontal或垂直Vertical
  • Value: 当前滚动条的值可以在 Inspector 中直接设置或通过脚本动态修改
  • Size: 滚动条手柄的大小表示当前显示内容占总内容的比例
  • OnValueChanged: 一个回调函数列表当滚动条的值发生变化时这些函数会被调用可以在这里添加脚本中的方法来响应滚动条值的变化

滚动条的子物体

  • Scrollbar/Background: 用于表示滚动条的背景
  • Scrollbar/Handle Rect: 代表滚动条手柄的矩形用户可以通过拖动它来改变滚动条的值

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using UnityEngine.UI;

public class ScrollbarExample : MonoBehaviour
{
public Scrollbar myScrollbar;

void Start()
{
// 注册事件监听器
myScrollbar.onValueChanged.AddListener(ScrollbarValueChanged);
}

void ScrollbarValueChanged(float value)
{
Debug.Log("Scrollbar value changed to: " + value);
// 执行滚动条值变化时的逻辑
}
}

高级用法

  • 动态调整值可以在脚本中动态地调整滚动条的值
  • 禁用滚动条可以禁用滚动条使其无法通过用户交互改变值
  • 自定义外观可以通过设置不同的 Sprite 或者改变颜色来自定义滚动条的外观
  • 与 ScrollRect 结合使用滚动条通常与 ScrollRect 组件结合使用以实现内容的滚动效果

滚动视图

UI 滚动视图组件ScrollView是一个重要的用户界面元素用于控制一个区域内的内容滚动通常与 PanelContent Size FitterLayout GroupScrollbar 等组件结合使用以实现内容的平滑滚动自动布局等功能

创建滚动视图

  1. 在 Unity 编辑器的 Hierarchy 视图中选择 GameObject > UI > Scroll View 来创建一个新的滚动视图对象
  2. 默认结构创建后Unity 会自动为你创建一个包含 Viewport视口Content内容容器Scrollbar滚动条等组件的滚动视图对象

基本概念

  • Viewport视口: 视口是一个矩形区域它决定了用户可以看到的内容部分
  • Content内容: 这是实际包含所有要显示的对象的游戏物体内容通常比视口大因此需要滚动才能看到所有的内容
  • Scrollbar滚动条: 滚动条用来控制和指示视口如何在内容上移动可以有垂直滚动条水平滚动条或两者都有

ScrollRect 组件

Scroll Rect是核心组件它定义了如何进行滚动操作

  • Scroll Rect: 选择哪个对象作为滚动区域
  • Content: 内容容器用来放置滚动的对象
  • Viewport: 可视窗口
  • Scrollbar (Horizontal/Vertical): 分别用于控制水平和垂直方向上的滚动
  • Movement Type: 定义了滚动的方式可以选择 Unrestricted, Clamped, Elastic, Snap
  • Inertia: 如果开启则滚动会有惯性效果
  • Scroll Sensitivity: 控制滚动的灵敏度
  • Scroll Rect Events: 事件触发器当滚动发生时可以触发脚本中的方法

水平布局组件

水平布局组Horizontal Layout Group用于将子物体水平排列每个子物体之间的间距是均匀的并且可以根据设置自动调整位置

  • Type: 布局类型这里是 Horizontal
  • Child Alignment: 子物体的对齐方式可以选择左对齐居中或右对齐
  • Padding: 布局的边缘填充可以设置上下左右四个方向的填充距离
  • Spacing: 子物体之间的间距

垂直布局组件

垂直布局组Vertical Layout Group用于将子物体垂直排列每个子物体之间的间距是均匀的并且可以根据设置自动调整位置

  • Type: 布局类型这里是 Vertical
  • Child Alignment: 子物体的对齐方式可以选择顶对齐居中或底对齐
  • Padding: 布局的边缘填充可以设置上下左右四个方向的填充距离
  • Spacing: 子物体之间的间距

网格布局组件

网格布局组Grid Layout Group用于将子物体按照网格的形式排列每个子物体占据网格中的一个单元格并且可以根据设置自动调整位置

  • Type: 布局类型这里是 Grid
  • Constraint: 网格的约束类型可以选择固定列数Fixed Column Count或固定行数Fixed Row Count
  • Constraint Count: 网格的列数或行数取决于 Constraint 属性
  • Cell Size: 单元格的大小可以分别设置宽度和高度
  • Padding: 布局的边缘填充可以设置上下左右四个方向的填充距离
  • Start Axis: 开始轴可以选择从左到右Horizontal或从上到下Vertical
  • Start Corner: 开始角可以选择左上角Top Left右上角Top Right左下角Bottom Left或右下角Bottom Right
  • Child Control Width: 如果勾选表示子物体的宽度将受网格单元格宽度的控制
  • Child Control Height: 如果勾选表示子物体的高度将受网格单元格高度的控制

自动扩容组件

自动扩容组件Content Size Fitter它可以根据其子物体的大小自动调整自身的大小这对于实现滚动视图的自动扩容非常有用尤其是在处理不确定数量的 UI 元素时

  • Fit on X: 如果勾选表示组件将在水平方向上自动调整大小
  • Fit on Y: 如果勾选表示组件将在垂直方向上自动调整大小
  • Preferred Width: 如果 Fit on X 未勾选则使用这个宽度值作为组件的首选宽度
  • Preferred Height: 如果 Fit on Y 未勾选则使用这个高度值作为组件的首选高度

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ScrollRectExample : MonoBehaviour
{
public ScrollRect myScrollRect;

void Start()
{
// 注册事件监听器
myScrollRect.onValueChanged.AddListener(ScrollRectValueChanged);
}

void ScrollRectValueChanged(Vector2 value)
{
Debug.Log("ScrollRect value changed to: " + value);
// 执行滚动视图值变化时的逻辑
}
}

输入框

Input Field 组件允许用户输入文本

基本属性

  • Text - 这是输入框显示的文本内容可以在这里直接输入默认文本或通过脚本来改变其值
  • Character Limit - 设置用户可以输入的最大字符数如果设置为0则没有字符限制
  • Input Type - 选择输入类型
    • 标准 - 普通的文本输入
    • 数字与小数点 - 只能输入数字和小数点
    • 整数 - 只能输入整数
    • ASCII可打印字符 - 只能输入ASCII可打印字符
    • 密码 - 输入时会隐藏文本通常用于密码
    • 多行 - 允许输入多行文本
  • Line Input - 如果输入类型为多行输入可以选择单行还是多行输入
  • Placeholder - 当输入框为空时显示的提示文本
  • Read Only - 设置输入框是否只读

高级属性

  • Selection Color - 文本选中时的颜色
  • Cursor Color - 文本输入光标的颜色
  • Cursor Toggle Rate - 光标闪烁的速度
  • Submit On Enter - 是否在按下回车键时提交输入

事件回调

On Value Submit - 用户提交输入时触发的事件可以通过脚本来监听这个事件例如点击屏幕外或按Enter键

下拉框

下拉菜单Dropdown是一个常用的UI组件它允许用户从多个选项中选择一个值

基础设置

  • Options - 这是下拉列表中的选项列表可以在Inspector面板中添加或删除选项每个选项都是一个字符串
  • Value - 当前选中的选项索引从0开始计数可以在此处设置默认选项或者通过脚本在运行时修改
  • Clear On Deselect - 如果勾选了此选项当用户选择了一个新选项后将清除之前的选择

事件回调

On Value Changed - 当选项发生改变时触发的事件可以添加事件监听器来响应选项的变化这通常用来执行某些操作比如更改游戏状态或更新其他UI元素

创建步骤

  • 创建Canvas - 在Hierarchy视图中创建一个新的Canvas作为UI容器
  • 添加Dropdown - 在Canvas下创建一个Dropdown对象可以通过菜单GameObject > UI > Dropdown来创建
  • 配置Dropdown - 在Inspector面板中设置Dropdown的属性如添加选项等

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using UnityEngine;
using UnityEngine.UI;

public class ExampleScript : MonoBehaviour
{
public Dropdown dropdown;

void Start()
{
// 获取Dropdown的所有选项
string[] options = dropdown.options;

// 添加新选项到Dropdown
Dropdown.OptionData optionData = new Dropdown.OptionData("New Option");
dropdown.AddOptions(new List<Dropdown.OptionData>() { optionData });

// 设置Dropdown的默认选项
dropdown.value = 1; // 设置选项索引为1的选项为默认选项

// 监听Dropdown选项变化
dropdown.onValueChanged.AddListener(delegate { OnValueChanged(dropdown.value); });
}

void OnValueChanged(int value)
{
Debug.Log("Selected option index: " + value);
// 根据value做相应的处理
}
}

事件接口

UI系统提供了多种事件处理接口以支持各种用户交互方式以下是Unity UI系统中常用的事件接口及其用途以及每个接口的基本使用示例代码

IPointerEnterHandler

当鼠标光标进入UI元素时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class PointerEnterExample : MonoBehaviour, IPointerEnterHandler
{
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log("Pointer Enter detected on " + gameObject.name);
}
}

IPointerExitHandler

当鼠标光标离开UI元素时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class PointerExitExample : MonoBehaviour, IPointerExitHandler
{
public void OnPointerExit(PointerEventData eventData)
{
Debug.Log("Pointer Exit detected on " + gameObject.name);
}
}

IPointerDownHandler

当鼠标按钮按下或触摸开始时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class PointerDownExample : MonoBehaviour, IPointerDownHandler
{
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Pointer Down detected on " + gameObject.name);
}
}

IPointerUpHandler

当鼠标按钮释放或触摸结束时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class PointerUpExample : MonoBehaviour, IPointerUpHandler
{
public void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Pointer Up detected on " + gameObject.name);
}
}

IPointerClickHandler

当用户点击按下并释放UI元素时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class PointerClickExample : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("Pointer Click detected on " + gameObject.name);
}
}

IDragHandler

当用户拖拽UI元素时每帧调用

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using UnityEngine.EventSystems;

public class DragExample : MonoBehaviour, IDragHandler
{
public void OnDrag(PointerEventData eventData)
{
Vector2 delta = eventData.delta / 100f; // 根据需要调整缩放因子
transform.Translate(delta.x, delta.y, 0);
}
}

IBeginDragHandler

当用户开始拖拽UI元素时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class BeginDragExample : MonoBehaviour, IBeginDragHandler
{
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("Begin Drag detected on " + gameObject.name);
}
}

IEndDragHandler

当用户结束拖拽UI元素时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class EndDragExample : MonoBehaviour, IEndDragHandler
{
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("End Drag detected on " + gameObject.name);
}
}

IScrollHandler

当用户滚动鼠标滚轮或触摸屏时调用

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using UnityEngine.EventSystems;

public class ScrollExample : MonoBehaviour, IScrollHandler
{
public void OnScroll(PointerEventData eventData)
{
float scrollDelta = eventData.scrollDelta.y;
Debug.Log("Scroll detected: " + scrollDelta);
}
}

ISelectHandler

当UI元素被选中时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class SelectExample : MonoBehaviour, ISelectHandler
{
public void OnSelect(BaseEventData eventData)
{
Debug.Log("Select detected on " + gameObject.name);
}
}

IDeselectHandler

当UI元素被取消选择时调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class DeselectExample : MonoBehaviour, IDeselectHandler
{
public void OnDeselect(BaseEventData eventData)
{
Debug.Log("Deselect detected on " + gameObject.name);
}
}

IUpdateSelectedHandler

当UI元素处于选中状态时每帧调用

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class UpdateSelectedExample : MonoBehaviour, IUpdateSelectedHandler
{
public void OnUpdateSelected(BaseEventData eventData)
{
Debug.Log("Update Selected detected on " + gameObject.name);
}
}

ISubmitHandler

当UI元素被提交时调用例如通过按下回车键或确认按钮

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class SubmitExample : MonoBehaviour, ISubmitHandler
{
public void OnSubmit(BaseEventData eventData)
{
Debug.Log("Submit detected on " + gameObject.name);
}
}

IInitializePotentialDragHandler

当用户按下鼠标或触摸屏幕但尚未移动时调用用于确定是否开始拖拽

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using UnityEngine.EventSystems;

public class InitializePotentialDragExample : MonoBehaviour, IInitializePotentialDragHandler
{
public void OnInitializePotentialDrag(PointerEventData eventData)
{
Debug.Log("Initialize Potential Drag detected on " + gameObject.name);
}
}

可视化着色器

Shader Graph 是 Unity 编辑器中的一个工具它允许开发者通过可视化编程的方式创建着色器Shader Graph 在 Unity 2019.1 版本中引入并且与传统的 Shader 脚本编写相比提供了一种更为直观的方式来构建复杂的渲染效果

安装和创建

  1. 在 Unity 编辑器的菜单栏中选择 Window > Package Manager打开 Package Manager 窗口在这里你可以看到所有已安装的包查找 Shader Graph 是否已经在列
  2. 如果 Shader Graph 没有出现在你的 Package Manager 中或者显示为可安装状态请点击+按钮旁边的Browse打开 Unity 包管理器的界面在搜索框中输入Shader Graph找到 Shader Graph 后点击安装
  3. 创建 Shader Graph 文件 Assets > Create > Shader Graph > Builtln > Lit Shader Graph

导航栏介绍

Blackboard

Blackboard 是 Shader Graph 中的一个重要组成部分它提供了当前 Shader Graph 的全局变量列表这些变量可以在 Shader Graph 中被引用并且可以在材质面板中进行编辑

Graph Inspector

Graph Inspector 提供了有关当前 Shader Graph 的详细信息包括但不限于 Graph 的类型内部结构输入和输出端口等

属性用途描述
Material 这里指的是 Shader Graph 本身所关联的材质 在 Unity Shader Graph 中Material 通常指的是当前 Shader Graph 应用到的材质球当你在 Shader Graph 中进行编辑时所做的更改会直接影响到关联的材质你可以在 Unity 编辑器中选择一个材质球并通过右键菜单选择 Create > Shader > Shader Graph 来创建一个新的 Shader Graph这个 Shader Graph 将会与当前选定的材质球相关联
Allow Material Override 这个选项控制着是否允许在运行时对材质进行覆盖 当勾选了 Allow Material Override意味着在运行时可以通过脚本来动态地替换当前材质球这在某些特定的应用场景下非常有用例如在游戏开发中可能需要根据游戏的状态动态地改变材质属性或者在调试过程中使用不同的材质进行测试
Surface Type 这个选项决定了 Shader Graph 的渲染模式 Opaque不透明模式是最常用的模式适用于完全不透明的对象
Cutout剪切模式适用于有明确边缘的半透明物体如树叶只有一部分像素会被渲染
Fade渐变模式适用于渐隐渐现的半透明效果如烟雾
Transparent透明模式适用于一般的透明物体如水或玻璃渲染顺序会影响最终结果
Render Face 决定了着色器应用于哪些面 Front仅渲染正面
Back仅渲染背面
Both渲染前后两面
Depth Write 决定了着色器是否写入深度缓冲区 如果开启则着色器可以更新深度缓冲区如果关闭则不会更新这意味着物体可能会出现穿透现象
Depth Test 决定了着色器如何与深度缓冲区进行比较 Always总是通过测试
Equal当前像素与深度缓冲区中的值相等时通过测试
Less当前像素小于深度缓冲区中的值时通过测试
LessEqual当前像素小于等于深度缓冲区中的值时通过测试
Greater当前像素大于深度缓冲区中的值时通过测试
NotEqual当前像素与深度缓冲区中的值不等时通过测试
Never从不通过测试
Alpha Clipping 用于剪切Clipping操作通常与 Cutout 模式一起使用 Alpha Clipping 设置了一个阈值当像素的 Alpha 值低于这个阈值时该像素将被丢弃
Fragment Normal Space 决定了法线空间 Tangent切线空间
Object对象空间
World世界空间
Camera摄像机空间
Custom Editor GUI 允许用户自定义 Shader Graph 编辑器的界面元素 这是一个高级功能允许开发者编写自己的 GUI 来扩展 Shader Graph 编辑器的功能这通常涉及到脚本编写并不是所有用户都会用到

Main Preview

Main Preview 是 Shader Graph 中的一个预览窗口它显示了当前 Shader Graph 应用到一个标准几何体通常是球体上的效果

节点介绍

Artistic 分类

Artistic 分类下的节点主要用于提供艺术家友好的功能使得非程序员也可以轻松地创造各种视觉效果

⭐⭐ Adjustment 分类

Adjustment 分类下的节点主要用于调整颜色和图像属性如混合通道调整对比度调整色调颜色反转替换颜色调整饱和度和调整白平衡等这些节点可以用来增强或修改图像的颜色表现从而在着色器中实现更加精细的颜色控制

  • Channel Mixer
    • 功能混合颜色通道
    • 描述此节点允许用户调整RGB颜色通道的贡献比例可以用来改变颜色的组成例如增加红色通道的权重减少绿色通道的权重等
  • Contrast
    • 功能调整对比度
    • 描述此节点通过增加或减少图像中颜色的对比度来增强或减弱图像的亮度差异提高对比度会使暗部更暗亮部更亮
  • Hue
    • 功能调整色调
    • 描述此节点允许用户调整颜色的色调分量色调是指颜色的基本颜色特性例如红色绿色蓝色等调整色调可以改变图像的整体颜色偏向
  • Invert Colors
    • 功能反转颜色
    • 描述此节点反转输入颜色的每个通道可以用来实现类似负片的效果
  • Replace Color
    • 功能替换颜色
    • 描述此节点允许用户将一个颜色替换为另一个颜色可以用来改变图像中的特定颜色
  • Saturation
    • 功能调整饱和度
    • 描述此节点增加或减少颜色的饱和度即颜色的纯度增加饱和度可以使颜色更加鲜艳减少饱和度可以使颜色趋向灰色
  • White Balance
    • 功能调整白平衡
    • 描述此节点用于调整图像的白平衡即调整红绿蓝三色的比例以改变图像的整体色调这通常用于校正光源引起的色彩偏差

⭐⭐ Blend 分类

Blend 分类下的节点主要用于混合两种或多种颜色或纹理这些节点可以帮助你在着色器中实现不同的混合效果比如透明度混合颜色叠加等尽管Blend分类下的节点可能相对简单但它们在处理多层纹理透明对象以及其他需要混合技术的场景时非常有用

⭐⭐ Filter 分类

Filter 分类下的节点主要用于执行图像或颜色过滤操作这些操作可以帮助开发者实现诸如抖动Dithering渐变过渡Fade Transition等效果这些效果可以用来改善图像质量或是实现某种视觉效果

  • Dither
    • 功能抖动Dithering
    • 描述此节点通过引入细微的噪声来模拟颜色渐变以减少因颜色量化导致的伪影抖动可以用来减少颜色带状现象尤其是在处理透明度渐变或低分辨率图像时
  • Fade Transition
    • 功能渐变过渡
    • 描述此节点用于实现两个颜色或图像之间的平滑过渡通常用于实现渐隐渐显效果或者在两个不同状态之间进行平滑转换

⭐⭐ Mask 分类

Mask 分类下的节点主要用于创建遮罩这些遮罩可以用来选择性地影响某些像素以便在渲染过程中应用特定的效果或调整

  • Channel Mask
    • 功能通道遮罩
    • 描述此节点允许用户选择特定的颜色通道R/G/B/A作为遮罩可以用来提取纹理中的单个颜色通道信息例如可以用来提取一个纹理中的红色通道信息作为遮罩
  • Color Mask
    • 功能颜色遮罩
    • 描述此节点允许用户基于颜色范围来创建遮罩可以用来选择图像中特定颜色的区域然后对这些区域进行特定的操作例如可以用来创建一个遮罩来只影响图像中的红色部分

⭐⭐ Normal 分类

Normal 分类下的节点主要用于处理法线信息这些信息对于实现光照表面细节凹凸效果等非常重要

  • Normal Blend
    • 功能法线混合
    • 描述此节点用于混合两个法线信息通常用于将多个法线贴图的效果结合起来例如在同一个模型上同时应用两个不同细节级别的法线贴图
  • Normal From Height
    • 功能从高度图生成法线
    • 描述此节点从一个高度图生成法线信息高度图是一种灰度图其中每个像素的灰度值表示该点的高度通过高度图可以计算出表面的凹凸感
  • Normal From Texture
    • 功能从纹理生成法线
    • 描述此节点直接从一个包含法线信息的纹理中读取法线数据通常用于应用预先准备好的法线贴图来模拟表面细节
  • Normal Reconstruct Z
    • 功能重建Z轴法线
    • 描述此节点用于从一个二维法线向量通常存储在法线贴图中中重建完整的三维法线向量这通常是因为法线贴图只存储了xy分量而z分量可以通过xy计算得出
  • Normal Strength
    • 功能调整法线强度
    • 描述此节点用于调整法线信息的强度可以用来增强或减弱表面细节的表现力
  • Normal Unpack
    • 功能解包法线
    • 描述此节点用于将压缩存储的法线信息解包成原始格式通常法线信息会以一种压缩的形式存储在纹理中这个节点负责将其还原

⭐⭐ Utility 分类

Utility 分类下的节点主要用于提供一些辅助性的功能这些功能可以帮助开发者更好地处理颜色空间转换等问题

  • Colorspace Conversion
    • 功能颜色空间转换
    • 描述此节点用于在不同的颜色空间之间转换颜色或纹理颜色空间是指用于描述颜色的方式常见的有sRGBLinearHSV等在处理光照合成等场景时颜色空间的转换尤为重要

Channel 分类

Channel 分类下的节点主要用于处理颜色通道这些节点可以帮助你合并翻转拆分和重新排列颜色通道

  • Combine
    • 功能合并颜色通道
    • 描述此节点用于将多个颜色通道的信息合并成一个颜色例如可以将一个纹理的红色通道与另一个纹理的绿色通道合并起来
  • Flip
    • 功能翻转颜色通道
    • 描述此节点用于交换颜色通道例如可以将RGB颜色通道变为GBR
  • Split
    • 功能拆分颜色通道
    • 描述此节点用于将一个颜色分解成其各个颜色通道例如可以从一个颜色中提取出红色绿色和蓝色分量
  • Swizzle
    • 功能重新排列颜色通道
    • 描述此节点用于根据指定的顺序重新排列颜色通道例如可以选择一个颜色中的特定通道并按照新的顺序输出

Custom Render Texture 分类

Custom Render Texture 分类下的节点主要用于处理自定义渲染纹理Custom Render Texture这些节点允许你在着色器中访问并操作这些纹理

  • Self
    • 功能获取当前渲染纹理
    • 描述此节点用于获取当前正在处理的渲染纹理它可以用来访问当前帧的像素数据以便进行进一步的处理
  • Size
    • 功能获取渲染纹理尺寸
    • 描述此节点用于获取当前渲染纹理的宽度和高度可以用来确定纹理的大小以便在处理时进行正确的坐标映射或缩放
  • Slice Index/Cubemap Face
    • 功能获取切片索引/立方体贴图面
    • 描述此节点用于获取当前渲染纹理的切片索引对于体积纹理或立方体贴图的当前面在处理体积纹理或立方体贴图时这个信息非常重要因为它指定了正在处理的具体切片或面

Input 分类

Input 分类下的节点主要用于获取片段着色器的各种输入数据这些输入数据可以包括但不限于顶点颜色UV坐标时间等

⭐⭐ 2D 分类

2D 分类下的节点主要用于处理2D图像和纹理

  • Light Texture
    • 功能处理光照纹理
    • 描述此节点用于获取光照纹理LightTexture的信息并将其应用于着色器中以实现光照效果光照纹理通常包含了光照方向颜色等信息可以用来模拟光源对物体的影响

⭐⭐ Basic 分类

Basic 分类下的节点主要用于提供基本的数据类型和常量

  • Boolean
    • 功能布尔值
    • 描述此节点用于提供布尔值true 或 false可以用来控制条件分支或开关
  • Color
    • 功能颜色值
    • 描述此节点用于提供一个颜色值通常用于设置着色器中的颜色如材质的基本颜色Base Color
  • Constant
    • 功能常量
    • 描述此节点用于提供一个固定的数值可以是任何数据类型如浮点数整数向量等
  • Float
    • 功能浮点数
    • 描述此节点用于提供一个浮点数值常用于控制光照强度颜色强度等
  • Integer
    • 功能整数
    • 描述此节点用于提供一个整数值常用于索引选择或其他需要整数值的场合
  • Slider
    • 功能滑块
    • 描述此节点用于提供一个可以通过滑块调节的数值方便在编辑器中调整参数
  • Time
    • 功能时间
    • 描述此节点用于提供当前游戏时间的信息如时间戳delta time等常用于动画或随时间变化的效果
  • Vector 2
    • 功能二维向量
    • 描述此节点用于提供一个二维向量常用于纹理坐标UV位移等
  • Vector 3
    • 功能三维向量
    • 描述此节点用于提供一个三维向量常用于表示位置方向法线等
  • Vector 4
    • 功能四维向量
    • 描述此节点用于提供一个四维向量常用于表示颜色RGBA齐次坐标等

⭐⭐ Geometry 分类

Geometry 分类下的节点主要用于提供几何信息这些信息对于处理光照纹理映射和其他基于位置的效果非常重要

  • Bitangent Vector
    • 功能双切线向量
    • 描述此节点用于获取双切线向量通常用于法线贴图处理帮助计算正确的光照效果
  • Instance ID
    • 功能实例ID
    • 描述此节点用于获取网格实例的ID可以用来区分场景中的多个相同网格实例
  • Normal Vector
    • 功能法线向量
    • 描述此节点用于获取表面法线向量用于光照计算和其他基于表面方向的效果
  • Position
    • 功能位置
    • 描述此节点用于获取顶点的位置可以用于计算光照阴影等效果
  • Screen Position
    • 功能屏幕位置
    • 描述此节点用于获取像素在屏幕上的位置可用于实现屏幕空间效果如屏幕空间反射或后处理效果
  • Tangent Vector
    • 功能切线向量
    • 描述此节点用于获取切线向量与双切线向量一起用于法线贴图处理帮助正确计算光照
  • UV
    • 功能纹理坐标
    • 描述此节点用于获取纹理坐标UV坐标用于将纹理映射到表面上
  • Vertex Color
    • 功能顶点颜色
    • 描述此节点用于获取顶点的颜色信息可以用来在顶点级别应用颜色
  • Vertex ID
    • 功能顶点ID
    • 描述此节点用于获取顶点的ID可以用来区分网格中的不同顶点
  • View Direction
    • 功能视图方向
    • 描述此节点用于获取从摄像机到表面点的方向向量用于计算光照效果如高光
  • View Vector
    • 功能视图向量
    • 描述此节点用于获取从摄像机到表面点的向量可用于计算光照和反射效果

⭐⭐ Gradient 分类

Gradient 分类下的节点主要用于处理梯度颜色这些节点可以帮助开发者生成和采样颜色梯度用于创建平滑的颜色过渡效果

  • Blackbody
    • 功能黑体辐射颜色
    • 描述此节点用于生成黑体辐射的颜色梯度根据温度值来决定颜色黑体辐射模型可以用来模拟光源的颜色变化如太阳光或白炽灯
  • Gradient
    • 功能颜色梯度
    • 描述此节点用于定义一个颜色梯度可以通过设置多个颜色键来创建一个平滑的颜色过渡可以用来生成渐变背景环境光晕等效果
  • Sample Gradient
    • 功能采样颜色梯度
    • 描述此节点用于从一个已经定义好的颜色梯度中采样颜色通过提供一个0到1之间的值可以获取对应位置的颜色

⭐⭐ Lighting 分类

Lighting 分类下的节点主要用于处理光照相关的功能这些节点可以帮助开发者实现不同的光照效果如环境光烘焙光主光源方向以及反射探针效果

  • Ambient
    • 功能环境光
    • 描述此节点用于获取场景中的环境光信息环境光是一种全局照明即使没有其他光源存在环境光也会照亮场景中的物体
  • Baked GI
    • 功能烘焙光照
    • 描述此节点用于获取烘焙光照信息烘焙光照是在构建场景时预计算的光照信息可以用来提高实时渲染的性能
  • Main Light Direction
    • 功能主光源方向
    • 描述此节点用于获取主光源的方向信息主光源通常是场景中最亮的光源如太阳或主要的灯光
  • Reflection Probe
    • 功能反射探针
    • 描述此节点用于获取反射探针的信息反射探针可以在场景中捕捉环境光和反射效果用于实时渲染中模拟物体周围的环境光照

⭐⭐ Matrix 分类

Matrix 分类下的节点主要用于处理矩阵运算这些矩阵可以用来进行坐标变换旋转缩放等各种操作

  • Matrix 2x2
    • 功能2x2 矩阵
    • 描述此节点用于提供一个 2x2 的矩阵通常用于处理二维图形的变换如旋转和平移
  • Matrix 3x3
    • 功能3x3 矩阵
    • 描述此节点用于提供一个 3x3 的矩阵通常用于处理三维图形的变换如旋转缩放和平移
  • Matrix 4x4
    • 功能4x4 矩阵
    • 描述此节点用于提供一个 4x4 的矩阵通常用于处理三维图形的齐次坐标变换可以包含平移旋转缩放等操作
  • Transformation Matrix
    • 功能变换矩阵
    • 描述此节点用于获取变换矩阵可以是世界矩阵视图矩阵投影矩阵等用于将坐标从一个空间变换到另一个空间

⭐⭐ Mesh Deformation 分类

Mesh Deformation 分类下的节点主要用于处理网格变形这些节点可以帮助开发者实现网格的动态变形效果如骨骼绑定权重混合等

  • Compute Deformation
    • 功能计算变形
    • 描述此节点用于计算网格变形通常用于实现高级的网格变形效果它可能涉及到复杂的计算过程如非线性变形基于物理的变形等
  • Linear Blend Skinning
    • 功能线性混合蒙皮
    • 描述此节点用于实现骨骼动画中的线性混合蒙皮技术即根据骨骼的权重混合多个骨骼的变换来实现网格的变形

⭐⭐ PBR 分类

PBRPhysically Based Rendering 物理基渲染分类下的节点主要用于实现基于物理特性的材质渲染这些节点可以帮助开发者实现更真实的材质表现

  • Dielectric Specular
    • 功能介电质高光
    • 描述此节点用于计算介电质材料如玻璃水等的高光效果介电质材料具有特定的光学特性其反射和折射行为不同于金属或哑光表面该节点通常用于实现菲涅耳效应Fresnel effect即随着视角的变化反射率发生变化的现象
  • Metal Reflectance
    • 功能金属反射率
    • 描述此节点用于计算金属材质的反射率金属材质具有较高的反射率特别是在某些角度下会表现出强烈的镜面反射效果该节点通常用于实现金属材质的真实感通过控制金属表面的反射强度来模拟不同金属的外观

⭐⭐ Scene 分类

Scene 分类下的节点主要用于处理场景中的信息这些信息可以用于实现各种特效如与摄像机相关的操作雾效深度信息等

  • Camera
    • 功能摄像机信息
    • 描述此节点用于获取当前摄像机的信息如摄像机的位置方向等
  • Eye Index
    • 功能眼睛索引
    • 描述此节点用于获取眼睛索引在立体渲染或多视图渲染中很有用用于区分左眼和右眼的渲染
  • Fog
    • 功能雾效
    • 描述此节点用于获取当前场景中的雾效信息可以用来在着色器中实现雾化效果
  • Object
    • 功能对象信息
    • 描述此节点用于获取当前对象的信息如对象的局部坐标世界坐标等
  • Scene Color
    • 功能场景颜色
    • 描述此节点用于获取场景的颜色信息可以用来在着色器中实现与场景颜色相关的特效
  • Scene Depth
    • 功能场景深度
    • 描述此节点用于获取当前像素在场景中的深度信息可以用来实现深度相关的特效如景深屏幕空间反射等
  • Scene Depth Difference
    • 功能场景深度差
    • 描述此节点用于获取当前像素与其周围像素的深度差异信息可以用来实现边缘检测屏幕空间阴影等特效
  • Screen
    • 功能屏幕信息
    • 描述此节点用于获取屏幕的信息如像素在屏幕上的位置等

⭐⭐ Texture 分类

Texture 分类下的节点主要用于处理纹理采样和相关操作这些节点可以帮助开发者实现不同类型的纹理效果如2D纹理立方体贴图3D纹理虚拟纹理等

  • Calculate Level Of Detail Texture 2D
    • 功能计算细节级别2D纹理
    • 描述此节点用于根据细节级别LOD计算并返回适当的2D纹理适用于需要根据不同细节级别显示不同纹理的情况
  • Cubemap Asset
    • 功能立方体贴图资源
    • 描述此节点用于引用一个立方体贴图资源可以用来实现环境光照或其他基于立方体贴图的效果
  • Gather Texture 2D
    • 功能聚集2D纹理
    • 描述此节点用于从2D纹理中采集多个样本点的数据并将结果合并成一个值常用于实现屏幕空间反射或抗锯齿等效果
  • Sample Cubemap
    • 功能采样立方体贴图
    • 描述此节点用于从立方体贴图中采样颜色信息通常用于实现环境光照反射等效果
  • Sample Reflected Cubemap
    • 功能采样反射立方体贴图
    • 描述此节点用于从立方体贴图中采样反射颜色信息通常用于实现反射效果
  • Sample Texture 2D
    • 功能采样2D纹理
    • 描述此节点用于从2D纹理中采样颜色信息是最常用的纹理采样节点之一
  • Sample Texture 2D Array
    • 功能采样2D数组纹理
    • 描述此节点用于从2D数组纹理中采样颜色信息适用于需要存储多个2D纹理的情况
  • Sample Texture 2D LOD
    • 功能根据细节级别采样2D纹理
    • 描述此节点用于根据细节级别LOD从2D纹理中采样颜色信息适用于需要根据不同细节级别显示不同纹理的情况
  • Sample Texture 3D
    • 功能采样3D纹理
    • 描述此节点用于从3D纹理中采样颜色信息适用于需要在三维空间内存储颜色数据的情况
  • Sample Virtual Texture
    • 功能采样虚拟纹理
    • 描述此节点用于从虚拟纹理中采样颜色信息虚拟纹理是一种大纹理分割成多个小纹理的技术可以减少内存占用
  • Sampler State
    • 功能采样器状态
    • 描述此节点用于设置纹理采样的状态如过滤模式地址模式等可以用来优化纹理采样性能
  • Split Texture Transform
    • 功能拆分纹理变换
    • 描述此节点用于拆分纹理变换矩阵提取出平移缩放和旋转等信息可以用来单独调整纹理的变换
  • Texture 2D Array Asset
    • 功能2D数组纹理资源
    • 描述此节点用于引用一个2D数组纹理资源适用于需要存储多个2D纹理的情况
  • Texture 2D Asset
    • 功能2D纹理资源
    • 描述此节点用于引用一个2D纹理资源是最常用的纹理引用节点之一
  • Texture 3D Asset
    • 功能3D纹理资源
    • 描述此节点用于引用一个3D纹理资源适用于需要在三维空间内存储颜色数据的情况
  • Texture Size
    • 功能纹理尺寸
    • 描述此节点用于获取纹理的尺寸信息可以用来根据纹理大小调整其他参数

⭐⭐ Universal 分类

Universal 分类下的节点主要用于处理通用的着色器功能这些功能通常与Unity的通用渲染管线URP相关

  • URP Sample Buffer
    • 功能采样URP缓冲区
    • 描述此节点用于从URP的缓冲区中采样数据它可以用来获取屏幕空间中的颜色深度等信息对于实现屏幕空间反射景深后处理效果等非常有用

Math 分类

Math 分类下的节点提供了多种数学运算功能可以帮助你执行基本的算术运算矢量操作逻辑判断以及其他复杂的数学计算

⭐⭐ Advanced 分类

Advanced 分类下的节点主要用于执行更复杂的数学运算这对于精确控制着色器中的颜色和光照计算非常重要

  • Absolute (Abs)
    • 功能计算绝对值
    • 描述此节点将输入值转换为其绝对值即如果输入是负数则转换为正数
  • Exponential (Exp)
    • 功能计算指数函数
    • 描述此节点计算e的输入次幂其中e是自然对数的底数约等于2.71828
  • Length
    • 功能计算向量长度
    • 描述此节点计算输入向量的欧几里得长度即向量的模
  • Log
    • 功能计算自然对数
    • 描述此节点计算输入值的自然对数即以e为底数的对数
  • Modulo (Mod)
    • 功能求模运算
    • 描述此节点计算输入值除以给定值后的余数常用于周期性操作
  • Negate
    • 功能取反
    • 描述此节点将输入值乘以-1即反转输入值的符号
  • Normalize
    • 功能归一化向量
    • 描述此节点将输入向量缩放为单位长度长度为1同时保持方向不变
  • Posterize
    • 功能减少颜色深度
    • 描述此节点减少输入颜色的位深度产生类似海报化的效果使得颜色分级更为明显
  • Reciprocal
    • 功能求倒数
    • 描述此节点计算输入值的倒数1 / 输入值
  • Reciprocal Square Root
    • 功能计算倒数平方根
    • 描述此节点计算输入值的倒数平方根1 / sqrt(输入值)在某些光照模型中用于快速近似归一化

⭐⭐ Basic 分类

Basic 分类下的节点主要用于执行基本的数学运算这些运算在着色器编程中非常常见

  • Add
    • 功能执行加法运算
    • 描述此节点将两个输入值相加并返回结果可以用于合并颜色增加亮度等
  • Divide
    • 功能执行除法运算
    • 描述此节点将第一个输入值除以第二个输入值并返回结果可以用于调整亮度颜色强度等
  • Multiply
    • 功能执行乘法运算
    • 描述此节点将两个输入值相乘并返回结果可以用于调整颜色强度混合颜色等
  • Power
    • 功能计算输入值的幂
    • 描述此节点将第一个输入值的每个分量提升到第二个输入值的指数并返回结果可以用于模拟光照的指数衰减等
  • Square Root
    • 功能计算输入值的平方根
    • 描述此节点计算输入值的平方根并返回结果在光照计算中常用也可以用于调整颜色强度
  • Subtract
    • 功能执行减法运算
    • 描述此节点将第二个输入值从第一个输入值中减去并返回结果可以用于颜色对比光照减淡等

⭐⭐ Derivative 分类

Derivative 分类下的节点主要用于计算导数即在片段着色器中计算像素值相对于屏幕空间位置的变化率这些导数可以用于实现诸如法线贴图屏幕空间反射SSR边缘检测等多种高级效果

  • DDX
    • 功能计算水平方向的导数
    • 描述此节点计算输入值在水平方向x轴上的变化率常用于计算法线贴图中的水平细节变化
  • DDY
    • 功能计算垂直方向的导数
    • 描述此节点计算输入值在垂直方向y轴上的变化率常用于计算法线贴图中的垂直细节变化
  • DDXY
    • 功能计算水平和垂直方向的导数
    • 描述此节点同时计算输入值在水平和垂直方向上的变化率常用于需要同时考虑两个方向变化的情况如屏幕空间反射或边缘检测

⭐⭐ Interpolation 分类

Interpolation 分类下的节点主要用于执行插值操作这些操作对于在不同值之间平滑过渡非常有用插值在许多场景中都有应用如动画渐变颜色光照计算等

  • Inverse Lerp
    • 功能计算值在两个边界值之间的相对位置
    • 描述此节点计算一个值在两个边界值之间的相对位置例如给定最小值 a 和最大值 b以及一个值 value它会返回 value 在 [a, b] 区间内的相对位置如果 value 等于 a则返回0如果 value 等于 b则返回1
  • Lerp
    • 功能线性插值
    • 描述此节点在线性范围内插值两个值它接收三个输入起始值 a结束值 b 以及一个 t 值0到1之间然后返回 a 到 b 之间的线性插值结果当 t 为0时结果为 a当 t 为1时结果为 b
  • Smoothstep
    • 功能平滑插值
    • 描述此节点执行平滑插值使得结果在 [0, 1] 区间内更加平滑它接收三个输入起始值 a结束值 b 以及一个 t 值0到1之间然后返回 a 到 b 之间的平滑插值结果当 t 为0时结果为 a当 t 为1时结果为 b但在 [0, 1] 区间内结果将变得更加平滑避免了线性插值可能产生的突然变化

⭐⭐ Matrix 分类

Matrix 分类下的节点主要用于处理矩阵操作这些操作在计算变换光照投影等方面非常有用

  • Matrix Construction
    • 功能构建矩阵
    • 描述此节点允许用户通过输入矩阵的行向量来构建一个矩阵它可以用于创建任意大小的矩阵通常用于构建变换矩阵如旋转缩放和平移矩阵
  • Matrix Determinant
    • 功能计算矩阵的行列式
    • 描述此节点计算输入矩阵的行列式行列式是一个标量值可以用于判断矩阵是否可逆只有方阵才有行列式
  • Matrix Split
    • 功能分解矩阵
    • 描述此节点将输入矩阵分解成其组成的行向量这对于提取矩阵中的特定信息如旋转角度缩放因子等非常有用
  • Matrix Transpose
    • 功能转置矩阵
    • 描述此节点计算输入矩阵的转置转置矩阵是指将原矩阵的行变成新矩阵的列原矩阵的列变成新矩阵的行

⭐⭐ Range 分类

Range 分类下的节点主要用于执行范围相关的操作如限制值的范围映射值到新的范围随机生成值等这些节点在创建着色器时非常有用尤其是在需要确保值处于特定区间或需要生成随机数的情况下

  • Clamp
    • 功能限制值的范围
    • 描述此节点将输入值限制在指定的最小值和最大值之间如果输入值小于最小值则返回最小值如果输入值大于最大值则返回最大值否则返回输入值本身
  • Fraction
    • 功能取小数部分
    • 描述此节点返回输入值的小数部分例如输入值为3.14则返回0.14
  • Maximum
    • 功能找到最大值
    • 描述此节点接收两个输入值并返回两者中的较大值
  • Minimum
    • 功能找到最小值
    • 描述此节点接收两个输入值并返回两者中的较小值
  • One Minus
    • 功能计算1减去输入值的结果
    • 描述此节点接收一个输入值并返回1减去该值的结果常用于颜色反转等操作
  • Random Range
    • 功能生成指定范围内的随机数
    • 描述此节点生成一个介于指定最小值和最大值之间的随机数可以用于创建随机效果
  • Remap
    • 功能映射值到新的范围
    • 描述此节点将输入值从一个范围映射到另一个范围例如可以将[0, 1]范围内的值映射到[0, 255]范围
  • Saturate
    • 功能饱和限制在0到1之间
    • 描述此节点将输入值限制在0到1之间如果输入值小于0则返回0如果输入值大于1则返回1否则返回输入值本身

⭐⭐ Round 分类

Round 分类下的节点主要用于执行数值的舍入操作如向上取整向下取整四舍五入等这些节点在处理需要精确控制数值的场景下非常有用特别是在需要确保数值符合特定格式或进行数值简化的情况下

  • Ceiling
    • 功能向上取整
    • 描述此节点将输入值向上取整到最接近的整数例如输入值为3.1则返回4
  • Floor
    • 功能向下取整
    • 描述此节点将输入值向下取整到最接近的整数例如输入值为3.9则返回3
  • Round
    • 功能四舍五入
    • 描述此节点将输入值四舍五入到最接近的整数例如输入值为3.5则返回4
  • Sign
    • 功能获取符号
    • 描述此节点返回输入值的符号如果输入值为正数则返回1如果输入值为零则返回0如果输入值为负数则返回-1
  • Step
    • 功能阶跃函数
    • 描述此节点接收两个输入值如果第一个输入值小于等于第二个输入值则返回1否则返回0常用于将连续值转换为二进制信号
  • Truncate
    • 功能截断
    • 描述此节点将输入值截断为整数部分去除小数部分例如输入值为3.7则返回3

⭐⭐ Trigonometry 分类

Trigonometry 分类下的节点主要用于执行三角函数操作这些操作在处理与角度旋转相关的问题时非常有用三角函数可以用于多种用途如计算光照方向旋转物体生成波形等

  • Arccosine (ACos)
    • 功能计算反余弦函数
    • 描述此节点计算输入值的反余弦值即返回一个角度θ使得cos(θ) = 输入值结果通常在 [0, π] 范围内
  • Arcsine (ASin)
    • 功能计算反正弦函数
    • 描述此节点计算输入值的反正弦值即返回一个角度θ使得sin(θ) = 输入值结果通常在 [-π/2, π/2] 范围内
  • Arctangent (ATan)
    • 功能计算反正切函数
    • 描述此节点计算输入值的反正切值即返回一个角度θ使得tan(θ) = 输入值结果通常在 [-π/2, π/2] 范围内
  • Arctangent2 (ATan2)
    • 功能计算两数的反正切函数
    • 描述此节点计算两个输入值 y 和 x 的反正切值即返回一个角度θ使得tan(θ) = y/x结果通常在 [-π, π] 范围内并且能正确区分象限
  • Cosine (Cos)
    • 功能计算余弦函数
    • 描述此节点计算输入角度的余弦值输入可以是弧度或度数具体取决于上下文
  • Degrees To Radians (DegToRad)
    • 功能将角度从度转换为弧度
    • 描述此节点将输入的角度值从度转换为弧度通常用于将角度值转换为适合三角函数使用的弧度形式
  • Hyperbolic Cosine (Cosh)
    • 功能计算双曲余弦函数
    • 描述此节点计算输入值的双曲余弦值即cosh(x) = (exp(x) + exp(-x)) / 2
  • Hyperbolic Sine (Sinh)
    • 功能计算双曲正弦函数
    • 描述此节点计算输入值的双曲正弦值即sinh(x) = (exp(x) - exp(-x)) / 2
  • Hyperbolic Tangent (Tanh)
    • 功能计算双曲正切函数
    • 描述此节点计算输入值的双曲正切值即tanh(x) = sinh(x) / cosh(x)
  • Radians To Degrees (RadToDeg)
    • 功能将角度从弧度转换为度
    • 描述此节点将输入的角度值从弧度转换为度通常用于将三角函数的结果转换为易于理解的度数形式
  • Sine (Sin)
    • 功能计算正弦函数
    • 描述此节点计算输入角度的正弦值输入可以是弧度或度数具体取决于上下文
  • Tangent (Tan)
    • 功能计算正切函数
    • 描述此节点计算输入角度的正切值输入可以是弧度或度数具体取决于上下文

⭐⭐ Vector 分类

Vector 分类下的节点主要用于执行向量操作这些操作在处理光照反射折射旋转等场景中非常有用

  • Cross Product (Cross)
    • 功能计算向量叉积
    • 描述此节点计算两个三维向量的叉积得到的结果是一个新的三维向量该向量垂直于输入的两个向量
  • Distance
    • 功能计算两点之间的距离
    • 描述此节点计算两个点之间的欧几里得距离
  • Dot Product (Dot)
    • 功能计算向量点积
    • 描述此节点计算两个向量的点积结果是一个标量值表示两个向量的方向关系
  • Fresnel Effect
    • 功能计算菲涅耳效应
    • 描述此节点计算菲涅耳效应用于模拟材料边缘的高光效果常用于水金属等材质
  • Projection
    • 功能计算向量投影
    • 描述此节点计算一个向量在另一个向量上的投影
  • Reflection
    • 功能计算反射向量
    • 描述此节点计算一个向量关于平面的反射向量常用于模拟光线反射
  • Refract
    • 功能计算折射向量
    • 描述此节点计算一个向量经过介质界面时的折射向量常用于模拟透明材质的折射效果
  • Rejection
    • 功能计算向量拒斥
    • 描述此节点计算一个向量在另一个向量方向上的拒斥向量即垂直于投影的部分
  • Rotate About Axis
    • 功能绕轴旋转向量
    • 描述此节点计算一个向量绕指定轴旋转一定角度后的结果向量
  • Sphere Mask
    • 功能生成球形遮罩
    • 描述此节点生成一个球形遮罩可以根据输入的位置和半径来确定遮罩区域
  • Transform
    • 功能变换向量
    • 描述此节点用于变换向量支持多种变换模式如从世界空间到视图空间从局部空间到世界空间等

⭐⭐ Wave 分类

Wave 分类下的节点主要用于生成不同类型的波形这些波形可以用来模拟各种自然现象如水波风声音频信号等

  • Noise Sine Wave
    • 功能生成带有噪声的正弦波
    • 描述此节点生成一个正弦波形并在其基础上添加随机噪声使其看起来更加自然和不规则
  • Sawtooth Wave
    • 功能生成锯齿波
    • 描述此节点生成一个锯齿波形波形的上升沿是斜线下降沿是直线
  • Square Wave
    • 功能生成方波
    • 描述此节点生成一个方波形波形在两个电平之间切换上升沿和下降沿是垂直的
  • Triangle Wave
    • 功能生成三角波
    • 描述此节点生成一个三角波形波形的上升沿和下降沿都是斜线形成一个三角形

Procedural 分类

Procedural 分类下的节点主要用于生成基于算法的图案和效果而不依赖于外部纹理这些节点可以帮助你在着色器中创建各种程序化的图案从而实现复杂的视觉效果这些节点可以用于创建各种程序化的图案和效果如噪声条纹圆环等

  • Checkerboard
    • 功能生成棋盘格图案
    • 描述此节点生成一个黑白相间的棋盘格图案可以通过调整UV坐标来改变棋盘格的大小和位置

⭐⭐ Noise 分类

Noise 分类下的节点主要用于生成噪声图案

  • Gradient Noise
    • 功能生成梯度噪声
    • 描述此节点生成一种基于梯度的噪声图案通常用于创建更加平滑的过渡效果梯度噪声在模拟自然现象如云朵地形等时非常有用
  • Simple Noise
    • 功能生成基本的噪声图案
    • 描述此节点生成一种基本的噪声图案通常用于模拟粗糙表面或创建随机效果
  • Voronoi
    • 功能生成Voronoi图案
    • 描述此节点生成基于Voronoi图的图案通常用于模拟蜂窝结构或创建具有几何美感的效果

⭐⭐ Shape 分类

Shape 分类下的节点则用于生成各种几何形状

  • Ellipse
    • 功能生成椭圆形状
    • 描述此节点生成一个椭圆形状可以通过调整UV坐标来改变椭圆的大小和位置适用于创建圆形或椭圆形的图案
  • Polygon
    • 功能生成多边形形状
    • 描述此节点生成一个多边形形状可以通过调整UV坐标来改变多边形的大小和位置适用于创建各种多边形图案
  • Rectangle
    • 功能生成矩形形状
    • 描述此节点生成一个矩形形状可以通过调整UV坐标来改变矩形的大小和位置适用于创建矩形图案
  • Rounded Polygon
    • 功能生成带圆角的多边形形状
    • 描述此节点生成一个带圆角的多边形形状可以通过调整UV坐标来改变多边形的大小和位置适用于创建带有圆角的多边形图案
  • Rounded Rectangle
    • 功能生成带圆角的矩形形状
    • 描述此节点生成一个带圆角的矩形形状可以通过调整UV坐标来改变矩形的大小和位置适用于创建带有圆角的矩形图案

SpeedTree 分类

SpeedTree 是一款专为游戏开发电影制作和可视化行业设计的专业级树木建模软件它允许用户创建逼真的树木模型并提供了丰富的工具集来定制树木的每一个方面SpeedTree 不仅可以用来创建单个树木模型还可以用来生成整个森林或者复杂的植被系统

  • SpeedTree8Billboard
    • 功能获取SpeedTree树叶的Billboard信息
    • 描述此节点返回SpeedTree树叶的Billboard信息用于在远距离观察时模拟树叶Billboard技术可以使树叶在远距离看起来更加密集减少绘制调用的数量
  • SpeedTree8ColorAlpha
    • 功能获取SpeedTree树叶的颜色和Alpha信息
    • 描述此节点返回SpeedTree树叶的颜色和Alpha值通常用于在着色器中进行颜色混合或其他颜色处理Alpha值可以用来实现透明效果
  • SpeedTree8InterpolatedNormals
    • 功能获取SpeedTree树叶的插值法线信息
    • 描述此节点返回SpeedTree树叶的插值法线信息用于模拟树叶表面的细节插值法线可以提高光照计算的真实感
  • SpeedTree8Wind
    • 功能获取SpeedTree树叶的风效信息
    • 描述此节点返回SpeedTree树叶受风影响的状态用于模拟风对树叶的影响可以用来创建动态的树叶效果

Utility 分类

Utility 分类下的节点主要用于提供各种实用工具这些工具可以帮助你进行颜色空间转换数据夹紧取模运算等操作这些节点通常在构建复杂着色器时起到辅助作用

  • Custom Function
    • 功能允许用户编写自定义的GLSL/HLSL代码
    • 描述此节点允许用户直接在Shader Graph中编写自定义的着色器代码片段这可以用来实现Shader Graph中尚未提供的功能或进行更复杂的计算
  • Preview
    • 功能提供Shader Graph的实时预览
    • 描述此节点用于实时预览当前Shader Graph的效果可以在编辑过程中即时查看着色器的变化

⭐⭐ Logic 分类

Logic 分类下的节点主要用于执行逻辑运算和条件判断这些节点可以帮助你在着色器中实现复杂的条件分支逻辑如根据某些条件选择不同的颜色或纹理或者执行逻辑运算来控制着色器的行为

  • All
    • 功能检查所有输入是否都为真
    • 描述此节点接收多个布尔输入并返回一个布尔值表示所有输入是否都为真如果所有输入都是真则返回真否则返回假
  • And
    • 功能执行逻辑与运算
    • 描述此节点接收两个布尔输入并返回一个布尔值表示两个输入是否都为真如果两个输入都是真则返回真否则返回假
  • Any
    • 功能检查是否有任一输入为真
    • 描述此节点接收多个布尔输入并返回一个布尔值表示是否有任一输入为真如果有任一输入为真则返回真否则返回假
  • Branch
    • 功能实现条件分支
    • 描述此节点根据输入的条件选择执行不同的路径它通常用于根据条件选择不同的颜色或纹理
  • Comparison
    • 功能执行数值比较
    • 描述此节点接收两个数值输入并根据选择的操作符如等于大于小于等返回一个布尔值
  • Is Front Face
    • 功能检查片段是否在面向摄像机的一侧
    • 描述此节点返回一个布尔值表示片段是否位于面向摄像机的一侧这对于双面渲染或实现某些特殊效果非常有用
  • Is Infinite
    • 功能检查输入值是否为无穷大
    • 描述此节点接收一个数值输入并返回一个布尔值表示输入值是否为无穷大+∞ 或 -∞
  • Is NaN
    • 功能检查输入值是否为NaN非数字
    • 描述此节点接收一个数值输入并返回一个布尔值表示输入值是否为NaN
  • Nand
    • 功能执行逻辑非与运算
    • 描述此节点接收两个布尔输入并返回一个布尔值表示两个输入是否都不为真如果两个输入都是真则返回假否则返回真
  • Not
    • 功能执行逻辑非运算
    • 描述此节点接收一个布尔输入并返回一个布尔值表示输入的逻辑非如果输入为真则返回假如果输入为假则返回真
  • Or
    • 功能执行逻辑或运算
    • 描述此节点接收两个布尔输入并返回一个布尔值表示两个输入中至少有一个为真如果两个输入中至少有一个为真则返回真否则返回假

UV 分类

UV 分类下的节点主要用于处理UV坐标这些坐标用于从纹理中采样颜色或其他信息UV坐标指的是纹理坐标用于定位纹理中的像素

  • Flipbook
    • 功能用于创建翻页书Flipbook效果
    • 描述此节点可以将UV坐标映射到一个更大的纹理区域使得每个片段可以采样不同的子纹理通常用于动画序列
  • Parallax Mapping
    • 功能实现视差贴图效果
    • 描述此节点通过调整UV坐标来模拟深度增强表面细节的真实感视差贴图通常用于模拟地形或具有深度变化的表面
  • Parallax Occlusion Mapping (POM)
    • 功能实现视差遮挡贴图效果
    • 描述此节点不仅调整UV坐标来模拟深度还考虑了遮挡效果可以更真实地模拟凸起或凹陷的表面细节
  • Polar Coordinates
    • 功能将UV坐标转换为极坐标系
    • 描述此节点将标准UV坐标转换为极坐标系用于创建圆形或旋转对称的纹理效果
  • Radial Shear
    • 功能创建径向扭曲效果
    • 描述此节点通过调整UV坐标来创建径向拉伸或挤压的效果适用于创建漩涡或放射状的纹理效果
  • Rotate
    • 功能旋转UV坐标
    • 描述此节点允许你旋转UV坐标从而改变纹理的方向这对于调整纹理的方向非常有用
  • Spherize
    • 功能将UV坐标映射到球面上
    • 描述此节点将UV坐标映射到一个球面上通常用于创建球形或球面投影的纹理效果
  • Tiling And Offset
    • 功能控制UV坐标的平铺和偏移
    • 描述此节点允许你调整UV坐标的平铺重复次数和偏移起始位置这可以用来控制纹理在物体表面上的重复次数和初始位置
  • Triplanar
    • 功能实现三平面映射效果
    • 描述此节点在三维空间中使用三个平面映射来避免UV接缝问题适用于没有UV贴图的复杂几何体如岩石或不规则形状的物体
  • Twirl
    • 功能创建旋涡效果
    • 描述此节点通过调整UV坐标来创建旋涡效果适用于创建旋涡状的纹理或动画效果

网络通信

Socket

在网络编程中Socket 是一种通信模型允许程序之间通过网络进行双向数据交换在 Unity 中使用 Socket 可以实现客户端与服务器之间的通信这对于多人在线游戏尤为重要Unity 可以利用 C# 的 Socket 类来进行 TCP 或 UDP 的通信

基本概念

  • Socket在计算机网络中Socket 是一对端点之间的通信链路两端之一用于在网络中进行消息传递Socket 可以是 TCP传输控制协议或 UDP用户数据报协议
  • TCP (Transmission Control Protocol)是一种面向连接的可靠的基于字节流的传输层通信协议
  • UDP (User Datagram Protocol)是一种无连接的协议它发送数据之前不需要建立连接因此也就不需要维护连接状态

实现步骤

  • 初始化客户端/服务器设置端口号并准备接收数据
  • 接收数据使用 BeginReceive 方法启动异步接收流程当数据到达时会调用 ReceiveCallback 方法处理数据
  • 处理数据解析接收到的数据并打印出来然后向客户端回应
  • 发送数据客户端构造要发送的数据并将其发送到指定的服务器地址
  • 关闭客户端/服务器在应用退出时关闭客户端或服务器释放资源

TCP 示例

💗💗 TCP 服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System.Threading;

public class TCPServer : MonoBehaviour
{
private Socket serverSocket;
private bool isListening = false;

void Start()
{
// 创建一个 Socket 对象
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.TCP);

// 设置服务器监听端口
int port = 8888;
IPEndPoint serverEP = new IPEndPoint(IPAddress.Any, port);

// 绑定到端口并开始监听
serverSocket.Bind(serverEP);
serverSocket.Listen(5); // 最大同时等待连接的数量

// 开始监听
Debug.Log("Server listening on port " + port);
ListenForClients();
}

// 开始监听客户端连接
private void ListenForClients()
{
if (!isListening)
{
isListening = true;
// 使用异步方式接受客户端连接
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), serverSocket);
}
}

// 接受客户端连接回调
private void AcceptCallback(IAsyncResult ar)
{
try
{
Socket clientSocket = serverSocket.EndAccept(ar);
Debug.Log("Client connected.");
// 接收客户端数据
ReceiveData(clientSocket);
// 继续监听新的连接
ListenForClients();
}
catch (Exception e)
{
Debug.LogError("Error accepting client connection: " + e.Message);
}
}

// 接收客户端数据
private void ReceiveData(Socket clientSocket)
{
byte[] buffer = new byte[1024];
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), clientSocket);
}

// 接收回调
private void ReceiveCallback(IAsyncResult ar)
{
try
{
Socket clientSocket = ar.AsyncState as Socket;
int bytesRead = clientSocket.EndReceive(ar);
string message = System.Text.Encoding.ASCII.GetString(buffer, 0, bytesRead);
Debug.Log("Received message: " + message);
// 回应客户端
SendData(clientSocket, message);
}
catch (Exception e)
{
Debug.LogError("Error receiving data: " + e.Message);
}
}

// 发送数据给客户端
private void SendData(Socket clientSocket, string message)
{
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(message);
clientSocket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallback), clientSocket);
}

// 发送回调
private void SendCallback(IAsyncResult ar)
{
try
{
Socket clientSocket = ar.AsyncState as Socket;
int sent = clientSocket.EndSend(ar);
Debug.Log("Sent " + sent + " bytes to client.");
}
catch (Exception e)
{
Debug.LogError("Error sending data: " + e.Message);
}
}
}

💗💗 TCP 客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System.Threading;

public class TCPClient : MonoBehaviour
{
private Socket clientSocket;

void Start()
{
ConnectToServer();
}

// 连接到服务器
private void ConnectToServer()
{
// 创建客户端 Socket 对象
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.TCP);

// 设定服务器 IP 地址和端口号
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
int port = 8888;

// 创建服务器端点
IPEndPoint serverEP = new IPEndPoint(ipAddress, port);

// 连接服务器
clientSocket.BeginConnect(serverEP, new AsyncCallback(ConnectCallback), clientSocket);
}

// 连接回调
private void ConnectCallback(IAsyncResult ar)
{
try
{
clientSocket.EndConnect(ar);
Debug.Log("Connected to server.");
// 发送消息给服务器
SendMessage("Hello from client!");
}
catch (Exception e)
{
Debug.LogError("Error connecting to server: " + e.Message);
}
}

// 发送消息给服务器
private void SendMessage(string message)
{
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(message);
clientSocket.Send(buffer);
Debug.Log("Message sent to server: " + message);
}
}

UDP 示例

💗💗 UDP 服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System;

public class UDPServer : MonoBehaviour
{
private UdpClient udpServer;
private IPEndPoint remoteEP;

void Start()
{
// 初始化 UDP 服务器
InitializeServer();
}

// 初始化 UDP 服务器
private void InitializeServer()
{
// 设置端口号
int port = 8888;
udpServer = new UdpClient(port);
remoteEP = new IPEndPoint(IPAddress.Any, 0);

// 开始接收数据
BeginReceive();
}

// 开始接收数据
private void BeginReceive()
{
udpServer.BeginReceive(new AsyncCallback(ReceiveCallback), udpServer);
}

// 接收回调函数
private void ReceiveCallback(IAsyncResult ar)
{
try
{
// 获取接收数据的 Socket
UdpClient client = ar.AsyncState as UdpClient;
// 接收数据
byte[] data = client.EndReceive(ar, ref remoteEP);
// 解析接收到的数据
string message = System.Text.Encoding.ASCII.GetString(data);
Debug.Log("Received message: " + message + " from " + remoteEP.Address.ToString());

// 回应客户端
RespondToClient(message);
}
catch (Exception e)
{
Debug.LogError("Error receiving data: " + e.Message);
}
finally
{
// 继续接收数据
BeginReceive();
}
}

// 向客户端回应
private void RespondToClient(string message)
{
byte[] response = System.Text.Encoding.ASCII.GetBytes("Echo: " + message);
udpServer.Send(response, response.Length, remoteEP);
Debug.Log("Responded to client.");
}

void OnApplicationQuit()
{
// 应用退出时关闭 UDP 客户端
udpServer.Close();
}
}

💗💗 UDP 客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System;

public class UDPClient : MonoBehaviour
{
private UdpClient udpClient;
private IPEndPoint serverEP;

void Start()
{
// 初始化 UDP 客户端
InitializeClient();
}

// 初始化 UDP 客户端
private void InitializeClient()
{
udpClient = new UdpClient();
// 设置服务器 IP 地址和端口号
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
int port = 8888;
serverEP = new IPEndPoint(ipAddress, port);

// 发送一条测试消息
SendMessage("Hello from client!");
}

// 发送消息
private void SendMessage(string message)
{
byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
udpClient.Send(data, data.Length, serverEP);
Debug.Log("Sent message: " + message);
}

void OnApplicationQuit()
{
// 应用退出时关闭 UDP 客户端
udpClient.Close();
}
}

UnityWebRequest

UnityWebRequest 是 Unity 引擎中的一个类用于处理所有类型的 Web 请求它是一个非常灵活的工具可以用来从互联网上下载数据无论是文本二进制文件还是其他类型的数据这个类是异步操作的因此非常适合用来处理那些可能会花费一些时间的任务比如从服务器下载数据

基本用法

1
var request = new UnityWebRequest("http://example.com/api/v1/data", "GET");

上传数据

1
2
3
4
5
6
//上传数据
var handler = new UploadHandlerBuffer(System.Text.Encoding.UTF8.GetBytes("key1=value1&key2=value2"));
var request = new UnityWebRequest("http://example.com/api/v1/upload", "POST", handler);

//当 UnityWebRequest 对象被释放时是否应该释放上传处理器
request.disposeUploadHandlerOnDispose = true;

下载数据

1
2
3
4
5
6
//下载数据
var handler = new DownloadHandlerBuffer();
var request = new UnityWebRequest("http://example.com/api/v1/data", "GET", handler);

//当 UnityWebRequest 对象被释放时是否应该释放下载处理器
request.disposeDownloadHandlerOnDispose = true;

设置请求头

1
request.SetRequestHeader("Content-Type", "application/json");

设置请求体

1
request.SetBody(System.Text.Encoding.UTF8.GetBytes("{ \"key\": \"value\" }"));

请求超时时间

1
request.timeout = 5000; // 设置超时时间为5秒

请求是否完成

1
while (!request.isDone) { /* 等待请求完成 */ }

HTTP 响应码

1
2
yield return request.SendWebRequest();
Debug.Log(request.responseCode); // 200 OK, 404 Not Found, etc.

请求失败

1
2
3
4
5
6
//如果请求失败返回错误信息否则返回 null
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError(request.error);
}

发送请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
IEnumerator Start()
{
var request = UnityWebRequest.Get("http://example.com/api/v1/data");
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError(request.error);
}
else
{
var result = request.downloadHandler.text;
Debug.Log(result);
}
}

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;

public class WebRequestExample : MonoBehaviour
{
private const string API_URL = "http://example.com/api/v1/data";
private const string IMAGE_URL = "http://example.com/images/example.png";

void Start()
{
StartCoroutine(GetDataFromAPI());
StartCoroutine(DownloadImage());
}

/// <summary>
/// 向 API 发送 GET 请求并获取 JSON 数据
/// </summary>
IEnumerator GetDataFromAPI()
{
// 创建 GET 请求
var request = UnityWebRequest.Get(API_URL);

// 设置超时时间
request.timeout = 5000; // 5秒

// 发送请求
yield return request.SendWebRequest();

// 检查请求是否成功
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError(request.error);
}
else
{
// 解析 JSON 数据
var result = request.downloadHandler.text;
Debug.Log("Received JSON data: " + result);
}

// 清理资源
request.Dispose();
}

/// <summary>
/// 向 API 发送 POST 请求并包含 JSON 数据
/// </summary>
IEnumerator PostDataToAPI()
{
// 构建 JSON 数据
var jsonData = "{ \"key\": \"value\" }";
var bodyRaw = Encoding.UTF8.GetBytes(jsonData);

// 创建 POST 请求
var request = UnityWebRequest.Post(API_URL, "POST");
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
request.downloadHandler = new DownloadHandlerBuffer();

// 设置请求头
request.SetRequestHeader("Content-Type", "application/json");

// 发送请求
yield return request.SendWebRequest();

// 检查请求是否成功
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError(request.error);
}
else
{
// 解析 JSON 数据
var result = request.downloadHandler.text;
Debug.Log("Received JSON data: " + result);
}

// 清理资源
request.Dispose();
}

/// <summary>
/// 下载图片并保存到本地
/// </summary>
IEnumerator DownloadImage()
{
// 创建 GET 请求
var request = UnityWebRequestTexture.GetTexture(IMAGE_URL);

// 设置超时时间
request.timeout = 5000; // 5秒

// 发送请求
yield return request.SendWebRequest();

// 检查请求是否成功
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError(request.error);
}
else
{
// 获取纹理对象
Texture2D texture = ((DownloadHandlerTexture)request.downloadHandler).texture;
Debug.Log("Received image texture.");

// 保存纹理为图片文件
byte[] bytes = texture.EncodeToPNG();
System.IO.File.WriteAllBytes(Application.persistentDataPath + "/image.png", bytes);
Debug.Log("Image saved to: " + Application.persistentDataPath + "/image.png");
}

// 清理资源
request.Dispose();
}
}

Unity Multiplayer

Unity Multiplayer之前称为UNET即Unity Networking是Unity提供的一套用于开发多人游戏的工具集和技术框架它旨在简化多人游戏的开发流程使得开发者能够更容易地创建联网游戏Unity Multiplayer 支持多种网络模式包括客户端-服务器模式点对点模式等并提供了一系列工具和服务来帮助开发者构建高性能的多人游戏

Unity Multiplayer 是一个强大的工具集可以帮助开发者快速搭建多人游戏的基础结构它不仅提供了基础的网络通信功能还包含了高级特性如客户端预测状态同步等以提高游戏性能和用户体验通过使用Unity Multiplayer开发者可以专注于游戏逻辑的实现而不必从头开始解决复杂的网络问题

主要特性

  • 网络传输层提供了基础的网络传输能力包括UDP和TCP的支持
  • 网络身份认证通过NetworkIdentity组件来标记哪些游戏对象需要在网络中同步
  • 同步变量SyncVars允许同步游戏对象的状态如位置旋转等
  • 远程过程调用RPCs允许客户端向服务器请求执行某些操作也可以让服务器向所有客户端广播消息
  • 命令Commands/Cmds客户端发送请求到服务器服务器验证后执行相应的命令
  • 事件Events/Evts服务器向所有客户端广播发生的事件
  • 客户端预测Client Prediction客户端提前预测动作结果以减少网络延迟带来的影响
  • 状态同步State Synchronization定期同步游戏状态确保客户端与服务器状态一致
  • 网络管理器NetworkManager管理网络会话如启动服务器连接客户端等

主要组件

  • NetworkManager用于管理和控制网络会话
  • NetworkIdentity标记需要进行网络同步的游戏对象
  • NetworkTransform同步物体的位置旋转和缩放
  • NetworkAnimator同步动画状态
  • NetworkBehavior提供网络同步的基本行为
  • NetworkClient表示客户端逻辑
  • NetworkServer表示服务器逻辑

API 和脚本

  • NetworkTransport用于发送和接收网络数据
  • NetworkSettings网络配置选项
  • NetworkConnection表示网络连接
  • NetworkReader 和 NetworkWriter用于序列化和反序列化数据
  • NetworkServer 和 NetworkClient提供服务端和客户端的API
  • NetworkIdentity用于管理网络对象的身份
  • RPCRemote Procedure Calls用于远程调用方法

工具和服务

除了基本的网络组件外Unity Multiplayer 还提供了一些工具和服务来帮助开发者构建完整的多人游戏

  • Unity Relay一种服务用于帮助客户端通过NAT网络地址转换建立连接
  • Unity Matchmaking用于管理玩家匹配的服务
  • Unity Dashboard用于监控和管理Unity Multiplayer服务的Web界面

开发流程

  • 设计网络架构确定游戏的网络模式如客户端-服务器P2P等
  • 设置NetworkManager配置NetworkManager组件以管理网络会话
  • 添加网络组件为需要同步的游戏对象添加NetworkIdentity和其他相关组件
  • 实现网络逻辑使用Unity Multiplayer提供的API编写客户端和服务器端的逻辑
  • 测试网络功能使用Unity编辑器或专用工具测试网络功能的正确性和性能
  • 部署游戏使用Unity提供的工具和服务来部署和管理多人游戏

Multiplayer Sample Framework

Unity 的 Multiplayer Sample Framework也称为 MSF是一个示例项目集合旨在展示如何构建多人游戏的各种网络模式和技术它包括了多种不同的网络模式的示例代码如权威服务器模式Authoritative Server客户端预测Client Prediction状态同步State Synchronization这些示例旨在帮助开发者理解如何构建和优化多人游戏的网络架构

Multiplayer Sample Framework 是一个非常有价值的资源对于希望深入理解 Unity 多人游戏开发的开发者来说它是不可或缺的学习工具通过研究这些示例开发者可以获得对网络同步延迟补偿预测以及其他多玩家游戏关键技术的深刻理解

主要特性

  • 权威服务器模式在这种模式下所有的游戏逻辑都在服务器上运行客户端只负责渲染和输入这种模式有助于防止作弊因为所有的关键决策都在服务器上做出
  • 客户端预测这是一种技术它允许客户端预测其命令的结果从而减少延迟感当服务器确认这些预测时如果有必要客户端将修正任何差异
  • 状态同步这是一种同步机制其中游戏状态周期性地从服务器发送到客户端以确保客户端的状态与服务器保持一致
  • 断开连接处理处理客户端断开连接的情况包括重新连接和断线后的游戏状态恢复
  • 性能优化提供有关如何减少网络带宽使用和提高网络效率的技术

示例项目

要使用Multiplayer Sample Framework你可以从Unity的官方资源库下载相关的示例项目通常这些示例可以通过Unity Asset Store获得或者在Unity的GitHub存储库中找到

  1. SimpleFPS一个简单的第一人称射击游戏示例展示了基础的网络同步和输入处理
  2. CharacterControllerFPS类似于SimpleFPS但使用了CharacterController而不是Rigidbody
  3. ClientPredictionFPS演示客户端预测技术的应用
  4. StateSynchronizationFPS演示如何使用状态同步来同步游戏状态
  5. Other examples可能还包括其他示例如多人赛车游戏合作游戏等每个示例都针对特定的网络挑战提出了解决方案

项目部署

Unity 是一个非常流行的跨平台游戏开发引擎支持多种平台的项目打包与部署

部署流程

💗💗 打包前的准备

  • 测试确保游戏在编辑器中运行无误并且所有功能都按预期工作
  • 优化检查游戏性能如帧率内存使用等并进行必要的优化
  • 资源清理删除不再需要的资源文件减少最终包体大小
  • 依赖检查确认所有外部插件和资源都已经正确导入并兼容
  • 权限设置根据目标平台的要求设置好应用所需的权限

💗💗 构建设置

进入 Unity 编辑器中的 File > Build Settings 或者使用快捷键 Ctrl+Shift+BWindows/ Cmd+Shift+BMac打开构建设置窗口在此处你可以选择目标平台并决定是否包含开发调试信息等

💗💗 配置玩家设置

对于不同平台你还需要配置对应的 Player Settings这可以通过 Edit > Project Settings > Player 访问这里可以设置应用程序名称图标公司名称等基本信息还可以选择分辨率目标设备等高级选项

💗💗 创建构建

选择好目标平台后点击 Build 或 Build and Run 按钮来创建一个可执行版本的项目如果你选择的是 Build and RunUnity 将会直接在连接的目标设备上运行你的游戏

💗💗 对于不同平台的具体步骤

  • Android
    • 确保安装了 Android SDK 和 JDK
    • 在 Build Settings 中选择 Android 平台
    • 可以选择生成 .apk 文件或者使用 Gradle 构建系统
    • 如果需要上传到 Google Play 商店还需要对 .apk 进行签名
  • iOS
    • 安装 Xcode 和 iOS 开发工具
    • 在 Build Settings 中选择 iOS 平台
    • 构建完成后会生成一个 .xcworkspace 文件使用 Xcode 打开并编译上传至 App Store
  • Windows/Mac/Linux
    • 直接选择对应的桌面平台
    • 创建 .exe.app 或者 .x86_64 文件
    • 根据需要发布到 SteamItch.io 或其他分发平台
  • WebGL
    • WebGL 是用于网页的游戏版本
    • 构建后会生成一系列 HTML 和 JavaScript 文件
    • 可以通过托管服务如 GitHub Pages来发布

💗💗 测试构建

构建完成后在正式发布前请务必在目标平台上对构建版本进行彻底的测试以确保没有问题

💗💗 发布

最后一步就是将游戏发布到相应的商店或平台上比如 SteamApp StoreGoogle Play 等

AssetBundles

Unity中的AssetBundles是一个强大的功能它允许开发者将游戏资源如纹理模型动画等打包成独立的文件并在运行时按需加载这种方法有助于优化资源管理减少内存使用并且可以支持资源的热更新

AB包资源打包

💗💗 编辑器打包

  1. 准备资源首先你需要准备好要打包成AssetBundles的游戏资源
  2. 构建设置
    • 在Unity编辑器中选择菜单栏上的File > Build Settings来配置你的构建设置
    • 选择Asset Bundles选项卡然后选择要包含在构建中的资源
  3. 构建AssetBundles通过脚本或者编辑器菜单如Assets > Build All AssetBundles将选定的资源打包成AssetBundles文件

💗💗 脚本打包

  1. 准备游戏资源
  2. 项目中新建 Editor 文件夹
  3. 把脚本放入文件夹中
  4. 在编辑器工具栏中运行 Tools/Build AssetBundles 选项
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    using System.IO;
    using UnityEngine;
    using UnityEditor;
    using UnityEditor.Callbacks;
    using UnityEditor.AssetBundles;

    public class BuildAssetBundles
    {
    // 指定输出目录
    private const string OutputPath = "Assets/StreamingAssets/AssetBundles";

    [MenuItem("Tools/Build AssetBundles")]
    public static void Build()
    {
    // 确保输出目录存在
    if (!Directory.Exists(OutputPath))
    {
    Directory.CreateDirectory(OutputPath);
    }

    // 获取所有要打包的资产
    string[] assetPaths = Selection.GetFiltered(typeof(Object), SelectionType.Assets);

    // 设置打包选项
    BuildAssetBundleOptions options = BuildAssetBundleOptions.None;

    // 添加需要的选项例如压缩
    options |= BuildAssetBundleOptions.ChunkBasedCompression; // 启用基于块的压缩

    // 构建AssetBundles
    BuildPipeline.BuildAssetBundles(OutputPath, options, EditorUserBuildSettings.activeBuildTarget);

    Debug.Log("AssetBundles have been built successfully.");
    }
    }

AB包资源加载

在Unity中AssetBundle类提供了多个方法来处理外部资源包AssetBundles以下是一些常用方法的详细介绍以及简单的代码示例

💗💗 LoadFromCacheOrDownload

此方法会尝试从缓存中加载AssetBundle如果不在缓存中则会下载成功后会返回AssetBundle对象

1
2
3
4
5
6
7
8
9
public IEnumerator LoadMyAssetBundle()
{
string url = "http://example.com/assetbundles/mybundle";
var ab = AssetBundle.LoadFromCacheOrDownload(url, 0, null);
yield return ab;

// 现在可以使用ab来加载资源
GameObject myGameObject = ab.LoadAsset<GameObject>("myGameObject");
}

💗💗 LoadFromFile

此方法用于从本地文件系统加载AssetBundle

1
2
3
4
5
string path = Application.persistentDataPath + "/assetbundles/mybundle";
AssetBundle ab = AssetBundle.LoadFromFile(path);

// 加载AssetBundle中的GameObject
GameObject myGameObject = ab.LoadAsset<GameObject>("myGameObject");

💗💗 LoadFromMemory

此方法用于从内存中的字节数组加载AssetBundle

1
2
3
4
5
byte[] bundleBytes = GetMyAssetBundleBytes(); // 假设这个函数返回AssetBundle的字节流
AssetBundle ab = AssetBundle.LoadFromMemory(bundleBytes);

// 加载AssetBundle中的GameObject
GameObject myGameObject = ab.LoadAsset<GameObject>("myGameObject");

💗💗 LoadAsset

此方法用于从已加载的AssetBundle中加载单个资源

1
GameObject myGameObject = ab.LoadAsset<GameObject>("myGameObject");

💗💗 AllAssets

此方法返回AssetBundle中所有资源的数组

1
2
3
4
5
Object[] allAssets = ab.GetAllAssets();
foreach (var asset in allAssets)
{
Debug.Log(asset.name);
}

💗💗 Unload

此方法用于卸载AssetBundle及其加载的所有对象

1
ab.Unload(true); // 卸载AssetBundle及所有加载的对象

AB资源包管理类

在Unity项目中为了更好地管理AssetBundles简称AB包通常会创建一个专门的类来处理与AB包相关的所有操作比如加载卸载获取资源等这样做的好处是可以集中管理AB包的生命周期简化代码结构并提高代码的可维护性和复用性

💗💗 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;

public static class AssetBundleManager
{
private static Dictionary<string, AssetBundle> _loadedBundles = new Dictionary<string, AssetBundle>();
private static Dictionary<string, List<AssetBundleLoadOperation>> _operationsInProgress = new Dictionary<string, List<AssetBundleLoadOperation>>();

/// <summary>
/// 异步加载AssetBundle
/// </summary>
/// <param name="bundleName">AssetBundle的名字</param>
/// <param name="onComplete">加载完成后回调</param>
public static void LoadAssetBundleAsync(string bundleName, System.Action<AssetBundle> onComplete)
{
StartCoroutine(LoadAssetBundleInternalAsync(bundleName, onComplete));
}

/// <summary>
/// 内部协程用于异步加载AssetBundle
/// </summary>
/// <param name="bundleName">AssetBundle的名字</param>
/// <param name="onComplete">加载完成后回调</param>
/// <returns>协程</returns>
private static IEnumerator LoadAssetBundleInternalAsync(string bundleName, System.Action<AssetBundle> onComplete)
{
if (_loadedBundles.ContainsKey(bundleName))
{
Debug.Log($"AssetBundle '{bundleName}' already loaded.");
onComplete?.Invoke(_loadedBundles[bundleName]);
yield break;
}

string url = "http://example.com/assetbundles/" + bundleName;

UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(url);
yield return uwr.SendWebRequest();

if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.LogError($"Failed to download AssetBundle '{bundleName}': {uwr.error}");
onComplete?.Invoke(null);
}
else
{
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(uwr);
if (bundle != null)
{
_loadedBundles[bundleName] = bundle;
Debug.Log($"AssetBundle '{bundleName}' loaded successfully.");
onComplete?.Invoke(bundle);
}
else
{
Debug.LogError($"Failed to load AssetBundle '{bundleName}'.");
onComplete?.Invoke(null);
}
}
}

/// <summary>
/// 从AssetBundle中加载资源
/// </summary>
/// <typeparam name="T">资源的类型</typeparam>
/// <param name="bundleName">AssetBundle的名字</param>
/// <param name="assetName">资源的名字</param>
/// <param name="onComplete">加载完成后回调</param>
public static void LoadAsset<T>(string bundleName, string assetName, System.Action<T> onComplete) where T : Object
{
StartCoroutine(LoadAssetInternalAsync<T>(bundleName, assetName, onComplete));
}

/// <summary>
/// 内部协程用于异步加载资源
/// </summary>
/// <typeparam name="T">资源的类型</typeparam>
/// <param name="bundleName">AssetBundle的名字</param>
/// <param name="assetName">资源的名字</param>
/// <param name="onComplete">加载完成后回调</param>
/// <returns>协程</returns>
private static IEnumerator LoadAssetInternalAsync<T>(string bundleName, string assetName, System.Action<T> onComplete) where T : Object
{
if (_loadedBundles.TryGetValue(bundleName, out AssetBundle bundle))
{
T asset = bundle.LoadAsset<T>(assetName);
onComplete?.Invoke(asset);
yield break;
}
else
{
Debug.LogError($"AssetBundle '{bundleName}' not found or not loaded.");
onComplete?.Invoke(default(T));
yield break;
}
}

/// <summary>
/// 卸载AssetBundle
/// </summary>
/// <param name="bundleName">AssetBundle的名字</param>
/// <param name="unloadAllLoadedObjects">是否卸载所有加载的对象</param>
public static void UnloadAssetBundle(string bundleName, bool unloadAllLoadedObjects)
{
if (_loadedBundles.ContainsKey(bundleName))
{
_loadedBundles[bundleName].Unload(unloadAllLoadedObjects);
_loadedBundles.Remove(bundleName);
Debug.Log($"AssetBundle '{bundleName}' unloaded.");
}
else
{
Debug.LogWarning($"Attempted to unload non-existent AssetBundle '{bundleName}'.");
}
}
}

💗💗 使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//假设你需要加载一个名为mybundle的AB包并从中获取一个名为myGameObject的游戏对象可以按照如下方式使用
IEnumerator Start()
{
yield return AssetBundleManager.LoadAssetBundleAsync("mybundle");

GameObject myGameObject = AssetBundleManager.LoadAsset<GameObject>("mybundle", "myGameObject");

if (myGameObject != null)
{
Instantiate(myGameObject); // 实例化GameObject
}
}

void OnDestroy()
{
// 游戏对象销毁时卸载AB包
AssetBundleManager.UnloadAssetBundle("mybundle", true);
}

设计模式

单例模式

单例模式Singleton Pattern是一种常用的软件设计模式它保证一个类只有一个实例并提供一个全局访问点

在游戏开发中我们经常需要在整个游戏中保持一些数据的一致性比如管理游戏的音量设置玩家数据或者共享资源等这时候就可以使用单例模式来确保这些数据只有一份副本

⭐⭐ 创建一个基础的 Singleton 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using UnityEngine;

//我们需要定义一个基类 Singleton<T>这里 T 是泛型参数代表继承此基类的具体类型
public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
private static object _lock = new object();

public static T Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
// 在这里可以添加更复杂的查找或创建逻辑
// 这里直接尝试查找已存在的实例
_instance = FindObjectOfType<T>();
if (_instance == null)
{
// 如果没有找到实例则创建一个新的 GameObject 并将组件附加到它上面
GameObject singleton = new GameObject($"[{typeof(T).Name}] Singleton");
_instance = singleton.AddComponent<T>();
}
}
}
}

return _instance;
}
}

protected virtual void Awake()
{
// 在 MonoBehaviour 的生命周期中Awake 方法是最早的几个调用之一
// 我们可以在这里做一些初始化的工作
DontDestroyOnLoad(this); // 防止场景切换时销毁实例
}
}

⭐⭐ 创建具体的单例实例

1
2
3
4
5
6
7
8
public class GameSettingsManager : Singleton<GameSettingsManager>
{
// 这里可以添加一些游戏设置相关的字段或属性
public int MusicVolume { get; set; } = 50;
public int SFXVolume { get; set; } = 75;

// 可以添加任何需要的逻辑
}

⭐⭐ 使用单例模式

1
2
3
4
5
6
//当你想要在游戏中的其他地方使用 GameSettingsManager 实例时只需要通过 Instance 属性来访问
void Start()
{
GameSettingsManager.Instance.MusicVolume = 60;
Debug.Log("Music Volume: " + GameSettingsManager.Instance.MusicVolume);
}

工厂模式

工厂模式Factory Pattern是一种创建型设计模式它提供了一个创建对象的最佳方式该模式的核心思想是通过一个工厂对象决定创建哪个类的对象而不需要指定具体的类名这有助于将对象创建与使用对象的代码分离提高系统的可扩展性和灵活性

在 Unity 游戏开发中工厂模式可以用来创建游戏中的各种对象如敌人子弹等

⭐⭐ 定义接口

1
2
3
4
5
6
//定义一个所有具体类都需要实现的接口
public interface IGameObject
{
void Initialize();
void UpdateState();
}

⭐⭐ 创建具体的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Enemy : MonoBehaviour, IGameObject
{
public void Initialize()
{
// 初始化敌人状态
}

public void UpdateState()
{
// 更新敌人的状态
}
}

public class Bullet : MonoBehaviour, IGameObject
{
public void Initialize()
{
// 初始化子弹状态
}

public void UpdateState()
{
// 更新子弹的状态
}
}

//创建一个产品类的枚举
public enum GameObjectType
{
Enemy,
Bullet
}

⭐⭐ 创建工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//创建一个工厂类来负责创建这些产品对象
public class GameObjectFactory
{
public static IGameObject CreateGameObject(GameObjectType type)
{
switch (type)
{
case GameObjectType.Enemy:
Enemy enemy = new GameObject("Enemy").AddComponent<Enemy>();
return enemy;
case GameObjectType.Bullet:
Bullet bullet = new GameObject("Bullet").AddComponent<Bullet>();
return bullet;
default:
throw new ArgumentException("Unsupported game object type!");
}
}
}

⭐⭐ 使用工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
//在你的游戏逻辑中你可以通过工厂模式来创建游戏对象而不需要关心具体的创建细节
public class GameManager : MonoBehaviour
{
void Start()
{
IGameObject enemy = GameObjectFactory.CreateGameObject(GameObjectType.Enemy);
enemy.Initialize(); // 初始化敌人

IGameObject bullet = GameObjectFactory.CreateGameObject(GameObjectType.Bullet);
bullet.Initialize(); // 初始化子弹
}
}

策略模式

策略模式Strategy Pattern是一种行为设计模式它使你能在运行时改变对象的行为策略模式定义了一系列算法并将每一个算法封装起来使它们可以相互替换策略模式让算法独立于使用它的客户

在 Unity 游戏开发中策略模式可以用来实现角色的不同行为模式比如 AI 控制的角色可以根据不同的情况选择不同的行动策略

⭐⭐ 定义接口

1
2
3
4
public interface IAttackStrategy
{
void Attack();
}

⭐⭐ 创建具体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SwordAttack : IAttackStrategy
{
public void Attack()
{
Debug.Log("Attacking with a sword!");
}
}

public class BowAttack : IAttackStrategy
{
public void Attack()
{
Debug.Log("Attacking with a bow!");
}
}

public class MagicAttack : IAttackStrategy
{
public void Attack()
{
Debug.Log("Casting magic spells!");
}
}

⭐⭐ 创建上下文类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//它将使用策略接口并在运行时根据需要更改其行为
public class Character : MonoBehaviour
{
private IAttackStrategy _attackStrategy;

public void SetAttackStrategy(IAttackStrategy strategy)
{
_attackStrategy = strategy;
}

public void PerformAttack()
{
_attackStrategy.Attack();
}
}

⭐⭐ 使用策略模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//在游戏逻辑中你可以根据需要动态地更改角色的攻击策略
public class CharacterController : MonoBehaviour
{
private Character _character;

void Start()
{
_character = GetComponent<Character>();

// 初始设定为剑攻击
_character.SetAttackStrategy(new SwordAttack());

// 在某些条件下改变攻击方式
ChangeAttackStrategyToBow();
}

private void ChangeAttackStrategyToBow()
{
_character.SetAttackStrategy(new BowAttack());
_character.PerformAttack();
}

private void ChangeAttackStrategyToMagic()
{
_character.SetAttackStrategy(new MagicAttack());
_character.PerformAttack();
}
}

代理模式

代理模式Proxy Pattern是一种结构型设计模式它允许你在不修改原始类的情况下增加额外的功能或控制访问代理模式通常用于在访问一个对象时提供间接层可以用来延迟初始化控制访问记录操作等

在 Unity 游戏开发中代理模式可以用来解决一些性能问题比如延迟加载大纹理或者控制对复杂对象的访问等

⭐⭐ 定义接口

1
2
3
4
public interface ITexture
{
void Display();
}

⭐⭐ 创建具体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RealTexture : ITexture
{
private Texture2D _texture;

public RealTexture(string imagePath)
{
// 加载纹理资源
_texture = Resources.Load<Texture2D>(imagePath);
}

public void Display()
{
Debug.Log("Displaying texture.");
// 显示纹理的操作这里只是打印一条信息模拟显示操作
}
}

⭐⭐ 创建代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ProxyTexture : ITexture
{
private RealTexture _realTexture;
private string _imagePath;

public ProxyTexture(string imagePath)
{
_imagePath = imagePath;
}

public void Display()
{
if (_realTexture == null)
{
_realTexture = new RealTexture(_imagePath);
}
_realTexture.Display();
}
}

⭐⭐ 使用代理模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//你可以通过代理对象来访问纹理资源代理会在首次请求时加载纹理
public class ExampleUsage : MonoBehaviour
{
private ITexture _texture;

void Start()
{
// 使用代理来加载纹理
_texture = new ProxyTexture("path/to/your/texture");

// 模拟用户交互触发纹理的加载和显示
ShowTexture();
}

void ShowTexture()
{
_texture.Display();
}
}

观察者模式

观察者模式Observer Pattern是一种行为设计模式它定义了对象间的一种一对多依赖关系以便当一个对象的状态发生改变时所有依赖于它的对象都会得到通知并自动更新这种模式非常适合用于实现事件处理系统

在 Unity 游戏开发中观察者模式可以用来处理游戏中的事件监听和响应机制比如游戏角色的生命值变化时通知 UI 界面更新或者玩家得分变化时通知排行榜更新等

⭐⭐ 定义接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface IObserver
{
void OnNotify(object sender, EventArgs args);
}

//ScoreChangedEventArgs 是一个自定义的事件参数类
public class ScoreChangedEventArgs : EventArgs
{
public int NewScore { get; }

public ScoreChangedEventArgs(int score)
{
NewScore = score;
}
}

⭐⭐ 创建具体观察者类

1
2
3
4
5
6
7
8
9
10
11
public class UIScoreUpdater : MonoBehaviour, IObserver
{
public void OnNotify(object sender, EventArgs args)
{
if (args is ScoreChangedEventArgs e)
{
Debug.Log($"Score updated to {e.NewScore}.");
// 更新 UI 中的分数显示
}
}
}

⭐⭐ 创建被观察者类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//它负责维护观察者的列表并在状态改变时通知所有观察者
public class ScoreManager : MonoBehaviour
{
private List<IObserver> _observers = new List<IObserver>();

public void RegisterObserver(IObserver observer)
{
_observers.Add(observer);
}

public void UnregisterObserver(IObserver observer)
{
_observers.Remove(observer);
}

public void NotifyObservers(int score)
{
foreach (var observer in _observers)
{
observer.OnNotify(this, new ScoreChangedEventArgs(score));
}
}

public void IncreaseScore(int amount)
{
int currentScore = GetScore(); // 假设这里有获取当前分数的方法
currentScore += amount;
SetScore(currentScore); // 假设这里有设置分数的方法
NotifyObservers(currentScore);
}

private int GetScore()
{
// 返回当前分数
return 0; // 示例中返回 0实际应返回真实分数
}

private void SetScore(int score)
{
// 设置新的分数
}
}

⭐⭐ 使用观察者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//在游戏逻辑中注册观察者并触发分数变化事件
public class GameManager : MonoBehaviour
{
private ScoreManager _scoreManager;
private UIScoreUpdater _scoreUpdater;

void Start()
{
_scoreManager = FindObjectOfType<ScoreManager>();
_scoreUpdater = FindObjectOfType<UIScoreUpdater>();

// 注册观察者
_scoreManager.RegisterObserver(_scoreUpdater);

// 增加分数触发通知
_scoreManager.IncreaseScore(100);
}

void OnDestroy()
{
// 清除观察者
_scoreManager.UnregisterObserver(_scoreUpdater);
}
}

实用工具

DoTween 动画库

DoTween是Unity的一个流行动画库它允许开发者使用简单而强大的API来创建平滑的动画与Unity内置的动画系统相比DoTween提供了更多的灵活性和控制选项并且可以用于几乎任何类型的动画包括但不限于位置旋转缩放等属性的变化

安装

  1. 通过Unity Package Manager安装
    • 打开Unity Hub选择你的项目
    • 在Unity编辑器中选择窗口>包管理器
    • 在Unity Package Manager窗口中点击左下角的”+”, 搜索”DoTween”并安装
  2. 手动下载
    • 访问DoTween的GitHub页面或者官方网站下载最新版本
    • 将下载的文件解压后将Plugins文件夹内的内容导入到Unity项目的Plugins文件夹内

文档

AB包打包工具

工具简介

Unity Asset Bundle Browser 工具是 Unity 标准功能之外的额外功能要访问此工具必须从 GitHub 下载并安装该过程独立于标准 Unity Editor 的下载和安装

此工具使用户能够查看和编辑 Unity 项目的资源包的配置它将阻止会创建无效捆绑包的编辑并告知现有捆绑包的任何问题此外还会提供基本的构建功能

通过使用此工具无需选择资源并在 Inspector 中手动设置资源包它可以放入 5.6 或更高版本的任何 Unity 项目中此工具将在 Window > AssetBundle Browser 中创建一个新菜单项捆绑包的配置和构建功能在新窗口中拆分为两个选项卡

官方手册

访问地址https://docs.unity3d.com/cn/2022.3/Manual/AssetBundles-Browser.html

手册左上角可以切换手册的版本

XLua 解释器

XLua 是一个针对 Unity 游戏引擎的 Lua 解释器它主要用于将 Lua 脚本集成到 Unity 中从而可以利用 Lua 的灵活性和轻量级特性来开发游戏逻辑XLua 的设计目标是在保证安全性的前提下提供高性能的 Lua 执行环境并且能够方便地与 C# 代码进行互操作

⭐⭐ Lua 脚本语言学习笔记

XLua 的特点

  1. 高性能XLua 采用了 LuaJIT 的 JIT 编译技术可以将 Lua 代码编译成本地机器码从而提高执行速度
  2. 安全性XLua 提供了沙箱环境可以限制 Lua 代码的执行权限防止恶意代码对游戏产生不良影响
  3. 易用性XLua 提供了丰富的 API可以方便地在 Lua 代码中调用 C# 方法反之亦然
  4. 调试支持XLua 支持 Lua 调试器可以方便地进行代码调试

XLua 的安装

  1. 下载地址和手册https://github.com/Tencent/xLua
  2. 从 Releases 中下载发行版, 或直接下载本仓库代码.
  3. 打开下载下来的源码压缩包, 你会看到一个 Assets 目录, 这目录就对应 Unity 工程的 Assets 目录保持这目录结构, 将其内容置入 Unity 项目即可.
  4. 注意Assets/Examples 目录下为示例代码, 你应该在生产环境下删去他们.
1
2
3
# 通过 Git 克隆 XLua 仓库
git clone https://github.com/Tencent/xLua.git
# 将克隆下来的 xlua 下的文件夹导入到你的 Unity 项目和 Assets 文件夹同级目录

核心组件

  • LuaEnvLuaEnv 是 XLua 的核心类用于创建 Lua 环境它负责管理 Lua 状态并提供执行 Lua 代码的方法
  • LuaTableLuaTable 类表示 Lua 中的表可以用于存储和操作 Lua 数据
  • GlobalGlobal 属性提供了对全局环境的访问可以用于注册 C# 函数到 Lua 环境中

沙箱环境

XLua 提供了沙箱环境来限制 Lua 代码的执行权限可以通过创建一个独立的 Lua 状态来实现这一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 创建一个独立的 Lua 状态
var env = new LuaEnv();
var sandbox = env.NewTable();

// 限制可用的函数
sandbox.Set("__index", new Dictionary<string, Delegate>
{
{ "print", (Action<string>)Console.WriteLine },
{ "math", new LuaTable() { ["abs"] = (Func<double, double>)Math.Abs } }
});

// 设置全局环境为沙箱
env.Global = sandbox;

// 在沙箱中执行 Lua 代码
env.DoString("print(math.abs(-10))"); // 输出 10

加载 Lua 脚本示例

💗💗 创建一个简单的 Lua 脚本 test.lua

1
2
3
4
5
6
7
function sayHello(name)
print("Hello, " .. name .. "!")
end

return {
sayHello = sayHello
}

💗💗 在 Unity 中创建一个 C# 脚本 LuaExample.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using UnityEngine;
using XLua;

public class LuaExample : MonoBehaviour
{
private LuaEnv env;

void Start()
{
// 创建 Lua 环境
env = new LuaEnv();

// 运行 Lua 脚本
//第一种直接执行 Lua 代码
env.DoString("print('Hello, World!')");

//第二种读取 Lua 脚本文件
// 读取 Lua 文件内容
string luaCode = File.ReadAllText("Assets/test.lua");
// 使用 DoString 执行 Lua 代码
env.DoString(luaCode);


//第三种使用 Load 方法加载 Lua 脚本文件
env.Load("Assets/test.lua");

// 调用 Lua 函数
var luaFunc = env.Global.Get<LuaFunction>("sayHello");
luaFunc.Call("Unity");
}

void OnDestroy()
{
// 清理 Lua 环境
env.Dispose();
}
}

Unity 与 Lua 交互

💗💗 在Unity中创建一个新的Lua文件比如叫做Example.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
-- Example.lua
---@class Example
---@field public Name string The name of the example.
---@field public Age number The age of the example.

---@constructor
function Example(name, age)
self.Name = name
self.Age = age
end

---@return Example
function Example:new(name, age)
local obj = {}
setmetatable(obj, self)
self.__index = self
Example.__init(obj, name, age)
return obj
end

---@param obj Example
---@param name string
---@param age number
function Example.__init(obj, name, age)
obj.Name = name
obj.Age = age
end

---@param value number
---@return number
function Example:AddAge(self, value)
self.Age = self.Age + value
return self.Age
end

---@param value string
---@return string
function Example:Greet(value)
return "Hello " .. value .. ", I am " .. self.Name .. " and my age is " .. tostring(self.Age)
end

💗💗 在Unity中写一个C#脚本来加载并使用上面定义的Lua脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using UnityEngine;
using XLua;

public class LuaInteraction : MonoBehaviour
{
private LuaTable Example;

void Start()
{
// 加载Lua环境
LuaEnv env = new LuaEnv();

// 执行Lua脚本
env.DoString(@"
require('Example') -- 假设Example.lua位于默认路径下
");

// 获取Lua中的Example类
Example = env.Global.Get< LuaTable >("Example");

// 创建一个Example实例
LuaTable exampleInstance = Example.Call< LuaTable >("new", "John Doe", 30);

// 调用Lua方法
int newAge = exampleInstance.Get<int>("AddAge")(5);
Debug.Log($"New age after adding 5 years: {newAge}");

// 调用另一个Lua方法
string greeting = exampleInstance.Get<string>("Greet")("Unity");
Debug.Log(greeting);

// 清理
env.Dispose();
}
}

Lua 与 Unity 交互

💗💗 在Unity中创建一个新的Lua文件比如叫做test.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 加载C#的命名空间
local CS = require('CS')

-- 使用UnityEngine的Debug类的Log方法
CS.UnityEngine.Debug.Log("Hello from Lua!")

-- 创建一个空的游戏对象
local go = CS.UnityEngine.GameObject('NewGameObject')
go.transform.position = CS.UnityEngine.Vector3(0, 0, 0)

-- 添加一个Cube Mesh Filter到新创建的游戏对象
local meshFilter = go.AddComponent(CS.UnityEngine.MeshFilter)
meshFilter.mesh = CS.UnityEngine.Mesh.PrimitiveMesh(CS.UnityEngine.PrimitiveType.Cube)

-- 添加一个Cube Collider
local collider = go.AddComponent(CS.UnityEngine.BoxCollider)
collider.size = CS.UnityEngine.Vector3(1, 1, 1)

-- 添加一个Renderer
local renderer = go.AddComponent(CS.UnityEngine.MeshRenderer)
renderer.material = CS.UnityEngine.Material.New(CS.UnityEngine.Color.red)

💗💗 在 Unity 中创建一个 C# 脚本 LuaExample.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;
using XLua;

public class LuaExample : MonoBehaviour
{
private LuaEnv env;

void Start()
{
// 创建 Lua 环境
env = new LuaEnv();

//使用 Load 方法加载 Lua 脚本文件
env.Load("Assets/test.lua");
}

void OnDestroy()
{
// 清理 Lua 环境
env.Dispose();
}
}

学习资源