关于 unity-csharp-scripting
面向 Unity 开发者的 C# 脚本参考,涵盖生命周期、物理、动画、音频、序列化、设计模式及 ECS/DOTS,并提供速查内容和详细示例索引。
主要功能
- 01MonoBehaviour 生命周期指导
- 02物理与碰撞 API
- 03动画和音频脚本
- 04序列化与设计模式
- 05ECS/DOTS 概念
适用场景
- 01选择 Unity 生命周期回调
- 02实现物理与碰撞行为
- 03组织低耦合的游戏系统
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ josiahsiegel/ claude-plugin-marketplace --skill unity-csharp-scripting安装后在 Claude Code 中输入/unity-csharp-scripting确认已加载。
附带文件references/design-patterns.mdreferences/physics-animation-audio.md
详细介绍
Unity C# 脚本模式
概述
Unity 开发的核心 C# 脚本参考。涵盖 MonoBehaviour 生命周期、物理与碰撞 API、动画脚本、音频、导航、常见设计模式、序列化,以及 ECS/DOTS 编码模式。
MonoBehaviour 生命周期
执行顺序
Awake() -> OnEnable() -> Start() -> FixedUpdate() -> Update() -> LateUpdate() -> OnDisable() -> OnDestroy()
| 方法 | 调用时机 | 用途 |
|---|---|---|
Awake() | 对象实例化时调用一次(在 Start 之前) | 自我初始化、缓存引用 |
OnEnable() | 每次对象变为活动状态时调用 | 订阅事件、重置状态 |
Start() | 首次 Update 之前调用一次(所有 Awake 执行之后) | 跨对象初始化 |
FixedUpdate() | 固定时间步长(默认为 0.02s) | 物理、Rigidbody 移动 |
Update() | 每帧调用 | 输入、非物理逻辑 |
LateUpdate() | 所有 Update 调用之后 | 相机跟随、移动后调整 |
OnDisable() | 对象停用时调用 | 取消订阅事件、保存状态 |
OnDestroy() | 对象销毁时调用 | 最终清理、释放资源 |
关键规则
- 即使组件已禁用,Awake 也会运行(但 GameObject 已禁用时不会)
- 切勿依赖脚本之间的 Awake/Start 顺序——请使用
[DefaultExecutionOrder(N)]或 Script Execution Order 设置 - 使用
OnValidate()在编辑器中验证序列化字段
协程与异步
协程
IEnumerator SpawnWaves(int count, float delay)
{
for (int i = 0; i < count; i++)
{
SpawnEnemy();
yield return new WaitForSeconds(delay);
}
}
// Start: Coroutine handle = StartCoroutine(SpawnWaves(5, 1f));
// Stop: StopCoroutine(handle); or StopAllCoroutines();
| Yield 指令 | 行为 |
|---|---|
yield return null | 等待一帧 |
yield return new WaitForSeconds(t) | 等待 t 秒(受 timeScale 影响) |
yield return new WaitForSecondsRealtime(t) | 使用非缩放时间 |
yield return new WaitForFixedUpdate() | 等待下一次 FixedUpdate |
yield return new WaitForEndOfFrame() | 等待渲染完成 |
yield return new WaitUntil(() => condition) | 等待谓词为 true |
yield return StartCoroutine(other) | 等待嵌套协程 |
Async/Await(Unity 6+ / UniTask)
对于 Unity 2023+/Unity 6,Awaitable 已内置。较旧版本请使用 UniTask。
async Awaitable LoadLevelAsync(string sceneName)
{
await Awaitable.WaitForSecondsAsync(1f);
var op = SceneManager.LoadSceneAsync(sceneName);
while (!op.isDone)
{
progressBar.value = op.progress;
await Awaitable.NextFrameAsync();
}
}
事件与委托
事件模式(推荐)
// Publisher
public class Health : MonoBehaviour
{
public event System.Action<float> OnDamaged; // event keyword prevents external invocation
public event System.Action OnDeath;
public void TakeDamage(float amount)
{
currentHealth -= amount;
OnDamaged?.Invoke(amount);
if (currentHealth <= 0) OnDeath?.Invoke();
}
}
// Subscriber
void OnEnable() => health.OnDamaged += HandleDamage;
void OnDisable() => health.OnDamaged -= HandleDamage;
void HandleDamage(float amount) => /* react */;
ScriptableObject 事件通道
无需直接引用即可解耦系统。将 GameEvent 创建为 ScriptableObject 资源,由发布者调用,并通过 GameEventListener MonoBehaviour 让订阅者监听。完整实现请参阅 references/design-patterns.md。
物理 API 速查
Rigidbody 移动(3D)
| 任务 | 方法 | 位置 |
|---|---|---|
| 持续施力 | rb.AddForce(dir * force) | FixedUpdate |
| 瞬时冲量 | rb.AddForce(dir * force, ForceMode.Impulse) | FixedUpdate |
| 直接设置速度 | rb.linearVelocity = dir * speed | FixedUpdate |
| 运动学移动 | rb.MovePosition(target) | FixedUpdate |
| 旋转 | rb.MoveRotation(targetRot) | FixedUpdate |
注意:在 Unity 6 中,Rigidbody.velocity 已重命名为 Rigidbody.linearVelocity。
射线检测
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask))
{
Debug.Log($"Hit {hit.collider.name} at {hit.point}");
}
// Use Physics.RaycastAll or Physics.RaycastNonAlloc for multiple hits
// 2D: Physics2D.Raycast, Physics2D.OverlapCircle, etc.
碰撞与触发器
| 回调 | 要求 | 用途 |
|---|---|---|
OnCollisionEnter/Stay/Exit | 双方都有碰撞体,且至少一方有 Rigidbody,isTrigger=false | 物理碰撞 |
OnTriggerEnter/Stay/Exit | 一方的碰撞体设置了 isTrigger=true,且至少一方有 Rigidbody | 区域、拾取物、检测 |
始终使用 CompareTag("Enemy"),而不是 other.tag == "Enemy"(避免 GC 分配)。
动画脚本
[RequireComponent(typeof(Animator))]
public class CharacterAnimation : MonoBehaviour
{
static readonly int SpeedHash = Animator.StringToHash("Speed");
static readonly int JumpTrigger = Animator.StringToHash("Jump");
Animator _anim;
void Awake() => _anim = GetComponent<Animator>();
void Update()
{
_anim.SetFloat(SpeedHash, moveSpeed);
if (jumped) _anim.SetTrigger(JumpTrigger);
}
}
将 Animator.StringToHash 的结果缓存为 static readonly 字段,以避免每帧进行哈希计算。使用 Animation Events 实现与游戏进程时间同步的回调(脚步声、命中帧)。对于 IK,请使用 SetIKPosition/Rotation/Weight 实现 OnAnimatorIK(int layerIndex)。
音频速查
[RequireComponent(typeof(AudioSource))]
public class SFXPlayer : MonoBehaviour
{
[SerializeField] AudioClip[] clips;
AudioSource _source;
void Awake() => _source = GetComponent<AudioSource>();
public void PlayRandom() => _source.PlayOneShot(clips[Random.Range(0, clips.Length)]);
}
使用 AudioSource.PlayOneShot() 播放可重叠的音效。使用带有公开参数的 AudioMixer 控制音量。使用快照实现状态切换(战斗与探索)。
序列化
| 类型 | 已序列化 | 备注 |
|---|---|---|
public 字段 | 是 | 在 Inspector 中可见 |
[SerializeField] private | 是 | 首选方案——保持封装性 |
[HideInInspector] public | 是,隐藏 | 已序列化,但不会显示 |
[NonSerialized] public | 否 | 不参与序列化 |
| 属性 | 否 | Unity 从不序列化属性 |
| 字典 | 否 | 使用序列化列表并重建,或使用 Odin/SerializedDictionary |
| 接口 | 否 | 使用抽象 ScriptableObject 或 SerializeReference |
使用 [SerializeReference] 对接口和抽象类型进行多态序列化。
常见设计模式
| 模式 | 适用场景 | 方法 |
|---|---|---|
| 单例 | 全局管理器(Audio、GameState) | 基于 ScriptableObject 或延迟初始化的 MonoBehaviour |
| 观察者 | 解耦通信 | C# 事件或 SO 事件通道 |
| 命令 | 输入/撤销系统 | 命令接口 + 历史记录栈 |
| 状态机 | AI、玩家状态 | 枚举 + switch,或基于类的状态 |
| 对象池 | 子弹、粒子、敌人 | 使用预实例化对象的 Queue |
| 服务定位器 | 可测试的全局访问 | 使用接口键的静态注册表 |
如需查看详细实现和代码示例,请参阅 references/design-patterns.md。
ECS/DOTS 快速参考
| 概念 | 作用 |
|---|---|
| Entity | 轻量级 ID(无 MonoBehaviour) |
| IComponentData | 实体上的纯数据结构体 |
| SystemBase / ISystem | 基于组件查询运行的逻辑 |
| EntityQuery | 按组件集合筛选实体 |
| Jobs (IJobEntity) | 多线程系统 |
| Burst Compiler | SIMD 优化的原生代码 |
如需了解 ECS 架构模式和迁移指南,请参阅 references/design-patterns.md。
其他资源
参考文件
references/design-patterns.md—— 单例、观察者、命令、状态机、对象池、服务定位器和 ECS 模式的完整实现references/physics-animation-audio.md—— 详细的物理设置(2D 和 3D)、高级动画(混合树、IK、根运动、Animation Rigging)和音频架构