修复已知的 bug,规划后期的开发方向
This commit is contained in:
parent
85b0205c73
commit
e151619c69
|
|
@ -80,6 +80,4 @@ crashlytics-build.properties
|
|||
/[Aa]ssets/[Ss]treamingAssets/aa/*
|
||||
|
||||
/UI参考
|
||||
|
||||
/DisplayItemInfoForm_Summary.md
|
||||
/UI_Design_Summary.md
|
||||
/AGENTS.md
|
||||
|
|
|
|||
44
AGENTS.md
44
AGENTS.md
|
|
@ -1,44 +0,0 @@
|
|||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
This is a Unity project (Unity 2022.3.62f3c1). Core game code and assets live under `Assets/`:
|
||||
- `Assets/GameMain/` for game-specific scripts, scenes, and content.
|
||||
- `Assets/GameFramework/` for shared framework code and editor tooling.
|
||||
- `Assets/Plugins/` for third-party integrations (e.g., DOTween).
|
||||
- `Assets/Resources/` and `Assets/StreamingAssets/` for runtime-loaded data.
|
||||
- `Json/` and `数据表/` for data files and tables used by the game.
|
||||
- `Tools/` for local utilities or pipelines.
|
||||
|
||||
Avoid editing generated folders: `Library/`, `Temp/`, `Logs/`, `obj/`, `ProjectSettings/` (unless configuration changes are intentional).
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
Primary workflow is through the Unity Editor:
|
||||
- Open the project in Unity Hub, then press Play to run locally.
|
||||
- Optional CLI launch: `Unity -projectPath .` (use your local Unity editor path).
|
||||
- The solution file `VampireLike.sln` supports IDE navigation (Rider/VS/VS Code).
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
- C# style: 4-space indentation, braces on the same line, one public type per file.
|
||||
- Match filename to main type (e.g., `PlayerController.cs` defines `PlayerController`).
|
||||
- Use `PascalCase` for public types/members, `camelCase` for locals/parameters.
|
||||
- Prefer `SerializeField` for private Unity fields that must be editable in the Inspector.
|
||||
|
||||
## Testing Guidelines
|
||||
`com.unity.test-framework` is included, but there is no dedicated `Assets/**/Tests` directory yet. When adding tests:
|
||||
- Place under `Assets/Tests/` or `Assets/<Module>/Tests/`.
|
||||
- Name files `*Tests.cs` and use NUnit-style `[Test]` methods.
|
||||
- Run via Unity Test Runner (Window > General > Test Runner).
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
This repository does not include Git history, so no commit convention is enforced. Recommended default:
|
||||
- Short, imperative subject (e.g., `Add enemy spawn tuning`).
|
||||
- Include scope tags when helpful (e.g., `UI: Fix pause menu layout`).
|
||||
|
||||
For pull requests, include:
|
||||
- A concise summary and testing notes.
|
||||
- Linked issues or tasks when applicable.
|
||||
- Screenshots or short clips for UI/visual changes.
|
||||
|
||||
## Configuration Tips
|
||||
- Keep `Packages/manifest.json` and `ProjectSettings/` in sync when changing dependencies or project settings.
|
||||
- Large binary assets should stay in `Assets/` with `.meta` files committed alongside.
|
||||
|
|
@ -93,6 +93,7 @@ namespace Entity
|
|||
set
|
||||
{
|
||||
if (value == _enable) return;
|
||||
_enable = value;
|
||||
_movementComponent.SetMove(value);
|
||||
_backpackComponent.SetWeaponState(value);
|
||||
_inputComponent.SetListening(value);
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@
|
|||
//------------------------------------------------------------
|
||||
|
||||
using Components;
|
||||
using Definition;
|
||||
using Definition.DataStruct;
|
||||
using Entity.EntityData;
|
||||
using StarForce;
|
||||
using Game.Utility;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using Definition.DataStruct;
|
|||
using Definition.Enum;
|
||||
using DG.Tweening;
|
||||
using Entity.EntityData;
|
||||
using StarForce;
|
||||
using Game.Utility;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
|
|
|
|||
|
|
@ -94,8 +94,6 @@ namespace Procedure
|
|||
GameEntry.Entity.ShowPlayer(_currentPlayerData);
|
||||
|
||||
GameEntry.UIRouter.OpenUI(UIFormType.HudForm);
|
||||
|
||||
InitGameState();
|
||||
}
|
||||
|
||||
protected override void OnUpdate(IFsm<IProcedureManager> procedureOwner, float elapseSeconds,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,183 @@
|
|||
using Definition.Enum;
|
||||
using GameFramework.Event;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public abstract class UIFormControllerCommonBase<TContext, TForm> : UIFormControllerBase<TContext>
|
||||
where TContext : UIContext
|
||||
where TForm : UGuiForm
|
||||
{
|
||||
private TContext _context;
|
||||
private TForm _form;
|
||||
private int? _formSerialId;
|
||||
private bool _pendingRefresh;
|
||||
private bool _isBindEvent;
|
||||
|
||||
protected TContext Context => _context;
|
||||
|
||||
protected TForm Form => _form;
|
||||
|
||||
protected int? FormSerialId => _formSerialId;
|
||||
|
||||
protected abstract UIFormType UIFormTypeId { get; }
|
||||
|
||||
protected abstract void RefreshUI(TForm form, TContext context);
|
||||
|
||||
protected virtual void SubscribeCustomEvents()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void UnsubscribeCustomEvents()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void CloseLoadedFormDirect(TForm form)
|
||||
{
|
||||
form.Close();
|
||||
}
|
||||
|
||||
protected void SetContext(TContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
protected void RefreshCurrentUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_form == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshUI(_form, _context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
protected override int? OpenUIInternal(TContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("{0}.OpenUI() context is null.", GetType().Name);
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_form != null && _formSerialId.HasValue && GameEntry.UI.HasUIForm(_formSerialId.Value))
|
||||
{
|
||||
RefreshUI(_form, _context);
|
||||
return _formSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_formSerialId = GameEntry.UI.OpenUIForm(UIFormTypeId, _context);
|
||||
return _formSerialId;
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
UnsubscribeEvents();
|
||||
|
||||
if (_formSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_formSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_formSerialId.Value);
|
||||
}
|
||||
|
||||
_form = null;
|
||||
_formSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_form != null)
|
||||
{
|
||||
CloseLoadedFormDirect(_form);
|
||||
_form = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribeEvents()
|
||||
{
|
||||
if (_isBindEvent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
SubscribeCustomEvents();
|
||||
|
||||
_isBindEvent = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
{
|
||||
if (!_isBindEvent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
UnsubscribeCustomEvents();
|
||||
|
||||
_isBindEvent = false;
|
||||
}
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_formSerialId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _formSerialId.Value || args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_form = args.UIForm.Logic as TForm;
|
||||
if (_form == null)
|
||||
{
|
||||
Log.Warning("{0} open success but form logic is invalid.", GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
RefreshCurrentUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _formSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_form = null;
|
||||
_formSerialId = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 696c0314286f89c4082c2aa2eddaec2d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,60 +1,17 @@
|
|||
using Definition.Enum;
|
||||
using GameFramework.Event;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class TUIFormController : UIFormControllerBase<UIContext>
|
||||
public class TUIFormController : UIFormControllerCommonBase<UIContext, TUIForm>
|
||||
{
|
||||
private IUIUseCase _useCase;
|
||||
private UIContext _context;
|
||||
private TUIForm _tForm;
|
||||
private int? _tFormSerialId;
|
||||
private bool _pendingRefresh;
|
||||
private bool _isBindEvent;
|
||||
|
||||
private void SubscribeEvents()
|
||||
protected override UIFormType UIFormTypeId => UIFormType.TUIForm;
|
||||
|
||||
protected override void RefreshUI(TUIForm form, UIContext context)
|
||||
{
|
||||
if (_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
_isBindEvent = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
{
|
||||
if (!_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
_isBindEvent = false;
|
||||
}
|
||||
|
||||
protected override int? OpenUIInternal(UIContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("TUIFormController open failed. context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_tForm != null && _tFormSerialId.HasValue &&
|
||||
GameEntry.UI.HasUIForm(_tFormSerialId.Value))
|
||||
{
|
||||
_tForm.RefreshUI(_context);
|
||||
return _tFormSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_tFormSerialId = GameEntry.UI.OpenUIForm(UIFormType.TUIForm, context);
|
||||
return _tFormSerialId;
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
|
|
@ -70,110 +27,18 @@ namespace UI
|
|||
return null;
|
||||
}
|
||||
|
||||
return OpenUIInternal(_context);
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
UnsubscribeEvents();
|
||||
|
||||
if (_tFormSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_tFormSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_tFormSerialId.Value);
|
||||
}
|
||||
|
||||
_tForm = null;
|
||||
_tFormSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tForm != null)
|
||||
{
|
||||
_tForm.Close();
|
||||
_tForm = null;
|
||||
}
|
||||
return OpenUIInternal(Context);
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (!(useCase is IUIUseCase UIFormUseCase))
|
||||
if (!(useCase is IUIUseCase uiFormUseCase))
|
||||
{
|
||||
Log.Error("LevelUpForm.BindUseCase() useCase is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
_useCase = UIFormUseCase;
|
||||
_useCase = uiFormUseCase;
|
||||
}
|
||||
|
||||
private void TryRefreshUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tForm == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_tForm.RefreshUI(_context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
#region EventHanlders
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_tFormSerialId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _tFormSerialId.Value || args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tForm = args.UIForm.Logic as TUIForm;
|
||||
|
||||
if (_tForm == null)
|
||||
{
|
||||
Log.Warning("DialogFormController open success but form logic is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
TryRefreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _tFormSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tForm = null;
|
||||
_tFormSerialId = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ namespace UI
|
|||
private const float OnHoverAlpha = 0.7f;
|
||||
private const float OnClickAlpha = 0.6f;
|
||||
|
||||
[SerializeField] private UnityEvent _onPointerEnterAction = null;
|
||||
[SerializeField] private UnityEvent _onPointerEnterAction = new();
|
||||
|
||||
[SerializeField] private UnityEvent _onClickAction = null;
|
||||
[SerializeField] private UnityEvent _onClickAction = new();
|
||||
|
||||
[SerializeField] private UnityEvent _onPointerExitAction = null;
|
||||
[SerializeField] private UnityEvent _onPointerExitAction = new();
|
||||
|
||||
[SerializeField] private bool _enableFade = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,39 +1,20 @@
|
|||
using Definition.Enum;
|
||||
using GameFramework.Event;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class DisplayItemInfoFormController : UIFormControllerBase<DisplayItemInfoFormContext>
|
||||
public class DisplayItemInfoFormController : UIFormControllerCommonBase<DisplayItemInfoFormContext, DisplayItemInfoForm>
|
||||
{
|
||||
private DisplayItemInfoFormContext _context;
|
||||
protected override UIFormType UIFormTypeId => UIFormType.DisplayItemInfoForm;
|
||||
|
||||
private DisplayItemInfoForm _itemInfoForm;
|
||||
|
||||
private int? _itemInfoFormSerialId;
|
||||
|
||||
private bool _pendingRefresh;
|
||||
|
||||
private bool _isBindEvent = false;
|
||||
|
||||
private void SubscribeEvents()
|
||||
protected override void RefreshUI(DisplayItemInfoForm form, DisplayItemInfoFormContext context)
|
||||
{
|
||||
if (_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
_isBindEvent = true;
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
protected override void CloseLoadedFormDirect(DisplayItemInfoForm form)
|
||||
{
|
||||
if (!_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
_isBindEvent = false;
|
||||
GameEntry.UI.CloseUIForm(form);
|
||||
}
|
||||
|
||||
private static DisplayItemInfoFormContext BuildContext(DisplayItemInfoFormRawData rawData)
|
||||
|
|
@ -56,32 +37,6 @@ namespace UI
|
|||
};
|
||||
}
|
||||
|
||||
#region UI Methods
|
||||
|
||||
protected override int? OpenUIInternal(DisplayItemInfoFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("ItemInfoFormController open failed. context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_itemInfoForm != null && _itemInfoFormSerialId.HasValue &&
|
||||
GameEntry.UI.HasUIForm(_itemInfoFormSerialId.Value))
|
||||
{
|
||||
_itemInfoForm.RefreshUI(_context);
|
||||
return _itemInfoFormSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_itemInfoFormSerialId = GameEntry.UI.OpenUIForm(UIFormType.DisplayItemInfoForm, context);
|
||||
return _itemInfoFormSerialId;
|
||||
}
|
||||
|
||||
public int? OpenUI(DisplayItemInfoFormRawData rawData)
|
||||
{
|
||||
DisplayItemInfoFormContext context = BuildContext(rawData);
|
||||
|
|
@ -106,32 +61,7 @@ namespace UI
|
|||
return null;
|
||||
}
|
||||
|
||||
return OpenUIInternal(_context);
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
|
||||
UnsubscribeEvents();
|
||||
|
||||
if (_itemInfoFormSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_itemInfoFormSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_itemInfoFormSerialId.Value);
|
||||
}
|
||||
|
||||
_itemInfoForm = null;
|
||||
_itemInfoFormSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_itemInfoForm != null)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_itemInfoForm);
|
||||
_itemInfoForm = null;
|
||||
}
|
||||
return OpenUIInternal(Context);
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
|
|
@ -139,78 +69,7 @@ namespace UI
|
|||
if (!(useCase is DisplayItemInfoFormUseCase))
|
||||
{
|
||||
Log.Error("DisplayItemInfoForm.BindUseCase() useCase is invalid.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryRefreshUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_itemInfoForm == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_itemInfoForm.RefreshUI(_context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EventHanlders
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_itemInfoFormSerialId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _itemInfoFormSerialId.Value || args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_itemInfoForm = args.UIForm.Logic as DisplayItemInfoForm;
|
||||
|
||||
if (_itemInfoForm == null)
|
||||
{
|
||||
Log.Warning("DisplayItemInfoFormController open success but form logic is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
TryRefreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _itemInfoFormSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_itemInfoForm = null;
|
||||
_itemInfoFormSerialId = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,15 @@
|
|||
using Definition.Enum;
|
||||
using GameFramework.Event;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class HudFormController : UIFormControllerBase<HudFormContext>
|
||||
public class HudFormController : UIFormControllerCommonBase<HudFormContext, HudForm>
|
||||
{
|
||||
private HudFormContext _context;
|
||||
protected override UIFormType UIFormTypeId => UIFormType.HudForm;
|
||||
|
||||
private HudForm _hudForm;
|
||||
|
||||
private int? _hudFormSerialId;
|
||||
|
||||
private bool _pendingRefresh;
|
||||
|
||||
private bool _isBindEvent;
|
||||
|
||||
private void SubscribeEvents()
|
||||
protected override void RefreshUI(HudForm form, HudFormContext context)
|
||||
{
|
||||
if (_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
_isBindEvent = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
{
|
||||
if (!_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
_isBindEvent = false;
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
private static HudFormContext BuildHudFormContext()
|
||||
|
|
@ -41,32 +17,6 @@ namespace UI
|
|||
return new HudFormContext();
|
||||
}
|
||||
|
||||
#region UI Methods
|
||||
|
||||
protected override int? OpenUIInternal(HudFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("HudFormController.OpenUI() context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_hudForm != null && _hudFormSerialId.HasValue &&
|
||||
GameEntry.UI.HasUIForm(_hudFormSerialId.Value))
|
||||
{
|
||||
_hudForm.RefreshUI(_context);
|
||||
return _hudFormSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_hudFormSerialId = GameEntry.UI.OpenUIForm(UIFormType.HudForm, context);
|
||||
return _hudFormSerialId;
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is HudFormContext context)
|
||||
|
|
@ -88,105 +38,9 @@ namespace UI
|
|||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
UnsubscribeEvents();
|
||||
|
||||
if (_hudFormSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_hudFormSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_hudFormSerialId.Value);
|
||||
}
|
||||
|
||||
_hudForm = null;
|
||||
_hudFormSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hudForm != null)
|
||||
{
|
||||
_hudForm.Close();
|
||||
_hudForm = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
Log.Info("HudFormController doesn't need UseCase");
|
||||
}
|
||||
|
||||
private void TryRefreshUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hudForm == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_hudForm.RefreshUI(_context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_hudFormSerialId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _hudFormSerialId.Value ||
|
||||
args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_hudForm = args.UIForm.Logic as HudForm;
|
||||
|
||||
if (_hudForm == null)
|
||||
{
|
||||
Log.Warning("HudFormController open success but form logic is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
TryRefreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _hudFormSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_hudForm = null;
|
||||
_hudFormSerialId = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,51 +3,40 @@ using CustomEvent;
|
|||
using Definition.Enum;
|
||||
using Game.Utility;
|
||||
using GameFramework.Event;
|
||||
using Procedure;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class LevelUpFormController : UIFormControllerBase<LevelUpFormContext>
|
||||
public class LevelUpFormController : UIFormControllerCommonBase<LevelUpFormContext, LevelUpForm>
|
||||
{
|
||||
private LevelUpFormUseCase _useCase;
|
||||
|
||||
private bool _pendingRefresh;
|
||||
protected override UIFormType UIFormTypeId => UIFormType.LevelUpForm;
|
||||
|
||||
private int? _levelUpFormSerialId;
|
||||
|
||||
private LevelUpForm _levelUpForm;
|
||||
|
||||
private LevelUpFormContext _context;
|
||||
|
||||
private bool _isBindEvent;
|
||||
|
||||
private void SubscribeEvents()
|
||||
protected override void RefreshUI(LevelUpForm form, LevelUpFormContext context)
|
||||
{
|
||||
if (_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
GameEntry.Event.Subscribe(RefreshEventArgs.EventId, OnRefresh);
|
||||
GameEntry.Event.Subscribe(LevelUpPropSelectedEventArgs.EventId, OnLevelUpPropSelected);
|
||||
|
||||
_isBindEvent = true;
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
protected override void SubscribeCustomEvents()
|
||||
{
|
||||
if (!_isBindEvent) return;
|
||||
GameEntry.Event.Subscribe(RefreshEventArgs.EventId, OnRefresh);
|
||||
GameEntry.Event.Subscribe(LevelUpPropSelectedEventArgs.EventId, OnLevelUpPropSelected);
|
||||
}
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
protected override void UnsubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(RefreshEventArgs.EventId, OnRefresh);
|
||||
GameEntry.Event.Unsubscribe(LevelUpPropSelectedEventArgs.EventId, OnLevelUpPropSelected);
|
||||
|
||||
_isBindEvent = false;
|
||||
}
|
||||
|
||||
private static LevelUpFormContext BuildContext(LevelUpFormRawData rawData)
|
||||
{
|
||||
if (rawData == null || rawData.Rewards == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<LevelUpRewardItemContext> props = new List<LevelUpRewardItemContext>(rawData.Rewards.Count);
|
||||
foreach (var reward in rawData.Rewards)
|
||||
{
|
||||
|
|
@ -72,33 +61,6 @@ namespace UI
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
#region UI Methods
|
||||
|
||||
protected override int? OpenUIInternal(LevelUpFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("LevelUpFormController.OpenUI() context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_levelUpForm != null && _levelUpFormSerialId.HasValue &&
|
||||
GameEntry.UI.HasUIForm(_levelUpFormSerialId.Value))
|
||||
{
|
||||
_levelUpForm.RefreshUI(_context);
|
||||
return _levelUpFormSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_levelUpFormSerialId = GameEntry.UI.OpenUIForm(UIFormType.LevelUpForm, context);
|
||||
return _levelUpFormSerialId;
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is LevelUpFormContext context)
|
||||
|
|
@ -133,47 +95,6 @@ namespace UI
|
|||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
private void TryRefreshUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_levelUpForm == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_levelUpForm.RefreshUI(_context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
UnsubscribeEvents();
|
||||
|
||||
if (_levelUpFormSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_levelUpFormSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_levelUpFormSerialId.Value);
|
||||
}
|
||||
|
||||
_levelUpForm = null;
|
||||
_levelUpFormSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_levelUpForm != null)
|
||||
{
|
||||
_levelUpForm.Close();
|
||||
_levelUpForm = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (!(useCase is LevelUpFormUseCase levelUpFormUseCase))
|
||||
|
|
@ -185,10 +106,6 @@ namespace UI
|
|||
_useCase = levelUpFormUseCase;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Service
|
||||
|
||||
private void SelectReward(int selectedIndex)
|
||||
{
|
||||
if (_useCase == null)
|
||||
|
|
@ -198,7 +115,6 @@ namespace UI
|
|||
}
|
||||
|
||||
LevelUpFormRawData rawData = _useCase.SelectReward(selectedIndex);
|
||||
|
||||
if (rawData == null)
|
||||
{
|
||||
return;
|
||||
|
|
@ -224,51 +140,6 @@ namespace UI
|
|||
OpenUI(rawData);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args)) return;
|
||||
|
||||
if (!_levelUpFormSerialId.HasValue) return;
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _levelUpFormSerialId.Value || args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_levelUpForm = args.UIForm.Logic as LevelUpForm;
|
||||
|
||||
if (_levelUpForm == null)
|
||||
{
|
||||
Log.Warning("LevelUpFormController open success but form logic is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
TryRefreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _levelUpFormSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_levelUpForm = null;
|
||||
_levelUpFormSerialId = null;
|
||||
}
|
||||
|
||||
private void OnRefresh(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(sender is LevelUpForm))
|
||||
|
|
@ -293,7 +164,5 @@ namespace UI
|
|||
|
||||
SelectReward(args.SelectedId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,50 +9,34 @@ using UnityGameFramework.Runtime;
|
|||
|
||||
namespace UI
|
||||
{
|
||||
public class ShopFormController : UIFormControllerBase<ShopFormContext>
|
||||
public class ShopFormController : UIFormControllerCommonBase<ShopFormContext, ShopForm>
|
||||
{
|
||||
private ShopFormUseCase _useCase;
|
||||
|
||||
private bool _pendingRefresh;
|
||||
|
||||
private int? _shopFormSerialId;
|
||||
|
||||
private ShopForm _shopForm;
|
||||
|
||||
private ShopFormRawData _rawData;
|
||||
|
||||
private ShopFormContext _context;
|
||||
protected override UIFormType UIFormTypeId => UIFormType.ShopForm;
|
||||
|
||||
private bool _isBindEvent;
|
||||
|
||||
private void SubscribeEvents()
|
||||
protected override void RefreshUI(ShopForm form, ShopFormContext context)
|
||||
{
|
||||
if (_isBindEvent) return;
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
protected override void SubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Subscribe(RefreshEventArgs.EventId, Refresh);
|
||||
GameEntry.Event.Subscribe(ShopPurchaseEventArgs.EventId, ShopPurchase);
|
||||
GameEntry.Event.Subscribe(ShopContinueEventArgs.EventId, ShopContinue);
|
||||
GameEntry.Event.Subscribe(DisplayItemShowEventArgs.EventId, DisplayItemShow);
|
||||
GameEntry.Event.Subscribe(DisplayItemHideEventArgs.EventId, DisplayItemHide);
|
||||
|
||||
_isBindEvent = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
protected override void UnsubscribeCustomEvents()
|
||||
{
|
||||
if (!_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
GameEntry.Event.Unsubscribe(RefreshEventArgs.EventId, Refresh);
|
||||
GameEntry.Event.Unsubscribe(ShopPurchaseEventArgs.EventId, ShopPurchase);
|
||||
GameEntry.Event.Unsubscribe(ShopContinueEventArgs.EventId, ShopContinue);
|
||||
GameEntry.Event.Unsubscribe(DisplayItemShowEventArgs.EventId, DisplayItemShow);
|
||||
GameEntry.Event.Unsubscribe(DisplayItemHideEventArgs.EventId, DisplayItemHide);
|
||||
|
||||
_isBindEvent = false;
|
||||
}
|
||||
|
||||
#region BuildContext
|
||||
|
|
@ -72,38 +56,52 @@ namespace UI
|
|||
RefreshPrice = rawData.RefreshPrice,
|
||||
PlayerCoin = rawData.PlayerCoin,
|
||||
GoodsItems = rawData.GoodsItems,
|
||||
PropListContext = BuildDisplayListAreaContext("道具", rawData.PropItems, rawData.PropMaxCount),
|
||||
WeaponListContext = BuildDisplayListAreaContext("武器", rawData.WeaponItems, rawData.WeaponMaxCount)
|
||||
PropListContext = BuildDisplayListAreaContext(DisplayListAreaType.Prop, rawData.PropItems, rawData.PropMaxCount),
|
||||
WeaponListContext = BuildDisplayListAreaContext(DisplayListAreaType.Weapon, rawData.WeaponItems, rawData.WeaponMaxCount)
|
||||
};
|
||||
}
|
||||
|
||||
private static DisplayListAreaContext BuildDisplayListAreaContext(string title, IReadOnlyList<object> items,
|
||||
private static DisplayListAreaContext BuildDisplayListAreaContext(DisplayListAreaType listType, IReadOnlyList<object> items,
|
||||
int maxCount)
|
||||
{
|
||||
DisplayItemContext[] itemContexts = new DisplayItemContext[items.Count];
|
||||
if (title == "武器")
|
||||
string title = GetDisplayListTitle(listType);
|
||||
if (items == null)
|
||||
{
|
||||
if (items is IReadOnlyList<WeaponBase> weapons)
|
||||
return new DisplayListAreaContext
|
||||
{
|
||||
for (int i = 0; i < weapons.Count; i++)
|
||||
{
|
||||
WeaponBase weapon = weapons[i];
|
||||
if (weapon == null) break;
|
||||
itemContexts[i] = BuildWeaponItem(weapon);
|
||||
}
|
||||
}
|
||||
Title = title,
|
||||
CurrentCount = 0,
|
||||
MaxCount = maxCount,
|
||||
ItemContexts = System.Array.Empty<DisplayItemContext>()
|
||||
};
|
||||
}
|
||||
else if (title == "道具")
|
||||
|
||||
DisplayItemContext[] itemContexts = new DisplayItemContext[items.Count];
|
||||
switch (listType)
|
||||
{
|
||||
if (items is IReadOnlyList<PropItem> propItems)
|
||||
{
|
||||
for (int i = 0; i < propItems.Count; i++)
|
||||
case DisplayListAreaType.Weapon:
|
||||
if (items is IReadOnlyList<WeaponBase> weapons)
|
||||
{
|
||||
PropItem propItem = propItems[i];
|
||||
if (propItem == null) break;
|
||||
itemContexts[i] = BuildPropItem(propItem);
|
||||
for (int i = 0; i < weapons.Count; i++)
|
||||
{
|
||||
WeaponBase weapon = weapons[i];
|
||||
if (weapon == null) break;
|
||||
itemContexts[i] = BuildWeaponItem(weapon);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DisplayListAreaType.Prop:
|
||||
if (items is IReadOnlyList<PropItem> propItems)
|
||||
{
|
||||
for (int i = 0; i < propItems.Count; i++)
|
||||
{
|
||||
PropItem propItem = propItems[i];
|
||||
if (propItem == null) break;
|
||||
itemContexts[i] = BuildPropItem(propItem);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int currentCount = itemContexts.Length;
|
||||
|
|
@ -116,6 +114,16 @@ namespace UI
|
|||
};
|
||||
}
|
||||
|
||||
private static string GetDisplayListTitle(DisplayListAreaType listType)
|
||||
{
|
||||
return listType switch
|
||||
{
|
||||
DisplayListAreaType.Weapon => "武器",
|
||||
DisplayListAreaType.Prop => "道具",
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
private static DisplayItemContext BuildPropItem(PropItem propItem)
|
||||
{
|
||||
string iconAssetName = null;
|
||||
|
|
@ -135,7 +143,6 @@ namespace UI
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
private static DisplayItemContext BuildWeaponItem(WeaponBase weaponBase)
|
||||
{
|
||||
string iconAssetName = null;
|
||||
|
|
@ -176,33 +183,8 @@ namespace UI
|
|||
|
||||
#endregion
|
||||
|
||||
|
||||
#region UI Methods
|
||||
|
||||
protected override int? OpenUIInternal(ShopFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("ShopFormController.OpenUI() context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_shopForm != null && _shopFormSerialId.HasValue &&
|
||||
GameEntry.UI.HasUIForm(_shopFormSerialId.Value))
|
||||
{
|
||||
_shopForm.RefreshUI(_context);
|
||||
return _shopFormSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_shopFormSerialId = GameEntry.UI.OpenUIForm(UIFormType.ShopForm, context);
|
||||
return _shopFormSerialId;
|
||||
}
|
||||
|
||||
public int? OpenUI(ShopFormRawData rawData)
|
||||
{
|
||||
ShopFormContext context = BuildContext(rawData);
|
||||
|
|
@ -237,29 +219,6 @@ namespace UI
|
|||
return OpenUI(rawData);
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
UnsubscribeEvents();
|
||||
if (_shopFormSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_shopFormSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_shopFormSerialId.Value);
|
||||
}
|
||||
|
||||
_shopForm = null;
|
||||
_shopFormSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_shopForm != null)
|
||||
{
|
||||
_shopForm.Close();
|
||||
_shopForm = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (!(useCase is ShopFormUseCase shopFormUseCase))
|
||||
|
|
@ -271,118 +230,60 @@ namespace UI
|
|||
_useCase = shopFormUseCase;
|
||||
}
|
||||
|
||||
private void TryRefreshUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_shopForm == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_shopForm.RefreshUI(_context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Service
|
||||
|
||||
private void RefreshGoodsItems(ShopRefreshResult result)
|
||||
{
|
||||
if (_context == null || result == null)
|
||||
if (Context == null || result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.GoodsItems = result.GoodsItems;
|
||||
_context.RefreshPrice = result.RefreshPrice;
|
||||
Context.GoodsItems = result.GoodsItems;
|
||||
Context.RefreshPrice = result.RefreshPrice;
|
||||
|
||||
if (_shopForm == null)
|
||||
if (Form == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_shopForm.RefreshGoodsItems(result.GoodsItems);
|
||||
_shopForm.RefreshRefreshPrice(result.RefreshPrice);
|
||||
Form.RefreshGoodsItems(result.GoodsItems);
|
||||
Form.RefreshRefreshPrice(result.RefreshPrice);
|
||||
}
|
||||
|
||||
private void ApplyGoodsPurchased(ShopPurchaseResult result)
|
||||
{
|
||||
if (_context == null || result == null)
|
||||
if (Context == null || result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_context.GoodsItems != null && result.GoodsIndex >= 0 && result.GoodsIndex < _context.GoodsItems.Count)
|
||||
if (Context.GoodsItems != null && result.GoodsIndex >= 0 && result.GoodsIndex < Context.GoodsItems.Count)
|
||||
{
|
||||
_context.GoodsItems[result.GoodsIndex] = null;
|
||||
Context.GoodsItems[result.GoodsIndex] = null;
|
||||
}
|
||||
|
||||
if (result.DisplayItem != null)
|
||||
{
|
||||
if (result.DisplayItem.IsWeapon)
|
||||
{
|
||||
AppendDisplayItemContext(_context.WeaponListContext, result.DisplayItem);
|
||||
AppendDisplayItemContext(Context.WeaponListContext, result.DisplayItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendDisplayItemContext(_context.PropListContext, result.DisplayItem);
|
||||
AppendDisplayItemContext(Context.PropListContext, result.DisplayItem);
|
||||
}
|
||||
}
|
||||
|
||||
_shopForm?.ApplyGoodsPurchased(result.GoodsIndex, result.DisplayItem);
|
||||
Form?.ApplyGoodsPurchased(result.GoodsIndex, result.DisplayItem);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args)) return;
|
||||
|
||||
if (!_shopFormSerialId.HasValue) return;
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _shopFormSerialId.Value || args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_shopForm = args.UIForm.Logic as ShopForm;
|
||||
|
||||
if (_shopForm == null)
|
||||
{
|
||||
Log.Warning("ShopFormController open success but form logic is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
TryRefreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _shopFormSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_shopForm = null;
|
||||
_shopFormSerialId = null;
|
||||
}
|
||||
|
||||
private void Refresh(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(sender is ShopForm))
|
||||
|
|
@ -442,7 +343,7 @@ namespace UI
|
|||
|
||||
private void DisplayItemShow(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is DisplayItemShowEventArgs args)) return;
|
||||
if (!(e is DisplayItemShowEventArgs args) || _rawData == null) return;
|
||||
|
||||
DisplayItemInfoFormRawData rawData = new();
|
||||
rawData.TargetPos = args.TargetPos;
|
||||
|
|
@ -472,7 +373,6 @@ namespace UI
|
|||
GameEntry.UIRouter.OpenUI(UIFormType.DisplayItemInfoForm, rawData);
|
||||
}
|
||||
|
||||
|
||||
private void DisplayItemHide(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is DisplayItemHideEventArgs)) return;
|
||||
|
|
|
|||
|
|
@ -242,6 +242,14 @@ namespace UI
|
|||
};
|
||||
}
|
||||
|
||||
if (goods.GoodsType == GoodsType.Weapon)
|
||||
{
|
||||
// TODO: Weapon purchase apply flow depends on the upcoming weapon system integration.
|
||||
// Implement weapon creation/equip/add-to-inventory here when weapon runtime model is ready.
|
||||
Log.Warning("ShopFormUseCase::ApplyGoodsPurchase: Weapon purchase flow is not implemented yet.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@ using UnityGameFramework.Runtime;
|
|||
|
||||
namespace UI
|
||||
{
|
||||
public enum DisplayListAreaType : byte
|
||||
{
|
||||
None = 0,
|
||||
Prop = 1,
|
||||
Weapon = 2
|
||||
}
|
||||
|
||||
public class DisplayListArea : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
|
|
@ -72,7 +79,7 @@ namespace UI
|
|||
|
||||
public void OnDestroy()
|
||||
{
|
||||
_displayItemObjectPool.ReleaseAllUnused();
|
||||
_displayItemObjectPool?.ReleaseAllUnused();
|
||||
}
|
||||
|
||||
public DisplayItem AddItem(DisplayItemContext itemContext)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ namespace UI
|
|||
}
|
||||
|
||||
if (_context.Props == null) return;
|
||||
for (int i = 0; i < _propItems.Length; i++)
|
||||
int count = Mathf.Min(_propItems.Length, _context.Props.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_propItems[i].gameObject.SetActive(true);
|
||||
_propItems[i].Init(_context.Props[i]);
|
||||
|
|
|
|||
|
|
@ -1,39 +1,20 @@
|
|||
using Definition.Enum;
|
||||
using GameFramework.Event;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class DialogFormController : UIFormControllerBase<DialogFormContext>
|
||||
public class DialogFormController : UIFormControllerCommonBase<DialogFormContext, DialogForm>
|
||||
{
|
||||
private DialogFormContext _context;
|
||||
protected override UIFormType UIFormTypeId => UIFormType.DialogForm;
|
||||
|
||||
private DialogForm _dialogForm;
|
||||
|
||||
private int? _dialogFormSerialId;
|
||||
|
||||
private bool _pendingRefresh;
|
||||
|
||||
private bool _isBindEvent;
|
||||
|
||||
private void SubscribeEvents()
|
||||
protected override void RefreshUI(DialogForm form, DialogFormContext context)
|
||||
{
|
||||
if (_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
_isBindEvent = true;
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
protected override void CloseLoadedFormDirect(DialogForm form)
|
||||
{
|
||||
if (!_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
_isBindEvent = false;
|
||||
GameEntry.UI.CloseUIForm(form);
|
||||
}
|
||||
|
||||
private static DialogFormContext BuildContext(DialogFormRawData rawData)
|
||||
|
|
@ -59,30 +40,6 @@ namespace UI
|
|||
};
|
||||
}
|
||||
|
||||
protected override int? OpenUIInternal(DialogFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("DialogFormController.OpenUI() context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_dialogForm != null && _dialogFormSerialId.HasValue &&
|
||||
GameEntry.UI.HasUIForm(_dialogFormSerialId.Value))
|
||||
{
|
||||
_dialogForm.RefreshUI(_context);
|
||||
return _dialogFormSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_dialogFormSerialId = GameEntry.UI.OpenUIForm(UIFormType.DialogForm, context);
|
||||
return _dialogFormSerialId;
|
||||
}
|
||||
|
||||
public int? OpenUI(DialogFormRawData rawData)
|
||||
{
|
||||
DialogFormContext context = BuildContext(rawData);
|
||||
|
|
@ -101,42 +58,13 @@ namespace UI
|
|||
return OpenUI(rawData);
|
||||
}
|
||||
|
||||
if (userData is DialogFormRawData dialogParams)
|
||||
{
|
||||
return OpenUIInternal(BuildContext(dialogParams));
|
||||
}
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
Log.Warning("DialogFormController.OpenUI() userData type is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return OpenUIInternal(_context);
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
UnsubscribeEvents();
|
||||
|
||||
if (_dialogFormSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_dialogFormSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_dialogFormSerialId.Value);
|
||||
}
|
||||
|
||||
_dialogForm = null;
|
||||
_dialogFormSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_dialogForm != null)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_dialogForm);
|
||||
_dialogForm = null;
|
||||
}
|
||||
return OpenUIInternal(Context);
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
|
|
@ -146,70 +74,5 @@ namespace UI
|
|||
Log.Warning("DialogFormController does not use a use case.");
|
||||
}
|
||||
}
|
||||
|
||||
private void TryRefreshUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_dialogForm == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_dialogForm.RefreshUI(_context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_dialogFormSerialId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _dialogFormSerialId.Value ||
|
||||
args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dialogForm = args.UIForm.Logic as DialogForm;
|
||||
|
||||
if (_dialogForm == null)
|
||||
{
|
||||
Log.Warning("DialogFormController open success but form logic is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
TryRefreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _dialogFormSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dialogForm = null;
|
||||
_dialogFormSerialId = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ namespace UI
|
|||
/// <summary>
|
||||
/// 用户自定义数据。
|
||||
/// </summary>
|
||||
public string UserData
|
||||
public object UserData
|
||||
{
|
||||
get;
|
||||
set;
|
||||
|
|
|
|||
|
|
@ -5,44 +5,29 @@ using UnityGameFramework.Runtime;
|
|||
|
||||
namespace UI
|
||||
{
|
||||
public class SelectRoleFormController : UIFormControllerBase<SelectRoleFormContext>
|
||||
public class SelectRoleFormController : UIFormControllerCommonBase<SelectRoleFormContext, SelectRoleForm>
|
||||
{
|
||||
private SelectRoleFormUseCase _useCase;
|
||||
|
||||
private SelectRoleFormContext _context;
|
||||
protected override UIFormType UIFormTypeId => UIFormType.SelectRoleForm;
|
||||
|
||||
private SelectRoleForm _selectRoleForm;
|
||||
|
||||
private int? _selectRoleFormSerialId;
|
||||
|
||||
private bool _pendingRefresh;
|
||||
|
||||
private bool _isBindEvent;
|
||||
|
||||
private void SubscribeEvents()
|
||||
protected override void RefreshUI(SelectRoleForm form, SelectRoleFormContext context)
|
||||
{
|
||||
if (_isBindEvent) return;
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
protected override void SubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Subscribe(MenuSelectRoleReturnEventArgs.EventId, OnMenuSelectRoleReturn);
|
||||
GameEntry.Event.Subscribe(MenuSelectRoleSelectedEventArgs.EventId, OnMenuSelectRoleSelected);
|
||||
GameEntry.Event.Subscribe(MenuSelectRoleConfirmEventArgs.EventId, OnMenuSelectRoleConfirm);
|
||||
|
||||
_isBindEvent = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
protected override void UnsubscribeCustomEvents()
|
||||
{
|
||||
if (!_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
GameEntry.Event.Unsubscribe(MenuSelectRoleReturnEventArgs.EventId, OnMenuSelectRoleReturn);
|
||||
GameEntry.Event.Unsubscribe(MenuSelectRoleSelectedEventArgs.EventId, OnMenuSelectRoleSelected);
|
||||
GameEntry.Event.Unsubscribe(MenuSelectRoleConfirmEventArgs.EventId, OnMenuSelectRoleConfirm);
|
||||
|
||||
_isBindEvent = false;
|
||||
}
|
||||
|
||||
private static SelectRoleFormContext BuildContext(SelectRoleFormRawData rawData)
|
||||
|
|
@ -85,32 +70,6 @@ namespace UI
|
|||
};
|
||||
}
|
||||
|
||||
#region UI Methods
|
||||
|
||||
protected override int? OpenUIInternal(SelectRoleFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("SelectRoleFormController.OpenUI() context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_selectRoleForm != null && _selectRoleFormSerialId.HasValue &&
|
||||
GameEntry.UI.HasUIForm(_selectRoleFormSerialId.Value))
|
||||
{
|
||||
_selectRoleForm.RefreshUI(_context);
|
||||
return _selectRoleFormSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_selectRoleFormSerialId = GameEntry.UI.OpenUIForm(UIFormType.SelectRoleForm, context);
|
||||
return _selectRoleFormSerialId;
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is SelectRoleFormContext selectRoleFormContext)
|
||||
|
|
@ -124,35 +83,17 @@ namespace UI
|
|||
return null;
|
||||
}
|
||||
|
||||
if (_useCase == null)
|
||||
{
|
||||
Log.Error("SelectRoleFormController.OpenUI() useCase is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
SelectRoleFormRawData rawData = _useCase.CreateModel();
|
||||
SelectRoleFormContext context = BuildContext(rawData);
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
UnsubscribeEvents();
|
||||
|
||||
if (_selectRoleFormSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_selectRoleFormSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_selectRoleFormSerialId.Value);
|
||||
}
|
||||
|
||||
_selectRoleForm = null;
|
||||
_selectRoleFormSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectRoleForm != null)
|
||||
{
|
||||
_selectRoleForm.Close();
|
||||
_selectRoleForm = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (!(useCase is SelectRoleFormUseCase selectRoleUseCase))
|
||||
|
|
@ -164,90 +105,14 @@ namespace UI
|
|||
_useCase = selectRoleUseCase;
|
||||
}
|
||||
|
||||
private void TryRefreshUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectRoleForm == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_selectRoleForm.RefreshUI(_context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Service
|
||||
|
||||
public void UpdateShowRole(RolePropertyAreaContext rolePropertyAreaContext)
|
||||
{
|
||||
if (_context != null)
|
||||
if (Context != null)
|
||||
{
|
||||
_context.RolePropertyAreaContext = rolePropertyAreaContext;
|
||||
Context.RolePropertyAreaContext = rolePropertyAreaContext;
|
||||
}
|
||||
|
||||
if (_selectRoleForm != null)
|
||||
{
|
||||
_selectRoleForm.UpdateShowRole(rolePropertyAreaContext);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_selectRoleFormSerialId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _selectRoleFormSerialId.Value ||
|
||||
args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_selectRoleForm = args.UIForm.Logic as SelectRoleForm;
|
||||
|
||||
if (_selectRoleForm == null)
|
||||
{
|
||||
Log.Warning("SelectRoleFormController open success but form logic is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
TryRefreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _selectRoleFormSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_selectRoleForm = null;
|
||||
_selectRoleFormSerialId = null;
|
||||
Form?.UpdateShowRole(rolePropertyAreaContext);
|
||||
}
|
||||
|
||||
private void OnMenuSelectRoleReturn(object sender, GameEventArgs e)
|
||||
|
|
@ -274,8 +139,8 @@ namespace UI
|
|||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
UpdateShowRole(_context.RolePropertyAreaContext);
|
||||
SetContext(context);
|
||||
UpdateShowRole(context.RolePropertyAreaContext);
|
||||
}
|
||||
|
||||
private void OnMenuSelectRoleConfirm(object sender, GameEventArgs e)
|
||||
|
|
@ -285,9 +150,7 @@ namespace UI
|
|||
return;
|
||||
}
|
||||
|
||||
_useCase.ConfirmSelectedRole();
|
||||
_useCase?.ConfirmSelectedRole();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,50 +5,33 @@ using UnityGameFramework.Runtime;
|
|||
|
||||
namespace UI
|
||||
{
|
||||
public class StartMenuFormController : UIFormControllerBase<StartMenuFormContext>
|
||||
public class StartMenuFormController : UIFormControllerCommonBase<StartMenuFormContext, StartMenuForm>
|
||||
{
|
||||
private StartMenuFormContext _context;
|
||||
protected override UIFormType UIFormTypeId => UIFormType.StartMenuForm;
|
||||
|
||||
private StartMenuForm _startMenuForm;
|
||||
|
||||
private int? _startMenuFormSerialId;
|
||||
|
||||
private bool _pendingRefresh;
|
||||
|
||||
private bool _isBindEvent;
|
||||
|
||||
private void SubscribeEvents()
|
||||
protected override void RefreshUI(StartMenuForm form, StartMenuFormContext context)
|
||||
{
|
||||
if (_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void SubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Subscribe(MenuStartGameEventArgs.EventId, OnMenuStartGameButtonClick);
|
||||
GameEntry.Event.Subscribe(MenuFileButtonClickEventArgs.EventId, OnMenuFileButtonClick);
|
||||
GameEntry.Event.Subscribe(MenuGuideButtonClickEventArgs.EventId, OnMenuGuideButtonClick);
|
||||
GameEntry.Event.Subscribe(MenuSettingButtonClickEventArgs.EventId, OnMenuSettingButtonClick);
|
||||
GameEntry.Event.Subscribe(MenuQuitButtonClickEventArgs.EventId, OnMenuQuitButtonClick);
|
||||
GameEntry.Event.Subscribe(MenuAboutButtonClickEventArgs.EventId, OnMenuAboutButtonClick);
|
||||
|
||||
_isBindEvent = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeEvents()
|
||||
protected override void UnsubscribeCustomEvents()
|
||||
{
|
||||
if (!_isBindEvent) return;
|
||||
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
|
||||
|
||||
GameEntry.Event.Unsubscribe(MenuStartGameEventArgs.EventId, OnMenuStartGameButtonClick);
|
||||
GameEntry.Event.Unsubscribe(MenuFileButtonClickEventArgs.EventId, OnMenuFileButtonClick);
|
||||
GameEntry.Event.Unsubscribe(MenuGuideButtonClickEventArgs.EventId, OnMenuGuideButtonClick);
|
||||
GameEntry.Event.Unsubscribe(MenuSettingButtonClickEventArgs.EventId, OnMenuSettingButtonClick);
|
||||
GameEntry.Event.Unsubscribe(MenuQuitButtonClickEventArgs.EventId, OnMenuQuitButtonClick);
|
||||
GameEntry.Event.Unsubscribe(MenuAboutButtonClickEventArgs.EventId, OnMenuAboutButtonClick);
|
||||
|
||||
_isBindEvent = false;
|
||||
}
|
||||
|
||||
private static StartMenuFormContext BuildStartMenuFormContext()
|
||||
|
|
@ -56,32 +39,6 @@ namespace UI
|
|||
return new StartMenuFormContext();
|
||||
}
|
||||
|
||||
#region UI Methods
|
||||
|
||||
protected override int? OpenUIInternal(StartMenuFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("StartMenuFormController.OpenUI() context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
if (_startMenuForm != null && _startMenuFormSerialId.HasValue &&
|
||||
GameEntry.UI.HasUIForm(_startMenuFormSerialId.Value))
|
||||
{
|
||||
_startMenuForm.RefreshUI(_context);
|
||||
return _startMenuFormSerialId;
|
||||
}
|
||||
|
||||
CloseUI();
|
||||
_pendingRefresh = true;
|
||||
SubscribeEvents();
|
||||
_startMenuFormSerialId = GameEntry.UI.OpenUIForm(UIFormType.StartMenuForm, context);
|
||||
return _startMenuFormSerialId;
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is StartMenuFormContext context)
|
||||
|
|
@ -103,105 +60,11 @@ namespace UI
|
|||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
public override void CloseUI()
|
||||
{
|
||||
_pendingRefresh = false;
|
||||
UnsubscribeEvents();
|
||||
|
||||
if (_startMenuFormSerialId.HasValue)
|
||||
{
|
||||
if (GameEntry.UI.HasUIForm(_startMenuFormSerialId.Value))
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_startMenuFormSerialId.Value);
|
||||
}
|
||||
|
||||
_startMenuForm = null;
|
||||
_startMenuFormSerialId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_startMenuForm != null)
|
||||
{
|
||||
_startMenuForm.Close();
|
||||
_startMenuForm = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
Log.Info("StartMenuForm doesn't need UseCase");
|
||||
}
|
||||
|
||||
private void TryRefreshUI()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_startMenuForm == null)
|
||||
{
|
||||
_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_startMenuForm.RefreshUI(_context);
|
||||
_pendingRefresh = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_startMenuFormSerialId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.UIForm == null || args.UIForm.SerialId != _startMenuFormSerialId.Value ||
|
||||
args.UserData != _context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_startMenuForm = args.UIForm.Logic as StartMenuForm;
|
||||
|
||||
if (_startMenuForm == null)
|
||||
{
|
||||
Log.Warning("StartMenuFormController open success but form logic is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingRefresh)
|
||||
{
|
||||
TryRefreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId != _startMenuFormSerialId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_startMenuForm = null;
|
||||
_startMenuFormSerialId = null;
|
||||
}
|
||||
|
||||
private void OnMenuStartGameButtonClick(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(sender is StartMenuForm) || !(e is MenuStartGameEventArgs))
|
||||
|
|
@ -270,7 +133,5 @@ namespace UI
|
|||
|
||||
Log.Warning("Menu about button click is not implemented.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,18 +15,16 @@ using UnityEngine;
|
|||
using UnityGameFramework.Runtime;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace StarForce
|
||||
namespace Game.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// AI 工具类。
|
||||
/// </summary>
|
||||
public static class AIUtility
|
||||
{
|
||||
private static Dictionary<CampPair, RelationType> s_CampPairToRelation =
|
||||
new Dictionary<CampPair, RelationType>();
|
||||
private static Dictionary<CampPair, RelationType> s_CampPairToRelation = new();
|
||||
|
||||
private static Dictionary<KeyValuePair<CampType, RelationType>, CampType[]> s_CampAndRelationToCamps =
|
||||
new Dictionary<KeyValuePair<CampType, RelationType>, CampType[]>();
|
||||
private static Dictionary<KeyValuePair<CampType, RelationType>, CampType[]> s_CampAndRelationToCamps = new();
|
||||
|
||||
static AIUtility()
|
||||
{
|
||||
|
|
@ -68,9 +66,7 @@ namespace StarForce
|
|||
{
|
||||
if (first > second)
|
||||
{
|
||||
CampType temp = first;
|
||||
first = second;
|
||||
second = temp;
|
||||
(first, second) = (second, first);
|
||||
}
|
||||
|
||||
RelationType relationType;
|
||||
|
|
@ -92,8 +88,7 @@ namespace StarForce
|
|||
public static CampType[] GetCamps(CampType camp, RelationType relation)
|
||||
{
|
||||
KeyValuePair<CampType, RelationType> key = new KeyValuePair<CampType, RelationType>(camp, relation);
|
||||
CampType[] result = null;
|
||||
if (s_CampAndRelationToCamps.TryGetValue(key, out result))
|
||||
if (s_CampAndRelationToCamps.TryGetValue(key, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
|
@ -194,7 +189,7 @@ namespace StarForce
|
|||
}
|
||||
|
||||
int entityDamageHP = CalcDamageHP(weaponImpactData.AttackBase, weaponImpactData.AttackStat,
|
||||
entityImpactData.DefenseStat, entityImpactData.DefenseStat);
|
||||
entityImpactData.DefenseStat, entityImpactData.DodgeStat);
|
||||
|
||||
entity.ApplyDamage(weapon, entityDamageHP);
|
||||
return;
|
||||
|
|
|
|||
40
Todo List.md
40
Todo List.md
|
|
@ -1,40 +0,0 @@
|
|||
# 3D 类土豆兄弟开发
|
||||
|
||||
## 流程设计
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["开始菜单"]-->B["进入游戏"]
|
||||
B-->C["选择角色(进阶)"]
|
||||
C-->D
|
||||
B-->D["选择初始武器"]
|
||||
D-->E["进入关卡"]
|
||||
E-->F["战斗,获取资源"]
|
||||
F-->G["关卡结束,进入商店"]
|
||||
G-->H["进入下一关"]
|
||||
H-->E
|
||||
F-->I["玩家死亡"]
|
||||
I-->J["游戏结算"]
|
||||
H-->K["完成所有关卡"]
|
||||
K-->J
|
||||
J-->A
|
||||
```
|
||||
|
||||
## 开发需求
|
||||
### 基础部分
|
||||
#### UI 部分
|
||||
- [x] 开始菜单 UI
|
||||
- [ ] 局内 HUD
|
||||
- [ ] 局内商店页面
|
||||
- [ ] 设置页面
|
||||
|
||||
#### 游戏逻辑部分
|
||||
- [ ] 玩家操作
|
||||
- [ ] 玩家武器
|
||||
- [ ] 自动攻击
|
||||
- [ ] 等级制,多次获得同一把武器会升级
|
||||
- [ ] 敌人:近战、远程……
|
||||
- [ ] 关卡制,每关结束后进入商店购买武器和道具
|
||||
|
||||
### 进阶部分
|
||||
- [ ] 武器词缀:每把高级武器会随机带有一个该等级的词缀,附带额外效果
|
||||
- [ ] 多角色:每个角色初始属性不同,不同角色技能不同
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
# 3D 类吸血鬼幸存者项目 Todo(GameMain 侧规划)
|
||||
|
||||
> 范围说明:本清单基于当前 `Assets/GameMain` 代码现状制定,未涉及 `Assets/GameFramework` 底层实现。
|
||||
|
||||
## 0. 当前代码现状(已确认)
|
||||
- [x] 已有完整流程骨架:`Menu -> Game(Battle/LevelUp/Shop)`,以及基础实体系统(Player/Enemy/Weapon/Drop/UI)。
|
||||
- [x] 目前仍是传统 `MonoBehaviour + 每实体 OnUpdate` 驱动,暂无 Job System/Burst 实装。
|
||||
- [x] 已有一个 Instancing Shader:`Assets/GameMain/Materials/Shaders/SimpleInstancedFlash.shader`,但未接入运行时批量渲染管线。
|
||||
- [x] 未发现代码热更新方案接入(如 HybridCLR/ILRuntime/xLua 等)。
|
||||
|
||||
## 1. P0 基线修正与性能基准
|
||||
- [ ] 建立性能基准场景(建议复用 `Game.unity` + 压测参数):
|
||||
- 指标:`1k / 3k / 5k` 敌人时的 FPS、CPU Main Thread、GC Alloc、Draw Calls。
|
||||
- 输出:一份基线表格(开发机配置 + Unity Profiler 截图)。
|
||||
- [x] 修正当前高风险逻辑问题(避免后续优化建立在不稳定行为上):
|
||||
- `ProcedureGame.OnEnter()` 与 `_hudInitialized` 逻辑中有重复初始化状态机风险(`InitGameState()` 被调用两次)。
|
||||
- `Player.Enable` setter 未更新 `_enable` 字段,状态切换语义不完整。
|
||||
- `PlayerData` 构造中 `MaxHealthBase` 初始化异常(自赋值)。
|
||||
- `AIUtility.PerformCollision()` 武器伤害计算参数里疑似把 `DodgeStat` 传成 `DefenseStat`。
|
||||
- [ ] 给关键战斗链路加最小回归测试(PlayMode):
|
||||
- 伤害结算、掉落、回合切换(Battle/LevelUp/Shop)。
|
||||
|
||||
**验收标准**
|
||||
- 基线数据可复现。
|
||||
- 以上问题修正后,核心流程可稳定连续跑 10 分钟无异常日志。
|
||||
|
||||
## 2. P1 Simulation 分层(为 Job/Burst 做结构准备)
|
||||
- [ ] 新建 `Simulation` 层(建议目录:`Assets/GameMain/Scripts/Simulation`):
|
||||
- `SimulationWorld`:统一持有敌人/投射物/掉落物的纯数据容器。
|
||||
- `EnemySimData / ProjectileSimData / PickupSimData`:结构化、连续内存友好的数据定义。
|
||||
- `EntityBinding`:维护 `EntityId <-> SimulationIndex` 映射。
|
||||
- [ ] 将“逻辑计算”和“表现层(Transform/Animator/特效/UI)”拆离:
|
||||
- 逻辑层输出 position/rotation/state。
|
||||
- 表现层只消费结果做显示。
|
||||
- [ ] 先保持现有 GameFramework 实体生命周期不变,仅替换更新路径。
|
||||
|
||||
**验收标准**
|
||||
- 敌人移动/追踪由 Simulation 统一调度,不再逐个 Enemy MonoBehaviour 执行核心逻辑。
|
||||
|
||||
## 3. P2 Job System + Burst 落地(核心性能阶段)
|
||||
- [ ] 引入并锁定依赖版本(Unity 2022.3 对应):
|
||||
- `com.unity.collections`
|
||||
- `com.unity.jobs`
|
||||
- `com.unity.burst`
|
||||
- `com.unity.mathematics`
|
||||
- [ ] 第一批 Job 化模块(优先级从高到低):
|
||||
1. 敌人移动与朝向更新(`IJobParallelFor`)。
|
||||
2. 目标选择加速(空间哈希/网格分桶,减少全量最近邻搜索)。
|
||||
3. 投射物批量移动与寿命回收。
|
||||
4. AOE/碰撞候选筛选(先 broad phase,后精算)。
|
||||
- [ ] Burst 编译策略:
|
||||
- 热路径 Job 全部 `[BurstCompile]`。
|
||||
- 禁止在 Job 内使用托管分配、虚调用、LINQ。
|
||||
- [ ] 主线程仅做:输入采样、状态切换、UI同步、实体显隐。
|
||||
|
||||
**验收标准**
|
||||
- 在 3k 敌人规模下,CPU Main Thread 明显下降(目标 >= 30%)。
|
||||
- Profiler 中战斗帧 GC Alloc 接近 0(持续帧)。
|
||||
|
||||
## 4. P3 GPU Instancing 渲染管线(与 Job 并行推进)
|
||||
- [ ] 先做“低风险版”批处理:
|
||||
- 同 Mesh/Material 的敌人分组,使用 `Graphics.DrawMeshInstanced`(每批最多 1023)。
|
||||
- [ ] 再升级“高上限版”:
|
||||
- 使用 `Graphics.DrawMeshInstancedIndirect` + `ComputeBuffer` 管理实例矩阵/颜色/状态。
|
||||
- [ ] 建立 `InstanceRendererComponent`:
|
||||
- 输入:Simulation 输出的 transform/state。
|
||||
- 输出:按 enemy archetype 的批量绘制。
|
||||
- [ ] 将受击闪白、稀有度颜色等通过 `MaterialPropertyBlock` 或实例化属性下发(复用现有 Instanced Shader 思路)。
|
||||
- [ ] 与现有碰撞体系解耦:
|
||||
- 逻辑碰撞走 Simulation,渲染不再依赖每敌人独立 GameObject Renderer。
|
||||
|
||||
**验收标准**
|
||||
- 5k 敌人规模 Draw Calls 显著下降。
|
||||
- 渲染主线程耗时可控,且视觉行为(受击、朝向、死亡)与逻辑一致。
|
||||
|
||||
## 5. P4 代码热更新(建议 HybridCLR)
|
||||
- [ ] 技术选型定稿:建议 `HybridCLR`(Unity 2022 + C# 生态兼容更自然)。
|
||||
- [ ] Assembly 拆分:
|
||||
- `Main`:启动、资源更新、基础桥接(不可热更)。
|
||||
- `Hotfix`:玩法规则、数值公式、技能与敌人行为树(可热更)。
|
||||
- [ ] 运行时加载流程:
|
||||
- 启动时通过现有资源更新流程拉取热更 DLL(与版本号绑定)。
|
||||
- 加载 AOT metadata + Hotfix DLL,反射启动 `HotfixEntry`。
|
||||
- [ ] 建立热更边界规范:
|
||||
- Hotfix 不直接依赖编辑器代码。
|
||||
- 跨域调用统一走接口/Facade(避免大量反射散落)。
|
||||
- [ ] 回滚机制:
|
||||
- DLL 校验失败时回退上一个稳定版本。
|
||||
|
||||
**验收标准**
|
||||
- 不发整包即可替换一条技能逻辑并在设备上生效。
|
||||
- 热更失败可自动回退,启动不中断。
|
||||
|
||||
## 6. P5 玩法目标对齐(与技术栈并行)
|
||||
- [ ] 武器系统补完:
|
||||
- 自动攻击、多武器并存、同武器升级/进化。
|
||||
- `Shop` 武器购买流程补完(当前已有 TODO)。
|
||||
- [ ] 敌人系统扩展:
|
||||
- 近战/远程/精英/首领模板化,支持波次参数化。
|
||||
- [ ] 关卡节奏:
|
||||
- `DRLevel` 扩展为“时间轴+事件波次+奖励节点”。
|
||||
- [ ] 数值可调试工具:
|
||||
- 实时查看 Dps、受击、击杀效率、掉落速率。
|
||||
|
||||
**验收标准**
|
||||
- 一局 10~20 分钟循环可闭环,且关卡难度曲线平滑。
|
||||
|
||||
## 7. 推荐执行顺序(避免返工)
|
||||
- [ ] 里程碑 A:`P0 -> P1`(稳定结构 + 可观测)
|
||||
- [ ] 里程碑 B:`P2`(CPU 性能突破)
|
||||
- [ ] 里程碑 C:`P3`(渲染性能突破)
|
||||
- [ ] 里程碑 D:`P4`(线上快速迭代能力)
|
||||
- [ ] 里程碑 E:`P5`(内容量与可玩性扩展)
|
||||
|
||||
## 8. 交付物清单(每阶段都要有)
|
||||
- [ ] 设计文档(接口、数据结构、生命周期)。
|
||||
- [ ] Profiling 对比(改造前后同场景同参数)。
|
||||
- [ ] 回归用例(至少战斗、关卡切换、商店、升级)。
|
||||
- [ ] 风险与回滚说明(特别是热更新与渲染链路)。
|
||||
Loading…
Reference in New Issue