mcpField
U
Unity Fundamentals

Unity Fundamentals

github.com/cryptorabea开发383 次安装44 周前更新
代码仓库

关于 Unity Fundamentals

这是一项面向 Unity 开发者的技能,讲解引擎核心模式的学习与应用,涵盖 MonoBehaviour 生命周期、序列化、组件架构和预制件工作流,并提供相关主题参考文档。

主要功能

  1. 01MonoBehaviour 生命周期指引
  2. 02序列化与 Inspector 实践
  3. 03组件查找与通信模式
  4. 04预制件创建及变体工作流
  5. 05主题详细参考文档

适用场景

  1. 01选择合适的生命周期回调
  2. 02在 Inspector 中编辑私有字段
  3. 03组织组件依赖关系
  4. 04创建可复用的预制件变体

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cryptorabea/claude_unity_dev_plugin --skill unity-fundamentals

安装后在 Claude Code 中输入/unity-fundamentals确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
serialization-guide.md
prefab-workflows.md
lifecycle-detailed.md
component-patterns.md

详细介绍

Unity 基础知识

全面介绍 Unity 核心系统、MonoBehaviour 生命周期、序列化、组件架构和预制体工作流。

概述

Unity 开发以 MonoBehaviour 组件、组件模式以及一套特定的回调方法生命周期为核心。理解这些基础知识对于编写正确且高性能的 Unity 代码至关重要。本指南涵盖:

  • MonoBehaviour 生命周期方法及其正确用法
  • 序列化系统与 Inspector 集成
  • 基于组件的架构与 GetComponent 模式
  • 预制体工作流和最佳实践

MonoBehaviour 生命周期

Unity 会按照预先确定的顺序调用 MonoBehaviour 脚本上的特定方法。使用错误的方法会导致错误、空引用和性能不佳。

初始化方法
Awake()

在脚本实例加载时调用,早于任何 Start() 调用。用于初始化此对象的状态。

private void Awake()
{
    // Cache component references on THIS GameObject
    rigidbody = GetComponent<Rigidbody>();
    animator = GetComponent<Animator>();

    // Initialize private state
    currentHealth = maxHealth;
    inventory = new List<Item>();

    // DON'T reference other objects - they may not be initialized yet
}

在以下场景中使用 Awake():

  • 缓存组件引用
  • 初始化私有字段
  • 设置内部状态
  • 创建单例/管理器

不要在以下场景中使用 Awake():

  • 引用其他 GameObject(改用 Start)
  • 执行依赖其他脚本已准备就绪的操作
Start()

在第一次 Update() 之前调用,并且所有 Awake() 调用都已完成。用于引用其他对象的设置工作。

private void Start()
{
    // Safe to reference other GameObjects - they're initialized
    playerTransform = GameObject.FindWithTag("Player").transform;
    gameManager = FindObjectOfType<GameManager>();

    // Register with managers or systems
    GameManager.Instance.RegisterEnemy(this);

    // Perform initialization that depends on other components
    SetupWeapon(gameManager.GetStartingWeapon());
}

在以下场景中使用 Start():

  • 查找并引用其他 GameObject
  • 调用其他组件上的初始化方法
  • 向管理器或系统注册
  • 执行依赖场景完全初始化的设置工作

常见模式:

private Camera mainCamera;  // Cache in Awake
private Transform target;    // Find in Start

private void Awake()
{
    mainCamera = Camera.main;  // Cache expensive lookup
}

private void Start()
{
    target = GameObject.FindWithTag("Target").transform;  // Find after scene loads
}
OnEnable() / OnDisable()

在 GameObject 激活/停用时调用。用于订阅/取消订阅事件。

private void OnEnable()
{
    // Subscribe to events
    GameEvents.OnPlayerDied += HandlePlayerDeath;
    InputManager.OnJumpPressed += HandleJump;

    // Re-enable functionality
    StartCoroutine(SpawnEnemies());
}

private void OnDisable()
{
    // Unsubscribe from events (prevents memory leaks!)
    GameEvents.OnPlayerDied -= HandlePlayerDeath;
    InputManager.OnJumpPressed -= HandleJump;

    // Clean up temporary state
    StopAllCoroutines();
}

**重要:**务必在 OnDisable() 中取消订阅,以防止内存泄漏。

更新方法
Update()

每帧调用。请谨慎使用——这对性能至关重要。

