第三篇:Unity进阶阶段(商业项目能力)

目标:可参与团队商业项目开发


第12章:脚本架构设计

12.1 SOLID 设计原则

原则 含义 Unity示例
S - 单一职责 一个类只做一件事 PlayerMovement只管移动
O - 开闭原则 对扩展开放,对修改关闭 用interface定义IWeapon
L - 里式替换 子类可替换父类 Enemy继承Character
I - 接口隔离 接口小而专 IDamageable/IHealable分离
D - 依赖反转 依赖抽象不依赖具体 依赖IDataService接口

12.2 常用设计模式

csharp 复制代码
// ═══ 单例模式 Singleton ═══
public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }
    void Awake()
    {
        if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); }
        else Destroy(gameObject);
    }
}

// ═══ 观察者模式 Observer ═══
public class HealthSystem : MonoBehaviour
{
    public event Action<float> OnHealthChanged;
    public event Action OnDeath;
    private float health;
    public void TakeDamage(float dmg)
    {
        health -= dmg;
        OnHealthChanged?.Invoke(health);
        if (health <= 0) OnDeath?.Invoke();
    }
}

// ═══ 命令模式 Command ═══
public interface ICommand { void Execute(); void Undo(); }
public class MoveCommand : ICommand
{
    Transform target; Vector3 from, to;
    public MoveCommand(Transform t, Vector3 dest)
    { target = t; from = t.position; to = dest; }
    public void Execute() => target.position = to;
    public void Undo() => target.position = from;
}
// 用途:技能释放、操作撤销、录像回放

// ═══ 状态机模式 State ═══
public interface IState { void Enter(); void Update(); void Exit(); }
public class StateMachine
{
    private IState current;
    public void ChangeState(IState newState)
    {
        current?.Exit();
        current = newState;
        current.Enter();
    }
    public void Update() => current?.Update();
}

// ═══ 工厂模式 Factory ═══
public static class EnemyFactory
{
    public static Enemy Create(EnemyType type) => type switch
    {
        EnemyType.Goblin => new Goblin(),
        EnemyType.Dragon => new Dragon(),
        _ => throw new ArgumentException()
    };
}

// ═══ 策略模式 Strategy ═══
public interface IAttackStrategy { void Attack(Transform attacker, Transform target); }
public class MeleeAttack : IAttackStrategy { public void Attack(...) { /* 近战 */ } }
public class RangedAttack : IAttackStrategy { public void Attack(...) { /* 远程 */ } }

12.3 Unity 常用架构

复制代码
Manager模式(最常用):
├── GameManager      ← 游戏流程控制
├── UIManager        ← UI管理
├── AudioManager     ← 音频管理
├── PoolManager      ← 对象池管理
├── SaveManager      ← 存档管理
└── EventManager     ← 事件管理

推荐框架:
├── QFramework    ← 国内主流,轻量级
├── Zenject       ← 依赖注入框架
├── UniTask       ← 异步方案(替代协程)
└── UniRx         ← 响应式编程

第13章:事件系统与消息通信

13.1 三种事件实现方式

csharp 复制代码
// ═══ 方式1:C# event(性能最好)═══
public class Player : MonoBehaviour
{
    public event Action<int> OnHPChanged;
    public event Action OnDeath;
    public void TakeDamage(int dmg) { hp -= dmg; OnHPChanged?.Invoke(hp); }
}
// 订阅:player.OnHPChanged += UpdateHPBar;
// 取消:player.OnHPChanged -= UpdateHPBar;

// ═══ 方式2:UnityEvent(Inspector可视化绑定)═══
[SerializeField] UnityEvent onDeath;
[SerializeField] UnityEvent<int> onScoreChanged;
// 代码触发:onDeath.Invoke();

// ═══ 方式3:EventBus(全局解耦通信)═══
public static class EventBus
{
    static Dictionary<Type, List<object>> handlers = new();
    public static void Subscribe<T>(Action<T> handler) { ... }
    public static void Unsubscribe<T>(Action<T> handler) { ... }
    public static void Publish<T>(T eventData) { ... }
}
// 发布:EventBus.Publish(new EnemyDeadEvent { enemy = this });
// 订阅:EventBus.Subscribe<EnemyDeadEvent>(OnEnemyDead);

// 对比:
// C# event:耦合度中,性能最好,适合组件间通信
// UnityEvent:可视化配置,适合UI交互
// EventBus:完全解耦,适合跨系统通信

第14章:对象池系统

14.1 为什么需要对象池

复制代码
Instantiate/Destroy 的问题:
1. 每次创建都分配内存 → GC频繁 → 卡顿
2. 内存碎片化 → 内存效率下降
3. 创建/销毁开销 → 帧率下降

对象池方案:
预先创建 → 用时激活 → 用完回收 → 循环复用

