unity-csharp-scripting
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUnity C# Scripting Patterns
Unity C# 脚本开发模式
Overview
概述
Core C# scripting reference for Unity development. Covers MonoBehaviour lifecycle, physics and collision APIs, animation scripting, audio, navigation, common design patterns, serialization, and ECS/DOTS coding patterns.
Unity开发核心C#脚本参考手册,涵盖MonoBehaviour生命周期、物理与碰撞API、动画脚本开发、音频、导航、常见设计模式、序列化以及ECS/DOTS编码模式。
MonoBehaviour Lifecycle
MonoBehaviour 生命周期
Execution Order
执行顺序
text
Awake() -> OnEnable() -> Start() -> FixedUpdate() -> Update() -> LateUpdate() -> OnDisable() -> OnDestroy()| Method | When Called | Use For |
|---|---|---|
| Once, when object instantiates (before Start) | Self-initialization, caching references |
| Each time object becomes active | Subscribe to events, reset state |
| Once, before first Update (after all Awake) | Cross-object initialization |
| Fixed timestep (default 0.02s) | Physics, Rigidbody movement |
| Every frame | Input, non-physics logic |
| After all Update calls | Camera follow, post-movement adjustments |
| When object deactivates | Unsubscribe events, save state |
| When object is destroyed | Final cleanup, resource release |
text
Awake() -> OnEnable() -> Start() -> FixedUpdate() -> Update() -> LateUpdate() -> OnDisable() -> OnDestroy()| 方法 | 调用时机 | 适用场景 |
|---|---|---|
| 对象实例化时调用一次(在Start之前) | 自我初始化、缓存引用 |
| 对象每次激活时调用 | 订阅事件、重置状态 |
| 首次Update之前调用一次(所有Awake执行完成后) | 跨对象初始化 |
| 固定时间步长调用(默认0.02秒) | 物理系统、Rigidbody移动 |
| 每帧调用 | 输入处理、非物理逻辑 |
| 所有Update调用完成后执行 | 相机跟随、移动后调整 |
| 对象失活时调用 | 取消事件订阅、保存状态 |
| 对象被销毁时调用 | 最终清理、释放资源 |
Key Rules
核心规则
- Awake runs even on disabled components (but not disabled GameObjects)
- Never rely on Awake/Start order between scripts -- use or Script Execution Order settings
[DefaultExecutionOrder(N)] - Use for editor-time validation of serialized fields
OnValidate()
- Awake即使在禁用的组件上也会运行(但禁用的GameObject不会)
- 不要依赖不同脚本间Awake/Start的执行顺序——使用或脚本执行顺序设置
[DefaultExecutionOrder(N)] - 使用在编辑器阶段验证序列化字段
OnValidate()
Coroutines and Async
协程与异步
Coroutines
协程
csharp
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 Instruction | Behavior |
|---|---|
| Wait one frame |
| Wait t seconds (affected by timeScale) |
| Unscaled time |
| Wait for next FixedUpdate |
| After rendering |
| Wait until predicate is true |
| Wait for nested coroutine |
csharp
IEnumerator SpawnWaves(int count, float delay)
{
for (int i = 0; i < count; i++)
{
SpawnEnemy();
yield return new WaitForSeconds(delay);
}
}
// 启动:Coroutine handle = StartCoroutine(SpawnWaves(5, 1f));
// 停止:StopCoroutine(handle); 或 StopAllCoroutines();| Yield指令 | 行为 |
|---|---|
| 等待一帧 |
| 等待t秒(受timeScale影响) |
| 使用不受缩放的时间 |
| 等待下一次FixedUpdate |
| 渲染完成后执行 |
| 等待直到条件为真 |
| 等待嵌套协程完成 |
Async/Await (Unity 6+ / UniTask)
Async/Await(Unity 6+ / UniTask)
For Unity 2023+/Unity 6, is built-in. For older versions, use UniTask.
Awaitablecsharp
async Awaitable LoadLevelAsync(string sceneName)
{
await Awaitable.WaitForSecondsAsync(1f);
var op = SceneManager.LoadSceneAsync(sceneName);
while (!op.isDone)
{
progressBar.value = op.progress;
await Awaitable.NextFrameAsync();
}
}对于Unity 2023+/Unity 6,是内置功能。旧版本请使用UniTask。
Awaitablecsharp
async Awaitable LoadLevelAsync(string sceneName)
{
await Awaitable.WaitForSecondsAsync(1f);
var op = SceneManager.LoadSceneAsync(sceneName);
while (!op.isDone)
{
progressBar.value = op.progress;
await Awaitable.NextFrameAsync();
}
}Events and Delegates
事件与委托
Event Pattern (Recommended)
事件模式(推荐)
csharp
// 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 */;csharp
// 发布者
public class Health : MonoBehaviour
{
public event System.Action<float> OnDamaged; // event关键字防止外部调用
public event System.Action OnDeath;
public void TakeDamage(float amount)
{
currentHealth -= amount;
OnDamaged?.Invoke(amount);
if (currentHealth <= 0) OnDeath?.Invoke();
}
}
// 订阅者
void OnEnable() => health.OnDamaged += HandleDamage;
void OnDisable() => health.OnDamaged -= HandleDamage;
void HandleDamage(float amount) => /* 响应逻辑 */;ScriptableObject Event Channels
ScriptableObject 事件通道
Decouple systems without direct references. Create as a ScriptableObject asset, invoke from publishers, and listen from subscribers via MonoBehaviours. See for full implementation.
GameEventGameEventListenerreferences/design-patterns.md无需直接引用即可解耦系统。创建作为ScriptableObject资源,从发布者调用,订阅者通过 MonoBehaviour监听。完整实现请参考。
GameEventGameEventListenerreferences/design-patterns.mdPhysics API Quick Reference
物理API快速参考
Rigidbody Movement (3D)
Rigidbody移动(3D)
| Task | Method | Where |
|---|---|---|
| Continuous force | | FixedUpdate |
| Instant impulse | | FixedUpdate |
| Direct velocity | | FixedUpdate |
| Kinematic move | | FixedUpdate |
| Rotation | | FixedUpdate |
Note: In Unity 6, is renamed to .
Rigidbody.velocityRigidbody.linearVelocity| 任务 | 方法 | 调用位置 |
|---|---|---|
| 持续力 | | FixedUpdate |
| 瞬时冲量 | | FixedUpdate |
| 直接设置速度 | | FixedUpdate |
| 运动学移动 | | FixedUpdate |
| 旋转 | | FixedUpdate |
注意:在Unity 6中,重命名为。
Rigidbody.velocityRigidbody.linearVelocityRaycasting
射线检测
csharp
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.csharp
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask))
{
Debug.Log($"Hit {hit.collider.name} at {hit.point}");
}
// 如需检测多个目标,使用Physics.RaycastAll或Physics.RaycastNonAlloc
// 2D场景:Physics2D.Raycast、Physics2D.OverlapCircle等Collision vs Trigger
碰撞 vs 触发器
| Callback | Requires | Use For |
|---|---|---|
| Both have colliders, at least one Rigidbody, isTrigger=false | Physical impacts |
| One collider has isTrigger=true, at least one Rigidbody | Zones, pickups, detection |
Always use instead of (avoids GC allocation).
CompareTag("Enemy")other.tag == "Enemy"| 回调函数 | 要求 | 适用场景 |
|---|---|---|
| 双方都有碰撞体,至少一个带有Rigidbody,isTrigger=false | 物理碰撞效果 |
| 其中一个碰撞体的isTrigger=true,至少一个带有Rigidbody | 区域检测、道具拾取、目标探测 |
始终使用而非(避免GC分配)。
CompareTag("Enemy")other.tag == "Enemy"Animation Scripting
动画脚本开发
csharp
[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);
}
}Cache results as static readonly fields to avoid per-frame hashing. Use Animation Events for gameplay-timed callbacks (footsteps, hit frames). For IK, implement with .
Animator.StringToHashOnAnimatorIK(int layerIndex)SetIKPosition/Rotation/Weightcsharp
[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);
}
}将的结果缓存为静态只读字段,避免每帧哈希计算。使用动画事件实现游戏玩法时序回调(如脚步声、击中帧)。如需IK(反向运动学),实现并使用方法。
Animator.StringToHashOnAnimatorIK(int layerIndex)SetIKPosition/Rotation/WeightAudio Quick Reference
音频快速参考
csharp
[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)]);
}Use for overlapping SFX. Use with exposed parameters for volume control. Use snapshots for state transitions (combat vs. exploration).
AudioSource.PlayOneShot()AudioMixercsharp
[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)]);
}使用播放重叠音效。使用并暴露参数实现音量控制。使用快照实现状态切换(战斗 vs 探索)。
AudioSource.PlayOneShot()AudioMixerSerialization
序列化
| Type | Serialized | Notes |
|---|---|---|
| Yes | Visible in Inspector |
| Yes | Preferred -- maintains encapsulation |
| Yes, hidden | Serialized but not shown |
| No | Opt-out of serialization |
| Properties | No | Never serialized by Unity |
| Dictionaries | No | Use serialized list + rebuild, or Odin/SerializedDictionary |
| Interfaces | No | Use abstract ScriptableObject or SerializeReference |
Use for polymorphic serialization of interfaces and abstract types.
[SerializeReference]| 类型 | 是否可序列化 | 说明 |
|---|---|---|
| 是 | 在Inspector面板可见 |
| 是 | 推荐使用——保持封装性 |
| 是,但隐藏 | 已序列化但不在面板显示 |
| 否 | 退出序列化 |
| 属性 | 否 | Unity永远不会序列化属性 |
| 字典 | 否 | 使用序列化列表+重建,或Odin/SerializedDictionary |
| 接口 | 否 | 使用抽象ScriptableObject或SerializeReference |
使用实现接口和抽象类型的多态序列化。
[SerializeReference]Common Design Patterns
常见设计模式
| Pattern | When to Use | Approach |
|---|---|---|
| Singleton | Global managers (Audio, GameState) | ScriptableObject-based or lazy MonoBehaviour |
| Observer | Decoupled communication | C# events or SO event channels |
| Command | Input/undo systems | Command interface + history stack |
| State Machine | AI, player states | Enum + switch, or class-based states |
| Object Pool | Bullets, particles, enemies | Queue<T> with pre-instantiation |
| Service Locator | Testable global access | Static registry with interface keys |
For detailed implementations and code examples, see .
references/design-patterns.md| 模式 | 适用场景 | 实现方式 |
|---|---|---|
| 单例模式 | 全局管理器(音频、游戏状态) | 基于ScriptableObject或延迟初始化的MonoBehaviour |
| 观察者模式 | 解耦通信 | C#事件或SO事件通道 |
| 命令模式 | 输入/撤销系统 | 命令接口+历史栈 |
| 状态机 | AI、玩家状态 | 枚举+switch,或基于类的状态 |
| 对象池 | 子弹、粒子、敌人 | 使用Queue<T>预实例化对象 |
| 服务定位器 | 可测试的全局访问 | 带接口键的静态注册表 |
详细实现和代码示例请参考。
references/design-patterns.mdECS/DOTS Quick Reference
ECS/DOTS快速参考
| Concept | Role |
|---|---|
| Entity | Lightweight ID (no MonoBehaviour) |
| IComponentData | Pure data struct on entities |
| SystemBase / ISystem | Logic operating on component queries |
| EntityQuery | Filter entities by component sets |
| Jobs (IJobEntity) | Multithreaded systems |
| Burst Compiler | SIMD-optimized native code |
For ECS architecture patterns and migration guidance, see .
references/design-patterns.md| 概念 | 作用 |
|---|---|
| Entity | 轻量级ID(无MonoBehaviour) |
| IComponentData | 实体上的纯数据结构体 |
| SystemBase / ISystem | 处理组件查询的逻辑 |
| EntityQuery | 按组件集过滤实体 |
| Jobs(IJobEntity) | 多线程系统 |
| Burst Compiler | SIMD优化的原生代码 |
ECS架构模式和迁移指南请参考。
references/design-patterns.mdAdditional Resources
额外资源
Reference Files
参考文件
- -- Full implementations of singleton, observer, command, state machine, object pool, service locator, and ECS patterns
references/design-patterns.md - -- Detailed physics setup (2D and 3D), advanced animation (blend trees, IK, root motion, Animation Rigging), and audio architecture
references/physics-animation-audio.md
- —— 单例、观察者、命令、状态机、对象池、服务定位器以及ECS模式的完整实现
references/design-patterns.md - —— 详细的物理设置(2D和3D)、高级动画(混合树、IK、根运动、Animation Rigging)以及音频架构
references/physics-animation-audio.md