private void Update()
{
    // Input handling
    if (Input.GetKeyDown(KeyCode.Space))
        Jump();

    // Frame-dependent logic
    UpdateUI();

    // AVOID expensive operations here
    // DON'T call GetComponent every frame
    // DON'T use Find methods
}

**尽可能避免使用 Update()。**优先采用事件驱动方式。

FixedUpdate()

以固定时间步长调用(默认 50 FPS)。仅用于物理操作。

private void FixedUpdate()
{
    // Physics operations
    rigidbody.AddForce(moveDirection * moveSpeed);

    // Physics-based movement
    rigidbody.MovePosition(transform.position + velocity * Time.fixedDeltaTime);

    // DON'T handle input here (use Update)
    // DON'T update UI here (use Update or LateUpdate)
}

**规则:**如果涉及 Rigidbody 或物理操作,就使用 FixedUpdate()。其他所有情况都使用 Update() 或事件驱动方式。

LateUpdate()

在所有 Update() 调用之后执行。用于摄像机跟随和最终调整。

private void LateUpdate()
{
    // Camera follow (after player moved in Update)
    transform.position = target.position + offset;

    // Final position adjustments
    ClampToBounds();

    // UI updates that depend on world state
    UpdateHealthBar();
}
销毁方法
OnDestroy()

在 GameObject 销毁时调用。用于最终清理。

private void OnDestroy()
{
    // Unsubscribe from static events
    GameEvents.OnPlayerDied -= HandlePlayerDeath;

    // Release resources
    if (texture != null)
        Destroy(texture);

    // Notify other systems
    GameManager.Instance.UnregisterEnemy(this);
}
生命周期顺序摘要
  1. Awake() - 初始化自身
  2. OnEnable() - 订阅事件
  3. Start() - 引用其他对象,完成最终设置
  4. FixedUpdate() - 物理操作(50 FPS)
  5. Update() - 每帧逻辑(可变 FPS)
  6. LateUpdate() - 摄像机和最终调整
  7. OnDisable() - 取消订阅事件
  8. OnDestroy() - 最终清理

序列化系统

Unity 的序列化系统决定 Inspector 中显示的内容以及数据的持久化方式。

序列化字段

使私有字段可在 Inspector 中编辑:

[SerializeField] private int maxHealth = 100;
[SerializeField] private float moveSpeed = 5f;
[SerializeField] private GameObject prefab;

优点:

  • 保持封装(私有访问权限)
  • 可在 Inspector 中编辑
  • 预制体/场景数据持久化

**最佳实践:**优先使用 [SerializeField] private,而不是 public 字段。

标题和工具提示

整理 Inspector 中的区块:

[Header("Movement Settings")]
[Tooltip("Maximum movement speed in units per second")]
[SerializeField] private float moveSpeed = 5f;

[Tooltip("Rotation speed in degrees per second")]
[SerializeField] private float rotationSpeed = 180f;

[Header("Combat Settings")]
[SerializeField] private int attackDamage = 25;
序列化规则

Unity 会序列化:

  • 公共字段(除非标记为 [HideInInspector])
  • 带有 [SerializeField] 的私有字段
  • 支持的类型:基元类型、Unity 对象、结构体、数组、List

Unity 不会序列化:

  • 属性
  • 没有 [SerializeField] 的私有字段
  • 字典
  • 接口
  • 静态字段
属性与序列化字段
// DON'T expose internal state directly
public int health;  // Bad: public field

// DO use properties for controlled access
[SerializeField] private int health = 100;
public int Health
{
    get => health;
    set
    {
        health = Mathf.Clamp(value, 0, maxHealth);
        OnHealthChanged?.Invoke(health);
    }
}

组件架构

Unity 使用组件模式——由多个组件组合构成功能。

GetComponent 模式

在 Awake() 中缓存引用,避免重复查找:

// ❌ BAD - Repeated GetComponent calls
private void Update()
{
    GetComponent<Rigidbody>().velocity = Vector3.forward;  // Expensive!
}

// ✅ GOOD - Cache once
private Rigidbody rb;

private void Awake()
{
    rb = GetComponent<Rigidbody>();
}

private void Update()
{
    rb.velocity = Vector3.forward;  // Fast
}
组件变体
// GetComponent<T>() - On this GameObject
rb = GetComponent<Rigidbody>();

// GetComponentInChildren<T>() - This GameObject and children
animator = GetComponentInChildren<Animator>();