14.2 Unity 内置 ObjectPool

csharp 复制代码
using UnityEngine.Pool;

// Unity 2021+ 内置对象池
ObjectPool<GameObject> bulletPool = new ObjectPool<GameObject>(
    createFunc: () => Instantiate(bulletPrefab),      // 创建
    actionOnGet: obj => obj.SetActive(true),           // 取出时
    actionOnRelease: obj => obj.SetActive(false),      // 归还时
    actionOnDestroy: obj => Destroy(obj),               // 销毁时
    collectionCheck: true,                              // 防重复归还
    defaultCapacity: 20,                                // 默认容量
    maxSize: 50                                         // 最大容量
);

// 使用
GameObject bullet = bulletPool.Get();        // 取出
bulletPool.Release(bullet);                   // 归还

14.3 自定义通用对象池

csharp 复制代码
public class PoolManager : MonoBehaviour
{
    public static PoolManager Instance;
    private Dictionary<string, Queue<GameObject>> pools = new();
    private Dictionary<string, GameObject> prefabs = new();

    public void RegisterPool(string key, GameObject prefab, int initSize)
    {
        prefabs[key] = prefab;
        pools[key] = new Queue<GameObject>();
        for (int i = 0; i < initSize; i++)
        {
            var obj = Instantiate(prefab);
            obj.SetActive(false);
            pools[key].Enqueue(obj);
        }
    }

    public GameObject Spawn(string key, Vector3 pos, Quaternion rot)
    {
        GameObject obj;
        if (pools[key].Count > 0)
            obj = pools[key].Dequeue();
        else
            obj = Instantiate(prefabs[key]);
        obj.transform.SetPositionAndRotation(pos, rot);
        obj.SetActive(true);
        return obj;
    }

    public void Recycle(string key, GameObject obj)
    {
        obj.SetActive(false);
        pools[key].Enqueue(obj);
    }
}

第15章:存档与数据持久化

15.1 PlayerPrefs(简单存储)

csharp 复制代码
// 存储(只支持 int/float/string)
PlayerPrefs.SetInt("HighScore", 9999);
PlayerPrefs.SetFloat("MusicVolume", 0.8f);
PlayerPrefs.SetString("PlayerName", "Alice");
PlayerPrefs.Save(); // 确保写入磁盘

// 读取(带默认值)
int score = PlayerPrefs.GetInt("HighScore", 0);
// 删除
PlayerPrefs.DeleteKey("HighScore");
PlayerPrefs.DeleteAll();

// ⚠️ 缺点:无加密、容量小、不适合复杂数据

15.2 JSON 存档

csharp 复制代码
[System.Serializable]
public class SaveData
{
    public string playerName;
    public int level;
    public float hp;
    public List<string> inventory;
    public Vector3Serializable position;
}

// Vector3不能直接序列化,需要包装
[System.Serializable]
public struct Vector3Serializable
{
    public float x, y, z;
    public Vector3Serializable(Vector3 v) { x=v.x; y=v.y; z=v.z; }
    public Vector3 ToVector3() => new Vector3(x, y, z);
}

// 保存
public static void Save(SaveData data)
{
    string json = JsonUtility.ToJson(data, true); // true=格式化
    string path = Path.Combine(Application.persistentDataPath, "save.json");
    File.WriteAllText(path, json);
}

// 加载
public static SaveData Load()
{
    string path = Path.Combine(Application.persistentDataPath, "save.json");
    if (!File.Exists(path)) return new SaveData();
    string json = File.ReadAllText(path);
    return JsonUtility.FromJson<SaveData>(json);
}

15.3 AES 加密存档

csharp 复制代码
using System.Security.Cryptography;
using System.IO;
using System.Text;

public static class EncryptedSave
{
    private static readonly string key = "1234567890ABCDEF"; // 16字节密钥
    private static readonly string iv  = "ABCDEF1234567890"; // 16字节IV

    public static void SaveEncrypted(SaveData data)
    {
        string json = JsonUtility.ToJson(data);
        byte[] encrypted = Encrypt(Encoding.UTF8.GetBytes(json));
        string path = Path.Combine(Application.persistentDataPath, "save.dat");
        File.WriteAllBytes(path, encrypted);
    }

    static byte[] Encrypt(byte[] data)
    {
        using var aes = Aes.Create();
        aes.Key = Encoding.UTF8.GetBytes(key);
        aes.IV = Encoding.UTF8.GetBytes(iv);
        using var enc = aes.CreateEncryptor();
        return enc.TransformFinalBlock(data, 0, data.Length);
    }
}

第16章:AI 与寻路系统

csharp 复制代码
using UnityEngine.AI;

// NavMeshAgent 组件
NavMeshAgent agent = GetComponent<NavMeshAgent>();
agent.speed = 5f;
agent.stoppingDistance = 1f;  // 停止距离
agent.destination = targetPos; // 设置目标位置

