Unity3D 学习笔记
前言
引擎就不介绍了
由于U3D的各种诡异的操作,我对于U3D的学习将只会停留在会用就行,重点在UE上
当然,我相信,其实都是差不多的
引擎安装
叨叨叨
现在国内能下载到非团结的只有2022了,Unity 6不再向大陆提供,取而代之的是团结引擎
常规安装通过Unity Hub进行,需要注册账号,安装时使用LTS版
还需要申请许可证才能使用,学习的话就只用个人版就行了,免费
至于国际版,就得用7根木棍飞到外面去,到官网上去下载,不然在点下载时还是会跳转到国内的下载界面
不过,使用国际版暂不清楚是否有问题,但学习吧,问题不大
中国官网: https://unity.cn/
国际官网: https://unity.com/
安装
添加版本时,可以选择一些额外的模块,是一些别的平台的开发包
可以选择Android, Windows, iOS,毕竟这三个算是常用的,其他的可能没有设备来测试
另外的文档Documentation,可以选上,查看一些说明
开始
创建项目
创建项目时,可以选择一些模板,比如2D,3D等,输入名称,选择项目保存的路径,点击创建即可
项目结构
Assets:存放资源文件,美术资源、脚本、预制体等都放在这里Library:存放库文件Logs:存放日志文件obj:编译产生的中间文件Packages:存放包配置信息ProjectSettings:存放项目设置
基本界面
使用的话其实建议直接用英文的界面,好看文档和一些教程,有时候中文翻译可能存在问题
窗口布局
右上角的位置Layout,可以调整窗口布局
Hierarchy(层级)
Hierarchy窗口,存放场景中的所有物体
包括模型、灯光、摄像机、UI等
左上角的+或者鼠标右键都可以创建物体
一些快捷键:
Ctrl + D:直接克隆一个当前选择的物体(复制+粘贴)F2:重命名当前选中的物体Delete:删除当前选中的物体Ctrl + C:复制当前选中的物体Ctrl + V:粘贴当前复制的物体
Scene(场景)
Scene窗口,显示当前场景的视图
窗口上方栏从左到右依次为(2022版):
- 枢轴选择,可选择物体的轴心,默认为
Pivot,可以选择Center,即物体的中心 - 坐标轴,可以选择物体的坐标轴,默认为
Global,可以选择Local,即物体的局部坐标轴 - 显示辅助线,即显示地平面上的网格线
- 吸附模式,只能在全局坐标轴下启用
- 吸附模式参数调整,按照特定的距离、角度进行调整
- 渲染模式
- 2D、3D切换
- 灯光显示
- 声音显示
- 粒子等其他特效的显示
- 场景可见性,即显示或隐藏场景中的物体
- 场景摄像机设置
- 其他的辅助功能设置
右上角的Persp是场景轴向,显示当前视图下的坐标轴方向
左侧栏是一些操作工具,对应的快捷键:
Q:平移W:移动E:旋转R:缩放T:2D上的操作Y:综合(除了2D)上述功能
鼠标的一些操作:
- 左键:选择物体
- 左键 +
Ctrl:添加选择物体 - 框选:选择框选范围内的物体
- 左键 +
Alt+ 拖动:相对视口中心点旋转 - 左键选择物体 +
F:最大化显示选择的物体 - 右键 + 拖动:平移
- 右键 +
WASD:漫游场景 - 右键 +
WASD+Shift:快速漫游场景 - 右键 +
Alt+ 拖动:相对屏幕中心拉近拉远 - 中键滚动:拉近拉远
- 中键 + 拖动:平移
- 中键滚动 +
Alt:按照鼠标位置拉近拉远
Game(游戏)
Game窗口,显示当前游戏运行时的视图,玩家可以看到的窗口
至少有一个摄像机才能在窗口中显示
在上方中央位置,有一个播放按钮,点击即可运行游戏;
第二个是暂停运行;第三个是逐帧运行
Game窗口中,上方位置的各栏:
- 选择运行平台,Game为PC端,Simulator为模拟器,模拟在移动平台上的效果
- 显示的显示器,一般就Display1
- 显示的分辨率,可以自定义分辨率和比例
- 显示缩放,缩放当前显示的内容
- 运行时显示模式,全屏或者窗口模式
- Show Frame Debugger,显示帧调试器
- Mute Audio,静音
- Unity Shorts,显示快捷键
- Stats,显示帧率等统计信息
- Gizmos,其他的显示设置,包括显示图标在内
Project(项目)
Project窗口,存放项目中的资源文件
默认文件夹Scene,存放场景文件,双击即可打开场景
官方拓展包Packages,存放项目使用的包
- 左上角
+创建资源文件,如C#脚本 - 搜索栏,搜索资源文件
- 搜索栏右侧第二个按钮(几何图形图标),按类型查找
- 搜索栏右侧第三个按钮(标签图标),按名字查找
Inspector(检查器)
Inspector窗口,显示当前选中物体的属性(其实也是与之相关的C#脚本信息)
第一块部分:
游戏对象的基本设置
- 左侧(默认是一个方块):打标签
- 复选框:是否激活
- 名称:物体的名称,可以重命名
- Tag:标签,用于分类
- Layer:层级,用于控制物体之间的交互
- Static:是否为静态物体,静态物体不会在运行时被销毁,可以优化性能
后面的部分都是C#关联的脚本
- Transform:与位置相关的脚本信息,改变物体的位置、旋转、缩放
- Box Collider:碰撞器,用于检测物体之间的碰撞
- Camera:摄像机,用于显示场景
- …
Console(控制台)
Console窗口,显示游戏运行时的日志信息
默认是不开启的,在Window -> General -> Console中开启
快捷键Ctrl + Shift + C,可以快速打开
上方栏:
- Clear,清空日志
- 右侧选项,分运行时清理,构建时清理和编译时清理
- Collapsed,折叠日志,折叠相同信息
- Error Pause,错误暂停,当出现错误时暂停游戏
- Editor,选择显示的日志信息
- 搜索栏,搜索日志信息
- 右侧三个图标,分别是是否显示错误、警告和普通打印信息,以及它们的计数
Toolbar(工具栏)
File:文件操作,如保存、导入、导出等Edit:编辑操作,如撤销、重做、复制、粘贴等Assets:资源操作,如创建资源、导入资源等GameObject:游戏对象操作,如创建游戏对象、添加组件等Components:组件(脚本)操作,如添加组件、移除组件等Service:服务操作Window:窗口操作,如打开窗口、关闭窗口等Help:帮助操作,如查看帮助文档、访问官网等
一些快捷键:
Ctrl + Shift + F:移动物体到当前视角,常用于摄像机视角的设置Ctrl + P:运行游戏Ctrl + Shift + P:暂停游戏Ctrl + Alt + P:逐帧运行游戏
父子级
在Hierarchy窗口中,物体之间有父子级关系,子物体跟随父物体移动
在选择物体时右键新建就会创建该物体的子对象
变换父对象时,子物体的坐标也会随之改变
但操作子对象时,父对象不会随之改变
注意子对象的Transform组件中显示的是相对父对象的
Unity工作原理
反射机制
Unity使用反射机制来加载和执行C#脚本
游戏中的物体是GameObject,脚本继承自MonoBehaviour,通过反射机制来加载和执行脚本
GameObject会自带一个Transform组件,用于控制物体的位置、旋转、缩放
反射的体现:
- 修改
Inspector面板中的Transform的内容:利用反射,已知对象、类名、变量名,通过反射为该对象设置值 - 新建一个脚本后,添加一个指定的GameObject对象:利用反射,已知类名,可以获取所有公共成员,所以在
Inspector面板中可以显示该脚本的所有公共成员
游戏场景
游戏场景的后缀名是.unity,可以用记事本打开,其中存储了场景上的内容,包括设置、物体等
本质是一个配置文件,由unity引擎读取
预设体
预设体用来保存物体的信息,包括位置、旋转、缩放、组件等,然后可以在其他场景中使用这个物体
即便Hierarchy窗口中的物体被删除,只要保存了预设体,就可以在其他场景中重新使用这个物体
后缀名为.prefab
创建预设体:
在Hierarchy窗口中,选中一个物体,直接拖入到Project窗口中,即可创建一个预设体使用预设体:
直接将Project中的预设体拖入到Hierarchy窗口中,即可使用修改预设体:
- 在
Hierarchy窗口中,编辑了预设体之后,在Inspector窗口中,第一块内容会多一行,
最右边的Overrides有两个选项,一个是重置所有,一个是应用到所有,前者会重置所有修改,后者会将修改应用到所有使用该预设体的物体 - 另一种简单粗暴的方法是将修改后的预设体重新拖入
Project窗口中,这样会覆盖原来的预设体 - 删除预设体中的部分内容,需要先打开预设体,然后才能删除
- 在
删除预设体:
在Project窗口中,选中预设体,直接删除独立预设体:
Unpack Prefab,将预设体中的内容独立出来,这样就可以单独修改预设体中的内容,而不会影响到其他使用该预设体的物体
资源包导入导出
可以将Project窗口中的资源导出为包,然后在其他项目中使用
后缀是.unitypackage
也可以导入资源包,将其他项目中的资源导入到当前项目中
Unity 脚本
基本规则
- 不直接在VS中创建脚本
- 可以在
Assets文件夹中创建脚本 - 类名和文件名必须一致,不然不能挂载
- 不要使用中文命名
- 没有特殊需求,不用管命名空间
- 创建的脚本默认继承MonoBehaviour
MonoBehaviour
MonoBehaviour是Unity中所有脚本的基类,所有的脚本都必须继承MonoBehaviour
- 只有继承了MonoBehaviour的脚本,才能挂载到GameObject上
- 继承了MonoBehaviour的脚本,可以挂载到GameObject上,但不能
new - 继承了MonoBehaviour的脚本不要写构造函数
- 继承MonoBehaviour的脚本,可以在一个GameObject上挂载多个(如果没有DisallowMultipleComponent特性,默认可以挂载多个,但过多的脚本会影响性能)
- 继承了MonoBehaviour的脚本,可以再被继承(但是要避免层层继承)
生命周期函数
生命周期函数是MonoBehaviour中的一些特殊函数,它们在游戏运行时会自动调用
关于帧
- Unity 底层已经做好了游戏的循环
- 需要我们来学习它的生命周期函数,用它的规则来执行游戏的相关逻辑
生命周期函数
- 所有继承
MonoBehaviour的类都有这些生命周期函数 - 生命周期函数是该脚本依附的GameObject在游戏运行过程中,从初始化到销毁会自动调用的一些函数
- Unity会帮助我们记录一个GameObject对象依附了哪些脚本,在运行过程中会自动得到这些对象,并通过反射去执行一些固定名字的函数
- 生命周期函数一般是private或protected的,因为它们是Unity自动调用的,我们不需要手动调用
- 生命周期函数的名字是固定的,不能自己定义
一些函数:
Awake:在类对象被创建时调用,只调用一次,用于初始化(类似于构造函数的存在)OnEnable:依附的GameObject每次激活时调用Start:在类对象被创建出来后,第一次帧更新之前被调用,只调用一次FixedUpdate:物理帧更新,在每个物理帧调用一次,可以处理一些物理逻辑(物理帧的频率可以在项目设置的Time中调整,默认是每0.02秒调用一次,即50次每秒)Update:每一帧调用一次,用于处理游戏逻辑LateUpdate:每一帧调用一次,在Update之后调用,用于处理一些需要在Update之后处理的逻辑,一般是处理摄像机的逻辑OnDisable:依附的GameObject每次被禁用时调用OnDestroy:在类对象被销毁时调用,只调用一次,用于清理资源
生命周期函数可以根据需要选择性地实现,不需要全部实现
也支持继承和重写
打印信息
在Unity中打印信息可以使用Debug.Log,它会将信息打印到控制台;
同时,衍生的方法是Debug.LogWarning和Debug.LogError,分别表示警告和错误
继承了MonoBehaviour的类,有一个专门的方法用来打印,print
Inspector窗口中可编辑的变量
Inspector窗口中显示的可编辑内容,就是脚本的公共成员变量
- 私有和受保护的变量不会显示在
Inspector窗口中,但可以使用[SerializeField]属性来显示私有变量 - 公共的变量会显示在
Inspector窗口中,但如果使用[HideInInspector]属性,则不会显示 - 大部分类型都可以显示在
Inspector窗口中,包括基本类型、字符串、枚举等 - 自定义类需要使用
[System.Serializable]属性才能显示在Inspector窗口中 - 字典不论如何都无法显示在
Inspector窗口中
一些辅助特性:
Header:标题,说明Tooltip:鼠标悬停时显示的提示信息Space:在变量之间添加空格Range:用于限制数值类型变量的范围,并在Inspector窗口中显示为滑动条Multiline:用于显示多行文本,默认为3行TextArea:用于显示多行文本区域ContextMenuItem:为方法添加右键菜单选项ContextMenu:让方法能在Inspector窗口中右键调用
注意:
- Inspector窗口中显示的变量就是该脚本的公共成员变量,运行时可以通过Inspector窗口修改这些变量的值
- 将脚本拖到
GameObject上后,再改变脚本中的默认值,界面上的值不会改变 - 运行中修改的信息不会保存,停止运行后会恢复到之前的值
1 | using System.Collections; |
MonoBehaviour中重要的内容
gameObject:获取当前脚本依附的GameObject对象1
2// 获取依附的GameObject
print(this.gameObject);transform:获取当前脚本依附的GameObject对象的Transform组件1
2
3
4// 获取GameObject的位置信息
print(this.transform.position);
print(this.transform.eulerAngles); // 获取欧拉角
print(this.transform.localScale); // 获取缩放信息enabled:获取或设置脚本是否激活1
2// 获取脚本是否激活
print(this.enabled);
重要方法
- 得到自己挂载的单个脚本
- 根据脚本名获取
- typeof获取
- 用泛型获取
1
2
3
4
5
6
7
8
9
10Lesson2 t = this.GetComponent("Lesson2") as Lesson2;
print(t);
// Type方式获取
Lesson2 t2 = this.GetComponent(typeof(Lesson2) ) as Lesson2;
print(t2);
// 泛型方式获取
Lesson2 t3 = this.GetComponent<Lesson2>();
print(t3);
- 得到自己挂载的多个脚本
1
2
3
4
5
6Lesson3[] arr = this.GetComponents<Lesson3>();
print(arr.Length);
List<Lesson3> list = new List<Lesson3>();
this.GetComponents<Lesson3>(list);
print(list.Count); - 得到子物体上的脚本(默认会找自己身上是否挂载该脚本,也可以获取多个),参数可填入一个布尔值,表示是否包含非激活的物体
1
2
3
4
5Lesson2 t4 = this.GetComponentInChildren<Lesson2>();
print(t4);
Lesson2[] arr2 = this.GetComponentsInChildren<Lesson2>();
print(arr2.Length); - 得到父物体上的脚本(默认会找自己身上是否挂载该脚本)
1
2
3
4Lesson2 t5 = this.GetComponentInParent<Lesson2>();
print(t5);
Lesson2[] arr3 = this.GetComponentsInParent<Lesson2>();
print(arr3.Length); - 尝试获取脚本,避免空引用异常
1
2
3Lesson2 t6;
bool ret = this.TryGetComponent<Lesson2>(out t6);
print(ret);
Unity重要组件和API
GameObject
成员变量
- 名字:
name
通过this.gameObject.name获取或设置物体名称 - 是否激活:
activeSelf
通过this.gameObject.activeSelf获取物体是否激活 - 是否是静态物体:
isStatic
通过this.gameObject.isStatic获取物体是否为静态物体 - 标签:
tag
通过this.gameObject.tag获取或设置物体标签 - 层级:
layer
通过this.gameObject.layer获取或设置物体层级 - 变换组件:
transform
通过this.gameObject.transform获取物体的变换组件(与this.transform相同)
1 | // GameObject的成员变量 |
静态方法
- 创建物体:
GameObject.CreatePrimitive(PrimitiveType type)
创建一个基本的几何体物体,参数为几何体类型1
2
3// 创建自带几何体
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.name = "MyCube"; - 查找对象:
- 查找单个物体
GameObject.Find(string name)根据名称查找物体,返回第一个找到的物体(这个方法效率较低)GameObject.FindWithTag(string tag)根据标签查找物体,返回第一个找到的物体GameObject.FindGameObjectWithTag(string tag)根据标签查找物体,返回第一个找到的物体(效果与上面的一样)- 上面的两个方法都无法找到未激活的对象,且如果场景中存在多个物体,也无法精确定位到具体的物体
- 查找多个物体
GameObject.FindGameObjectsWithTag(string tag)根据标签查找物体,返回所有找到的物体数组- 也是只能找到激活的物体
- 用的比较少的方法(效率更低,是Object类中的方法)
GameObject.FindObjectsOfType<T>()根据类型查找物体,返回所有找到的物体数组(泛型可填特定脚本的名字,用来找到挂载特定脚本的物体)GameObject.FindObjectOfType<T>()根据类型查找物体,返回第一个找到的物体
- 查找单个物体
1 | // 查找场景中的物体 |
- 实例化(克隆)对象(动态创建物体)
- 准备用来克隆的对象必须是预设体或者场景中的物体
GameObject.Instantiate(GameObject original)克隆一个物体,返回克隆后的物体Instantiate方法有多个重载,可以指定位置、旋转和父物体
1 | // 实例化物体 |
- 销毁对象
GameObject.Destroy(GameObject obj)销毁一个物体- 同样也有多个重载,可以指定延迟时间
Destroy也可以销毁组件和脚本- 移除操作是在下一帧进行的,并从内存中移除
DestroyImmediate立即销毁物体,一般只在编辑器模式下使用
1 | // 销毁物体 |
- 过场景不移除物体
GameObject.DontDestroyOnLoad(GameObject obj)设置物体在场景切换时不被销毁1
2// 不销毁物体
GameObject.DontDestroyOnLoad(obj);
成员方法
创建空物体:
new GameObject(string name)
创建一个空的物体,参数为物体名称(可选),也可以加脚本1
2
3
4
5
6// 创建空物体
GameObject obj4 = new GameObject("MyEmptyObject");
print(obj4.name);
// 创建空物体并添加脚本
GameObject obj5 = new GameObject("MyScriptedObject", typeof(Lesson2));
print(obj5.name);添加脚本组件:
AddComponent<T>()
为物体添加一个脚本组件,返回添加的脚本组件1
2
3
4
5
6// 为物体添加脚本组件
Lesson2 lesson = obj4.AddComponent<Lesson2>();
print(lesson);
// 也可以用类型方式添加(不常用)
Lesson2 lesson2 = obj4.AddComponent(typeof(Lesson2)) as Lesson2;
print(lesson2);标签比较:
CompareTag(string tag)
比较物体的标签是否与指定标签相同,返回布尔值1
2
3
4
5
6
7
8if (obj4.CompareTag("Player"))
{
print("This is a Player");
}
else
{
print("This is not a Player");
}设置物体激活状态:
SetActive(bool value)
设置物体是否激活1
2
3
4// 激活物体
obj4.SetActive(true);
// 禁用物体
obj4.SetActive(false);其他的方法:通过广播或者发送消息的方法,让自己或者别人执行行为方法
SendMessage(string FunName):向物体发送消息,调用物体上所有脚本中的指定方法BroadcastMessage(string FunName):向物体及其所有子物体发送消息,调用所有脚本中的指定方法
Time相关内容
Time是Unity中用于获取时间信息的类,主要用于控制游戏的时间流逝和获取时间相关的信息
时间缩放比例
Time.timeScale:获取或设置时间缩放比例,默认值为1.0,设置为0.0时游戏暂停1
2
3
4// 设置时间缩放比例
Time.timeScale = 0.5f; // 游戏时间变慢一半
Time.timeScale = 1.0f; // 恢复正常时间
Time.timeScale = 0.0f; // 暂停游戏
帧间隔时间
指最近的两帧之间的时间间隔
Time.deltaTime:获取上一帧到当前帧的时间间隔,单位为秒
它会受到时间缩放比例的影响Time.unscaledDeltaTime:获取上一帧到当前帧的时间间隔,单位为秒
它不受时间缩放比例的影响注意:在编辑器模式下,如果不设置帧率,编辑器会以CPU的最高性能运行,所以帧间隔会很小1
2
3// 获取帧间隔时间
print("Delta Time: " + Time.deltaTime);
print("Unscaled Delta Time: " + Time.unscaledDeltaTime);
实际开发这,根据需要去选择参与计算的时间间隔
游戏运行时间
指游戏从开始运行到当前的总时间(一般用于计时)
Time.time:获取游戏从开始运行到当前的总时间,单位为秒
同样它会受到时间缩放比例的影响Time.unscaledTime:获取游戏从开始运行到当前的总时间,单位为秒
它不受时间缩放比例的影响1
2
3// 获取游戏运行时间
print("Game Time: " + Time.time);
print("Unscaled Game Time: " + Time.unscaledTime);
物理帧时间
指物理引擎更新的时间间隔
Time.fixedDeltaTime:获取物理帧的时间间隔,单位为秒,不受时间缩放比例影响
它是一个固定值,默认值为0.02(即50帧每秒),在项目设置的Time中可以调整1
2
3// 获取物理帧时间
print("Fixed Delta Time: " + Time.fixedDeltaTime);
print("Fixed Unscaled Delta Time: " + Time.fixedUnscaledDeltaTime);
帧率相关
Time.frameCount:获取从游戏开始运行到当前的总帧数1
2// 获取总帧数
print("Frame Count: " + Time.frameCount);
Transform相关内容
Transform是Unity中用于表示物体位置、旋转和缩放的组件,每个GameObject都有一个Transform组件
Vector3
Vector3是Unity中用于表示三维向量和位置的结构体,包含x、y、z三个分量
创建Vector3
1
2Vector3 v1 = new Vector3(1.0f, 2.0f, 3.0f);
Vector3 v2 = new Vector3(); // 默认值为(0, 0, 0)基本运算
1
2
3
4
5
6
7
8
9
10Vector3 a = new Vector3(1.0f, 2.0f, 3.0f);
Vector3 b = new Vector3(4.0f, 5.0f, 6.0f);
Vector3 add = a + b; // 向量加法
Vector3 sub = a - b; // 向量减法
Vector3 mul = a * 2.0f; // 向量数乘
Vector3 div = a / 2.0f; // 向量数除
float dot = Vector3.Dot(a, b); // 向量点积
Vector3 cross = Vector3.Cross(a, b); // 向量叉积
float magnitude = a.magnitude; // 向量长度
Vector3 normalized = a.normalized; // 单位向量一些常用的量
1
2
3
4
5
6
7
8Vector3 v3 = Vector3.zero; // 零向量(0, 0, 0)
Vector3 v4 = Vector3.one; // 单位向量(1, 1, 1)
Vector3 v5 = Vector3.up; // 上方向向量(0, 1, 0)
Vector3 v6 = Vector3.down; // 下方向向量(0, -1, 0)
Vector3 v7 = Vector3.left; // 左方向向量(-1, 0, 0)
Vector3 v8 = Vector3.right; // 右方向向量(1, 0, 0)
Vector3 v9 = Vector3.forward; // 前方向向量(0, 0, 1)
Vector3 v10 = Vector3.back; // 后方向向量(0, 0, -1)计算距离的方法
Distance:计算两个点(向量)之间的距离1
2
3
4Vector3 p1 = new Vector3(1.0f, 2.0f, 3.0f);
Vector3 p2 = new Vector3(4.0f, 5.0f, 6.0f);
float distance = Vector3.Distance(p1, p2);
print("Distance: " + distance);
位置
- 获取或设置物体的位置
transform.position:获取或设置物体在世界坐标系中的位置transform.localPosition:获取或设置物体在父物体坐标系中的位置
位置的设置不能单一改变某一个分量,只能整体赋值
1 | // 获取物体位置 |
或者可以先获取位置,然后修改某个分量,再赋值回去
1 | Vector3 pos = transform.position; |
- 对象的朝向
transform.forward:获取物体的前方向向量transform.up:获取物体的上方向向量transform.right:获取物体的右方向向量1
2
3
4
5
6Vector3 forward = transform.forward;
Vector3 up = transform.up;
Vector3 right = transform.right;
print("Forward: " + forward);
print("Up: " + up);
print("Right: " + right);
位移
理解坐标系下的位移计算
自己计算:当前位置 + 方向向量 * 速度 * 时间
1
this.transform.position += this.transform.forward * 5.0f;
API计算:
Transform.Translate(Vector3 translation)translation:位移向量relativeTo:(可选参数)相对空间,默认是相对于自身坐标系(Space.Self),也可以选择相对于世界坐标系(Space.World)1
2this.transform.Translate(Vector3.forward * 5.0f);
this.transform.Translate(Vector3.forward * 5.0f, Space.World);
角度与旋转
获取或设置物体的旋转
transform.rotation:获取或设置物体在世界坐标系中的旋转,返回值为四元数(Quaternion)transform.localRotation:获取或设置物体在父物体坐标系中的旋转,返回值为四元数(Quaternion)transform.eulerAngles:获取或设置物体在世界坐标系中的旋转,返回值为欧拉角(Vector3)transform.localEulerAngles:获取或设置物体在父物体坐标系中的旋转,返回值为欧拉角(Vector3)1
2
3
4
5
6
7
8
9
10// 获取物体旋转
Quaternion worldRotation = transform.rotation;
Quaternion localRotation = transform.localRotation;
Vector3 worldEulerAngles = transform.eulerAngles;
Vector3 localEulerAngles = transform.localEulerAngles;
// 设置物体旋转
transform.rotation = Quaternion.Euler(0, 90, 0);
transform.localRotation = Quaternion.Euler(0, 45, 0);
transform.eulerAngles = new Vector3(0, 90, 0);
transform.localEulerAngles = new Vector3(0, 45, 0);
旋转物体
Transform.Rotate(Vector3 eulerAngles):绕每个轴旋转指定的欧拉角Transform.Rotate(Vector3 axis, float angle):绕指定轴旋转指定的角度relativeTo:(可选参数)相对空间,默认是相对于自身坐标系(Space.Self),也可以选择相对于世界坐标系(Space.World)1
2this.transform.Rotate(new Vector3(0, 90, 0));
this.transform.Rotate(new Vector3(0, 90, 0), Space.World);
也可以让它实现自转
1 | this.transform.Rotate(Vector3.up * 90.0f * Time.deltaTime); |
相对于某个轴旋转
1 | this.transform.Rotate(Vector3.right, 90.0f * Time.deltaTime); |
绕轴旋转中的RotateAround方法已弃用,所以直接使用transform.Rotate来实现
相对于一个点旋转
1 | this.transform.RotateAround(Vector3.zero, Vector3.up, 90.0f * Time.deltaTime); |
缩放与看向
- 获取或设置物体的缩放
transform.localScale:获取或设置物体在父物体坐标系中的缩放,返回值为Vector3transform.lossyScale:获取物体在世界坐标系中的缩放,返回值为Vector3(只读)
世界坐标系下的缩放不能修改,只能修改局部缩放
且不能只修改某一个分量,只能整体赋值
1 | transform.localScale = new Vector3(2, 2, 2); |
Unity也没有直接设置缩放的API,只能通过修改localScale来实现
1 | transform.localScale += Vector3.one * Time.deltaTime; |
可以通过上面的方式来实现逐渐放大
- 让物体看向某个点
Transform.LookAt(Vector3 target):让物体朝向指定的目标点1
2transform.LookAt(new Vector3(0, 0, 0));
transform.LookAt(targetTransform.position);
Transform.LookAt(Transform target):让物体朝向指定的目标物体这个方法可以用在摄像机上,让摄像机始终看向某个物体1
transform.LookAt(targetTransform);
父子关系
获取或设置物体的父物体
transform.parent:获取或设置物体的父物体的Transform组件transform.SetParent(Transform parent):设置物体的父物体transform.SetParent(Transform parent, bool worldPositionStays):设置物体的父物体,并指定是否保持世界位置不变1
2
3
4
5
6// 获取
Transform parentTransform = transform.parent;
// 设置
transform.parent = newParentTransform;
transform.parent = null; // 清除父对象
transform.SetParent("Cube");
去除所有子对象
transform.DetachChildren():将物体的所有子物体从该物体中分离出来,变为独立的物体1
transform.DetachChildren();
获取子对象
transform.Find(string name):按名字查找子物体,返回第一个找到的子物体的Transform组件(Find能找到非激活的子物体)transform.childCount:获取子物体的数量1
2Transform childTransform = transform.Find("ChildName");
int childCount = transform.childCount;
根据索引获取子物体
1 | for (int i = 0; i < transform.childCount; i++) |
- 子对象的操作
transform.IsChildOf(Transform parent):判断物体是否是指定父物体的子物体,返回布尔值transform.GetSiblingIndex():获取物体在兄弟物体中的索引transform.SetAsFirstSibling():将物体设置为兄弟物体中的第一个transform.SetAsLastSibling():将物体设置为兄弟物体中的最后一个transform.SetSiblingIndex(int index):将物体设置为兄弟物体中的指定索引位置(超出范围则设置为最后一个)
坐标转换
世界坐标系转换为局部坐标系
transform.InverseTransformPoint(Vector3 position):将世界坐标系中的点转换为物体的局部坐标系中的点(会受缩放影响)transform.InverseTransformDirection(Vector3 direction):将世界坐标系中的方向转换为物体的局部坐标系中的方向(不受缩放影响)transform.InverseTransformVector(Vector3 vector):将世界坐标系中的向量转换为物体的局部坐标系中的向量(会受缩放影响)1
2Vector3 localPos = transform.InverseTransformPoint(new Vector3(10, 0, 0));
print("Local Position: " + localPos);
局部坐标系转换为世界坐标系
transform.TransformPoint(Vector3 pos):将局部坐标系的点转换到世界坐标系中transform.TransformDirection(Vector3 direction):将局部坐标系的方向转换到世界坐标系中transform.TransformVector(Vector3 vector):将局部坐标系中的向量转换到世界坐标系中
输入Input
鼠标所在位置
Input.mousePosition:获取鼠标在屏幕坐标系中的位置,返回值为Vector3,z分量为0屏幕坐标的原点是在左下角,x轴向右,y轴向上1
2Vector3 mousePos = Input.mousePosition;
print("Mouse Position: " + mousePos);
鼠标按键状态
Input.GetMouseButtonDown(int button):鼠标按下一瞬间执行,返回布尔值1
2
3
4if (Input.GetMouseButtonDown(0))
{
print("Left Mouse Button is just pressed");
}Input.GetMouseButtonUp(int button):鼠标抬起一瞬间执行,返回布尔值1
2
3
4if (Input.GetMouseButtonUp(0))
{
print("Left Mouse Button is just released");
}Input.GetMouseButton(int button):鼠标持续按下时执行,返回布尔值1
2
3
4if (Input.GetMouseButton(0))
{
print("Left Mouse Button is being held down");
}button参数表示鼠标按键,0表示左键,1表示右键,2表示中键Input.mouseScrollDelta:获取鼠标滚轮的滚动增量,返回值为Vector2,y分量表示滚动方向和幅度1
2Vector2 scrollDelta = Input.mouseScrollDelta;
print("Mouse Scroll Delta: " + scrollDelta);-1表示向下滚动,1表示向上滚动,0表示没有滚动
键盘按键状态
Input.GetKeyDown(KeyCode key):按键按下一瞬间执行,返回布尔值1
2
3
4
5
6
7
8
9if (Input.GetKeyDown(KeyCode.Space))
{
print("Space key is just pressed");
}
// 另一个重载方法(不能大写)
if (Input.GetKeyDown("space"))
{
print("Space key is just pressed");
}Input.GetKeyUp(KeyCode key):按键抬起一瞬间执行,返回布尔值1
2
3
4if (Input.GetKeyUp(KeyCode.Space))
{
print("Space key is just released");
}Input.GetKey(KeyCode key):按键持续按下时执行,返回布尔值1
2
3
4if (Input.GetKey(KeyCode.Space))
{
print("Space key is being held down");
}KeyCode枚举包含了所有键盘按键的定义
输入轴
控制轴输入是一种抽象的输入方式,可以通过配置输入轴来实现对多个按键或控制器的支持
也是Unity为开发者提供的一种方便的输入处理方式,更好地处理对象的移动和操作
- AD键返回-1至1之间的值,A键为-1,D键为1,X轴
- WS键返回-1至1之间的值,S键为-1,W键为1,Y轴
在Edit -> Project Settings -> Input Manager中可以查看和修改输入轴的配置
Input.GetAxis(string axisName):获取指定输入轴的值,返回值为浮点数1
2
3
4
5
6
7float horizontal = Input.GetAxis("Horizontal"); // X轴
float vertical = Input.GetAxis("Vertical"); // Y轴
print("Horizontal: " + horizontal + ", Vertical: " + vertical);
float mouseX = Input.GetAxis("Mouse X"); // 鼠标X轴
float mouseY = Input.GetAxis("Mouse Y"); // 鼠标Y轴
print("Mouse X: " + mouseX + ", Mouse Y: " + mouseY);Input.GetAxisRaw(string axisName):获取指定输入轴的原始值,返回值为浮点数
它的返回值没有中间值,只有-1、0、1三种状态1
2
3float horizontalRaw = Input.GetAxisRaw("Horizontal"); // X轴
float verticalRaw = Input.GetAxisRaw("Vertical"); // Y轴
print("Horizontal Raw: " + horizontalRaw + ", Vertical Raw: " + verticalRaw);
其他
Input.anyKey:检测是否有任意按键被按下,返回布尔值1
2
3
4if (Input.anyKey)
{
print("Some key is being pressed");
}Input.anyKeyDown:检测是否有任意按键刚刚被按下,返回布尔值1
2
3
4if (Input.anyKeyDown)
{
print("Some key is just pressed");
}Input.inputString:获取当前帧输入的字符串,返回值为字符串1
2string inputStr = Input.inputString;
print("Input String: " + inputStr);Input.GetJoystickNames():获取连接的所有手柄名称,返回值为字符串数组1
2
3
4
5string[] joystickNames = Input.GetJoystickNames();
foreach (string name in joystickNames)
{
print("Joystick Name: " + name);
}Input.touches:获取当前所有触摸点的信息,返回值为Touch数组1
2
3
4
5Touch[] touches = Input.touches;
foreach (Touch touch in touches)
{
print("Touch Position: " + touch.position);
}Input.touchCount:获取当前触摸点的数量,返回值为整数1
2int touchCount = Input.touchCount;
print("Touch Count: " + touchCount);Input.multiTouchEnabled:获取或设置是否启用多点触控,返回值为布尔值1
2
3bool isMultiTouchEnabled = Input.multiTouchEnabled;
print("Multi-Touch Enabled: " + isMultiTouchEnabled);
Input.multiTouchEnabled = true; // 启用多点触控Input.gyro:获取设备的陀螺仪信息,返回值为Gyroscope对象1
2
3
4
5Gyroscope gyro = Input.gyro;
print("Gyro Attitude: " + gyro.attitude);
gyro.enabled = true; // 启用陀螺仪
print("Gravity: " + Input.gyro.gravity); // 获取重力加速度
print("Rotation Rate: " + Input.gyro.rotationRate); // 获取旋转速率
Screen相关内容
Screen类用于获取和设置屏幕相关的信息
屏幕分辨率
Screen.currentResolution:获取当前屏幕(显示器)的分辨率,返回值为Resolution结构体currentResolution结构体包含以下成员变量:width:屏幕宽度,单位为像素height:屏幕高度,单位为像素refreshRate:屏幕刷新率,单位为赫兹Hz1
2Resolution res = Screen.currentResolution;
print("Current Resolution: " + res.width + "x" + res.height + " @ " + res.refreshRate + "Hz");
Screen.width:获取屏幕的宽度,单位为像素Screen.height:获取屏幕的高度,单位为像素
以上两个属性返回当前游戏窗口的宽度和高度1
2print("Screen Width: " + Screen.width);
print("Screen Height: " + Screen.height);Screen.sleepTimeout:获取或设置屏幕休眠超时时间,单位为秒- 设置为
SleepTimeout.NeverSleep表示永不休眠1
2Screen.sleepTimeout = SleepTimeout.NeverSleep;
print("Sleep Timeout: " + Screen.sleepTimeout);
- 设置为
全屏与窗口模式
Screen.fullScreen:获取或设置是否为全屏模式,返回值为布尔值1
2
3
4Screen.fullScreen = true; // 设置为全屏模式
print("Is Fullscreen: " + Screen.fullScreen);
Screen.fullScreen = false; // 设置为窗口模式
print("Is Fullscreen: " + Screen.fullScreen);Screen.fullScreenMode:获取或设置全屏模式,返回值为FullScreenMode枚举FullScreenMode.ExclusiveFullScreen:独占全屏模式FullScreenMode.FullScreenWindow:全屏窗口模式FullScreenMode.MaximizedWindow:最大化窗口模式FullScreenMode.Windowed:窗口模式1
2
3
4Screen.fullScreenMode = FullScreenMode.FullScreenWindow;
print("FullScreen Mode: " + Screen.fullScreenMode);
Screen.fullScreenMode = FullScreenMode.Windowed;
print("FullScreen Mode: " + Screen.fullScreenMode);
移动设备屏幕方向
Screen.autoRotationToLandscapeLeft:获取或设置是否允许自动旋转到左横屏模式,返回值为布尔值Screen.autoRotationToLandscapeRight:获取或设置是否允许自动旋转到右横屏模式,返回值为布尔值Screen.autoRotationToPortrait:获取或设置是否允许自动旋转到竖屏模式,返回值为布尔值Screen.autoRotationToPortraitUpsideDown:获取或设置是否允许自动旋转到倒竖屏模式,返回值为布尔值Screen.orientation:获取或设置屏幕的当前方向,返回值为ScreenOrientation枚举ScreenOrientation.Portrait:竖屏模式ScreenOrientation.LandscapeLeft:左横屏模式ScreenOrientation.LandscapeRight:右横屏模式ScreenOrientation.PortraitUpsideDown:倒竖屏模式ScreenOrientation.AutoRotation:自动旋转模式
设置分辨率
Screen.SetResolution(int width, int height, bool fullscreen):设置屏幕分辨率和全屏模式width:屏幕宽度,单位为像素height:屏幕高度,单位为像素fullscreen:是否为全屏模式,布尔值1
2
3
4Screen.SetResolution(1920, 1080, true);
print("Resolution Set to 1920x1080 Fullscreen");
Screen.SetResolution(1280, 720, false);
print("Resolution Set to 1280x720 Windowed");
Camera相关内容
以下内容是组件中的参数设置
Clear Flags
Skybox:使用天空盒清除背景Solid Color:使用纯色清除背景Depth Only:只清除深度缓冲区,只画该层,背景透明Don't Clear:不清除任何缓冲区,直接在上面绘制
Culling Mask
- 用于设置摄像机渲染哪些层级的物体,可以通过勾选或取消勾选来控制摄像机的渲染范围
Projection
Perspective:透视投影FOV Axis:视野轴,可以选择水平视野(Horizontal)或垂直视野(Vertical)Field of View:视野角度,表示摄像机的视野范围,单位为度Physical Camera:物理摄像机选项,启用后可以模拟真实摄像机的参数
Orthographic:正交投影,一般用于2D游戏开发Size:正交大小,表示摄像机的视野范围
Clipping Planes
Near:近裁剪面,表示摄像机能够看到的最近距离(即离摄像机多近的物体会被渲染)Far:远裁剪面,表示摄像机能够看到的最远距离(即离摄像机多远的物体会被渲染)
Depth
- 用于设置摄像机的渲染顺序,数值越大,渲染优先级越高
在制作UI界面时,通常会将UI摄像机的Depth值设置得比主摄像机高,以确保UI元素能够正确显示在前景,同时也可以保证后面的3D场景不会被UI遮挡
Target Texture
- 用于将摄像机的渲染结果输出到一个Render Texture(渲染纹理)中,而不是直接显示在屏幕上,主要用于制作小地图等
Occlusion Culling
- 用于启用或禁用遮挡剔除功能,提升渲染性能
当摄像机启用遮挡剔除功能时,只有摄像机视野内且未被其他物体遮挡的物体才会被渲染,从而减少不必要的渲染计算,提高游戏性能
Viewport Rect
- 用于设置摄像机在屏幕上的显示区域,参数包括X、Y、Width、Height,取值范围为0到1
Rendering Path
- 用于设置摄像机的渲染路径,常用的有以下几种:
Use Graphics Settings:使用图形设置中的渲染路径Forward:前向渲染路径Deferred:延迟渲染路径Legacy Vertex Lit:传统顶点光照渲染路径
Allow HDR
- 用于启用或禁用高动态范围渲染(HDR),提升图像质量
Allow MSAA
- 用于启用或禁用多重采样抗锯齿(MSAA),提升图像质量
Allow Dynamic Resolution
- 用于启用或禁用动态分辨率调整,提升性能
Target Display
- 用于设置摄像机渲染到哪个显示器,适用于多显示器环境
代码相关
Camera.main:获取场景中标记为MainCamera的摄像机1
Camera mainCamera = Camera.main;
Camera.allCameras:得到所有摄像机1
Camera[] allCameras = Camera.allCameras;
Camera.current:获取当前正在渲染的摄像机1
Camera currentCamera = Camera.current;
Camera.onPreCull:委托,剔除前处理Camera.onPreRender:委托,渲染前处理Camera.onPostRender:委托,渲染后处理Camera.main.depth:获取或设置主摄像机的深度Camera.main.WorldToScreenPoint:世界坐标转换为屏幕坐标(比如敌人上方的血条)Camera.main.ScreenToWorldPoint:屏幕坐标转换为世界坐标(注意传入坐标的z,为0则直接在摄像机焦点上,不为0则在距离该距离的横截面上)





