unity-csharp-scripting

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unity 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()
MethodWhen CalledUse For
Awake()
Once, when object instantiates (before Start)Self-initialization, caching references
OnEnable()
Each time object becomes activeSubscribe to events, reset state
Start()
Once, before first Update (after all Awake)Cross-object initialization
FixedUpdate()
Fixed timestep (default 0.02s)Physics, Rigidbody movement
Update()
Every frameInput, non-physics logic
LateUpdate()
After all Update callsCamera follow, post-movement adjustments
OnDisable()
When object deactivatesUnsubscribe events, save state
OnDestroy()
When object is destroyedFinal cleanup, resource release
text
Awake() -> OnEnable() -> Start() -> FixedUpdate() -> Update() -> LateUpdate() -> OnDisable() -> OnDestroy()
方法调用时机适用场景
Awake()
对象实例化时调用一次(在Start之前)自我初始化、缓存引用
OnEnable()
对象每次激活时调用订阅事件、重置状态
Start()
首次Update之前调用一次(所有Awake执行完成后)跨对象初始化
FixedUpdate()
固定时间步长调用(默认0.02秒)物理系统、Rigidbody移动
Update()
每帧调用输入处理、非物理逻辑
LateUpdate()
所有Update调用完成后执行相机跟随、移动后调整
OnDisable()
对象失活时调用取消事件订阅、保存状态
OnDestroy()
对象被销毁时调用最终清理、释放资源

Key Rules

核心规则

  • Awake runs even on disabled components (but not disabled GameObjects)
  • Never rely on Awake/Start order between scripts -- use
    [DefaultExecutionOrder(N)]
    or Script Execution Order settings
  • Use
    OnValidate()
    for editor-time validation of serialized fields
  • 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 InstructionBehavior
yield return null
Wait one frame
yield return new WaitForSeconds(t)
Wait t seconds (affected by timeScale)
yield return new WaitForSecondsRealtime(t)
Unscaled time
yield return new WaitForFixedUpdate()
Wait for next FixedUpdate
yield return new WaitForEndOfFrame()
After rendering
yield return new WaitUntil(() => condition)
Wait until predicate is true
yield return StartCoroutine(other)
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指令行为
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)
等待直到条件为真
yield return StartCoroutine(other)
等待嵌套协程完成

Async/Await (Unity 6+ / UniTask)

Async/Await(Unity 6+ / UniTask)

For Unity 2023+/Unity 6,
Awaitable
is built-in. For older versions, use UniTask.
csharp
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,
Awaitable
是内置功能。旧版本请使用UniTask。
csharp
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
GameEvent
as a ScriptableObject asset, invoke from publishers, and listen from subscribers via
GameEventListener
MonoBehaviours. See
references/design-patterns.md
for full implementation.
无需直接引用即可解耦系统。创建
GameEvent
作为ScriptableObject资源,从发布者调用,订阅者通过
GameEventListener
MonoBehaviour监听。完整实现请参考
references/design-patterns.md

Physics API Quick Reference

物理API快速参考

Rigidbody Movement (3D)

Rigidbody移动(3D)

TaskMethodWhere
Continuous force
rb.AddForce(dir * force)
FixedUpdate
Instant impulse
rb.AddForce(dir * force, ForceMode.Impulse)
FixedUpdate
Direct velocity
rb.linearVelocity = dir * speed
FixedUpdate
Kinematic move
rb.MovePosition(target)
FixedUpdate
Rotation
rb.MoveRotation(targetRot)
FixedUpdate
Note: In Unity 6,
Rigidbody.velocity
is renamed to
Rigidbody.linearVelocity
.
任务方法调用位置
持续力
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

Raycasting

射线检测

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 触发器

