Unity稳定手册

Unity6->Help->Unity Manual
Unity6文档手册:https://docs.unity3d.com/cn/current/Manual/UnityManual.html

创建与使用脚本

创建脚本

  1. Project目录->右键->Create->Scripting->Monobehaviour Script
  2. 重命名脚本

使用脚本

  1. 拖拽到目标物体上
  2. 目标物体Inspector面板->AddComponent->New script(起名,创建新脚本)

基础模板(新建默认脚本)

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

// 脚本类名必须和文件名完全一致
public class TestScript : MonoBehaviour
{
// 物体启用时执行一次,游戏开始前
void Start()
{
Debug.Log("游戏启动");
}

// 每帧执行,帧间隔不固定
void Update()
{
// 每帧都会跑
}
}

两个最核心生命周期

  1. Start():脚本启用,物体激活,只执行1次,做初始化。
  2. Update():每帧调用,用来处理输入、移动、旋转。

MonoBehaviour脚本生命周期内事件函数的执行顺序


![[unity6-study-notes-2-lifecycle.png]]

  1. Editor编辑阶段(仅编辑器)
    Reset()
    当组件挂载物体,或点击Inspector的Reset按钮时调用。
  2. Initialization初始化阶段(进入运行模式)
    1. Awake() 物体激活时调用,仅一次;不受脚本勾选与否影响,只要物体active就执行。
    2. OnEnable() 物体/脚本启用激活时调用,可以多次执行(反复开关物体都会触发)。
    3. Start() 在第一帧Update之前执行;必须脚本勾选启用才会执行,仅一次。
  3. Game Loop游戏主循环(每帧循环执行)

    每一轮帧的顺序: FixedUpdate()→Update()→LateUpdate()

  • FixedUpdate() 固定时间间隔执行(默认 0.02s),不受帧率波动影响,刚体物理代码放这里。
  • Update() 每帧执行一次。输入检测、普通移动逻辑写这里。
  • LateUpdate() 所有物体的Update全部执行完毕之后才调用。适合相机跟随、物体跟随逻辑。
  • OnGUI() 每帧会执行多次,用于旧版IMGUI界面绘制,性能差,新项目尽量不用。
  1. Deactivation/Destruction 禁用与销毁
    1. OnDisable() 物体/脚本取消启用、SetActive(false)时调用,可以多次执行。
    2. OnDestroy() 物体被Destroy销毁时调用,仅一次,用于资源释放、清理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Reset()
↓
Awake()
↓
OnEnable()
↓
Start()
↓
┌─────────────────────┐
│ FixedUpdate() │
│ ↓ │
│ Update() │
│ ↓ │ Repeat every frame
│ LateUpdate() │
│ ↓ │
│ OnGUI() │
└─────────────────────┘
↓
OnDisable()
↓
OnDestroy()

Debug.Log

Debug.Log():在 Console 控制台打印信息,用来调试代码、看变量值、判断代码有没有执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 普通信息:白色文字 
Debug.Log("普通日志");
// 警告:黄色感叹号,提醒潜在问题
Debug.LogWarning("警告信息");
// 错误:红色,代码报错,会暂停(不中断游戏,但标记错误)
Debug.LogError("错误信息");


// 使用 LogFormat 格式化输出
float speed = 5;
Debug.Log("移动速度 = " + speed);
// 插值写法(推荐,更清晰)
Debug.Log($"移动速度 = {speed}");
Debug.Log("这是玩家", gameObject);
int score = 100;
string playerName = "Player1";
Debug.LogFormat("玩家 {0} 的得分为: {1}", playerName, score);

// 使用富文本标记颜色和粗体
Debug.Log("<color=red><b>致命错误:</b></color> 资源包未找到");
Debug.Log("<color=green>操作成功!</color>");

Console 窗口常用功能

打开方式:Window->General->Console,快捷键 Ctrl + Shift + C。

  • Clear:清除脚本输出的日志。
  • Collapse:折叠重复日志,避免刷屏。
  • Clear On Play:每次进入Play模式自动清空。
  • Error Pause:遇到LogError自动暂停,方便断点式排查。
  • 搜索/过滤:可按关键字、日志级别筛选。
  • 三个图标:分别控制 Log/Warning/Error 是否显示
  • 堆栈跟踪:可设为 None/ScriptOnly/Full,Full更详细但更耗性能。

频繁调用 Debug.Log 会带来性能开销,因为它涉及字符串分配和堆栈跟踪解析。

获取用户输入

Unity获取用户输入主要分两套:旧Input Manager 和 新Input System。新项目、跨平台项目建议优先用新系统;小Demo、原型、学习阶段用旧系统更直接。

旧输入系统Input Manager

通过UnityEngine.Input访问,适合快速写输入逻辑。

