11
This commit is contained in:
parent
584a4b1b91
commit
0fa29206c7
7
Client/.claude/settings.local.json
Normal file
7
Client/.claude/settings.local.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(find 'C:\\\\Users\\\\admin/Library/Mobile Documents/com~apple~CloudDocs' -name *.cs -path */GameObjectPool/*)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Client/Assets/Scripts/New.meta
Normal file
8
Client/Assets/Scripts/New.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fba5680fc6bcbe442a903a4b86f67d39
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Client/Assets/Scripts/New/Example.meta
Normal file
8
Client/Assets/Scripts/New/Example.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 402c505f0455ab940813845ca361f217
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Client/Assets/Scripts/New/Example/App.meta
Normal file
8
Client/Assets/Scripts/New/Example/App.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e448305295085ec44a5199e6d99109a5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
17
Client/Assets/Scripts/New/Example/App/ExampleAppRoot.cs
Normal file
17
Client/Assets/Scripts/New/Example/App/ExampleAppRoot.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
public sealed class ExampleAppRoot : GameServiceRoot
|
||||||
|
{
|
||||||
|
[SerializeField] private bool _registerTimerService = true;
|
||||||
|
|
||||||
|
protected override void RegisterAppServices(ServiceScope appScope)
|
||||||
|
{
|
||||||
|
if (_registerTimerService)
|
||||||
|
{
|
||||||
|
appScope.Register(new TimerService());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Client/Assets/Scripts/New/Example/App/ExampleAppRoot.cs.meta
Normal file
11
Client/Assets/Scripts/New/Example/App/ExampleAppRoot.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 67511ef137aa82745b794acd2f35016e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
17
Client/Assets/Scripts/New/Example/App/ITimerService.cs
Normal file
17
Client/Assets/Scripts/New/Example/App/ITimerService.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
public interface ITimerService : IService
|
||||||
|
{
|
||||||
|
float ElapsedTime { get; }
|
||||||
|
|
||||||
|
int Once(float delay, Action callback);
|
||||||
|
|
||||||
|
int Repeat(float interval, Action callback, int repeatCount = -1, float firstDelay = -1f);
|
||||||
|
|
||||||
|
bool Cancel(int timerId);
|
||||||
|
|
||||||
|
void CancelAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Client/Assets/Scripts/New/Example/App/ITimerService.cs.meta
Normal file
11
Client/Assets/Scripts/New/Example/App/ITimerService.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 497da23b9df8595458b3705d38d90f38
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
150
Client/Assets/Scripts/New/Example/App/TimerService.cs
Normal file
150
Client/Assets/Scripts/New/Example/App/TimerService.cs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
public sealed class TimerService : ServiceBase, ITimerService, IServiceTickable, IServiceOrder
|
||||||
|
{
|
||||||
|
private readonly List<TimerTask> _timers = new List<TimerTask>();
|
||||||
|
|
||||||
|
private int _nextTimerId = 1;
|
||||||
|
|
||||||
|
public float ElapsedTime { get; private set; }
|
||||||
|
|
||||||
|
public int Order => -1000;
|
||||||
|
|
||||||
|
public int Once(float delay, Action callback)
|
||||||
|
{
|
||||||
|
if (callback == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddTimer(delay, 0f, 1, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Repeat(float interval, Action callback, int repeatCount = -1, float firstDelay = -1f)
|
||||||
|
{
|
||||||
|
if (callback == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval <= 0f)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(interval), "Repeat interval must be greater than zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var delay = firstDelay < 0f ? interval : firstDelay;
|
||||||
|
return AddTimer(delay, interval, repeatCount, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Cancel(int timerId)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < _timers.Count; i++)
|
||||||
|
{
|
||||||
|
if (_timers[i].Id != timerId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_timers.RemoveAt(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelAll()
|
||||||
|
{
|
||||||
|
_timers.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Tick(float deltaTime)
|
||||||
|
{
|
||||||
|
ElapsedTime += deltaTime;
|
||||||
|
|
||||||
|
for (var i = _timers.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var timer = _timers[i];
|
||||||
|
timer.Remaining -= deltaTime;
|
||||||
|
if (timer.Remaining > 0f)
|
||||||
|
{
|
||||||
|
_timers[i] = timer;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Callback?.Invoke();
|
||||||
|
|
||||||
|
if (timer.RepeatCount == 1)
|
||||||
|
{
|
||||||
|
_timers.RemoveAt(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer.RepeatCount > 1)
|
||||||
|
{
|
||||||
|
timer.RepeatCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer.Interval <= 0f)
|
||||||
|
{
|
||||||
|
_timers.RemoveAt(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Remaining += timer.Interval;
|
||||||
|
if (timer.Remaining <= 0f)
|
||||||
|
{
|
||||||
|
timer.Remaining = timer.Interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
_timers[i] = timer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
ElapsedTime = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDestroyService()
|
||||||
|
{
|
||||||
|
CancelAll();
|
||||||
|
ElapsedTime = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int AddTimer(float delay, float interval, int repeatCount, Action callback)
|
||||||
|
{
|
||||||
|
if (delay < 0f)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(delay), "Delay cannot be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeatCount == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(repeatCount), "Repeat count cannot be zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = _nextTimerId++;
|
||||||
|
_timers.Add(new TimerTask
|
||||||
|
{
|
||||||
|
Id = id,
|
||||||
|
Remaining = delay,
|
||||||
|
Interval = interval,
|
||||||
|
RepeatCount = repeatCount,
|
||||||
|
Callback = callback,
|
||||||
|
});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct TimerTask
|
||||||
|
{
|
||||||
|
public int Id;
|
||||||
|
public float Remaining;
|
||||||
|
public float Interval;
|
||||||
|
public int RepeatCount;
|
||||||
|
public Action Callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Client/Assets/Scripts/New/Example/App/TimerService.cs.meta
Normal file
11
Client/Assets/Scripts/New/Example/App/TimerService.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 76e7933ad4d82024890e70055ada0d28
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Client/Assets/Scripts/New/Example/Battle.meta
Normal file
8
Client/Assets/Scripts/New/Example/Battle.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5797a82d5a1e5a24abf2b98f114cb1ab
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
public sealed class BattleLoopService : ServiceBase, IBattleLoopService, IServiceTickable, IServiceOrder
|
||||||
|
{
|
||||||
|
private ITimerService _timerService;
|
||||||
|
private int _pulseTimerId = -1;
|
||||||
|
|
||||||
|
public float ElapsedTime { get; private set; }
|
||||||
|
|
||||||
|
public int PulseCount { get; private set; }
|
||||||
|
|
||||||
|
public int Order => 100;
|
||||||
|
|
||||||
|
public void Tick(float deltaTime)
|
||||||
|
{
|
||||||
|
ElapsedTime += deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
ElapsedTime = 0f;
|
||||||
|
PulseCount = 0;
|
||||||
|
|
||||||
|
if (World.TryGet(out _timerService))
|
||||||
|
{
|
||||||
|
_pulseTimerId = _timerService.Repeat(1f, OnPulse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDestroyService()
|
||||||
|
{
|
||||||
|
if (_timerService != null && _pulseTimerId > 0)
|
||||||
|
{
|
||||||
|
_timerService.Cancel(_pulseTimerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pulseTimerId = -1;
|
||||||
|
_timerService = null;
|
||||||
|
ElapsedTime = 0f;
|
||||||
|
PulseCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPulse()
|
||||||
|
{
|
||||||
|
PulseCount++;
|
||||||
|
Debug.Log($"[BattleLoopService] Pulse {PulseCount}, Elapsed={ElapsedTime:F2}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b4ddefabf153f4542aab67ba6bd036e5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
7
Client/Assets/Scripts/New/Example/Battle/BattleScope.cs
Normal file
7
Client/Assets/Scripts/New/Example/Battle/BattleScope.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 战斗 Scope 标记类。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BattleScope { }
|
||||||
|
}
|
||||||
11
Client/Assets/Scripts/New/Example/Battle/BattleScope.cs.meta
Normal file
11
Client/Assets/Scripts/New/Example/Battle/BattleScope.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fed8623053560524d8ed617cd2348343
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
public sealed class ExampleBattleScopeController : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] private int _scopeOrder = 100;
|
||||||
|
[SerializeField] private bool _toggleByInput = true;
|
||||||
|
|
||||||
|
private ServiceScope _battleScope;
|
||||||
|
|
||||||
|
public bool HasBattleScope => _battleScope != null && !_battleScope.IsDisposed;
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (!_toggleByInput) return;
|
||||||
|
|
||||||
|
if (!GameServices.TryGet<IInputService>(out var inputService)) return;
|
||||||
|
|
||||||
|
if (!inputService.ToggleBattlePressedThisFrame) return;
|
||||||
|
|
||||||
|
if (HasBattleScope) ExitBattle();
|
||||||
|
else EnterBattle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
ExitBattle();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ContextMenu("Enter Battle")]
|
||||||
|
public void EnterBattle()
|
||||||
|
{
|
||||||
|
if (HasBattleScope) return;
|
||||||
|
|
||||||
|
if (!GameServices.HasWorld)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[ExampleBattleScopeController] Cannot create Battle scope: ServiceWorld does not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_battleScope = GameServices.CreateScope<BattleScope>(_scopeOrder);
|
||||||
|
_battleScope.Register(new BattleLoopService());
|
||||||
|
Debug.Log("[ExampleBattleScopeController] Created BattleScope.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[ContextMenu("Exit Battle")]
|
||||||
|
public void ExitBattle()
|
||||||
|
{
|
||||||
|
if (_battleScope == null) return;
|
||||||
|
|
||||||
|
if (GameServices.HasWorld)
|
||||||
|
GameServices.DestroyScope<BattleScope>();
|
||||||
|
|
||||||
|
_battleScope = null;
|
||||||
|
Debug.Log("[ExampleBattleScopeController] Destroyed BattleScope.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 546eeb5c7490a8e4ab7b2baafc332852
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
public interface IBattleLoopService : IService
|
||||||
|
{
|
||||||
|
float ElapsedTime { get; }
|
||||||
|
|
||||||
|
int PulseCount { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7b7d08a93f695ca4c8576510d3c72f93
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Client/Assets/Scripts/New/Example/Scene.meta
Normal file
8
Client/Assets/Scripts/New/Example/Scene.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 73ff46e60c117724986b35ac3575e001
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
[DefaultExecutionOrder(-100)]
|
||||||
|
public sealed class ExampleInputManager : MonoServiceBehaviour<SceneScope>, IInputService
|
||||||
|
{
|
||||||
|
[SerializeField] private string _horizontalAxis = "Horizontal";
|
||||||
|
[SerializeField] private string _verticalAxis = "Vertical";
|
||||||
|
[SerializeField] private KeyCode _confirmKey = KeyCode.Space;
|
||||||
|
[SerializeField] private KeyCode _toggleBattleKey = KeyCode.B;
|
||||||
|
|
||||||
|
public Vector2 Move { get; private set; }
|
||||||
|
|
||||||
|
public bool ConfirmPressedThisFrame { get; private set; }
|
||||||
|
|
||||||
|
public bool ToggleBattlePressedThisFrame { get; private set; }
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
Move = new Vector2(Input.GetAxisRaw(_horizontalAxis), Input.GetAxisRaw(_verticalAxis));
|
||||||
|
ConfirmPressedThisFrame = Input.GetKeyDown(_confirmKey);
|
||||||
|
ToggleBattlePressedThisFrame = Input.GetKeyDown(_toggleBattleKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LateUpdate()
|
||||||
|
{
|
||||||
|
ConfirmPressedThisFrame = false;
|
||||||
|
ToggleBattlePressedThisFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnServiceDestroy()
|
||||||
|
{
|
||||||
|
Move = Vector2.zero;
|
||||||
|
ConfirmPressedThisFrame = false;
|
||||||
|
ToggleBattlePressedThisFrame = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a6a3327a01fceb94ba49f5dec2888d6b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
13
Client/Assets/Scripts/New/Example/Scene/IInputService.cs
Normal file
13
Client/Assets/Scripts/New/Example/Scene/IInputService.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
public interface IInputService : IService
|
||||||
|
{
|
||||||
|
Vector2 Move { get; }
|
||||||
|
|
||||||
|
bool ConfirmPressedThisFrame { get; }
|
||||||
|
|
||||||
|
bool ToggleBattlePressedThisFrame { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2ef833d119cbe7c43bd3d5e61dae31a9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
7
Client/Assets/Scripts/New/Example/Scene/SceneScope.cs
Normal file
7
Client/Assets/Scripts/New/Example/Scene/SceneScope.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Aliciza.Services.Example
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 示例场景 Scope 标记类。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SceneScope { }
|
||||||
|
}
|
||||||
11
Client/Assets/Scripts/New/Example/Scene/SceneScope.cs.meta
Normal file
11
Client/Assets/Scripts/New/Example/Scene/SceneScope.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e109d3faffc69294b90a5ca646ce2f69
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Client/Assets/Scripts/New/ServiceFramework.meta
Normal file
8
Client/Assets/Scripts/New/ServiceFramework.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7907eae3ee5d54e438b951d07879f538
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Client/Assets/Scripts/New/ServiceFramework/Core.meta
Normal file
8
Client/Assets/Scripts/New/ServiceFramework/Core.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 48e89f2eceb320547b4986db840128da
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 框架内置的 App Scope 标记类,生命周期与 ServiceWorld 相同。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AppScope { }
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d205821b5dae0264fb6c5d1ea78f9b43
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
public static class GameServices
|
||||||
|
{
|
||||||
|
private static ServiceWorld _world;
|
||||||
|
|
||||||
|
public static bool HasWorld => _world != null;
|
||||||
|
|
||||||
|
public static ServiceWorld World
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_world == null)
|
||||||
|
throw new InvalidOperationException("ServiceWorld has not been created yet.");
|
||||||
|
return _world;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceScope App => World.AppScope;
|
||||||
|
|
||||||
|
public static ServiceWorld EnsureWorld(int appScopeOrder = -10000)
|
||||||
|
{
|
||||||
|
if (_world == null)
|
||||||
|
_world = new ServiceWorld(appScopeOrder);
|
||||||
|
return _world;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Scope 管理 ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static ServiceScope CreateScope<TScope>(int order = 0) where TScope : class
|
||||||
|
=> World.CreateScope<TScope>(order);
|
||||||
|
|
||||||
|
public static ServiceScope GetOrCreateScope<TScope>(int order = 0) where TScope : class
|
||||||
|
=> World.GetOrCreateScope<TScope>(order);
|
||||||
|
|
||||||
|
public static bool TryGetScope<TScope>(out ServiceScope scope) where TScope : class
|
||||||
|
{
|
||||||
|
if (_world == null) { scope = null; return false; }
|
||||||
|
return _world.TryGetScope<TScope>(out scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool DestroyScope<TScope>() where TScope : class
|
||||||
|
{
|
||||||
|
if (_world == null) return false;
|
||||||
|
return _world.DestroyScope<TScope>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Service 查找 ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static bool TryGet<T>(out T service) where T : class, IService
|
||||||
|
{
|
||||||
|
if (_world == null) { service = null; return false; }
|
||||||
|
return _world.TryGet(out service);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Require<T>() where T : class, IService
|
||||||
|
=> World.Require<T>();
|
||||||
|
|
||||||
|
// ── 生命周期 ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static void Shutdown()
|
||||||
|
{
|
||||||
|
if (_world == null) return;
|
||||||
|
_world.Dispose();
|
||||||
|
_world = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7186431b5e61b3c4f9865bd9901ce831
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 标记一个 IService 实现来自 MonoBehaviour。
|
||||||
|
/// ServiceScope 通过此接口识别 Mono 服务,避免 Core 层依赖 UnityEngine。
|
||||||
|
/// </summary>
|
||||||
|
public interface IMonoService : IService { }
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 64c7431bc2d38d24594f92006ac398d9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
public interface IService
|
||||||
|
{
|
||||||
|
void Initialize(ServiceContext context);
|
||||||
|
void Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1cab09453acbb93498bd781202713656
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
public interface IServiceOrder
|
||||||
|
{
|
||||||
|
int Order { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f66ea6b1b8a144b4184b2b078305f8df
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
public interface IServiceTickable
|
||||||
|
{
|
||||||
|
void Tick(float deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IServiceLateTickable
|
||||||
|
{
|
||||||
|
void LateTick(float deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IServiceFixedTickable
|
||||||
|
{
|
||||||
|
void FixedTick(float fixedDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IServiceGizmoDrawable
|
||||||
|
{
|
||||||
|
void DrawGizmos();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7ff388c5465f4c439bf85bb76c2d786
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
internal sealed class ReferenceComparer<T> : IEqualityComparer<T>
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
public static readonly ReferenceComparer<T> Instance = new ReferenceComparer<T>();
|
||||||
|
|
||||||
|
private ReferenceComparer() { }
|
||||||
|
|
||||||
|
public bool Equals(T x, T y) => ReferenceEquals(x, y);
|
||||||
|
|
||||||
|
public int GetHashCode(T obj)
|
||||||
|
=> System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f3afc1438ca0eba4892ac93b1165d60d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
public abstract class ServiceBase : IService
|
||||||
|
{
|
||||||
|
public ServiceContext Context { get; private set; }
|
||||||
|
|
||||||
|
public bool IsInitialized { get; private set; }
|
||||||
|
|
||||||
|
protected ServiceWorld World => Context.World;
|
||||||
|
|
||||||
|
protected ServiceScope Scope => Context.Scope;
|
||||||
|
|
||||||
|
void IService.Initialize(ServiceContext context)
|
||||||
|
{
|
||||||
|
if (IsInitialized)
|
||||||
|
throw new System.InvalidOperationException($"{GetType().FullName} is already initialized.");
|
||||||
|
|
||||||
|
Context = context;
|
||||||
|
IsInitialized = true;
|
||||||
|
OnInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IService.Destroy()
|
||||||
|
{
|
||||||
|
if (!IsInitialized) return;
|
||||||
|
|
||||||
|
OnDestroyService();
|
||||||
|
IsInitialized = false;
|
||||||
|
Context = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void OnInitialize();
|
||||||
|
|
||||||
|
protected abstract void OnDestroyService();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af65a59b2e4b11b419fc09505ecd1c88
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
public readonly struct ServiceContext
|
||||||
|
{
|
||||||
|
public ServiceContext(ServiceWorld world, ServiceScope scope)
|
||||||
|
{
|
||||||
|
World = world;
|
||||||
|
Scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceWorld World { get; }
|
||||||
|
|
||||||
|
public ServiceScope Scope { get; }
|
||||||
|
|
||||||
|
public ServiceScope AppScope => World.AppScope;
|
||||||
|
|
||||||
|
public T Require<T>() where T : class, IService
|
||||||
|
=> World.Require<T>(Scope);
|
||||||
|
|
||||||
|
public bool TryGet<T>(out T service) where T : class, IService
|
||||||
|
=> World.TryGet(Scope, out service);
|
||||||
|
|
||||||
|
public ServiceScope CreateScope<TScope>(int order = 0) where TScope : class
|
||||||
|
=> World.CreateScope<TScope>(order);
|
||||||
|
|
||||||
|
public ServiceScope GetOrCreateScope<TScope>(int order = 0) where TScope : class
|
||||||
|
=> World.GetOrCreateScope<TScope>(order);
|
||||||
|
|
||||||
|
public bool TryGetScope<TScope>(out ServiceScope scope) where TScope : class
|
||||||
|
=> World.TryGetScope<TScope>(out scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fd30fae6aee72a04db7507d194215b40
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
internal static class ServiceContractUtility
|
||||||
|
{
|
||||||
|
private static readonly HashSet<Type> ExcludedContracts = new HashSet<Type>
|
||||||
|
{
|
||||||
|
typeof(IService),
|
||||||
|
typeof(IServiceTickable),
|
||||||
|
typeof(IServiceLateTickable),
|
||||||
|
typeof(IServiceFixedTickable),
|
||||||
|
typeof(IServiceGizmoDrawable),
|
||||||
|
typeof(IServiceOrder),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static List<Type> Collect(Type serviceType, IReadOnlyList<Type> extraContracts)
|
||||||
|
{
|
||||||
|
var contracts = new List<Type> { serviceType };
|
||||||
|
var unique = new HashSet<Type> { serviceType };
|
||||||
|
|
||||||
|
var interfaces = serviceType.GetInterfaces();
|
||||||
|
for (var i = 0; i < interfaces.Length; i++)
|
||||||
|
{
|
||||||
|
var contract = interfaces[i];
|
||||||
|
if (!typeof(IService).IsAssignableFrom(contract)) continue;
|
||||||
|
if (ExcludedContracts.Contains(contract)) continue;
|
||||||
|
if (unique.Add(contract)) contracts.Add(contract);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraContracts == null) return contracts;
|
||||||
|
|
||||||
|
for (var i = 0; i < extraContracts.Count; i++)
|
||||||
|
{
|
||||||
|
var extraContract = extraContracts[i];
|
||||||
|
ValidateExtraContract(serviceType, extraContract);
|
||||||
|
if (unique.Add(extraContract)) contracts.Add(extraContract);
|
||||||
|
}
|
||||||
|
|
||||||
|
return contracts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateExtraContract(Type serviceType, Type contract)
|
||||||
|
{
|
||||||
|
if (contract == null)
|
||||||
|
throw new ArgumentNullException(nameof(contract));
|
||||||
|
|
||||||
|
if (!typeof(IService).IsAssignableFrom(contract))
|
||||||
|
throw new InvalidOperationException($"{contract.FullName} must inherit {nameof(IService)}.");
|
||||||
|
|
||||||
|
if (!contract.IsAssignableFrom(serviceType))
|
||||||
|
throw new InvalidOperationException($"{serviceType.FullName} does not implement {contract.FullName}.");
|
||||||
|
|
||||||
|
if (ExcludedContracts.Contains(contract))
|
||||||
|
throw new InvalidOperationException($"{contract.FullName} cannot be used as a service contract.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 95c468fca1e70ab4b931d26d98af3fd9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
260
Client/Assets/Scripts/New/ServiceFramework/Core/ServiceScope.cs
Normal file
260
Client/Assets/Scripts/New/ServiceFramework/Core/ServiceScope.cs
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
public sealed class ServiceScope : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Type, IService> _servicesByContract = new Dictionary<Type, IService>();
|
||||||
|
private readonly Dictionary<IService, List<Type>> _contractsByService = new Dictionary<IService, List<Type>>(ReferenceComparer<IService>.Instance);
|
||||||
|
private readonly List<IService> _registrationOrder = new List<IService>();
|
||||||
|
|
||||||
|
private readonly List<IServiceTickable> _tickables = new List<IServiceTickable>();
|
||||||
|
private readonly List<IServiceLateTickable> _lateTickables = new List<IServiceLateTickable>();
|
||||||
|
private readonly List<IServiceFixedTickable> _fixedTickables = new List<IServiceFixedTickable>();
|
||||||
|
private readonly List<IServiceGizmoDrawable> _gizmoDrawables = new List<IServiceGizmoDrawable>();
|
||||||
|
|
||||||
|
private IServiceTickable[] _tickableSnapshot = Array.Empty<IServiceTickable>();
|
||||||
|
private IServiceLateTickable[] _lateTickableSnapshot = Array.Empty<IServiceLateTickable>();
|
||||||
|
private IServiceFixedTickable[] _fixedTickableSnapshot = Array.Empty<IServiceFixedTickable>();
|
||||||
|
private IServiceGizmoDrawable[] _gizmoSnapshot = Array.Empty<IServiceGizmoDrawable>();
|
||||||
|
|
||||||
|
private bool _tickablesDirty;
|
||||||
|
private bool _lateTickablesDirty;
|
||||||
|
private bool _fixedTickablesDirty;
|
||||||
|
private bool _gizmoDrawablesDirty;
|
||||||
|
|
||||||
|
internal ServiceScope(ServiceWorld world, string name, int order)
|
||||||
|
{
|
||||||
|
World = world ?? throw new ArgumentNullException(nameof(world));
|
||||||
|
Name = string.IsNullOrWhiteSpace(name) ? throw new ArgumentException("Scope name cannot be empty.", nameof(name)) : name;
|
||||||
|
Order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceWorld World { get; }
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public int Order { get; }
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
public T Register<T>(T service, params Type[] extraContracts)
|
||||||
|
where T : class, IService
|
||||||
|
{
|
||||||
|
EnsureNotDisposed();
|
||||||
|
|
||||||
|
if (service == null)
|
||||||
|
throw new ArgumentNullException(nameof(service));
|
||||||
|
|
||||||
|
ValidateService(service);
|
||||||
|
|
||||||
|
if (_contractsByService.ContainsKey(service))
|
||||||
|
throw new InvalidOperationException($"Service {service.GetType().FullName} is already registered in scope {Name}.");
|
||||||
|
|
||||||
|
var contracts = ServiceContractUtility.Collect(service.GetType(), extraContracts);
|
||||||
|
for (var i = 0; i < contracts.Count; i++)
|
||||||
|
{
|
||||||
|
var contract = contracts[i];
|
||||||
|
if (_servicesByContract.TryGetValue(contract, out var existing))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Scope {Name} already contains contract {contract.FullName} bound to {existing.GetType().FullName}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_contractsByService.Add(service, contracts);
|
||||||
|
_registrationOrder.Add(service);
|
||||||
|
for (var i = 0; i < contracts.Count; i++)
|
||||||
|
_servicesByContract.Add(contracts[i], service);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
service.Initialize(new ServiceContext(World, this));
|
||||||
|
AddToLifecycleLists(service);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
RemoveBindings(service);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unregister<T>() where T : class, IService
|
||||||
|
{
|
||||||
|
if (!_servicesByContract.TryGetValue(typeof(T), out var service))
|
||||||
|
return false;
|
||||||
|
return Unregister(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unregister(IService service)
|
||||||
|
{
|
||||||
|
if (service == null || !_contractsByService.ContainsKey(service))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RemoveFromLifecycleLists(service);
|
||||||
|
RemoveBindings(service);
|
||||||
|
service.Destroy();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGet<T>(out T service) where T : class, IService
|
||||||
|
{
|
||||||
|
if (_servicesByContract.TryGetValue(typeof(T), out var raw))
|
||||||
|
{
|
||||||
|
service = raw as T;
|
||||||
|
return service != null;
|
||||||
|
}
|
||||||
|
service = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Require<T>() where T : class, IService
|
||||||
|
{
|
||||||
|
if (TryGet(out T service)) return service;
|
||||||
|
throw new InvalidOperationException($"Scope {Name} does not contain service {typeof(T).FullName}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasContract(Type contractType)
|
||||||
|
=> _servicesByContract.ContainsKey(contractType);
|
||||||
|
|
||||||
|
internal void Tick(float deltaTime)
|
||||||
|
{
|
||||||
|
var snapshot = GetTickSnapshot();
|
||||||
|
for (var i = 0; i < snapshot.Length; i++) snapshot[i].Tick(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LateTick(float deltaTime)
|
||||||
|
{
|
||||||
|
var snapshot = GetLateTickSnapshot();
|
||||||
|
for (var i = 0; i < snapshot.Length; i++) snapshot[i].LateTick(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void FixedTick(float fixedDeltaTime)
|
||||||
|
{
|
||||||
|
var snapshot = GetFixedTickSnapshot();
|
||||||
|
for (var i = 0; i < snapshot.Length; i++) snapshot[i].FixedTick(fixedDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DrawGizmos()
|
||||||
|
{
|
||||||
|
var snapshot = GetGizmoSnapshot();
|
||||||
|
for (var i = 0; i < snapshot.Length; i++) snapshot[i].DrawGizmos();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (IsDisposed) return;
|
||||||
|
|
||||||
|
var snapshot = _registrationOrder.ToArray();
|
||||||
|
for (var i = snapshot.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var service = snapshot[i];
|
||||||
|
if (!_contractsByService.ContainsKey(service)) continue;
|
||||||
|
RemoveFromLifecycleLists(service);
|
||||||
|
RemoveBindings(service);
|
||||||
|
service.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
IsDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureNotDisposed()
|
||||||
|
{
|
||||||
|
if (IsDisposed) throw new ObjectDisposedException(Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateService(IService service)
|
||||||
|
{
|
||||||
|
if (service is IMonoService &&
|
||||||
|
(service is IServiceTickable ||
|
||||||
|
service is IServiceLateTickable ||
|
||||||
|
service is IServiceFixedTickable ||
|
||||||
|
service is IServiceGizmoDrawable))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Mono service {service.GetType().FullName} cannot implement tick lifecycle interfaces.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddToLifecycleLists(IService service)
|
||||||
|
{
|
||||||
|
if (service is IServiceTickable tickable) { _tickables.Add(tickable); _tickablesDirty = true; }
|
||||||
|
if (service is IServiceLateTickable late) { _lateTickables.Add(late); _lateTickablesDirty = true; }
|
||||||
|
if (service is IServiceFixedTickable fixed_) { _fixedTickables.Add(fixed_); _fixedTickablesDirty = true; }
|
||||||
|
if (service is IServiceGizmoDrawable gizmo) { _gizmoDrawables.Add(gizmo); _gizmoDrawablesDirty = true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveFromLifecycleLists(IService service)
|
||||||
|
{
|
||||||
|
if (service is IServiceTickable tickable && _tickables.Remove(tickable)) _tickablesDirty = true;
|
||||||
|
if (service is IServiceLateTickable late && _lateTickables.Remove(late)) _lateTickablesDirty = true;
|
||||||
|
if (service is IServiceFixedTickable fixed_ && _fixedTickables.Remove(fixed_)) _fixedTickablesDirty = true;
|
||||||
|
if (service is IServiceGizmoDrawable gizmo && _gizmoDrawables.Remove(gizmo)) _gizmoDrawablesDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveBindings(IService service)
|
||||||
|
{
|
||||||
|
if (_contractsByService.TryGetValue(service, out var contracts))
|
||||||
|
{
|
||||||
|
for (var i = 0; i < contracts.Count; i++)
|
||||||
|
_servicesByContract.Remove(contracts[i]);
|
||||||
|
}
|
||||||
|
_contractsByService.Remove(service);
|
||||||
|
_registrationOrder.Remove(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IServiceTickable[] GetTickSnapshot()
|
||||||
|
{
|
||||||
|
if (_tickablesDirty)
|
||||||
|
{
|
||||||
|
_tickables.Sort(CompareByOrder);
|
||||||
|
_tickableSnapshot = _tickables.Count > 0 ? _tickables.ToArray() : Array.Empty<IServiceTickable>();
|
||||||
|
_tickablesDirty = false;
|
||||||
|
}
|
||||||
|
return _tickableSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IServiceLateTickable[] GetLateTickSnapshot()
|
||||||
|
{
|
||||||
|
if (_lateTickablesDirty)
|
||||||
|
{
|
||||||
|
_lateTickables.Sort(CompareByOrder);
|
||||||
|
_lateTickableSnapshot = _lateTickables.Count > 0 ? _lateTickables.ToArray() : Array.Empty<IServiceLateTickable>();
|
||||||
|
_lateTickablesDirty = false;
|
||||||
|
}
|
||||||
|
return _lateTickableSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IServiceFixedTickable[] GetFixedTickSnapshot()
|
||||||
|
{
|
||||||
|
if (_fixedTickablesDirty)
|
||||||
|
{
|
||||||
|
_fixedTickables.Sort(CompareByOrder);
|
||||||
|
_fixedTickableSnapshot = _fixedTickables.Count > 0 ? _fixedTickables.ToArray() : Array.Empty<IServiceFixedTickable>();
|
||||||
|
_fixedTickablesDirty = false;
|
||||||
|
}
|
||||||
|
return _fixedTickableSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IServiceGizmoDrawable[] GetGizmoSnapshot()
|
||||||
|
{
|
||||||
|
if (_gizmoDrawablesDirty)
|
||||||
|
{
|
||||||
|
_gizmoDrawables.Sort(CompareByOrder);
|
||||||
|
_gizmoSnapshot = _gizmoDrawables.Count > 0 ? _gizmoDrawables.ToArray() : Array.Empty<IServiceGizmoDrawable>();
|
||||||
|
_gizmoDrawablesDirty = false;
|
||||||
|
}
|
||||||
|
return _gizmoSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CompareByOrder<T>(T a, T b)
|
||||||
|
{
|
||||||
|
var left = a is IServiceOrder oa ? oa.Order : 0;
|
||||||
|
var right = b is IServiceOrder ob ? ob.Order : 0;
|
||||||
|
return left.CompareTo(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 90a7d784a3aff2f4f832fa67901e6931
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
157
Client/Assets/Scripts/New/ServiceFramework/Core/ServiceWorld.cs
Normal file
157
Client/Assets/Scripts/New/ServiceFramework/Core/ServiceWorld.cs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
public sealed class ServiceWorld : IDisposable
|
||||||
|
{
|
||||||
|
private readonly List<ServiceScope> _scopes = new List<ServiceScope>();
|
||||||
|
private readonly Dictionary<Type, ServiceScope> _scopesByType = new Dictionary<Type, ServiceScope>();
|
||||||
|
|
||||||
|
private ServiceScope[] _scopeSnapshot = Array.Empty<ServiceScope>();
|
||||||
|
private bool _scopesDirty;
|
||||||
|
|
||||||
|
public ServiceWorld(int appScopeOrder = -10000)
|
||||||
|
{
|
||||||
|
AppScope = CreateScopeInternal(typeof(AppScope), appScopeOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceScope AppScope { get; }
|
||||||
|
|
||||||
|
// ── Scope 管理(Type-based) ────────────────────────────────────────────
|
||||||
|
|
||||||
|
public ServiceScope CreateScope<TScope>(int order = 0) where TScope : class
|
||||||
|
{
|
||||||
|
var type = typeof(TScope);
|
||||||
|
if (_scopesByType.ContainsKey(type))
|
||||||
|
throw new InvalidOperationException($"Scope {type.Name} already exists.");
|
||||||
|
return CreateScopeInternal(type, order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceScope GetOrCreateScope<TScope>(int order = 0) where TScope : class
|
||||||
|
{
|
||||||
|
var type = typeof(TScope);
|
||||||
|
if (_scopesByType.TryGetValue(type, out var existing))
|
||||||
|
return existing;
|
||||||
|
return CreateScopeInternal(type, order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetScope<TScope>(out ServiceScope scope) where TScope : class
|
||||||
|
=> _scopesByType.TryGetValue(typeof(TScope), out scope);
|
||||||
|
|
||||||
|
public ServiceScope GetScope<TScope>() where TScope : class
|
||||||
|
{
|
||||||
|
if (TryGetScope<TScope>(out var scope)) return scope;
|
||||||
|
throw new InvalidOperationException($"Scope {typeof(TScope).Name} does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DestroyScope<TScope>() where TScope : class
|
||||||
|
{
|
||||||
|
if (typeof(TScope) == typeof(AppScope))
|
||||||
|
throw new InvalidOperationException("AppScope can only be destroyed when the world is disposed.");
|
||||||
|
|
||||||
|
var type = typeof(TScope);
|
||||||
|
if (!_scopesByType.TryGetValue(type, out var scope))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_scopesByType.Remove(type);
|
||||||
|
_scopes.Remove(scope);
|
||||||
|
_scopesDirty = true;
|
||||||
|
scope.Dispose();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Service 查找 ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public bool TryGet<T>(out T service) where T : class, IService
|
||||||
|
=> TryGet(null, out service);
|
||||||
|
|
||||||
|
public bool TryGet<T>(ServiceScope preferredScope, out T service) where T : class, IService
|
||||||
|
{
|
||||||
|
if (preferredScope != null && !preferredScope.IsDisposed && preferredScope.TryGet(out service))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var snapshot = GetScopeSnapshot();
|
||||||
|
for (var i = snapshot.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var scope = snapshot[i];
|
||||||
|
if (ReferenceEquals(scope, preferredScope)) continue;
|
||||||
|
if (scope.TryGet(out service)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
service = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Require<T>() where T : class, IService => Require<T>(null);
|
||||||
|
|
||||||
|
public T Require<T>(ServiceScope preferredScope) where T : class, IService
|
||||||
|
{
|
||||||
|
if (TryGet(preferredScope, out T service)) return service;
|
||||||
|
throw new InvalidOperationException($"Service {typeof(T).FullName} was not found in any active scope.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Tick ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public void Tick(float deltaTime)
|
||||||
|
{
|
||||||
|
var snapshot = GetScopeSnapshot();
|
||||||
|
for (var i = 0; i < snapshot.Length; i++) snapshot[i].Tick(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LateTick(float deltaTime)
|
||||||
|
{
|
||||||
|
var snapshot = GetScopeSnapshot();
|
||||||
|
for (var i = 0; i < snapshot.Length; i++) snapshot[i].LateTick(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FixedTick(float fixedDeltaTime)
|
||||||
|
{
|
||||||
|
var snapshot = GetScopeSnapshot();
|
||||||
|
for (var i = 0; i < snapshot.Length; i++) snapshot[i].FixedTick(fixedDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawGizmos()
|
||||||
|
{
|
||||||
|
var snapshot = GetScopeSnapshot();
|
||||||
|
for (var i = 0; i < snapshot.Length; i++) snapshot[i].DrawGizmos();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Dispose ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
var snapshot = _scopes.ToArray();
|
||||||
|
for (var i = snapshot.Length - 1; i >= 0; i--)
|
||||||
|
snapshot[i].Dispose();
|
||||||
|
_scopes.Clear();
|
||||||
|
_scopesByType.Clear();
|
||||||
|
_scopeSnapshot = Array.Empty<ServiceScope>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 内部 ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private ServiceScope CreateScopeInternal(Type scopeType, int order)
|
||||||
|
{
|
||||||
|
var scope = new ServiceScope(this, scopeType.Name, order);
|
||||||
|
_scopes.Add(scope);
|
||||||
|
_scopesByType.Add(scopeType, scope);
|
||||||
|
_scopes.Sort(CompareScopeOrder);
|
||||||
|
_scopesDirty = true;
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceScope[] GetScopeSnapshot()
|
||||||
|
{
|
||||||
|
if (_scopesDirty)
|
||||||
|
{
|
||||||
|
_scopeSnapshot = _scopes.Count > 0 ? _scopes.ToArray() : Array.Empty<ServiceScope>();
|
||||||
|
_scopesDirty = false;
|
||||||
|
}
|
||||||
|
return _scopeSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CompareScopeOrder(ServiceScope left, ServiceScope right)
|
||||||
|
=> left.Order.CompareTo(right.Order);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d48a8032734a55647a565ee72471c5c2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Client/Assets/Scripts/New/ServiceFramework/Unity.meta
Normal file
8
Client/Assets/Scripts/New/ServiceFramework/Unity.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c69106e6f9633554ea75fbc122821e2a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
[DefaultExecutionOrder(-32000)]
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public class GameServiceRoot : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static GameServiceRoot s_activeRoot;
|
||||||
|
|
||||||
|
[SerializeField] private bool _dontDestroyOnLoad = true;
|
||||||
|
[SerializeField] private int _appScopeOrder = -10000;
|
||||||
|
|
||||||
|
private bool _ownsWorld;
|
||||||
|
|
||||||
|
protected virtual void Awake()
|
||||||
|
{
|
||||||
|
if (s_activeRoot != null && s_activeRoot != this)
|
||||||
|
{
|
||||||
|
enabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_activeRoot = this;
|
||||||
|
|
||||||
|
if (_dontDestroyOnLoad)
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
|
|
||||||
|
var createdWorld = !GameServices.HasWorld;
|
||||||
|
var world = GameServices.EnsureWorld(_appScopeOrder);
|
||||||
|
_ownsWorld = createdWorld;
|
||||||
|
|
||||||
|
if (createdWorld)
|
||||||
|
RegisterAppServices(world.AppScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Update()
|
||||||
|
{
|
||||||
|
if (GameServices.HasWorld) GameServices.World.Tick(Time.deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void LateUpdate()
|
||||||
|
{
|
||||||
|
if (GameServices.HasWorld) GameServices.World.LateTick(Time.deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void FixedUpdate()
|
||||||
|
{
|
||||||
|
if (GameServices.HasWorld) GameServices.World.FixedTick(Time.fixedDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
if (GameServices.HasWorld) GameServices.World.DrawGizmos();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDestroy()
|
||||||
|
{
|
||||||
|
if (s_activeRoot == this)
|
||||||
|
s_activeRoot = null;
|
||||||
|
|
||||||
|
if (_ownsWorld && GameServices.HasWorld)
|
||||||
|
GameServices.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void RegisterAppServices(ServiceScope appScope) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f1e04080395e4e649a43e98b673c5911
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Aliciza.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Mono 服务基类(不自动注册,适合需要手动控制注册时机的场景)。
|
||||||
|
/// </summary>
|
||||||
|
public abstract class MonoServiceBehaviour : MonoBehaviour, IMonoService
|
||||||
|
{
|
||||||
|
public ServiceContext Context { get; private set; }
|
||||||
|
|
||||||
|
public bool IsInitialized { get; private set; }
|
||||||
|
|
||||||
|
protected ServiceWorld World => Context.World;
|
||||||
|
|
||||||
|
protected ServiceScope Scope => Context.Scope;
|
||||||
|
|
||||||
|
void IService.Initialize(ServiceContext context)
|
||||||
|
{
|
||||||
|
if (IsInitialized)
|
||||||
|
throw new System.InvalidOperationException($"{GetType().FullName} is already initialized.");
|
||||||
|
|
||||||
|
Context = context;
|
||||||
|
IsInitialized = true;
|
||||||
|
OnServiceInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IService.Destroy()
|
||||||
|
{
|
||||||
|
if (!IsInitialized) return;
|
||||||
|
|
||||||
|
OnServiceDestroy();
|
||||||
|
IsInitialized = false;
|
||||||
|
Context = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnServiceInitialize() { }
|
||||||
|
|
||||||
|
protected virtual void OnServiceDestroy() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mono 服务基类(自动注册到 <typeparamref name="TScope"/>)。
|
||||||
|
/// <para>
|
||||||
|
/// 场景服务:<c>_dontDestroyOnLoad = false</c>(默认),销毁时自动注销。<br/>
|
||||||
|
/// 跨场景服务:<c>_dontDestroyOnLoad = true</c>,首个实例持久化并注册;
|
||||||
|
/// 后续场景中出现的重复实例自动销毁自身。
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// 子类通过 <see cref="OnAwake"/> 执行额外的 Awake 逻辑,
|
||||||
|
/// 通过 <see cref="OnServiceInitialize"/> 执行注册后的初始化,
|
||||||
|
/// 通过 <see cref="OnServiceDestroy"/> 执行注销前的清理。
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public abstract class MonoServiceBehaviour<TScope> : MonoServiceBehaviour
|
||||||
|
where TScope : class
|
||||||
|
{
|
||||||
|
[SerializeField] private bool _dontDestroyOnLoad = false;
|
||||||
|
|
||||||
|
// 注意:使用 Start 而非 Awake 注册,确保 GameServiceRoot.Awake(创建 World)必然先于此执行。
|
||||||
|
// DefaultExecutionOrder 会影响所有生命周期(含 Awake),用 Start 可彻底规避执行顺序陷阱。
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (_dontDestroyOnLoad)
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
|
|
||||||
|
OnAwake();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
var scope = GameServices.GetOrCreateScope<TScope>();
|
||||||
|
|
||||||
|
// 跨场景重复实例检测:契约已被占用则销毁自身
|
||||||
|
if (scope.HasContract(GetType()))
|
||||||
|
{
|
||||||
|
Destroy(gameObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.Register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
if (!IsInitialized) return;
|
||||||
|
if (!GameServices.HasWorld) return;
|
||||||
|
if (!GameServices.TryGetScope<TScope>(out var scope)) return;
|
||||||
|
|
||||||
|
scope.Unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在 Awake 阶段执行(早于 Start 中的自动注册)。
|
||||||
|
/// 适合缓存组件引用等不依赖服务系统的初始化。
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnAwake() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af63e3465b03eaf4ca3a82aebcab6e26
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
12
Client/Assets/Scripts/New/readme.md
Normal file
12
Client/Assets/Scripts/New/readme.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
先不要分析我的代码,先对我提出的要求设计一个架构。
|
||||||
|
|
||||||
|
为了避免游戏逻辑业务繁琐 Manager 满天飞 到处都是Instance引用 Update顺序不可控提出以下设计。
|
||||||
|
|
||||||
|
游戏中会存在一开始就在场景里的Manager挂载GameObject上,因为这类Mono他需要对场景里的资源有引用比如InputManager,这类
|
||||||
|
Manager生命周期由自己管理
|
||||||
|
第二种Manager纯C#的形式存在 不依赖Mono 所有生命周期都有顶层管理,比如TimerManager 它会继承IService IService 也可以
|
||||||
|
继承Initlize 和Destory 这是必须要继承的,也可以同时继承IServiceTickable 实现类似Update的功能,同理Late Fixed OnGizmo
|
||||||
|
等等。Mono类的接口 可以继承IService,但是不允许继承Tick 等等这种的重复生命周期的接口。
|
||||||
|
|
||||||
|
游戏内会存在比如一开始就要创建的纯C# Manager 也有启动游戏在登陆后或者进入大厅后才创建的Manager,也有一开始就放在场景
|
||||||
|
里的Manager,也有进入战斗场景后才会创建的CameraManager和TimelineManager等等,禁止使用依赖注入。
|
||||||
7
Client/Assets/Scripts/New/readme.md.meta
Normal file
7
Client/Assets/Scripts/New/readme.md.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 48f5f075d7e396348956f3a822a97d56
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 76cc128c4f9744150c2af60d5b2b06fc20249059
|
Subproject commit 4a0465450a0940f697978bdd7d5cf156117efb80
|
||||||
@ -19,7 +19,7 @@ MonoBehaviour:
|
|||||||
width: 1920
|
width: 1920
|
||||||
height: 997
|
height: 997
|
||||||
m_ShowMode: 4
|
m_ShowMode: 4
|
||||||
m_Title: Project
|
m_Title: Inspector
|
||||||
m_RootView: {fileID: 4}
|
m_RootView: {fileID: 4}
|
||||||
m_MinSize: {x: 875, y: 300}
|
m_MinSize: {x: 875, y: 300}
|
||||||
m_MaxSize: {x: 10000, y: 10000}
|
m_MaxSize: {x: 10000, y: 10000}
|
||||||
@ -41,7 +41,7 @@ MonoBehaviour:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 665
|
y: 665
|
||||||
width: 485
|
width: 403
|
||||||
height: 282
|
height: 282
|
||||||
m_MinSize: {x: 51, y: 71}
|
m_MinSize: {x: 51, y: 71}
|
||||||
m_MaxSize: {x: 4001, y: 4021}
|
m_MaxSize: {x: 4001, y: 4021}
|
||||||
@ -70,12 +70,12 @@ MonoBehaviour:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
width: 485
|
width: 403
|
||||||
height: 947
|
height: 947
|
||||||
m_MinSize: {x: 100, y: 100}
|
m_MinSize: {x: 100, y: 100}
|
||||||
m_MaxSize: {x: 8096, y: 16192}
|
m_MaxSize: {x: 8096, y: 16192}
|
||||||
vertical: 1
|
vertical: 1
|
||||||
controlID: 25
|
controlID: 26
|
||||||
draggingID: 0
|
draggingID: 0
|
||||||
--- !u!114 &4
|
--- !u!114 &4
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
@ -174,7 +174,7 @@ MonoBehaviour:
|
|||||||
m_MinSize: {x: 400, y: 100}
|
m_MinSize: {x: 400, y: 100}
|
||||||
m_MaxSize: {x: 32384, y: 16192}
|
m_MaxSize: {x: 32384, y: 16192}
|
||||||
vertical: 0
|
vertical: 0
|
||||||
controlID: 24
|
controlID: 154
|
||||||
draggingID: 0
|
draggingID: 0
|
||||||
--- !u!114 &8
|
--- !u!114 &8
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
@ -193,7 +193,7 @@ MonoBehaviour:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
width: 485
|
width: 403
|
||||||
height: 665
|
height: 665
|
||||||
m_MinSize: {x: 201, y: 221}
|
m_MinSize: {x: 201, y: 221}
|
||||||
m_MaxSize: {x: 4001, y: 4021}
|
m_MaxSize: {x: 4001, y: 4021}
|
||||||
@ -219,14 +219,14 @@ MonoBehaviour:
|
|||||||
- {fileID: 11}
|
- {fileID: 11}
|
||||||
m_Position:
|
m_Position:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 485
|
x: 403
|
||||||
y: 0
|
y: 0
|
||||||
width: 396
|
width: 351
|
||||||
height: 947
|
height: 947
|
||||||
m_MinSize: {x: 100, y: 100}
|
m_MinSize: {x: 100, y: 100}
|
||||||
m_MaxSize: {x: 8096, y: 16192}
|
m_MaxSize: {x: 8096, y: 16192}
|
||||||
vertical: 1
|
vertical: 1
|
||||||
controlID: 70
|
controlID: 71
|
||||||
draggingID: 0
|
draggingID: 0
|
||||||
--- !u!114 &10
|
--- !u!114 &10
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
@ -245,7 +245,7 @@ MonoBehaviour:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
width: 396
|
width: 351
|
||||||
height: 595
|
height: 595
|
||||||
m_MinSize: {x: 200, y: 200}
|
m_MinSize: {x: 200, y: 200}
|
||||||
m_MaxSize: {x: 4000, y: 4000}
|
m_MaxSize: {x: 4000, y: 4000}
|
||||||
@ -271,7 +271,7 @@ MonoBehaviour:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 595
|
y: 595
|
||||||
width: 396
|
width: 351
|
||||||
height: 352
|
height: 352
|
||||||
m_MinSize: {x: 102, y: 121}
|
m_MinSize: {x: 102, y: 121}
|
||||||
m_MaxSize: {x: 4002, y: 4021}
|
m_MaxSize: {x: 4002, y: 4021}
|
||||||
@ -295,9 +295,9 @@ MonoBehaviour:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Position:
|
m_Position:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 881
|
x: 754
|
||||||
y: 0
|
y: 0
|
||||||
width: 455
|
width: 470
|
||||||
height: 947
|
height: 947
|
||||||
m_MinSize: {x: 232, y: 271}
|
m_MinSize: {x: 232, y: 271}
|
||||||
m_MaxSize: {x: 10002, y: 10021}
|
m_MaxSize: {x: 10002, y: 10021}
|
||||||
@ -321,12 +321,12 @@ MonoBehaviour:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Position:
|
m_Position:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 1336
|
x: 1224
|
||||||
y: 0
|
y: 0
|
||||||
width: 584
|
width: 696
|
||||||
height: 947
|
height: 947
|
||||||
m_MinSize: {x: 275, y: 50}
|
m_MinSize: {x: 276, y: 71}
|
||||||
m_MaxSize: {x: 4000, y: 4000}
|
m_MaxSize: {x: 4001, y: 4021}
|
||||||
m_ActualView: {fileID: 20}
|
m_ActualView: {fileID: 20}
|
||||||
m_Panes:
|
m_Panes:
|
||||||
- {fileID: 20}
|
- {fileID: 20}
|
||||||
@ -354,7 +354,7 @@ MonoBehaviour:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 738
|
y: 738
|
||||||
width: 484
|
width: 402
|
||||||
height: 261
|
height: 261
|
||||||
m_SerializedDataModeController:
|
m_SerializedDataModeController:
|
||||||
m_DataMode: 0
|
m_DataMode: 0
|
||||||
@ -408,23 +408,23 @@ MonoBehaviour:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 21
|
y: 21
|
||||||
width: 484
|
width: 402
|
||||||
height: 240
|
height: 240
|
||||||
m_Scale: {x: 0.22222222, y: 0.22222222}
|
m_Scale: {x: 0.209375, y: 0.209375}
|
||||||
m_Translation: {x: 242, y: 120}
|
m_Translation: {x: 201, y: 120}
|
||||||
m_MarginLeft: 0
|
m_MarginLeft: 0
|
||||||
m_MarginRight: 0
|
m_MarginRight: 0
|
||||||
m_MarginTop: 0
|
m_MarginTop: 0
|
||||||
m_MarginBottom: 0
|
m_MarginBottom: 0
|
||||||
m_LastShownAreaInsideMargins:
|
m_LastShownAreaInsideMargins:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: -1089
|
x: -960
|
||||||
y: -540
|
y: -573.13434
|
||||||
width: 2178
|
width: 1920
|
||||||
height: 1080
|
height: 1146.2687
|
||||||
m_MinimalGUI: 1
|
m_MinimalGUI: 1
|
||||||
m_defaultScale: 0.22222222
|
m_defaultScale: 0.209375
|
||||||
m_LastWindowPixelSize: {x: 484, y: 261}
|
m_LastWindowPixelSize: {x: 402, y: 261}
|
||||||
m_ClearInEditMode: 1
|
m_ClearInEditMode: 1
|
||||||
m_NoCameraWarning: 1
|
m_NoCameraWarning: 1
|
||||||
m_LowResolutionForAspectRatios: 01000000000000000000
|
m_LowResolutionForAspectRatios: 01000000000000000000
|
||||||
@ -522,7 +522,7 @@ MonoBehaviour:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 73
|
y: 73
|
||||||
width: 484
|
width: 402
|
||||||
height: 644
|
height: 644
|
||||||
m_SerializedDataModeController:
|
m_SerializedDataModeController:
|
||||||
m_DataMode: 0
|
m_DataMode: 0
|
||||||
@ -1163,9 +1163,9 @@ MonoBehaviour:
|
|||||||
m_Tooltip:
|
m_Tooltip:
|
||||||
m_Pos:
|
m_Pos:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 485
|
x: 403
|
||||||
y: 73
|
y: 73
|
||||||
width: 394
|
width: 349
|
||||||
height: 574
|
height: 574
|
||||||
m_SerializedDataModeController:
|
m_SerializedDataModeController:
|
||||||
m_DataMode: 0
|
m_DataMode: 0
|
||||||
@ -1180,23 +1180,23 @@ MonoBehaviour:
|
|||||||
m_SceneHierarchy:
|
m_SceneHierarchy:
|
||||||
m_TreeViewState:
|
m_TreeViewState:
|
||||||
scrollPos: {x: 0, y: 0}
|
scrollPos: {x: 0, y: 0}
|
||||||
m_SelectedIDs: 3a6d0000
|
m_SelectedIDs: 142a0000
|
||||||
m_LastClickedID: 0
|
m_LastClickedID: 0
|
||||||
m_ExpandedIDs: c6a8ffffc8a8ffffa2aaffff0efbffff10fbfffff4ffffffc8720000
|
m_ExpandedIDs: 0efbffff10fbffff
|
||||||
m_RenameOverlay:
|
m_RenameOverlay:
|
||||||
m_UserAcceptedRename: 0
|
m_UserAcceptedRename: 0
|
||||||
m_Name: Cube(Clone)
|
m_Name:
|
||||||
m_OriginalName: Cube(Clone)
|
m_OriginalName:
|
||||||
m_EditFieldRect:
|
m_EditFieldRect:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
width: 0
|
width: 0
|
||||||
height: 0
|
height: 0
|
||||||
m_UserData: -24750
|
m_UserData: 0
|
||||||
m_IsWaitingForDelay: 0
|
m_IsWaitingForDelay: 0
|
||||||
m_IsRenaming: 0
|
m_IsRenaming: 0
|
||||||
m_OriginalEventType: 0
|
m_OriginalEventType: 11
|
||||||
m_IsRenamingFilename: 0
|
m_IsRenamingFilename: 0
|
||||||
m_ClientGUIView: {fileID: 10}
|
m_ClientGUIView: {fileID: 10}
|
||||||
m_SearchString:
|
m_SearchString:
|
||||||
@ -1226,9 +1226,9 @@ MonoBehaviour:
|
|||||||
m_Tooltip:
|
m_Tooltip:
|
||||||
m_Pos:
|
m_Pos:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 485
|
x: 403
|
||||||
y: 668
|
y: 668
|
||||||
width: 394
|
width: 349
|
||||||
height: 331
|
height: 331
|
||||||
m_SerializedDataModeController:
|
m_SerializedDataModeController:
|
||||||
m_DataMode: 0
|
m_DataMode: 0
|
||||||
@ -1260,9 +1260,9 @@ MonoBehaviour:
|
|||||||
m_Tooltip:
|
m_Tooltip:
|
||||||
m_Pos:
|
m_Pos:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 881
|
x: 754
|
||||||
y: 73
|
y: 73
|
||||||
width: 453
|
width: 468
|
||||||
height: 926
|
height: 926
|
||||||
m_SerializedDataModeController:
|
m_SerializedDataModeController:
|
||||||
m_DataMode: 0
|
m_DataMode: 0
|
||||||
@ -1285,7 +1285,7 @@ MonoBehaviour:
|
|||||||
m_SkipHidden: 0
|
m_SkipHidden: 0
|
||||||
m_SearchArea: 2
|
m_SearchArea: 2
|
||||||
m_Folders:
|
m_Folders:
|
||||||
- Packages/com.alicizax.unity.framework/Runtime/Localization
|
- Packages/com.alicizax.unity.framework/Runtime
|
||||||
m_Globs: []
|
m_Globs: []
|
||||||
m_OriginalText:
|
m_OriginalText:
|
||||||
m_ImportLogFlags: 0
|
m_ImportLogFlags: 0
|
||||||
@ -1301,7 +1301,7 @@ MonoBehaviour:
|
|||||||
scrollPos: {x: 0, y: 0}
|
scrollPos: {x: 0, y: 0}
|
||||||
m_SelectedIDs: e48c0000
|
m_SelectedIDs: e48c0000
|
||||||
m_LastClickedID: 36068
|
m_LastClickedID: 36068
|
||||||
m_ExpandedIDs: 000000007e020000fe0c0000606a0000626a0000646a0000666a0000686a00006a6a00006c6a00006e6a0000706a0000726a0000746a0000766a0000786a00007a6a00007c6a00007e6a0000806a0000826a0000846a0000866a0000886a00008a6a00008c6a00008e6a0000906a0000926a0000946a0000966a0000986a00009a6a00009c6a00009e6a0000a06a0000a26a0000a46a0000a66a0000a86a0000aa6a0000ac6a0000ae6a0000b06a0000b26a0000b46a0000b66a0000b86a0000ba6a0000bc6a0000be6a0000c06a0000c26a0000c46a0000c66a0000c86a0000ca6a0000cc6a0000ce6a0000d06a0000d26a0000d46a0000d66a0000d86a0000da6a0000dc6a0000de6a0000e06a0000e26a0000e46a0000e66a0000e86a0000ea6a0000ec6a0000ee6a0000f06a0000f26a0000f46a0000f66a0000f86a0000
|
m_ExpandedIDs: 000000007e020000060d0000a06a0000a26a0000a46a0000a66a0000a86a0000aa6a0000ac6a0000ae6a0000b06a0000b26a0000b46a0000b66a0000b86a0000ba6a0000bc6a0000be6a0000c06a0000c26a0000c46a0000c66a0000c86a0000ca6a0000cc6a0000ce6a0000d06a0000d26a0000d46a0000d66a0000d86a0000da6a0000dc6a0000de6a0000e06a0000e26a0000e46a0000e66a0000e86a0000ea6a0000ec6a0000ee6a0000f06a0000f26a0000f46a0000f66a0000f86a0000fa6a0000fc6a0000fe6a0000006b0000026b0000046b0000066b0000086b00000a6b00000c6b00000e6b0000106b0000126b0000146b0000166b0000186b00001a6b00001c6b00001e6b0000206b0000226b0000246b0000266b0000286b00002a6b00002c6b00002e6b0000306b0000326b0000346b0000366b0000386b0000
|
||||||
m_RenameOverlay:
|
m_RenameOverlay:
|
||||||
m_UserAcceptedRename: 0
|
m_UserAcceptedRename: 0
|
||||||
m_Name:
|
m_Name:
|
||||||
@ -1326,24 +1326,24 @@ MonoBehaviour:
|
|||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_ResourceFile:
|
m_ResourceFile:
|
||||||
m_AssetTreeState:
|
m_AssetTreeState:
|
||||||
scrollPos: {x: 0, y: 580}
|
scrollPos: {x: 0, y: 768}
|
||||||
m_SelectedIDs:
|
m_SelectedIDs:
|
||||||
m_LastClickedID: 0
|
m_LastClickedID: 0
|
||||||
m_ExpandedIDs: ffffffff000000007e020000fe0c0000606a0000626a0000646a0000666a0000686a00006a6a00006c6a0000706a0000726a0000746a0000766a0000786a00007a6a00007c6a00007e6a0000806a0000826a0000846a0000866a0000886a00008a6a00008c6a00008e6a0000906a0000926a0000946a0000966a0000986a00009a6a00009c6a00009e6a0000a06a0000a26a0000a46a0000a66a0000a86a0000aa6a0000ac6a0000ae6a0000b06a0000b26a0000b46a0000b66a0000b86a0000ba6a0000bc6a0000c06a0000c26a0000c46a0000c66a0000c86a0000ca6a0000cc6a0000d06a0000d26a0000d46a0000d66a0000d86a0000da6a0000dc6a0000e66a0000e86a0000ea6a0000ec6a0000ee6a0000f06a0000f26a0000f66a0000f86a0000ee6c0000fc6c00003a6d0000267500002a750000367500007c77000090770000ffffff7f
|
m_ExpandedIDs: ffffffff000000007e020000060d0000a06a0000a26a0000a46a0000a66a0000a86a0000aa6a0000ac6a0000ae6a0000b06a0000b26a0000b46a0000b66a0000b86a0000ba6a0000bc6a0000be6a0000c06a0000c26a0000c46a0000c66a0000c86a0000ca6a0000cc6a0000ce6a0000d06a0000d26a0000d46a0000d66a0000d86a0000da6a0000dc6a0000de6a0000e06a0000e26a0000e46a0000e66a0000e86a0000ea6a0000ec6a0000ee6a0000f06a0000f26a0000f46a0000f66a0000f86a0000fa6a0000fc6a0000fe6a0000006b0000026b0000046b0000066b0000086b00000a6b00000c6b00000e6b0000106b0000126b0000146b0000166b0000186b00001a6b00001c6b00001e6b0000206b0000246b0000266b0000286b00002a6b00002c6b0000306b0000326b0000346b0000366b0000386b00008c6d0000aa6d0000c86d0000d46d0000d66d0000ffffff7f
|
||||||
m_RenameOverlay:
|
m_RenameOverlay:
|
||||||
m_UserAcceptedRename: 0
|
m_UserAcceptedRename: 0
|
||||||
m_Name: ObjectPoolSystemDesign
|
m_Name:
|
||||||
m_OriginalName: ObjectPoolSystemDesign
|
m_OriginalName:
|
||||||
m_EditFieldRect:
|
m_EditFieldRect:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
width: 0
|
width: 0
|
||||||
height: 0
|
height: 0
|
||||||
m_UserData: 11722
|
m_UserData: 0
|
||||||
m_IsWaitingForDelay: 0
|
m_IsWaitingForDelay: 0
|
||||||
m_IsRenaming: 0
|
m_IsRenaming: 0
|
||||||
m_OriginalEventType: 0
|
m_OriginalEventType: 11
|
||||||
m_IsRenamingFilename: 1
|
m_IsRenamingFilename: 1
|
||||||
m_ClientGUIView: {fileID: 12}
|
m_ClientGUIView: {fileID: 12}
|
||||||
m_SearchString:
|
m_SearchString:
|
||||||
@ -1405,9 +1405,9 @@ MonoBehaviour:
|
|||||||
m_Tooltip:
|
m_Tooltip:
|
||||||
m_Pos:
|
m_Pos:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
x: 1336
|
x: 1224
|
||||||
y: 73
|
y: 73
|
||||||
width: 583
|
width: 695
|
||||||
height: 926
|
height: 926
|
||||||
m_SerializedDataModeController:
|
m_SerializedDataModeController:
|
||||||
m_DataMode: 0
|
m_DataMode: 0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user