Unity笔记——Unity 封装方法指南

在 Unity 开发中,良好的封装可以提高代码的可维护性、可读性和复用性。以下是几种常见的封装方法:

1. 方法封装

cs 复制代码
// 基础方法封装
public class PlayerController : MonoBehaviour
{
    private float speed = 5f;
    
    // 封装移动逻辑
    private void MovePlayer()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        Vector3 movement = new Vector3(horizontal, 0, vertical) * speed * Time.deltaTime;
        transform.Translate(movement);
    }
    
    void Update()
    {
        MovePlayer();
    }
}

2. 属性封装

cs 复制代码
// 使用属性进行封装
public class PlayerStats : MonoBehaviour
{
    private int _health = 100;
    
    // 封装健康值属性
    public int Health
    {
        get => _health;
        set
        {
            _health = Mathf.Clamp(value, 0, 100);
            Debug.Log($"Health changed to: {_health}");
        }
    }
    
    public void TakeDamage(int damage)
    {
        Health -= damage;
    }
}

3. 组件封装

cs 复制代码
// 封装常用组件访问
public class PlayerMovement : MonoBehaviour
{
    private Rigidbody _rb;
    private Animator _animator;
    
    private void Awake()
    {
        _rb = GetComponent<Rigidbody>();
        _animator = GetComponent<Animator>();
    }
    
    public void Move(Vector3 direction, float speed)
    {
        _rb.velocity = direction * speed;
        _animator.SetBool("IsMoving", direction. Magnitude > 0);
    }
}

4. 单例模式封装

cs 复制代码
// 单例模式封装
public class GameManager : MonoBehaviour
{
    private static GameManager _instance;
    
    public static GameManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<GameManager>();
                if (_instance == null)
                {
                    GameObject obj = new GameObject();
                    obj.name = typeof(GameManager).Name;
                    _instance = obj.AddComponent<GameManager>();
                    DontDestroyOnLoad(obj);
                }
            }
            return _instance;
        }
    }
    
    private void Awake()
    {
        if (_instance == null)
        {
            _instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
    
    // 其他游戏管理方法...
}

单例模式其实是很常见的模式,写法有很多种,这里更好的封装应该是将单例抽出来,做成基类,把让需要使用单例的类继承这个基类。

cs 复制代码
// 利用泛型给不同的子类创建单例模式
public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
    private static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = FindObjectOfType<T>();
                if (instance == null)
                {
                    GameObject obj = new GameObject(typeof(T).Name);
                    instance = obj.AddComponent<T>();
                    DontDestroyOnLoad(obj); // 按需持久化
                }
            }
            return instance;
        }
    }

    protected virtual void Awake()
    {
        if (instance != null)
        {
            GameObject.Destroy(gameObject);
        }
        else
        {
            instance = this as T;
            // 按需持久化,不要在场景切换时销毁
            DontDestroyOnLoad(gameObject);
        }
    }
}

// 在其他需要使用单例的情况下就可以继承该基类,而不用重写一个新的单例模式
public class GameManager : SingletonMono<GameManager>
{
    protected override void Awake()
    {
        base. Awake();
    }
}

5. 事件系统封装

cs 复制代码
// 事件系统封装
public class EventManager : MonoBehaviour
{
    public delegate void PlayerDeathHandler();
    public static event PlayerDeathHandler OnPlayerDeath;
    
    public static void TriggerPlayerDeath()
    {
        OnPlayerDeath?.Invoke();
    }
}

// 使用示例
public class Player : MonoBehaviour
{
    private void Die()
    {
        // 玩家死亡逻辑...
        EventManager.TriggerPlayerDeath();
    }
}

public class UIManager : MonoBehaviour
{
    private void OnEnable()
    {
        EventManager.OnPlayerDeath += ShowGameOverScreen;
    }
    
    private void OnDisable()
    {
        EventManager.OnPlayerDeath -= ShowGameOverScreen;
    }
    
    private void ShowGameOverScreen()
    {
        // 显示游戏结束UI
    }
}

6. 脚本间通信封装(接口)

cs 复制代码
// 使用接口进行封装
public interface IDamageable
{
    void TakeDamage(int amount);
}

public class Enemy : MonoBehaviour, IDamageable
{
    public void TakeDamage(int amount)
    {
        // 敌人受伤逻辑
    }
}

public class PlayerCombat : MonoBehaviour
{
    private void Attack(IDamageable target)
    {
        target.TakeDamage(10);
    }
}

7. 协程封装

cs 复制代码
// 协程方法封装
public class CoroutineUtils : MonoBehaviour
{
    public static IEnumerator FadeOut(Renderer renderer, float duration)
    {
        Color originalColor = renderer.material.color;
        float elapsed = 0f;
        
        while (elapsed < duration)
        {
            float alpha = Mathf.Lerp(1f, 0f, elapsed / duration);
            renderer.material.color = new Color(originalColor.r, originalColor.g, originalColor.b, alpha);
            elapsed += Time.deltaTime;
            yield return null;
        }
    }
}

// 使用示例
StartCoroutine(CoroutineUtils.FadeOut(GetComponent<Renderer>(), 2f));

实践建议

  1. 单一职责原则:每个方法/类只做一件事

  2. 适当的访问修饰符:使用 private/protected/public 控制访问级别

  3. 参数验证:在公共方法中添加参数检查

  4. 添加注释:使用 /// <summary>注释 或者 // 注释说明方法用途和参数

  5. 避免过度封装:保持简单,只在必要时封装,当同一方法或者类似逻辑的实现出现第二次时就可以考虑封装该方法、将其抽象为抽象类方法 或者 考虑接口实现

良好的封装可以使你的 Unity 项目更易于维护和扩展,特别是在团队协作中。

相关推荐
小帅一把手1 小时前
RCE随笔(1)
笔记
阿群今天学习了吗2 小时前
面向对象基础笔记
笔记·学习·算法
wyn200011284 小时前
Git学习笔记
笔记·git·学习
死也不注释5 小时前
【Unity编辑器开发GUI.Window】
unity·编辑器·游戏引擎
rannn_1115 小时前
【Java学习|黑马笔记|Day18】Stream流|获取、中间方法、终结方法、收集方法及其练习
java·笔记·学习
冻咸鱼6 小时前
Ajax简单介绍及Axios请求方式的别名
前端·javascript·笔记·ajax
小小码农正在搬砖中9 小时前
【Python数据采集】Python爬取小红书搜索关键词下面的所有笔记的内容、点赞数量、评论数量等数据,绘制词云图、词频分析、数据分析
笔记·python·数据分析
智者知已应修善业9 小时前
【51单片机仿真复位电阻电容参数】2022-5-17
经验分享·笔记·嵌入式硬件·51单片机
freexyn9 小时前
Matlab自学笔记六十四:求解自变量带有约束条件的方程
笔记·算法·matlab
丘大梨10 小时前
Qt笔记整理(1)
笔记