Unity TEngine5实战:用它的UI模块和事件系统,快速搭建一个战斗界面(含代码)

张开发
2026/4/16 7:27:44 15 分钟阅读

分享文章

Unity TEngine5实战:用它的UI模块和事件系统,快速搭建一个战斗界面(含代码)
Unity TEngine5实战从零构建战斗UI系统全流程解析在游戏开发中一个高效、响应迅速的战斗界面系统往往决定了玩家的核心体验。本文将基于TEngine5框架手把手带你实现一个完整的战斗UI系统涵盖UI构建、事件驱动和数据绑定三大核心模块。1. 环境准备与项目初始化首先确保你的Unity项目已经集成TEngine5框架。这个开源框架提供了完整的UI工作流和事件系统能显著提升开发效率。新建Unity项目后通过Package Manager导入TEngine5或者直接从GitHub仓库克隆最新版本。关键依赖检查清单Unity 2021 LTS或更新版本TextMeshPro基础包用于高质量文本渲染TEngine5核心模块UI、Event、Resource// 初始化TEngine框架的典型入口代码 public class GameLaunch : MonoBehaviour { void Start() { // 初始化框架模块 GameModule.CreateModules(); // 启动游戏逻辑 StartGameLogic().Forget(); } private async UniTaskVoid StartGameLogic() { // 加载必要资源 await GameModule.Resource.LoadAssetsAsync(UI/Battle); // 进入战斗场景 GameModule.Scene.LoadScene(BattleScene).Forget(); } }2. 战斗UI界面架构设计一个标准的战斗UI通常包含以下核心组件组件类型功能描述实现方式血条系统显示玩家/敌人生命值Slider 事件监听分数显示实时更新战斗得分TextMeshPro 数据绑定技能按钮触发角色技能Button 事件派发暂停菜单游戏状态控制弹出式UI窗口UI层级规划建议Background Layer背景元素非交互Gameplay Layer核心战斗信息血条、分数Interaction Layer按钮和可操作元素Popup Layer暂停菜单等临时界面3. 使用UIWindow构建基础界面TEngine5的UIWindow基类提供了完整的生命周期管理。我们首先创建战斗主界面[Window(UILayer.Gameplay)] public class UIBattleMain : UIWindow { #region 自动生成的组件引用 private Slider m_hpSlider; private TMP_Text m_scoreText; private Button m_skillButton; private Button m_pauseButton; protected override void ScriptGenerator() { m_hpSlider FindChildComponentSlider(HPBar); m_scoreText FindChildComponentTMP_Text(ScoreText); m_skillButton FindChildComponentButton(SkillBtn); m_pauseButton FindChildComponentButton(PauseBtn); m_skillButton.onClick.AddListener(OnSkillButtonClick); m_pauseButton.onClick.AddListener(OnPauseButtonClick); } #endregion private void OnSkillButtonClick() { GameEvent.Send(BattleEventDefine.PlayerCastSkill); } private void OnPauseButtonClick() { GameModule.UI.ShowWindowUIPauseMenu(); } }UIWidget的巧妙应用 对于可复用的UI元素如血条建议封装为UIWidgetpublic class UIHealthBar : UIWidget { private Slider m_slider; private Entity m_bindEntity; public void BindEntity(Entity entity) { m_bindEntity entity; // 初始值设置 UpdateHealth(entity.CurrentHealth, entity.MaxHealth); // 监听血量变化事件 entity.OnHealthChanged UpdateHealth; } private void UpdateHealth(float current, float max) { m_slider.value current / max; } public override void OnDestroy() { if(m_bindEntity ! null) m_bindEntity.OnHealthChanged - UpdateHealth; } }4. 事件系统的深度集成TEngine5的GameEvent系统是UI与游戏逻辑解耦的关键。我们需要定义战斗相关事件public static class BattleEventDefine { // 分数变化事件携带int参数 public static readonly int ScoreUpdate StringId.StringToHash(Battle.ScoreUpdate); // 玩家释放技能事件 public static readonly int PlayerCastSkill StringId.StringToHash(Battle.CastSkill); // 游戏暂停事件 public static readonly int GamePaused StringId.StringToHash(Battle.GamePaused); }事件监听与响应的最佳实践// 在UIWindow中注册事件监听 protected override void RegisterEvent() { AddUIEventint(BattleEventDefine.ScoreUpdate, OnScoreUpdated); AddUIEvent(BattleEventDefine.GamePaused, OnGamePaused); } private void OnScoreUpdated(int newScore) { m_scoreText.text $SCORE: {newScore}; // 添加分数变化动画 PlayScoreAnimation(); } private void OnGamePaused() { m_pauseButton.interactable false; }在游戏逻辑层派发事件public class BattleSystem : BehaviourSingletonBattleSystem { private int m_currentScore; public void AddScore(int points) { m_currentScore points; GameEvent.Send(BattleEventDefine.ScoreUpdate, m_currentScore); } public void TogglePause() { bool isPaused Time.timeScale 0; Time.timeScale isPaused ? 1 : 0; GameEvent.Send(BattleEventDefine.GamePaused); } }5. 数据驱动视图的高级技巧现代游戏UI越来越倾向于数据驱动的设计模式。TEngine5虽然没有内置的MVVM框架但我们可以实现轻量级的数据绑定public class ObservablePropertyT { private T m_value; public event ActionT OnValueChanged; public T Value { get m_value; set { if(!Equals(m_value, value)) { m_value value; OnValueChanged?.Invoke(value); } } } } // 在UI中的使用示例 public class UIPlayerStatus : UIWindow { private ObservablePropertyfloat m_health new(); private Slider m_healthSlider; protected override void OnCreate() { m_healthSlider FindChildComponentSlider(HealthBar); m_health.OnValueChanged UpdateHealthBar; } private void UpdateHealthBar(float value) { m_healthSlider.value value; } public void SetHealth(float current, float max) { m_health.Value current / max; } }性能优化建议对于高频更新的数据如帧数显示使用UniTask的每帧检测而非事件驱动批量更新UI元素时先禁用Canvas渲染更新完成后再启用使用对象池管理频繁创建/销毁的UI元素// 高性能分数更新方案 private async UniTaskVoid UpdateScorePerFrame() { while (true) { if (m_lastScore ! m_currentScore) { m_scoreText.text m_currentScore.ToString(); m_lastScore m_currentScore; } await UniTask.Yield(PlayerLoopTiming.Update); } }6. 实战构建完整的战斗UI流程让我们把这些技术整合到一个实际案例中场景加载阶段private async UniTaskVoid EnterBattle() { // 显示加载界面 await GameModule.UI.ShowWindowAsyncUILoading(); // 加载战斗场景 await GameModule.Scene.LoadScene(BattleScene); // 初始化战斗系统 BattleSystem.Instance.Initialize(); // 显示主界面 GameModule.UI.ShowWindowUIBattleMain(); // 隐藏加载界面 GameModule.UI.CloseWindowUILoading(); }战斗进行阶段的典型事件流玩家攻击敌人 - EnemyDefeated事件 - 分数系统处理 - ScoreUpdate事件 - UI更新 玩家点击暂停 - PauseGame事件 - 时间系统暂停 - 显示暂停菜单暂停菜单实现[Window(UILayer.Popup)] public class UIPauseMenu : UIWindow { private Button m_resumeButton; private Button m_quitButton; protected override void ScriptGenerator() { m_resumeButton FindChildComponentButton(ResumeBtn); m_quitButton FindChildComponentButton(QuitBtn); m_resumeButton.onClick.AddListener(OnResume); m_quitButton.onClick.AddListener(OnQuit); } private void OnResume() { BattleSystem.Instance.ResumeGame(); Close(); } private void OnQuit() { GameModule.UI.ShowMessageBox(确认退出战斗吗, () { BattleSystem.Instance.EndBattle(); GameModule.Scene.LoadScene(MainMenu).Forget(); }); } }7. 调试与性能分析TEngine5提供了强大的调试工具UI层级查看器// 在控制台查看当前UI堆栈 GameModule.UI.DebugPrintWindowStack();事件监控// 跟踪特定事件 GameEvent.AddDebugListener(BattleEventDefine.ScoreUpdate, (score) Debug.Log($Score updated: {score}));性能统计// 在UIWindow中标记性能关键代码 TProfiler.BeginSample(HealthBarUpdate); UpdateHealthBar(); TProfiler.EndSample();常见问题解决方案UI点击无响应检查Canvas的Raycaster设置和UI层级顺序事件未触发确认事件ID一致性和监听器生命周期内存泄漏确保所有事件监听在OnDestroy中移除8. 进阶技巧动态UI与特效集成对于需要动态生成的UI元素如伤害数字推荐使用TEngine5的对象池系统public class DamageNumberManager : MonoBehaviour { public void ShowDamage(Vector3 worldPos, int damage) { var uiDamage GameModule.UI.CreateWidgetUIDamageNumber(); // 转换世界坐标到屏幕空间 var screenPos Camera.main.WorldToScreenPoint(worldPos); uiDamage.ShowDamage(damage, screenPos); } } [Window(UILayer.Effect)] public class UIDamageNumber : UIWindow { private TMP_Text m_text; private RectTransform m_rect; public void ShowDamage(int damage, Vector2 screenPos) { m_text.text damage.ToString(); m_rect.anchoredPosition screenPos; // 播放动画 PlayAnimation().Forget(); } private async UniTaskVoid PlayAnimation() { // 向上飘动动画 await m_rect.DOAnchorPosY(100, 0.5f) .SetRelative() .ToUniTask(); // 回收实例 GameModule.UI.DestroyWindow(this); } }特效与UI的配合技巧使用Shader实现UI元素的特殊效果如血条预警闪烁将粒子系统渲染到UI层需要调整RenderTexture设置对于复杂的UI动画推荐使用Dotween配合UniTask// 血条危险状态特效Shader Shader UI/HealthBarWarning { Properties { _MainTex (Texture, 2D) white {} _WarningColor (Warning Color, Color) (1,0,0,1) _WarningThreshold (Warning Threshold, Range(0,1)) 0.3 _FlashSpeed (Flash Speed, Float) 2.0 } // ... shader代码实现闪烁效果 }9. 多平台适配与响应式布局针对不同设备屏幕尺寸TEngine5提供了多种适配方案Canvas Scaler配置UI Scale Mode: Scale With Screen SizeReference Resolution: 1920x1080根据项目基准设置Screen Match Mode: Expand保持比例同时适应屏幕锚点与布局组关键UI元素应设置适当的锚点使用Horizontal/Vertical Layout Group自动排列元素Content Size Fitter实现动态尺寸调整设备特定适配protected override void OnAdaption() { // 移动设备隐藏部分特效 if(Application.isMobilePlatform) { FindChild(HighEndEffects).SetActive(false); } // 超宽屏调整布局 float aspect Screen.width / (float)Screen.height; if(aspect 2.1f) { m_sidePanels.anchoredPosition new Vector2(100, 0); } }响应式设计模式[Window(UILayer.Gameplay)] public class UIBattleAdaptive : UIWindow { private RectTransform m_leftPanel; private RectTransform m_rightPanel; protected override void OnRectTransformDimensionsChange() { base.OnRectTransformDimensionsChange(); UpdateLayout(); } private void UpdateLayout() { float safePadding Screen.safeArea.width * 0.05f; m_leftPanel.anchoredPosition new Vector2(safePadding, 0); m_rightPanel.anchoredPosition new Vector2(-safePadding, 0); } }10. 项目架构建议与扩展思路基于TEngine5构建大型项目时推荐采用以下架构BattleSystem (游戏逻辑) ↑ ↓ GameEvent (事件系统) ↑ ↓ UISystem (界面呈现) ↑ ↓ ResourceManager (资源加载)可扩展方向UI主题系统通过ScriptableObject配置不同风格的UI主题多语言支持集成TEngine5的本地化模块UI自动化测试基于事件系统模拟用户操作编辑器扩展自定义UI脚本生成模板// 主题系统示例 [CreateAssetMenu] public class UITheme : ScriptableObject { public Color primaryColor; public Color dangerColor; public TMP_FontAsset mainFont; public Sprite buttonSprite; } // 在UI中的应用 public class ThemedUI : UIWindow { [SerializeField] private UITheme m_theme; protected override void OnRefresh() { ApplyTheme(m_theme); } private void ApplyTheme(UITheme theme) { m_button.image.sprite theme.buttonSprite; m_text.font theme.mainFont; m_warningText.color theme.dangerColor; } }在实际项目中我们曾遇到一个棘手问题当快速切换界面时由于异步加载未完成就发起新请求导致UI状态混乱。解决方案是引入操作队列系统public class UIOperationQueue { private QueueFuncUniTask m_operations new(); private bool m_isProcessing; public void Enqueue(FuncUniTask operation) { m_operations.Enqueue(operation); if(!m_isProcessing) ProcessNext().Forget(); } private async UniTaskVoid ProcessNext() { m_isProcessing true; try { while(m_operations.Count 0) { var op m_operations.Dequeue(); await op(); } } finally { m_isProcessing false; } } } // 使用示例 UIOperationQueue queue new(); queue.Enqueue(async () { await GameModule.UI.ShowWindowAsyncUIWindow1(); }); queue.Enqueue(async () { await GameModule.UI.CloseWindowUIWindow1(); await GameModule.UI.ShowWindowAsyncUIWindow2(); });

更多文章