// GetComponentInParent<T>() - This GameObject and parents
canvas = GetComponentInParent<Canvas>();

// GetComponents<T>() - Multiple components
Collider[] colliders = GetComponents<Collider>();

**性能:**缓存所有 GetComponent 结果。切勿在 Update/FixedUpdate 中调用。

必需组件

声明组件依赖项:

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class PlayerMovement : MonoBehaviour
{
    private Rigidbody rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();  // Guaranteed to exist
    }
}
组件通信

直接引用(检查器):

[SerializeField] private HealthDisplay healthDisplay;

private void TakeDamage(int amount)
{
    health -= amount;
    healthDisplay.UpdateHealth(health);  // Direct call
}

GetComponent(运行时):

private void OnTriggerEnter(Collider other)
{
    var health = other.GetComponent<Health>();
    if (health != null)
        health.TakeDamage(10);
}

事件(解耦):

public event Action<int> OnHealthChanged;

private void TakeDamage(int amount)
{
    health -= amount;
    OnHealthChanged?.Invoke(health);  // Any subscribers get notified
}

预制体工作流

预制体是可重复使用的 GameObject 模板。了解预制体工作流可以避免常见问题。

创建预制体

将 GameObject 从 Hierarchy 拖到 Project 窗口。Hierarchy 中的蓝色文字表示预制体实例。

预制体实例

对预制体实例所做的更改:

  • 覆盖 - 仅更改此实例(蓝色粗体)
  • 应用 - 将更改推送到预制体资源(影响所有实例)
  • 还原 - 放弃实例更改,使其与预制体一致
预制体变体

基于基础预制体创建变体:

Base Prefab: Enemy
├── Variant: FastEnemy (increased speed)
├── Variant: TankEnemy (increased health)
└── Variant: FlyingEnemy (added flight)

对基础预制体的更改会传播到变体。

嵌套预制体

预制体可以包含其他预制体:

Car Prefab
├── Wheel Prefab (x4)
├── Engine Prefab
└── Door Prefab (x4)

可以单独编辑嵌套预制体。

以编程方式使用预制体
[SerializeField] private GameObject enemyPrefab;
[SerializeField] private Transform spawnPoint;

private void SpawnEnemy()
{
    // Instantiate prefab
    GameObject enemy = Instantiate(enemyPrefab, spawnPoint.position, Quaternion.identity);

    // Configure instance
    enemy.GetComponent<Enemy>().SetTarget(player);

    // Parent to container (optional)
    enemy.transform.SetParent(enemyContainer);
}
预制体最佳实践
  1. 所有运行时生成的对象都使用预制体(敌人、投射物、UI 面板)
  2. 创建预制体变体,而不是复制预制体
  3. 谨慎应用更改 - 会影响所有实例
  4. 将预制体放在分类整理的文件夹中 - Prefabs/Characters、Prefabs/UI 等
  5. 先测试预制体更改,再应用到所有实例

常见模式

单例管理器
public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}
对象初始化
public class Enemy : MonoBehaviour
{
    [SerializeField] private int health = 100;

    private Rigidbody rb;
    private Transform target;

    // 1. Cache components on self
    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }

    // 2. Find external references
    private void Start()
    {
        target = GameObject.FindWithTag("Player").transform;
    }

    // 3. Subscribe to events
    private void OnEnable()
    {
        GameEvents.OnWaveComplete += HandleWaveComplete;
    }

    // 4. Unsubscribe from events
    private void OnDisable()
    {
        GameEvents.OnWaveComplete -= HandleWaveComplete;
    }
}

其他资源

参考文件

有关特定主题的详细指南:

  • references/lifecycle-detailed.md - 生命周期方法完整参考
  • references/serialization-guide.md - 高级序列化模式
  • references/component-patterns.md - 组件架构最佳实践
  • references/prefab-workflows.md - 预制体用法综合指南
快速参考

**生命周期顺序:**Awake → OnEnable → Start → FixedUpdate → Update → LateUpdate → OnDisable → OnDestroy

**序列化:**优先使用 [SerializeField] private,而非 public

**组件:**在 Awake() 中缓存,切勿在 Update() 中调用 GetComponent

**预制体:**用于可复用对象,应用更改前先进行测试


遵循这些基础原则,构建性能出色、易于维护且没有错误的 Unity 项目。

评论

支持 Markdown