CallbackRequiresUse For
OnCollisionEnter/Stay/Exit
Both have colliders, at least one Rigidbody, isTrigger=falsePhysical impacts
OnTriggerEnter/Stay/Exit
One collider has isTrigger=true, at least one RigidbodyZones, pickups, detection
Always use
CompareTag("Enemy")
instead of
other.tag == "Enemy"
(avoids GC allocation).
回调函数要求适用场景
OnCollisionEnter/Stay/Exit
双方都有碰撞体,至少一个带有Rigidbody,isTrigger=false物理碰撞效果
OnTriggerEnter/Stay/Exit
其中一个碰撞体的isTrigger=true,至少一个带有Rigidbody区域检测、道具拾取、目标探测
始终使用
CompareTag("Enemy")
而非
other.tag == "Enemy"
(避免GC分配)。

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
Animator.StringToHash
results as static readonly fields to avoid per-frame hashing. Use Animation Events for gameplay-timed callbacks (footsteps, hit frames). For IK, implement
OnAnimatorIK(int layerIndex)
with
SetIKPosition/Rotation/Weight
.
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);
    }
}
Animator.StringToHash
的结果缓存为静态只读字段,避免每帧哈希计算。使用动画事件实现游戏玩法时序回调(如脚步声、击中帧)。如需IK(反向运动学),实现
OnAnimatorIK(int layerIndex)
并使用
SetIKPosition/Rotation/Weight
方法。

Audio 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
AudioSource.PlayOneShot()
for overlapping SFX. Use
AudioMixer
with exposed parameters for volume control. Use snapshots for state transitions (combat vs. exploration).
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)]);
}
使用
AudioSource.PlayOneShot()
播放重叠音效。使用
AudioMixer
并暴露参数实现音量控制。使用快照实现状态切换(战斗 vs 探索)。

Serialization

序列化

TypeSerializedNotes
public
fields
YesVisible in Inspector
[SerializeField] private
YesPreferred -- maintains encapsulation
[HideInInspector] public
Yes, hiddenSerialized but not shown
[NonSerialized] public
NoOpt-out of serialization
PropertiesNoNever serialized by Unity
DictionariesNoUse serialized list + rebuild, or Odin/SerializedDictionary
InterfacesNoUse abstract ScriptableObject or SerializeReference
Use
[SerializeReference]
for polymorphic serialization of interfaces and abstract types.
类型是否可序列化说明
public
字段
在Inspector面板可见
[SerializeField] private
推荐使用——保持封装性
[HideInInspector] public
是,但隐藏已序列化但不在面板显示
[NonSerialized] public
退出序列化
属性Unity永远不会序列化属性
字典使用序列化列表+重建,或Odin/SerializedDictionary
接口使用抽象ScriptableObject或SerializeReference
使用
[SerializeReference]
实现接口和抽象类型的多态序列化。

Common Design Patterns

常见设计模式

PatternWhen to UseApproach
SingletonGlobal managers (Audio, GameState)ScriptableObject-based or lazy MonoBehaviour
ObserverDecoupled communicationC# events or SO event channels
CommandInput/undo systemsCommand interface + history stack
State MachineAI, player statesEnum + switch, or class-based states
Object PoolBullets, particles, enemiesQueue<T> with pre-instantiation
Service LocatorTestable global accessStatic 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.md

ECS/DOTS Quick Reference

ECS/DOTS快速参考

ConceptRole
EntityLightweight ID (no MonoBehaviour)
IComponentDataPure data struct on entities
SystemBase / ISystemLogic operating on component queries
EntityQueryFilter entities by component sets
Jobs (IJobEntity)Multithreaded systems
Burst CompilerSIMD-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 CompilerSIMD优化的原生代码
ECS架构模式和迁移指南请参考
references/design-patterns.md

Additional Resources

额外资源

Reference Files

参考文件

  • references/design-patterns.md
    -- Full implementations of singleton, observer, command, state machine, object pool, service locator, and ECS patterns
  • references/physics-animation-audio.md
    -- Detailed physics setup (2D and 3D), advanced animation (blend trees, IK, root motion, Animation Rigging), and audio architecture
  • references/design-patterns.md
    —— 单例、观察者、命令、状态机、对象池、服务定位器以及ECS模式的完整实现
  • references/physics-animation-audio.md
    —— 详细的物理设置(2D和3D)、高级动画(混合树、IK、根运动、Animation Rigging)以及音频架构