需求 旧系统写法 说明
按键按下 Input.GetKeyDown(KeyCode.Space) 只在按下的那一帧返回 true
按键按住 Input.GetKey(KeyCode.W) 按住期间每帧返回 true
按键抬起 Input.GetKeyUp(KeyCode.Escape) 抬起那一帧返回 true
鼠标按下 Input.GetMouseButtonDown(0) 0 左键,1 右键,2 中键
鼠标按住 Input.GetMouseButton(0) 按住期间持续返回 true
轴输入 Input.GetAxis("Horizontal") 返回 -1 ~ 1,带平滑
原始轴输入 Input.GetAxisRaw("Horizontal") 返回 -1 / 0 / 1,无平滑

移动、转向类输入优先用GetAxis;跳跃、射击这类动作事件用GetButtonDown或GetKeyDown。

按住持续触发

1
2
3
4
5
6
7
8
9
// GetKey:按住就一直返回true
if (Input.GetKey(KeyCode.W))
{
Debug.Log("W按住,向前");
}
if (Input.GetKey(KeyCode.S))
{
Debug.Log("S按住,向后");
}

按下瞬间

1
2
3
4
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("空格 按下一瞬间");
}

松开瞬间

1
2
3
4
if (Input.GetKeyUp(KeyCode.Space))
{
Debug.Log("空格松开");
}

轴向输入(推荐WASD移动)
Horizontal水平(A/D)、Vertical垂直(W/S),返回值 -1 ~ 0 ~ +1。

1
2
3
4
5
6
7
// A/D 左右:-1左,+1右,没按0
float h = Input.GetAxis("Horizontal");
// W/S 前后:-1后,+1前
float v = Input.GetAxis("Vertical");

Vector3 dir = new Vector3(h,0,v);
transform.Translate(dir * speed * Time.deltaTime);

优点:自带平滑过渡,按键松开不会瞬间归零,手感更好。

注意:需要在 Edit > Project Settings > Input Manager 里查看轴配置(默认自带 Horizontal、Vertical)

鼠标输入

1
2
3
4
5
6
7
8
9
10
11
12
// 鼠标左键按下瞬间
if(Input.GetMouseButtonDown(0))
{
Debug.Log("鼠标左键点击");
}
// 鼠标右键 1,中键 2
if(Input.GetMouseButton(0)) //按住左键持续
{

}
// 鼠标屏幕坐标
Vector2 mousePos = Input.mousePosition;

鼠标滚轮

1
float scroll = Input.GetAxis("Mouse ScrollWheel");

新输入系统Input System

需要先安装包:Package Manager→搜索Input System,安装;安装后会提示重启Unity。
支持键盘、手柄、触屏、VR,一套代码适配多设备。
需要引入命名空间using UnityEngine.InputSystem;

暂略

输入检测必须写在Update(),不能写在FixedUpdate中
FixedUpdate是物理帧,和渲染帧不同步,按键会漏检测。

transform组件的获取和位置控制

Transform是Unity中每个GameObject必带且不可移除的组件,负责管理对象的位置、旋转、缩放和父子层级。

位置(Position)

属性 含义
transform.position 世界坐标
transform.localPosition 相对于父物体的局部坐标
transform.forward 自身 Z 轴正方向(世界空间)
transform.right 自身 X 轴正方向(世界空间)
transform.up 自身 Y 轴正方向(世界空间)
1
2
3
4
5
6
7
8
9
10
11
12
// 直接赋值(必须整体赋值,不能单独改 .x/.y/.z) 
transform.position = new Vector3(0, 5, 0);
// 修改单个分量
Vector3 pos = transform.position;
pos.x = 10;
transform.position = pos;

transform.position = transform.position + new Vector3(0.1f, 0, 0);
// 沿自身前方移动
transform.Translate(Vector3.forward * speed * Time.deltaTime);
// 沿世界坐标移动
transform.Translate(Vector3.up * speed * Time.deltaTime, Space.World);

旋转(Rotation)

属性 含义
transform.rotation 世界旋转(Quaternion)
transform.localRotation 局部旋转(Quaternion)
transform.eulerAngles 世界欧拉角(Vector3,度)
transform.localEulerAngles 局部欧拉角
1
2
3
4
5
6
7
8
// 设置旋转(推荐用 Quaternion) 
transform.rotation = Quaternion.Euler(0, 90, 0);
// 增量旋转
transform.Rotate(0, 30 * Time.deltaTime, 0);
// 看向目标
transform.LookAt(target);
// 绕某点旋转
transform.RotateAround(point, Vector3.up, 45 * Time.deltaTime);

缩放(Scale)

属性 含义
transform.localScale 相对于父物体的缩放
transform.lossyScale 全局缩放(只读)
1
2
transform.localScale = new Vector3(2, 2, 2);
transform.localScale = transform.localScale + new Vector3(0.1f, 0.1f, 0.1f);