// 检查是否到达
if (!agent.pathPending && agent.remainingDistance < agent.stoppingDistance)
    Debug.Log("到达目标!");

// 烘焙NavMesh:Window → AI → Navigation → Bake

16.2 有限状态机 FSM

csharp 复制代码
public class EnemyAI : MonoBehaviour
{
    enum AIState { Patrol, Chase, Attack, Flee }
    AIState state = AIState.Patrol;

    void Update()
    {
        float distToPlayer = Vector3.Distance(transform.position, player.position);
        switch (state)
        {
            case AIState.Patrol:
                PatrolBehavior();
                if (distToPlayer < detectRange) state = AIState.Chase;
                break;
            case AIState.Chase:
                agent.destination = player.position;
                if (distToPlayer < attackRange) state = AIState.Attack;
                if (distToPlayer > loseRange) state = AIState.Patrol;
                break;
            case AIState.Attack:
                AttackBehavior();
                if (distToPlayer > attackRange) state = AIState.Chase;
                if (hp < maxHP * 0.2f) state = AIState.Flee;
                break;
            case AIState.Flee:
                Vector3 fleeDir = (transform.position - player.position).normalized;
                agent.destination = transform.position + fleeDir * 10f;
                break;
        }
    }
}

16.3 行为树概念

复制代码
行为树节点类型:
├── Composite(组合节点)
│   ├── Sequence:顺序执行,全成功才成功(AND)
│   ├── Selector:选择执行,一个成功就成功(OR)
│   └── Parallel:并行执行
├── Decorator(装饰节点)
│   ├── Inverter:结果取反
│   └── Repeater:重复执行
└── Leaf(叶节点)
    ├── Action:执行动作(移动/攻击/拾取)
    └── Condition:判断条件(是否看到敌人/HP是否低)

第17章:Editor 扩展开发

17.1 Custom Inspector

csharp 复制代码
using UnityEditor;

[CustomEditor(typeof(EnemyConfig))]
public class EnemyConfigEditor : Editor
{
    public override void OnInspectorGUI()
    {
        EnemyConfig config = (EnemyConfig)target;

        EditorGUILayout.LabelField("敌人配置", EditorStyles.boldLabel);
        config.enemyName = EditorGUILayout.TextField("名称", config.enemyName);
        config.hp = EditorGUILayout.IntSlider("HP", config.hp, 1, 1000);

        if (config.hp < 100)
            EditorGUILayout.HelpBox("HP过低!", MessageType.Warning);

        if (GUILayout.Button("重置默认值"))
            config.ResetDefaults();

        if (GUI.changed) EditorUtility.SetDirty(config);
    }
}

17.2 Editor Window

csharp 复制代码
public class MyToolWindow : EditorWindow
{
    [MenuItem("Tools/My Tool Window")]
    public static void ShowWindow()
    {
        GetWindow<MyToolWindow>("我的工具");
    }

    void OnGUI()
    {
        GUILayout.Label("自定义工具窗口", EditorStyles.boldLabel);
        if (GUILayout.Button("执行操作"))
            Debug.Log("按钮被点击");
    }
}

17.3 Gizmos

csharp 复制代码
// 在Scene视图中绘制调试信息
void OnDrawGizmosSelected()
{
    Gizmos.color = Color.red;
    Gizmos.DrawWireSphere(transform.position, attackRange); // 攻击范围
    Gizmos.color = Color.yellow;
    Gizmos.DrawWireSphere(transform.position, detectRange); // 检测范围
}

相关推荐
Yuk丶6 小时前
Procedural Dialogue Engine - UE4程序化对话系统的技术实现
c++·游戏引擎·ue4·游戏程序·虚幻
RReality7 小时前
【Unity Shader URP】屏幕空间扭曲后处理(Screen Space Distortion)实战教程
ui·unity·游戏引擎·图形渲染·材质
zcc8580797629 小时前
Unity 事件驱动架构
unity
心之所向,自强不息9 小时前
VSCode + EmmyLua 调试 Unity Lua(最简接入 + 不阻塞运行版)
vscode·unity·lua
空中海9 小时前
第六篇:Unity专项方向
unity·游戏引擎
mxwin10 小时前
Unity Shader 屏幕空间反射 (SSR) 原理解析
jvm·unity·游戏引擎·shader
心前阳光10 小时前
Unity之利用特性给ScriptableObject分组
unity·游戏引擎
mxwin10 小时前
Unity Shader 屏幕空间法线重建 从深度缓冲反推世界法线——原理、踩坑与 URP Shader 实战
unity·游戏引擎·shader
空中海10 小时前
第五篇:Unity工程化能力
elasticsearch·unity·游戏引擎