概述 入门系列第 20 篇做了一个 2D 平台游戏的综合实战。这一篇的实战是 搭建可扩展的游戏项目框架 ——这不是一个具体的游戏,而是一个 架构模板 ,当你开始新项目时,可以用这个框架作为起点。
框架集成本系列所有进阶知识:依赖注入、事件总线、状态机、对象池、Addressables、Editor 工具、以及性能监控。
1. 框架目录结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Assets/ Framework/ # 框架核心(不依赖项目逻辑) DI/ # 依赖注入(VContainer) Event/ # 事件总线 Pool/ # 对象池 StateMachine/ # 状态机 Input/ # 输入系统封装 Scene/ # 场景管理 Audio/ # 音频系统 UI/ # UI 框架 Editor/ # 编辑器工具 Game/ # 游戏业务逻辑 Managers/ # 管理器 Systems/ # 游戏系统 Entities/ # 游戏实体 Configs/ # ScriptableObject 配置 UI/ # 游戏 UI Art/ # 美术资源 Audio/ # 音频资源 Prefabs/ # 预制体 Addressables/ # Addressables 分组
2. 框架启动流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class GameBootstrapper : MonoBehaviour { private async UniTaskVoid Start () { DontDestroyOnLoad(gameObject); await InitializeFrameworkAsync(); await LoadCoreAssetsAsync(); await SceneManager.LoadSceneAsync("MainMenu" ); } private async UniTask InitializeFrameworkAsync () { FrameworkCore.Initialize(new FrameworkConfig { UseVContainer = true , EnableEventBus = true , PoolConfig = new PoolConfig { MaxPools = 20 }, EnableProfiler = Debug.isDebugBuild }); await UniTask.Yield(); } private async UniTask LoadCoreAssetsAsync () { var handle = Addressables.LoadAssetsAsync<GameObject>( new List<string > { "CoreAssets" }, null , Addressables.MergeMode.Union); await handle.ToUniTask(); } }
3. 框架核心模块 3.1 EventBus 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static class EventBus { private static readonly Dictionary<Type, List<Delegate>> _handlers = new (); public static void Initialize () { } public static void Subscribe <T >(Action<T> handler ) where T : struct { var type = typeof (T); if (!_handlers.ContainsKey(type)) _handlers[type] = new List<Delegate>(); _handlers[type].Add(handler); } public static void Unsubscribe <T >(Action<T> handler ) where T : struct { var type = typeof (T); if (_handlers.TryGetValue(type, out var handlers)) { handlers.Remove(handler); if (handlers.Count == 0 ) _handlers.Remove(type); } } public static void Publish <T >(T eventData ) where T : struct { var type = typeof (T); if (_handlers.TryGetValue(type, out var handlers)) { foreach (var handler in handlers.ToArray()) (handler as Action<T>)?.Invoke(eventData); } } }
3.2 场景管理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public static class SceneManager { private static readonly Dictionary<string , SceneGroup> _sceneGroups = new (); public static void RegisterSceneGroup (string name, string [] scenes ) { _sceneGroups[name] = new SceneGroup { scenes = scenes }; } public static async UniTask LoadSceneGroupAsync (string groupName ) { if (!_sceneGroups.TryGetValue(groupName, out var group )) return ; foreach (var scene in group .scenes) { await UnityEngine.SceneManagement.SceneManager .LoadSceneAsync(scene, LoadSceneMode.Additive).ToUniTask(); } } private struct SceneGroup { public string [] scenes; } }
3.3 UI 框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public abstract class BaseUI : MonoBehaviour { protected CanvasGroup CanvasGroup { get ; private set ; } private CancellationTokenSource _cts; protected virtual void Awake () { CanvasGroup = GetComponent<CanvasGroup>(); if (CanvasGroup == null ) CanvasGroup = gameObject.AddComponent<CanvasGroup>(); } public async UniTask ShowAsync (float duration = 0.3f ) { _cts?.Cancel(); _cts = new CancellationTokenSource(); gameObject.SetActive(true ); await FadeAlphaAsync(0f , 1f , duration, _cts.Token); CanvasGroup.interactable = true ; CanvasGroup.blocksRaycasts = true ; } public async UniTask HideAsync (float duration = 0.2f ) { _cts?.Cancel(); _cts = new CancellationTokenSource(); CanvasGroup.interactable = false ; await FadeAlphaAsync(1f , 0f , duration, _cts.Token); gameObject.SetActive(false ); } private async UniTask FadeAlphaAsync (float from , float to, float duration, CancellationToken token ) { var elapsed = 0f ; while (elapsed < duration) { elapsed += Time.deltaTime; CanvasGroup.alpha = Mathf.Lerp(from , to, elapsed / duration); await UniTask.Yield(token); } CanvasGroup.alpha = to; } }
4. 运行时 Profiler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class RuntimeProfilerDisplay : MonoBehaviour { private float _fps; private void Update () { _fps = 1f / Time.unscaledDeltaTime; } private void OnGUI () { var style = new GUIStyle { fontSize = 14 }; style.normal.textColor = _fps >= 50 ? Color.green : (_fps >= 30 ? Color.yellow : Color.red); GUILayout.BeginArea(new Rect(10 , 10 , 200 , 40 )); GUILayout.Label("FPS: " + _fps.ToString("F1" ), style); GUILayout.EndArea(); } }
5. 框架使用流程 1 2 3 4 5 6 7 8 1. 复制 Framework/ 目录到新项目 2. 安装依赖(VContainer、UniTask、Addressables) 3. 创建 GameBootstrapper 场景 4. 按项目需求实现 GameEntryPoint 5. 用 Scene Groups 管理关卡 6. 用 EventBus 实现模块间通信 7. 用 PoolManager + Addressables 管理对象 8. 用 BaseUI 继承实现所有 UI 面板
结语 至此,Unity3D 进阶教程的 20 篇全部完成。
从 架构模式 -> 依赖注入 -> 状态机 -> UniTask -> 编辑器工具 -> Addressables -> URP -> Shader -> 后处理 -> 渲染优化 -> Profiler -> 内存 -> 代码优化 -> ECS -> Job System -> Netcode -> 多人架构 -> Input System -> 高级物理 -> 项目框架 ,覆盖了 Unity 开发的各个深度维度。
进阶之路不仅是”会用更多功能”,更是 理解原理、选择合适方案、构建可维护的架构 。继续实践,继续探索。