Tag

Tag(标签),给游戏物体打标记,用来区分物体类型,方便代码快速识别、筛选物体。一个物体只能有1个Tag。

内置默认Tag

  • Untagged:无标签(默认)
  • Respawn:重生物体
  • Finish:终点
  • EditorOnly:仅编辑器存在,打包后消失
  • MainCamera:主相机
  • Player:玩家(最常用)

创建自定义Tag

  1. Hierarchy选中物体→Inspector上方Tag下拉框
  2. 点击”Add Tag…”按钮
  3. 点+号,输入标签名字

⚠️注意:Tag名字区分大小写,Enemy 和 enemy 是两个不同标签!

1
2
3
4
5
6
7
8
9
10
11
12
//判断当前物体的标签
if (gameObject.CompareTag("Player"))
{
Debug.Log("这是玩家");
}
// 查找场景里"第一个"带Player标签的物体
GameObject player = GameObject.FindWithTag("Player");

// 查找场景"所有"带Enemy标签的物体,返回数组
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
//给物体动态设置标签
gameObject.tag = "Enemy";

public属性

1
public float speed = 5f;
  1. 可以在Inspector面板直接看到、修改这个变量,不用改代码、不用重新编译。其他脚本也能访问这个变量。
  2. private私有变量,默认 Inspector 看不到,别的脚本不能直接访问。

Collider

Collider:碰撞体,形状外壳,用来检测相交、物理碰撞。

基础规则

  1. 勾选Is Trigger→这个Collider变成触发器
    • ❌不再发生物理撞击,不会挡住物体,直接穿过去
    • ✅只检测物体是否进入/停留在/离开这个区域
  2. 不勾选Is Trigger(默认):物理碰撞体
    • ✅有阻挡,物体撞上去会停下、反弹(Rigidbody才会响应物理)
    • ✅触发碰撞回调函数OnCollisionXXX

碰撞事件Collision

OnCollision 参数:Collision collision,需要 .gameObject 拿物体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void OnCollisionEnter(Collision collision)
{
Debug.Log("CubeA: Collision Enter");
}

private void OnCollisionExit(Collision collision)
{
Debug.Log("CubeA: Collision Exit");
}

private void OnCollisionStay(Collision collision)
{
Debug.Log("CubeA: Collision Stay");
}

触发事件OnTriggerXXX

至少其中一个物体带有Rigidbody刚体!

简单记:
物体A(触发器Collider+IsTrigger)
物体B(Collider)
A或者B,必须有Rigidbody,OnTrigger才会执行。

小提醒:Rigidbody可以勾选Is Kinematic(运动学刚体)也能触发。

Unity手册: 不同类型碰撞体之间的交互https://docs.unity3d.com/cn/current/Manual/collider-types-interaction.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void OnTriggerEnter(Collider other)
{
Debug.Log("CubeA: Trigger Enter");
}

private void OnTriggerExit(Collider other)
{
Debug.Log("CubeA: Trigger Exit");
}

private void OnTriggerStay(Collider other)
{
Debug.Log("CubeA: Trigger Stay");
}
1
2
3
4
5
6
7
8
9
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Robot"))
{
Destroy(gameObject);
Instantiate(vfx, transform.position, Quaternion.identity);
}

}

GetComponent

GetComponent 是Unity里获取组件引用的核心方法,主要用来访问同一个GameObject上的脚本、刚体、渲染器、碰撞器等组件。

Transform不需要GetComponent,直接transform就能访问,是GameObject内置快捷变量。

方法 作用
GetComponent<T>() 获取当前对象上第一个 T 类型组件
GetComponents<T>() 获取当前对象上所有 T 类型组件,返回数组
GetComponentInChildren<T>() 从自身和子物体中查找第一个匹配组件
GetComponentInParent<T>() 从自身和父物体中查找第一个匹配组件
GetComponentsInChildren<T>() 获取自身和所有子物体中的全部匹配组件
GetComponentsInParent<T>() 获取自身和所有父物体中的全部匹配组件
TryGetComponent<T>(out T) 安全获取,避免 null 引用
1
2
3
4
5
if (Input.GetKeyDown(KeyCode.Space))
{
Rigidbody rigidbody = GetComponent<Rigidbody>();
rigidbody.AddForce(Vector3.up*5, ForceMode.VelocityChange);
}

获取组件后一定要判空,不然找不到组件会报NullReferenceException空引用崩溃。

1
2
3
4
5
Rigidbody rb = GetComponent<Rigidbody>();
if(rb != null)
{
rb.velocity = Vector3.right;
}

❌不要在Update/FixedUpdate里面反复调用GetComponent!
Update每帧执行,频繁查找物体组件开销大。
✅ 正确:在 tart/Awake一次性获取,存到变量