第三篇:Unity进阶阶段(商业项目能力)
目标:可参与团队商业项目开发
第12章:脚本架构设计
12.1 SOLID 设计原则
| 原则 | 含义 | Unity示例 |
|---|---|---|
| S - 单一职责 | 一个类只做一件事 | PlayerMovement只管移动 |
| O - 开闭原则 | 对扩展开放,对修改关闭 | 用interface定义IWeapon |
| L - 里式替换 | 子类可替换父类 | Enemy继承Character |
| I - 接口隔离 | 接口小而专 | IDamageable/IHealable分离 |
| D - 依赖反转 | 依赖抽象不依赖具体 | 依赖IDataService接口 |
12.2 常用设计模式
// ═══ 单例模式 Singleton ═══publicclassGameManager:MonoBehaviour{publicstaticGameManagerInstance{get;privateset;}voidAwake(){if(Instance==null){Instance=this;DontDestroyOnLoad(gameObject);}elseDestroy(gameObject);}}// ═══ 观察者模式 Observer ═══publicclassHealthSystem:MonoBehaviour{publiceventAction<float>OnHealthChanged;publiceventActionOnDeath;privatefloathealth;publicvoidTakeDamage(floatdmg){health-=dmg;OnHealthChanged?.Invoke(health);if(health<=0)OnDeath?.Invoke();}}// ═══ 命令模式 Command ═══publicinterfaceICommand{voidExecute();voidUndo();}publicclassMoveCommand:ICommand{Transformtarget;Vector3from,to;publicMoveCommand(Transformt,Vector3dest){target=t;from=t.position;to=dest;}publicvoidExecute()=>target.position=to;publicvoidUndo()=>target.position=from;}// 用途:技能释放、操作撤销、录像回放// ═══ 状态机模式 State ═══publicinterfaceIState{voidEnter();voidUpdate();voidExit();}publicclassStateMachine{privateIStatecurrent;publicvoidChangeState(IStatenewState){current?.Exit();current=newState;current.Enter();}publicvoidUpdate()=>current?.Update();}// ═══ 工厂模式 Factory ═══publicstaticclassEnemyFactory{publicstaticEnemyCreate(EnemyTypetype)=>typeswitch{EnemyType.Goblin=>newGoblin(),EnemyType.Dragon=>newDragon(),_=>thrownewArgumentException()};}// ═══ 策略模式 Strategy ═══publicinterfaceIAttackStrategy{voidAttack(Transformattacker,Transformtarget);}publicclassMeleeAttack:IAttackStrategy{publicvoidAttack(...){/* 近战 */}}publicclassRangedAttack:IAttackStrategy{publicvoidAttack(...){/* 远程 */}}12.3 Unity 常用架构
Manager模式(最常用): ├── GameManager ← 游戏流程控制 ├── UIManager ← UI管理 ├── AudioManager ← 音频管理 ├── PoolManager ← 对象池管理 ├── SaveManager ← 存档管理 └── EventManager ← 事件管理 推荐框架: ├── QFramework ← 国内主流,轻量级 ├── Zenject ← 依赖注入框架 ├── UniTask ← 异步方案(替代协程) └── UniRx ← 响应式编程第13章:事件系统与消息通信
13.1 三种事件实现方式
// ═══ 方式1:C# event(性能最好)═══publicclassPlayer:MonoBehaviour{publiceventAction<int>OnHPChanged;publiceventActionOnDeath;publicvoidTakeDamage(intdmg){hp-=dmg;OnHPChanged?.Invoke(hp);}}// 订阅:player.OnHPChanged += UpdateHPBar;// 取消:player.OnHPChanged -= UpdateHPBar;// ═══ 方式2:UnityEvent(Inspector可视化绑定)═══[SerializeField]UnityEventonDeath;[SerializeField]UnityEvent<int>onScoreChanged;// 代码触发:onDeath.Invoke();// ═══ 方式3:EventBus(全局解耦通信)═══publicstaticclassEventBus{staticDictionary<Type,List<object>>handlers=new();publicstaticvoidSubscribe<T>(Action<T>handler){...}publicstaticvoidUnsubscribe<T>(Action<T>handler){...}publicstaticvoidPublish<T>(TeventData){...}}// 发布: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
usingUnityEngine.Pool;// Unity 2021+ 内置对象池ObjectPool<GameObject>bulletPool=newObjectPool<GameObject>(createFunc:()=>Instantiate(bulletPrefab),// 创建actionOnGet:obj=>obj.SetActive(true),// 取出时actionOnRelease:obj=>obj.SetActive(false),// 归还时actionOnDestroy:obj=>Destroy(obj),// 销毁时collectionCheck:true,// 防重复归还defaultCapacity:20,// 默认容量maxSize:50// 最大容量);// 使用GameObjectbullet=bulletPool.Get();// 取出bulletPool.Release(bullet);// 归还14.3 自定义通用对象池
publicclassPoolManager:MonoBehaviour{publicstaticPoolManagerInstance;privateDictionary<string,Queue<GameObject>>pools=new();privateDictionary<string,GameObject>prefabs=new();publicvoidRegisterPool(stringkey,GameObjectprefab,intinitSize){prefabs[key]=prefab;pools[key]=newQueue<GameObject>();for(inti=0;i<initSize;i++){varobj=Instantiate(prefab);obj.SetActive(false);pools[key].Enqueue(obj);}}publicGameObjectSpawn(stringkey,Vector3pos,Quaternionrot){GameObjectobj;if(pools[key].Count>0)obj=pools[key].Dequeue();elseobj=Instantiate(prefabs[key]);obj.transform.SetPositionAndRotation(pos,rot);obj.SetActive(true);returnobj;}publicvoidRecycle(stringkey,GameObjectobj){obj.SetActive(false);pools[key].Enqueue(obj);}}第15章:存档与数据持久化
15.1 PlayerPrefs(简单存储)
// 存储(只支持 int/float/string)PlayerPrefs.SetInt("HighScore",9999);PlayerPrefs.SetFloat("MusicVolume",0.8f);PlayerPrefs.SetString("PlayerName","Alice");PlayerPrefs.Save();// 确保写入磁盘// 读取(带默认值)intscore=PlayerPrefs.GetInt("HighScore",0);// 删除PlayerPrefs.DeleteKey("HighScore");PlayerPrefs.DeleteAll();// ⚠️ 缺点:无加密、容量小、不适合复杂数据15.2 JSON 存档
[System.Serializable]publicclassSaveData{publicstringplayerName;publicintlevel;publicfloathp;publicList<string>inventory;publicVector3Serializableposition;}// Vector3不能直接序列化,需要包装[System.Serializable]publicstructVector3Serializable{publicfloatx,y,z;publicVector3Serializable(Vector3v){x=v.x;y=v.y;z=v.z;}publicVector3ToVector3()=>newVector3(x,y,z);}// 保存publicstaticvoidSave(SaveDatadata){stringjson=JsonUtility.ToJson(data,true);// true=格式化stringpath=Path.Combine(Application.persistentDataPath,"save.json");File.WriteAllText(path,json);}// 加载publicstaticSaveDataLoad(){stringpath=Path.Combine(Application.persistentDataPath,"save.json");if(!File.Exists(path))returnnewSaveData();stringjson=File.ReadAllText(path);returnJsonUtility.FromJson<SaveData>(json);}15.3 AES 加密存档
usingSystem.Security.Cryptography;usingSystem.IO;usingSystem.Text;publicstaticclassEncryptedSave{privatestaticreadonlystringkey="1234567890ABCDEF";// 16字节密钥privatestaticreadonlystringiv="ABCDEF1234567890";// 16字节IVpublicstaticvoidSaveEncrypted(SaveDatadata){stringjson=JsonUtility.ToJson(data);byte[]encrypted=Encrypt(Encoding.UTF8.GetBytes(json));stringpath=Path.Combine(Application.persistentDataPath,"save.dat");File.WriteAllBytes(path,encrypted);}staticbyte[]Encrypt(byte[]data){usingvaraes=Aes.Create();aes.Key=Encoding.UTF8.GetBytes(key);aes.IV=Encoding.UTF8.GetBytes(iv);usingvarenc=aes.CreateEncryptor();returnenc.TransformFinalBlock(data,0,data.Length);}}第16章:AI 与寻路系统
16.1 NavMesh 导航
usingUnityEngine.AI;// NavMeshAgent 组件NavMeshAgentagent=GetComponent<NavMeshAgent>();agent.speed=5f;agent.stoppingDistance=1f;// 停止距离agent.destination=targetPos;// 设置目标位置// 检查是否到达if(!agent.pathPending&&agent.remainingDistance<agent.stoppingDistance)Debug.Log("到达目标!");// 烘焙NavMesh:Window → AI → Navigation → Bake16.2 有限状态机 FSM
publicclassEnemyAI:MonoBehaviour{enumAIState{Patrol,Chase,Attack,Flee}AIStatestate=AIState.Patrol;voidUpdate(){floatdistToPlayer=Vector3.Distance(transform.position,player.position);switch(state){caseAIState.Patrol:PatrolBehavior();if(distToPlayer<detectRange)state=AIState.Chase;break;caseAIState.Chase:agent.destination=player.position;if(distToPlayer<attackRange)state=AIState.Attack;if(distToPlayer>loseRange)state=AIState.Patrol;break;caseAIState.Attack:AttackBehavior();if(distToPlayer>attackRange)state=AIState.Chase;if(hp<maxHP*0.2f)state=AIState.Flee;break;caseAIState.Flee:Vector3fleeDir=(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
usingUnityEditor;[CustomEditor(typeof(EnemyConfig))]publicclassEnemyConfigEditor:Editor{publicoverridevoidOnInspectorGUI(){EnemyConfigconfig=(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
publicclassMyToolWindow:EditorWindow{[MenuItem("Tools/My Tool Window")]publicstaticvoidShowWindow(){GetWindow<MyToolWindow>("我的工具");}voidOnGUI(){GUILayout.Label("自定义工具窗口",EditorStyles.boldLabel);if(GUILayout.Button("执行操作"))Debug.Log("按钮被点击");}}17.3 Gizmos
// 在Scene视图中绘制调试信息voidOnDrawGizmosSelected(){Gizmos.color=Color.red;Gizmos.DrawWireSphere(transform.position,attackRange);// 攻击范围Gizmos.color=Color.yellow;Gizmos.DrawWireSphere(transform.position,detectRange);// 检测范围}