TEngine 6

This commit is contained in:
Alex-Rachel
2025-03-07 23:09:46 +08:00
parent aad8ff3ee5
commit 551727687f
1988 changed files with 46223 additions and 94880 deletions

View File

@@ -1,79 +0,0 @@
namespace TEngine
{
/// <summary>
/// 基础LogicSys,生命周期由TEngine实现推荐给系统实现
/// 减少多余的Mono保持系统层面只有一个Update。
/// 用主Mono来驱动LogicSys的生命周期。
/// </summary>
/// <typeparam name="T">逻辑系统类型。</typeparam>
public abstract class BaseLogicSys<T> : ILogicSys where T : new()
{
private static T _instance;
public static bool HasInstance => _instance != null;
public static T Instance
{
get
{
if (null == _instance)
{
_instance = new T();
}
return _instance;
}
}
#region virtual function
public virtual bool OnInit()
{
if (null == _instance)
{
_instance = new T();
}
return true;
}
public virtual void OnStart()
{
}
public virtual void OnUpdate()
{
}
public virtual void OnLateUpdate()
{
}
public virtual void OnFixedUpdate()
{
}
public virtual void OnRoleLogin()
{
}
public virtual void OnRoleLogout()
{
}
public virtual void OnDestroy()
{
}
public virtual void OnDrawGizmos()
{
}
public virtual void OnApplicationPause(bool pause)
{
}
public virtual void OnMapChanged()
{
}
#endregion
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: fc4ce19b17fd4277951d189b66f503e2
timeCreated: 1683120353

View File

@@ -1,309 +0,0 @@
using System;
using System.Collections.Generic;
namespace TEngine
{
/// <summary>
/// 通过LogicSys来驱动且具备Unity完整生命周期的单例不继承MonoBehaviour
/// <remarks>Update、FixUpdate以及LateUpdate这些敏感帧更新需要加上对应的Attribute以最优化性能。</remarks>
/// </summary>
/// <typeparam name="T">完整生命周期的类型。</typeparam>
public abstract class BehaviourSingleton<T> : BaseBehaviourSingleton where T : BaseBehaviourSingleton, new()
{
private static T _instance;
public static T Instance
{
get
{
if (null == _instance)
{
_instance = new T();
Log.Assert(_instance != null);
_instance.Awake();
RegSingleton(_instance);
}
return _instance;
}
}
private static void RegSingleton(BaseBehaviourSingleton inst)
{
BehaviourSingleSystem.Instance.RegSingleton(inst);
}
}
#region Attribute
/// <summary>
/// 帧更新属性。
/// <remarks>适用于BehaviourSingleton。</remarks>
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class UpdateAttribute : Attribute
{
}
/// <summary>
/// 物理帧更新属性。
/// <remarks>适用于BehaviourSingleton。</remarks>
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class FixedUpdateAttribute : Attribute
{
}
/// <summary>
/// 后帧更新属性。
/// <remarks>适用于BehaviourSingleton。</remarks>
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class LateUpdateAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
public class RoleLoginAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
public class RoleLogoutAttribute : Attribute
{
}
#endregion
/// <summary>
/// 基础Behaviour单例。
/// <remarks>(抽象类)</remarks>
/// </summary>
public abstract class BaseBehaviourSingleton
{
/// <summary>
/// 是否已经Start。
/// </summary>
public bool IsStart = false;
public virtual void Awake()
{
}
public virtual void Start()
{
}
/// <summary>
/// 帧更新。
/// <remarks>需要UpdateAttribute。</remarks>
/// </summary>
public virtual void Update()
{
}
/// <summary>
/// 后帧更新。
/// <remarks>需要LateUpdateAttribute。</remarks>
/// </summary>
public virtual void LateUpdate()
{
}
/// <summary>
/// 物理帧更新。
/// <remarks>需要FixedUpdateAttribute。</remarks>
/// </summary>
public virtual void FixedUpdate()
{
}
public virtual void Destroy()
{
}
public virtual void OnPause()
{
}
public virtual void OnResume()
{
}
public virtual void OnDrawGizmos()
{
}
}
/// <summary>
/// 通过LogicSys来驱动且具备Unity完整生命周期的驱动系统不继承MonoBehaviour
/// </summary>
public sealed class BehaviourSingleSystem : BaseLogicSys<BehaviourSingleSystem>
{
private readonly List<BaseBehaviourSingleton> _listInst = new List<BaseBehaviourSingleton>();
private readonly List<BaseBehaviourSingleton> _listStart = new List<BaseBehaviourSingleton>();
private readonly List<BaseBehaviourSingleton> _listUpdate = new List<BaseBehaviourSingleton>();
private readonly List<BaseBehaviourSingleton> _listLateUpdate = new List<BaseBehaviourSingleton>();
private readonly List<BaseBehaviourSingleton> _listFixedUpdate = new List<BaseBehaviourSingleton>();
/// <summary>
/// 注册单例。
/// <remarks>调用Instance时自动调用。</remarks>
/// </summary>
/// <param name="inst">单例实例。</param>
internal void RegSingleton(BaseBehaviourSingleton inst)
{
Log.Assert(!_listInst.Contains(inst));
_listInst.Add(inst);
_listStart.Add(inst);
if (HadAttribute<UpdateAttribute>(inst.GetType()))
{
_listUpdate.Add(inst);
}
if (HadAttribute<LateUpdateAttribute>(inst.GetType()))
{
_listLateUpdate.Add(inst);
}
if (HadAttribute<FixedUpdateAttribute>(inst.GetType()))
{
_listFixedUpdate.Add(inst);
}
}
public void UnRegSingleton(BaseBehaviourSingleton inst)
{
if (inst == null)
{
Log.Error($"BaseBehaviourSingleton Is Null");
return;
}
Log.Assert(_listInst.Contains(inst));
if (_listInst.Contains(inst))
{
_listInst.Remove(inst);
}
if (_listStart.Contains(inst))
{
_listStart.Remove(inst);
}
if (_listUpdate.Contains(inst))
{
_listUpdate.Remove(inst);
}
if (_listLateUpdate.Contains(inst))
{
_listLateUpdate.Remove(inst);
}
inst.Destroy();
inst = null;
}
public override void OnUpdate()
{
var listStart = _listStart;
var listToUpdate = _listUpdate;
int count = listStart.Count;
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var inst = listStart[i];
Log.Assert(!inst.IsStart);
inst.IsStart = true;
inst.Start();
}
listStart.Clear();
}
var listUpdateCnt = listToUpdate.Count;
for (int i = 0; i < listUpdateCnt; i++)
{
var inst = listToUpdate[i];
TProfiler.BeginFirstSample(inst.GetType().FullName);
inst.Update();
TProfiler.EndFirstSample();
}
}
public override void OnLateUpdate()
{
var listLateUpdate = _listLateUpdate;
var listLateUpdateCnt = listLateUpdate.Count;
for (int i = 0; i < listLateUpdateCnt; i++)
{
var inst = listLateUpdate[i];
TProfiler.BeginFirstSample(inst.GetType().FullName);
inst.LateUpdate();
TProfiler.EndFirstSample();
}
}
public override void OnFixedUpdate()
{
var listFixedUpdate = _listFixedUpdate;
var listFixedUpdateCnt = listFixedUpdate.Count;
for (int i = 0; i < listFixedUpdateCnt; i++)
{
var inst = listFixedUpdate[i];
TProfiler.BeginFirstSample(inst.GetType().FullName);
inst.FixedUpdate();
TProfiler.EndFirstSample();
}
}
public override void OnDestroy()
{
int count = _listInst.Count;
for (int i = 0; i < count; i++)
{
var inst = _listInst[i];
inst.Destroy();
}
}
public override void OnApplicationPause(bool pause)
{
int count = _listInst.Count;
for (int i = 0; i < count; i++)
{
var inst = _listInst[i];
if (pause)
{
inst.OnPause();
}
else
{
inst.OnResume();
}
}
}
public override void OnDrawGizmos()
{
int count = _listInst.Count;
for (int i = 0; i < count; i++)
{
var inst = _listInst[i];
inst.OnDrawGizmos();
}
}
private bool HadAttribute<T>(Type type) where T : Attribute
{
T attribute = Attribute.GetCustomAttribute(type, typeof(T)) as T;
return attribute != null;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5c0e9c1c8c9d4ce99a1c991fb62a0256
timeCreated: 1683120460

View File

@@ -1,16 +0,0 @@
{
"name": "GameBase",
"rootNamespace": "GameBase",
"references": [
"GUID:24c092aee38482f4e80715eaa8148782"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -1,58 +0,0 @@
/// <summary>
/// 定义通用的逻辑接口,统一生命期调用
/// </summary>
public interface ILogicSys
{
/// <summary>
/// 初始化接口
/// </summary>
/// <returns></returns>
bool OnInit();
/// <summary>
/// 销毁系统
/// </summary>
void OnDestroy();
/// <summary>
/// 初始化后,第一帧统一调用
/// </summary>
void OnStart();
/// <summary>
/// 更新接口
/// </summary>
/// <returns></returns>
void OnUpdate();
/// <summary>
/// 渲染后调用
/// </summary>
void OnLateUpdate();
/// <summary>
/// 物理帧更新
/// </summary>
void OnFixedUpdate();
/// <summary>
/// 登录账号/角色时调用
/// </summary>
void OnRoleLogin();
/// <summary>
/// 清理数据接口,切换账号/角色时调用
/// </summary>
void OnRoleLogout();
/// <summary>
/// 绘制调试接口
/// </summary>
void OnDrawGizmos();
/// <summary>
/// 暂停游戏
/// </summary>
/// <param name="pause"></param>
void OnApplicationPause(bool pause);
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 63ff6535a43f41d7ac793b8a153a37b6
timeCreated: 1681213932

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: b68a449df312429cbb27873984ec238e
timeCreated: 1681214042

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: bc365e281d234e61891bf9f922a0897a
timeCreated: 1715574965

View File

@@ -1,141 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace GameBase
{
public interface ISingleton
{
/// <summary>
/// 激活接口,通常用于在某个时机手动实例化
/// </summary>
void Active();
/// <summary>
/// 释放接口
/// </summary>
void Release();
}
/// <summary>
/// 框架中的全局对象与Unity场景依赖相关的DontDestroyOnLoad需要统一管理方便重启游戏时清除工作
/// </summary>
public static class SingletonSystem
{
private static List<ISingleton> _singletons;
private static Dictionary<string, GameObject> _gameObjects;
public static void Retain(ISingleton go)
{
if (_singletons == null)
{
_singletons = new List<ISingleton>();
}
_singletons.Add(go);
}
public static void Retain(GameObject go)
{
if (_gameObjects == null)
{
_gameObjects = new Dictionary<string, GameObject>();
}
if (_gameObjects.TryAdd(go.name, go))
{
if (Application.isPlaying)
{
Object.DontDestroyOnLoad(go);
}
}
}
public static void Release(GameObject go)
{
if (_gameObjects != null && _gameObjects.ContainsKey(go.name))
{
_gameObjects.Remove(go.name);
Object.Destroy(go);
}
}
public static void Release(ISingleton go)
{
if (_singletons != null && _singletons.Contains(go))
{
_singletons.Remove(go);
}
}
public static void Release()
{
if (_gameObjects != null)
{
foreach (var item in _gameObjects)
{
Object.Destroy(item.Value);
}
_gameObjects.Clear();
}
if (_singletons != null)
{
for (int i = _singletons.Count -1; i >= 0; i--)
{
_singletons[i].Release();
}
_singletons.Clear();
}
Resources.UnloadUnusedAssets();
}
public static GameObject GetGameObject(string name)
{
GameObject go = null;
if (_gameObjects != null)
{
_gameObjects.TryGetValue(name, out go);
}
return go;
}
internal static bool ContainsKey(string name)
{
if (_gameObjects != null)
{
return _gameObjects.ContainsKey(name);
}
return false;
}
public static void Restart()
{
if (Camera.main != null)
{
Camera.main.gameObject.SetActive(false);
}
Release();
SceneManager.LoadScene(0);
}
internal static ISingleton GetSingleton(string name)
{
for (int i = 0; i < _singletons.Count; ++i)
{
if (_singletons[i].ToString() == name)
{
return _singletons[i];
}
}
return null;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 4ad00b596d0743a4b04591fe52087d0f
timeCreated: 1715574577

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3026f5e2510440618f6d4f28b37c5244
timeCreated: 1695289286

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 40a878a415f34e7a855fc4916bbb8e6b
timeCreated: 1702479104

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1dcaa491f139438dbd963d8bbf0dba85
timeCreated: 1702385397

View File

@@ -1,9 +0,0 @@
using System;
namespace GameLogic
{
[AttributeUsage(AttributeTargets.Class)]
public class BaseAttribute: Attribute
{
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 819c4eaddddd4646a100da2e3f19c3c7
timeCreated: 1702385397

View File

@@ -1,75 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace GameLogic
{
public class CodeTypes
{
private static CodeTypes _instance;
public static CodeTypes Instance => _instance ??= new CodeTypes();
private readonly Dictionary<string, Type> _allTypes = new();
private readonly UnOrderMultiMapSet<Type, Type> _types = new();
public void Init(Assembly[] assemblies)
{
Dictionary<string, Type> addTypes = GetAssemblyTypes(assemblies);
foreach ((string fullName, Type type) in addTypes)
{
_allTypes[fullName] = type;
if (type.IsAbstract)
{
continue;
}
// 记录所有的有BaseAttribute标记的的类型
object[] objects = type.GetCustomAttributes(typeof(BaseAttribute), true);
foreach (object o in objects)
{
_types.Add(o.GetType(), type);
}
}
}
public HashSet<Type> GetTypes(Type systemAttributeType)
{
if (!_types.ContainsKey(systemAttributeType))
{
return new HashSet<Type>();
}
return _types[systemAttributeType];
}
public Dictionary<string, Type> GetTypes()
{
return _allTypes;
}
public Type GetType(string typeName)
{
return _allTypes[typeName];
}
public static Dictionary<string, Type> GetAssemblyTypes(params Assembly[] args)
{
Dictionary<string, Type> types = new Dictionary<string, Type>();
foreach (Assembly ass in args)
{
foreach (Type type in ass.GetTypes())
{
if (type.FullName != null)
{
types[type.FullName] = type;
}
}
}
return types;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 01fdfc4515314c579523ac3716005210
timeCreated: 1702385429

View File

@@ -1,80 +0,0 @@
using System.Collections.Generic;
namespace GameLogic
{
public class UnOrderMultiMapSet<TKey, TValue>: Dictionary<TKey, HashSet<TValue>>
{
public new HashSet<TValue> this[TKey t]
{
get
{
HashSet<TValue> set;
if (!TryGetValue(t, out set))
{
set = new HashSet<TValue>();
}
return set;
}
}
public Dictionary<TKey, HashSet<TValue>> GetDictionary()
{
return this;
}
public void Add(TKey t, TValue k)
{
HashSet<TValue> set;
TryGetValue(t, out set);
if (set == null)
{
set = new HashSet<TValue>();
base[t] = set;
}
set.Add(k);
}
public bool Remove(TKey t, TValue k)
{
HashSet<TValue> set;
TryGetValue(t, out set);
if (set == null)
{
return false;
}
if (!set.Remove(k))
{
return false;
}
if (set.Count == 0)
{
Remove(t);
}
return true;
}
public bool Contains(TKey t, TValue k)
{
HashSet<TValue> set;
TryGetValue(t, out set);
if (set == null)
{
return false;
}
return set.Contains(k);
}
public new int Count
{
get
{
int count = 0;
foreach (KeyValuePair<TKey,HashSet<TValue>> kv in this)
{
count += kv.Value.Count;
}
return count;
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: b798f0c1317c4caf9ace168f07b51d4f
timeCreated: 1702385485

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d1f693ff76ae490fbe194855d94e8266
timeCreated: 1702479172

View File

@@ -1,21 +0,0 @@
using System;
using TEngine;
namespace GameLogic
{
/// <summary>
/// 事件接口帮助类。
/// </summary>
internal class EventInterfaceHelper
{
/// <summary>
/// 初始化。
/// </summary>
public static void Init()
{
GameEvent.EventMgr.Init();
RegisterEventInterface_Logic.Register(GameEvent.EventMgr);
RegisterEventInterface_UI.Register(GameEvent.EventMgr);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 9afaf331ee7249adb5cc0953dfd3413c
timeCreated: 1702379658

View File

@@ -1,16 +0,0 @@
using TEngine;
namespace GameLogic
{
[System.AttributeUsage(System.AttributeTargets.Class)]
internal class EventInterfaceImpAttribute : BaseAttribute
{
private EEventGroup _eGroup;
public EEventGroup EventGroup => _eGroup;
public EventInterfaceImpAttribute(EEventGroup group)
{
_eGroup = group;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8bbf40942b0e4470bb8d8a82577f713c
timeCreated: 1702479403

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: de49bf2e9f0a4fac85851a582e2fb4ed
timeCreated: 1702379835

View File

@@ -1,70 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by autoBindTool.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
using TEngine;
namespace GameLogic
{
public partial class IActorLogicEvent_Event
{
public static readonly int OnMainPlayerDataChange = RuntimeId.ToRuntimeId("IActorLogicEvent_Event.OnMainPlayerDataChange");
public static readonly int OnMainPlayerLevelChange = RuntimeId.ToRuntimeId("IActorLogicEvent_Event.OnMainPlayerLevelChange");
public static readonly int OnMainPlayerGoldChange = RuntimeId.ToRuntimeId("IActorLogicEvent_Event.OnMainPlayerGoldChange");
public static readonly int OnMainPlayerDiamondChange = RuntimeId.ToRuntimeId("IActorLogicEvent_Event.OnMainPlayerDiamondChange");
public static readonly int OnMainPlayerBindDiamondChange = RuntimeId.ToRuntimeId("IActorLogicEvent_Event.OnMainPlayerBindDiamondChange");
public static readonly int OnMainPlayerCurrencyChange = RuntimeId.ToRuntimeId("IActorLogicEvent_Event.OnMainPlayerCurrencyChange");
public static readonly int OnMainPlayerExpChange = RuntimeId.ToRuntimeId("IActorLogicEvent_Event.OnMainPlayerExpChange");
}
[EventInterfaceImp(EEventGroup.GroupLogic)]
public partial class IActorLogicEvent_Gen : IActorLogicEvent
{
private EventDispatcher _dispatcher;
public IActorLogicEvent_Gen(EventDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public void OnMainPlayerDataChange()
{
_dispatcher.Send(IActorLogicEvent_Event.OnMainPlayerDataChange);
}
public void OnMainPlayerLevelChange()
{
_dispatcher.Send(IActorLogicEvent_Event.OnMainPlayerLevelChange);
}
public void OnMainPlayerGoldChange(System.UInt32 oldVal,System.UInt32 newVal)
{
_dispatcher.Send(IActorLogicEvent_Event.OnMainPlayerGoldChange,oldVal,newVal);
}
public void OnMainPlayerDiamondChange(System.UInt32 oldVal,System.UInt32 newVal)
{
_dispatcher.Send(IActorLogicEvent_Event.OnMainPlayerDiamondChange,oldVal,newVal);
}
public void OnMainPlayerBindDiamondChange(System.UInt32 oldVal,System.UInt32 newVal)
{
_dispatcher.Send(IActorLogicEvent_Event.OnMainPlayerBindDiamondChange,oldVal,newVal);
}
public void OnMainPlayerCurrencyChange(GameLogic.CurrencyType type,System.UInt32 oldVal,System.UInt32 newVal)
{
_dispatcher.Send(IActorLogicEvent_Event.OnMainPlayerCurrencyChange,type,oldVal,newVal);
}
public void OnMainPlayerExpChange(System.UInt64 oldVal,System.UInt64 newVal)
{
_dispatcher.Send(IActorLogicEvent_Event.OnMainPlayerExpChange,oldVal,newVal);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 12d7d4edd7d06bc4286ea4af153380c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 615ca01c7a524654c91935631f39f570
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,40 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by autoBindTool.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
using TEngine;
namespace GameLogic
{
public partial class ILoginUI_Event
{
public static readonly int OnRoleLogin = RuntimeId.ToRuntimeId("ILoginUI_Event.OnRoleLogin");
public static readonly int OnRoleLoginOut = RuntimeId.ToRuntimeId("ILoginUI_Event.OnRoleLoginOut");
}
[EventInterfaceImp(EEventGroup.GroupUI)]
public partial class ILoginUI_Gen : ILoginUI
{
private EventDispatcher _dispatcher;
public ILoginUI_Gen(EventDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public void OnRoleLogin(System.Boolean isReconnect)
{
_dispatcher.Send(ILoginUI_Event.OnRoleLogin,isReconnect);
}
public void OnRoleLoginOut()
{
_dispatcher.Send(ILoginUI_Event.OnRoleLoginOut);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 7cf3381dedbf4daeb53e710a5c544204
timeCreated: 1702433587

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 610229edeca4417685ffd07f18b2b9f1
timeCreated: 1702379817

View File

@@ -1,38 +0,0 @@
using System;
using TEngine;
namespace GameLogic
{
/// <summary>
/// 示例货币枚举。
/// </summary>
public enum CurrencyType
{
None,
Gold,
Diamond,
}
/// <summary>
/// 示意逻辑层事件。
/// <remarks> 优化抛出事件,通过接口约束事件参数。</remarks>
/// <remarks> example: GameEvent.Get<IActorLogicEvent>().OnMainPlayerCurrencyChange(CurrencyType.Gold,oldVal,newVal); </remarks>
/// </summary>
[EventInterface(EEventGroup.GroupLogic)]
interface IActorLogicEvent
{
void OnMainPlayerDataChange();
void OnMainPlayerLevelChange();
void OnMainPlayerGoldChange(uint oldVal, uint newVal);
void OnMainPlayerDiamondChange(uint oldVal, uint newVal);
void OnMainPlayerBindDiamondChange(uint oldVal, uint newVal);
void OnMainPlayerCurrencyChange(CurrencyType type, uint oldVal, uint newVal);
void OnMainPlayerExpChange(ulong oldVal, ulong newVal);
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8d4558cb74e8462a86f0ee3461f6b7c9
timeCreated: 1702383645

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 90e13cc92c5d42f28b4f5fab599f472a
timeCreated: 1702379805

View File

@@ -1,17 +0,0 @@
using TEngine;
namespace GameLogic
{
/// <summary>
/// 示意UI层事件。
/// <remarks> 优化抛出事件,通过接口约束事件参数。</remarks>
/// <remarks> example: GameEvent.Get<ILoginUI>().OnRoleLogin(isReconnect); </remarks>
/// </summary>
[EventInterface(EEventGroup.GroupUI)]
public interface ILoginUI
{
public void OnRoleLogin(bool isReconnect);
public void OnRoleLoginOut();
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 33b45e62bd3447498acfe874017b9a35
timeCreated: 1702433755

View File

@@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using TEngine;
namespace GameLogic
{
/// <summary>
/// 逻辑层事件接口。
/// </summary>
internal class RegisterEventInterface_Logic
{
/// <summary>
/// 注册逻辑层事件接口。
/// </summary>
/// <param name="mgr">事件管理器。</param>
public static void Register(EventMgr mgr)
{
HashSet<Type> types = CodeTypes.Instance.GetTypes(typeof(EventInterfaceImpAttribute));
foreach (Type type in types)
{
object[] attrs = type.GetCustomAttributes(typeof(EventInterfaceImpAttribute), false);
if (attrs.Length == 0)
{
continue;
}
EventInterfaceImpAttribute httpHandlerAttribute = (EventInterfaceImpAttribute)attrs[0];
if (httpHandlerAttribute.EventGroup != EEventGroup.GroupLogic)
{
continue;
}
object obj = Activator.CreateInstance(type, mgr.Dispatcher);
mgr.RegWrapInterface(obj.GetType().GetInterfaces()[0]?.FullName, obj);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: b8bdf6c139b44758aa16db2e1837f5d9
timeCreated: 1702379518

View File

@@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using TEngine;
namespace GameLogic
{
/// <summary>
/// UI层事件接口。
/// </summary>
internal class RegisterEventInterface_UI
{
/// <summary>
/// 注册UI层事件接口。
/// </summary>
/// <param name="mgr">事件管理器。</param>
public static void Register(EventMgr mgr)
{
HashSet<Type> types = CodeTypes.Instance.GetTypes(typeof(EventInterfaceImpAttribute));
foreach (Type type in types)
{
object[] attrs = type.GetCustomAttributes(typeof(EventInterfaceImpAttribute), false);
if (attrs.Length == 0)
{
continue;
}
EventInterfaceImpAttribute httpHandlerAttribute = (EventInterfaceImpAttribute)attrs[0];
if (httpHandlerAttribute.EventGroup != EEventGroup.GroupUI)
{
continue;
}
object obj = Activator.CreateInstance(type, mgr.Dispatcher);
mgr.RegWrapInterface(obj.GetType().GetInterfaces()[0]?.FullName, obj);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f53b67f2cfbe4912bffee9593cd60970
timeCreated: 1702379505

View File

@@ -1,159 +1,39 @@
using System.Collections.Generic;
using System.Reflection;
using GameBase;
using GameLogic;
using TEngine;
using UnityEngine.Networking;
/// <summary>
/// 游戏App。
/// </summary>
public partial class GameApp:Singleton<GameApp>
public partial class GameApp
{
private static List<Assembly> _hotfixAssembly;
/// <summary>
/// 热更域App主入口。
/// </summary>
/// <param name="objects"></param>
public static void Entrance(object[] objects)
{
GameEventHelper.Init();
_hotfixAssembly = (List<Assembly>)objects[0];
Log.Warning("======= 看到此条日志代表你成功运行了热更新代码 =======");
Log.Warning("======= Entrance GameApp =======");
Instance.Active();
Instance.Start();
Utility.Unity.AddUpdateListener(Instance.Update);
Utility.Unity.AddFixedUpdateListener(Instance.FixedUpdate);
Utility.Unity.AddLateUpdateListener(Instance.LateUpdate);
Utility.Unity.AddDestroyListener(Instance.OnDestroy);
Utility.Unity.AddOnDrawGizmosListener(Instance.OnDrawGizmos);
Utility.Unity.AddOnApplicationPauseListener(Instance.OnApplicationPause);
GameModule.Procedure.RestartProcedure(new GameLogic.OnEnterGameAppProcedure());
Instance.StartGameLogic();
ModuleSystem.GetModule<IUpdateDriver>().AddDestroyListener(Release);
StartGameLogic();
}
/// <summary>
/// 开始游戏业务层逻辑。
/// <remarks>显示UI、加载场景等。</remarks>
/// </summary>
private void StartGameLogic()
private static void StartGameLogic()
{
GameEvent.Get<ILoginUI>().ShowLoginUI(NetworkError.Ok);
GameModule.UI.ShowUIAsync<BattleMainUI>();
}
/// <summary>
/// 关闭游戏。
/// </summary>
/// <param name="shutdownType">关闭游戏框架类型。</param>
public static void Shutdown(ShutdownType shutdownType)
private static void Release()
{
Log.Info("GameApp Shutdown");
if (shutdownType == ShutdownType.None)
{
return;
}
if (shutdownType == ShutdownType.Restart)
{
Utility.Unity.RemoveUpdateListener(Instance.Update);
Utility.Unity.RemoveFixedUpdateListener(Instance.FixedUpdate);
Utility.Unity.RemoveLateUpdateListener(Instance.LateUpdate);
Utility.Unity.RemoveDestroyListener(Instance.OnDestroy);
Utility.Unity.RemoveOnDrawGizmosListener(Instance.OnDrawGizmos);
Utility.Unity.RemoveOnApplicationPauseListener(Instance.OnApplicationPause);
}
SingletonSystem.Release();
}
private void Start()
{
var listLogic = _listLogicMgr;
var logicCnt = listLogic.Count;
for (int i = 0; i < logicCnt; i++)
{
var logic = listLogic[i];
logic.OnStart();
}
}
private void Update()
{
TProfiler.BeginFirstSample("Update");
var listLogic = _listLogicMgr;
var logicCnt = listLogic.Count;
for (int i = 0; i < logicCnt; i++)
{
var logic = listLogic[i];
TProfiler.BeginSample(logic.GetType().FullName);
logic.OnUpdate();
TProfiler.EndSample();
}
TProfiler.EndFirstSample();
}
private void FixedUpdate()
{
TProfiler.BeginFirstSample("FixedUpdate");
var listLogic = _listLogicMgr;
var logicCnt = listLogic.Count;
for (int i = 0; i < logicCnt; i++)
{
var logic = listLogic[i];
TProfiler.BeginSample(logic.GetType().FullName);
logic.OnFixedUpdate();
TProfiler.EndSample();
}
TProfiler.EndFirstSample();
}
private void LateUpdate()
{
TProfiler.BeginFirstSample("LateUpdate");
var listLogic = _listLogicMgr;
var logicCnt = listLogic.Count;
for (int i = 0; i < logicCnt; i++)
{
var logic = listLogic[i];
TProfiler.BeginSample(logic.GetType().FullName);
logic.OnLateUpdate();
TProfiler.EndSample();
}
TProfiler.EndFirstSample();
}
private void OnDestroy()
{
var listLogic = _listLogicMgr;
var logicCnt = listLogic.Count;
for (int i = 0; i < logicCnt; i++)
{
var logic = listLogic[i];
logic.OnDestroy();
}
Shutdown(ShutdownType.Restart);
}
private void OnDrawGizmos()
{
#if UNITY_EDITOR
var listLogic = _listLogicMgr;
var logicCnt = listLogic.Count;
for (int i = 0; i < logicCnt; i++)
{
var logic = listLogic[i];
logic.OnDrawGizmos();
}
#endif
}
private void OnApplicationPause(bool isPause)
{
var listLogic = _listLogicMgr;
var logicCnt = listLogic.Count;
for (int i = 0; i < logicCnt; i++)
{
var logic = listLogic[i];
logic.OnApplicationPause(isPause);
}
Log.Warning("======= Release GameApp =======");
}
}

View File

@@ -1,59 +0,0 @@
using System.Collections.Generic;
using GameBase;
using GameLogic;
using TEngine;
public partial class GameApp : Singleton<GameApp>
{
private List<ILogicSys> _listLogicMgr;
public override void Active()
{
CodeTypes.Instance.Init(_hotfixAssembly.ToArray());
EventInterfaceHelper.Init();
_listLogicMgr = new List<ILogicSys>();
RegisterAllSystem();
InitSystemSetting();
}
/// <summary>
/// 设置一些通用的系统属性。
/// </summary>
private void InitSystemSetting()
{
}
/// <summary>
/// 注册所有逻辑系统
/// </summary>
private void RegisterAllSystem()
{
//带生命周期的单例系统。
AddLogicSys(BehaviourSingleSystem.Instance);
}
/// <summary>
/// 注册逻辑系统。
/// </summary>
/// <param name="logicSys">ILogicSys</param>
/// <returns></returns>
private bool AddLogicSys(ILogicSys logicSys)
{
if (_listLogicMgr.Contains(logicSys))
{
Log.Fatal("Repeat add logic system: {0}", logicSys.GetType().Name);
return false;
}
if (!logicSys.OnInit())
{
Log.Fatal("{0} Init failed", logicSys.GetType().Name);
return false;
}
_listLogicMgr.Add(logicSys);
return true;
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 44cc95ba54fa4fd49012f55199ddd907
timeCreated: 1695288685

View File

@@ -3,11 +3,14 @@
"rootNamespace": "GameLogic",
"references": [
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:cbb0d51b565003841ae81cdbaf747114",
"GUID:8f58f15387c7a6f4fad9857024eb47f7",
"GUID:24c092aee38482f4e80715eaa8148782",
"GUID:af760644fe07b7945b1afa1ccb7cff14",
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:e34a5702dd353724aa315fb8011f08c3"
"GUID:e34a5702dd353724aa315fb8011f08c3",
"GUID:47f9fc774596be54ebfed7739cd70c86",
"GUID:1aa3e8589868c80499255710874679c0",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:756335c0388f7114790e504ed368ae1d"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -0,0 +1,111 @@
using GameLogic;
using TEngine;
using Object = UnityEngine.Object;
public class GameModule
{
#region
/// <summary>
/// 获取游戏基础模块。
/// </summary>
public static RootModule Base
{
get => _base ??= Object.FindObjectOfType<RootModule>();
private set => _base = value;
}
private static RootModule _base;
/// <summary>
/// 获取调试模块。
/// </summary>
public static IDebuggerModule Debugger
{
get => _debugger ??= Get<IDebuggerModule>();
private set => _debugger = value;
}
private static IDebuggerModule _debugger;
/// <summary>
/// 获取有限状态机模块。
/// </summary>
public static IFsmModule Fsm => _fsm ??= Get<IFsmModule>();
private static IFsmModule _fsm;
/// <summary>
/// 流程管理模块。
/// </summary>
public static IProcedureModule Procedure => _procedure ??= Get<IProcedureModule>();
private static IProcedureModule _procedure;
/// <summary>
/// 获取资源模块。
/// </summary>
public static IResourceModule Resource => _resource ??= Get<IResourceModule>();
private static IResourceModule _resource;
/// <summary>
/// 获取音频模块。
/// </summary>
public static IAudioModule Audio => _audio ??= Get<IAudioModule>();
private static IAudioModule _audio;
/// <summary>
/// 获取UI模块。
/// </summary>
public static UIModule UI => _ui ??= UIModule.Instance;
private static UIModule _ui;
/// <summary>
/// 获取场景模块。
/// </summary>
public static ISceneModule Scene => _scene ??= Get<ISceneModule>();
private static ISceneModule _scene;
/// <summary>
/// 获取计时器模块。
/// </summary>
public static ITimerModule Timer => _timer ??= Get<ITimerModule>();
private static ITimerModule _timer;
#endregion
/// <summary>
/// 获取游戏框架模块类。
/// </summary>
/// <typeparam name="T">游戏框架模块类。</typeparam>
/// <returns>游戏框架模块实例。</returns>
private static T Get<T>() where T : class
{
T module = ModuleSystem.GetModule<T>();
Log.Assert(condition: module != null, $"{typeof(T)} is null");
return module;
}
public static void Shutdown()
{
Log.Info("GameModule Shutdown");
_base = null;
_debugger = null;
_fsm = null;
_procedure = null;
_resource = null;
_audio = null;
_ui = null;
_scene = null;
_timer = null;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 45e088b7b85a4090badd937f497d9a54
timeCreated: 1741268386

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 80ad446616504117a54aa052e3859c56
timeCreated: 1741357387

View File

@@ -0,0 +1,12 @@
using TEngine;
namespace GameLogic
{
[EventInterface(EEventGroup.GroupUI)]
public interface ILoginUI
{
void ShowLoginUI(UnityEngine.Networking.NetworkError tNetworkError);
void CloseLoginUI();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 767a914650f74a009cd9b75a91ceb848
timeCreated: 1741357395

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 56c0a6f4a9654e5181e863120b2debd1
timeCreated: 1730998610

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6635aa4e94cc66546b8b89c48f0a0099
guid: 1c2c045781eb1824e897633e0984ce8d
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ef0df61f4a790a34198c00018eae3e21
guid: 7bb4592c3b9cea842bebcb566fb64a07
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,30 @@
using System;
using UnityEngine;
namespace GameLogic
{
public class ErrorLogger : IDisposable
{
private readonly UIModule _uiModule;
public ErrorLogger(UIModule uiModule)
{
_uiModule = uiModule;
Application.logMessageReceived += LogHandler;
}
public void Dispose()
{
Application.logMessageReceived -= LogHandler;
}
private void LogHandler(string condition, string stacktrace, LogType type)
{
if (type == LogType.Exception)
{
string des = $"客户端报错, \n#内容#---{condition} \n#位置#---{stacktrace}";
_uiModule.ShowUIAsync<LogUI>(des);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9ccc9fc5faf14d93baec285acaa97907
timeCreated: 1681807720

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine.UI;
namespace GameLogic
{
[Window(UILayer.System, fromResources: true)]
class LogUI : UIWindow
{
private readonly Stack<string> _errorTextString = new Stack<string>();
#region
private Text m_textError;
private Button m_btnClose;
protected override void ScriptGenerator()
{
m_textError = FindChildComponent<Text>("m_textError");
m_btnClose = FindChildComponent<Button>("m_btnClose");
m_btnClose.onClick.AddListener(OnClickCloseBtn);
}
#endregion
#region
private void OnClickCloseBtn()
{
PopErrorLog().Forget();
}
#endregion
protected override void OnRefresh()
{
_errorTextString.Push(UserData.ToString());
m_textError.text = UserData.ToString();
}
private async UniTaskVoid PopErrorLog()
{
if (_errorTextString.Count <= 0)
{
await UniTask.Yield();
Close();
return;
}
string error = _errorTextString.Pop();
m_textError.text = error;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: faa647c543e54de9b2d55e189aef0eff
timeCreated: 1681807986

View File

@@ -0,0 +1,69 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using TEngine;
using UnityEngine;
namespace GameLogic
{
/// <summary>
/// UI资源加载器接口。
/// </summary>
public interface IUIResourceLoader
{
/// <summary>
/// 同步加载游戏物体并实例化。
/// </summary>
/// <param name="location">资源的定位地址。</param>
/// <param name="parent">资源实例父节点。</param>
/// <param name="packageName">指定资源包的名称。不传使用默认资源包</param>
/// <returns>资源实例。</returns>
/// <remarks>会实例化资源到场景无需主动UnloadAssetDestroy时自动UnloadAsset。</remarks>
public GameObject LoadGameObject(string location, Transform parent = null, string packageName = "");
/// <summary>
/// 异步加载游戏物体并实例化。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">资源实例父节点。</param>
/// <param name="cancellationToken">取消操作Token。</param>
/// <param name="packageName">指定资源包的名称。不传使用默认资源包</param>
/// <returns>异步游戏物体实例。</returns>
/// <remarks>会实例化资源到场景无需主动UnloadAssetDestroy时自动UnloadAsset。</remarks>
public UniTask<GameObject> LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = "");
}
/// <summary>
/// 默认UI资源加载器。
/// </summary>
public class UIResourceLoader : IUIResourceLoader
{
private readonly IResourceModule m_ResourceLoaderImp = ModuleSystem.GetModule<IResourceModule>();
/// <summary>
/// 同步加载游戏物体并实例化。
/// </summary>
/// <param name="location">资源的定位地址。</param>
/// <param name="parent">资源实例父节点。</param>
/// <param name="packageName">指定资源包的名称。不传使用默认资源包</param>
/// <returns>资源实例。</returns>
/// <remarks>会实例化资源到场景无需主动UnloadAssetDestroy时自动UnloadAsset。</remarks>
public GameObject LoadGameObject(string location, Transform parent = null, string packageName = "")
{
return m_ResourceLoaderImp.LoadGameObject(location, parent, packageName);
}
/// <summary>
/// 异步加载游戏物体并实例化。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">资源实例父节点。</param>
/// <param name="cancellationToken">取消操作Token。</param>
/// <param name="packageName">指定资源包的名称。不传使用默认资源包</param>
/// <returns>异步游戏物体实例。</returns>
/// <remarks>会实例化资源到场景无需主动UnloadAssetDestroy时自动UnloadAsset。</remarks>
public async UniTask<GameObject> LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = "")
{
return await m_ResourceLoaderImp.LoadGameObjectAsync(location, parent, cancellationToken, packageName);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 16dc9f62ef87425db617e33106a49a05
timeCreated: 1735213496

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: bff68b49afffbe54b9d5ff4e4cad4f23
guid: e3fe9f1f232445443bb3a05918c700af
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,356 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2683445925571829816
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2683445925571829817}
- component: {fileID: 2683445925571829820}
- component: {fileID: 2683445925571829822}
m_Layer: 5
m_Name: LogUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2683445925571829817
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445925571829816}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2683445927021212027}
- {fileID: 2683445926897574365}
- {fileID: 6164213652418733423}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!223 &2683445925571829820
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445925571829816}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!114 &2683445925571829822
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445925571829816}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!1 &2683445926897574364
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2683445926897574365}
- component: {fileID: 2683445926897574339}
- component: {fileID: 2683445926897574338}
m_Layer: 5
m_Name: m_textError
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2683445926897574365
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445926897574364}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2683445925571829817}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: -10}
m_SizeDelta: {x: -40, y: -60}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2683445926897574339
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445926897574364}
m_CullTransparentMesh: 1
--- !u!114 &2683445926897574338
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445926897574364}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 1, b: 0.25490198, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 24
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 2
m_MaxSize: 40
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text:
--- !u!1 &2683445927021212026
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2683445927021212027}
- component: {fileID: 2683445927021212025}
- component: {fileID: 2683445927021212024}
m_Layer: 5
m_Name: Panel
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2683445927021212027
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445927021212026}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2683445925571829817}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2683445927021212025
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445927021212026}
m_CullTransparentMesh: 1
--- !u!114 &2683445927021212024
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2683445927021212026}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0.34901962}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &4229713281919198382
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6164213652418733423}
- component: {fileID: 9023329926868908831}
- component: {fileID: 5643637730633076069}
- component: {fileID: 6030240422437967231}
m_Layer: 5
m_Name: m_btnClose
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6164213652418733423
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4229713281919198382}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2683445925571829817}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &9023329926868908831
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4229713281919198382}
m_CullTransparentMesh: 1
--- !u!114 &5643637730633076069
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4229713281919198382}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0.007843138}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &6030240422437967231
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4229713281919198382}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 5643637730633076069}
m_OnClick:
m_PersistentCalls:
m_Calls: []

View File

@@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: cbb0d51b565003841ae81cdbaf747114
AssemblyDefinitionImporter:
guid: 4b00e982fe4450248b4c7b9b62148cb2
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:

View File

@@ -0,0 +1,563 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using TEngine;
using UnityEngine;
namespace GameLogic
{
/// <summary>
/// UI基类。
/// </summary>
public class UIBase
{
/// <summary>
/// UI类型。
/// </summary>
public enum UIType
{
/// <summary>
/// 类型无。
/// </summary>
None,
/// <summary>
/// 类型Windows。
/// </summary>
Window,
/// <summary>
/// 类型Widget。
/// </summary>
Widget,
}
/// <summary>
/// 所属UI父节点。
/// </summary>
protected UIBase parent = null;
/// <summary>
/// UI父节点。
/// </summary>
public UIBase Parent => parent;
/// <summary>
/// 自定义数据集。
/// </summary>
protected System.Object[] userDatas;
/// <summary>
/// 自定义数据。
/// </summary>
public System.Object UserData
{
get
{
if (userDatas != null && userDatas.Length >= 1)
{
return userDatas[0];
}
else
{
return null;
}
}
}
/// <summary>
/// 自定义数据集。
/// </summary>
public System.Object[] UserDatas => userDatas;
/// <summary>
/// 窗口的实例资源对象。
/// </summary>
public virtual GameObject gameObject { protected set; get; }
/// <summary>
/// 窗口位置组件。
/// </summary>
public virtual Transform transform { protected set; get; }
/// <summary>
/// 窗口矩阵位置组件。
/// </summary>
public virtual RectTransform rectTransform { protected set; get; }
/// <summary>
/// UI类型。
/// </summary>
public virtual UIType Type => UIType.None;
/// <summary>
/// 资源是否准备完毕。
/// </summary>
public bool IsPrepare { protected set; get; }
/// <summary>
/// UI子组件列表。
/// </summary>
public List<UIWidget> ListChild = new List<UIWidget>();
/// <summary>
/// 存在Update更新的UI子组件列表。
/// </summary>
protected List<UIWidget> m_listUpdateChild = null;
/// <summary>
/// 是否持有Update行为。
/// </summary>
protected bool m_updateListValid = false;
/// <summary>
/// 代码自动生成绑定。
/// </summary>
protected virtual void ScriptGenerator()
{
}
/// <summary>
/// 绑定UI成员元素。
/// </summary>
protected virtual void BindMemberProperty()
{
}
/// <summary>
/// 注册事件。
/// </summary>
protected virtual void RegisterEvent()
{
}
/// <summary>
/// 窗口创建。
/// </summary>
protected virtual void OnCreate()
{
}
/// <summary>
/// 窗口刷新
/// </summary>
protected virtual void OnRefresh()
{
}
/// <summary>
/// 是否需要Update
/// </summary>
protected bool HasOverrideUpdate = true;
/// <summary>
/// 窗口更新
/// </summary>
protected virtual void OnUpdate()
{
HasOverrideUpdate = false;
}
internal void CallDestroy()
{
OnDestroy();
}
/// <summary>
/// 窗口销毁
/// </summary>
protected virtual void OnDestroy()
{
}
/// <summary>
/// 当触发窗口的层级排序。
/// </summary>
protected virtual void OnSortDepth(int depth)
{
}
/// <summary>
/// 当因为全屏遮挡触或者窗口可见性触发窗口的显隐。
/// </summary>
protected virtual void OnSetVisible(bool visible)
{
}
internal void SetUpdateDirty()
{
m_updateListValid = false;
if (Parent != null)
{
Parent.SetUpdateDirty();
}
}
#region FindChildComponent
public Transform FindChild(string path)
{
return FindChildImp(rectTransform, path);
}
public Transform FindChild(Transform trans, string path)
{
return FindChildImp(trans, path);
}
public T FindChildComponent<T>(string path) where T : Component
{
return FindChildComponentImp<T>(rectTransform, path);
}
public T FindChildComponent<T>(Transform trans, string path) where T : Component
{
return FindChildComponentImp<T>(trans, path);
}
private static Transform FindChildImp(Transform transform, string path)
{
var findTrans = transform.Find(path);
return findTrans != null ? findTrans : null;
}
private static T FindChildComponentImp<T>(Transform transform, string path) where T : Component
{
var findTrans = transform.Find(path);
if (findTrans != null)
{
return findTrans.gameObject.GetComponent<T>();
}
return null;
}
#endregion
#region UIEvent
private GameEventMgr _eventMgr;
protected GameEventMgr EventMgr
{
get
{
if (_eventMgr == null)
{
_eventMgr = MemoryPool.Acquire<GameEventMgr>();
}
return _eventMgr;
}
}
public void AddUIEvent(int eventType, Action handler)
{
EventMgr.AddEvent(eventType, handler);
}
protected void AddUIEvent<T>(int eventType, Action<T> handler)
{
EventMgr.AddEvent(eventType, handler);
}
protected void AddUIEvent<T, U>(int eventType, Action<T, U> handler)
{
EventMgr.AddEvent(eventType, handler);
}
protected void AddUIEvent<T, U, V>(int eventType, Action<T, U, V> handler)
{
EventMgr.AddEvent(eventType, handler);
}
protected void AddUIEvent<T, U, V, W>(int eventType, Action<T, U, V, W> handler)
{
EventMgr.AddEvent(eventType, handler);
}
protected void RemoveAllUIEvent()
{
if (_eventMgr != null)
{
MemoryPool.Release(_eventMgr);
}
}
#endregion
#region UIWidget
/// <summary>
/// 创建UIWidget通过父UI位置节点。
/// <remarks>因为资源实例已经存在父物体所以不需要异步。</remarks>
/// </summary>
/// <param name="goPath">父UI位置节点。</param>
/// <param name="visible">是否可见。</param>
/// <typeparam name="T">UIWidget。</typeparam>
/// <returns>UIWidget实例。</returns>
public T CreateWidget<T>(string goPath, bool visible = true) where T : UIWidget, new()
{
var goRootTrans = FindChild(goPath);
if (goRootTrans != null)
{
return CreateWidget<T>(goRootTrans.gameObject, visible);
}
return null;
}
/// <summary>
/// 创建UIWidget通过父UI位置节点。
/// <remarks>因为资源实例已经存在父物体所以不需要异步。</remarks>
/// </summary>
/// <param name="parentTrans"></param>
/// <param name="goPath">父UI位置节点。</param>
/// <param name="visible">是否可见。</param>
/// <typeparam name="T">UIWidget。</typeparam>
/// <returns>UIWidget实例。</returns>
public T CreateWidget<T>(Transform parentTrans, string goPath, bool visible = true) where T : UIWidget, new()
{
var goRootTrans = FindChild(parentTrans, goPath);
if (goRootTrans != null)
{
return CreateWidget<T>(goRootTrans.gameObject, visible);
}
return null;
}
/// <summary>
/// 创建UIWidget通过游戏物体。
/// <remarks>因为资源实例已经存在父物体所以不需要异步。</remarks>
/// </summary>
/// <param name="goRoot">游戏物体。</param>
/// <param name="visible">是否可见。</param>
/// <typeparam name="T">UIWidget。</typeparam>
/// <returns>UIWidget实例。</returns>
public T CreateWidget<T>(GameObject goRoot, bool visible = true) where T : UIWidget, new()
{
var widget = new T();
if (widget.Create(this, goRoot, visible))
{
return widget;
}
return null;
}
/// <summary>
/// 创建UIWidget通过资源定位地址。
/// </summary>
/// <param name="parentTrans">资源父节点。</param>
/// <param name="assetLocation">资源定位地址。</param>
/// <param name="visible">是否可见。</param>
/// <typeparam name="T">UIWidget。</typeparam>
/// <returns>UIWidget实例。</returns>
public T CreateWidgetByPath<T>(Transform parentTrans, string assetLocation, bool visible = true) where T : UIWidget, new()
{
GameObject goInst = UIModule.Resource.LoadGameObject(assetLocation, parent: parentTrans);
return CreateWidget<T>(goInst, visible);
}
/// <summary>
/// 创建UIWidget通过资源定位地址。
/// </summary>
/// <param name="parentTrans">资源父节点。</param>
/// <param name="assetLocation">资源定位地址。</param>
/// <param name="visible">是否可见。</param>
/// <typeparam name="T">UIWidget。</typeparam>
/// <returns>UIWidget实例。</returns>
public async UniTask<T> CreateWidgetByPathAsync<T>(Transform parentTrans, string assetLocation, bool visible = true) where T : UIWidget, new()
{
GameObject goInst = await UIModule.Resource.LoadGameObjectAsync(assetLocation, parentTrans, gameObject.GetCancellationTokenOnDestroy());
return CreateWidget<T>(goInst, visible);
}
/// <summary>
/// 根据prefab或者模版来创建新的 widget。
/// </summary>
/// <param name="goPrefab">资源创建副本。</param>
/// <param name="parentTrans">资源父节点。</param>
/// <param name="visible">是否可见。</param>
/// <typeparam name="T">UIWidget。</typeparam>
/// <returns>UIWidget实例。</returns>
public T CreateWidgetByPrefab<T>(GameObject goPrefab, Transform parentTrans = null, bool visible = true) where T : UIWidget, new()
{
var widget = new T();
if (!widget.CreateByPrefab(this, goPrefab, parentTrans, visible))
{
return null;
}
return widget;
}
/// <summary>
/// 通过UI类型来创建widget。
/// </summary>
/// <param name="parentTrans">资源父节点。</param>
/// <param name="visible">是否可见。</param>
/// <typeparam name="T">UIWidget。</typeparam>
/// <returns>UIWidget实例。</returns>
public T CreateWidgetByType<T>(Transform parentTrans, bool visible = true) where T : UIWidget, new()
{
return CreateWidgetByPath<T>(parentTrans, typeof(T).Name, visible);
}
/// <summary>
/// 通过UI类型来创建widget。
/// </summary>
/// <param name="parentTrans">资源父节点。</param>
/// <param name="visible">是否可见。</param>
/// <typeparam name="T">UIWidget。</typeparam>
/// <returns>UIWidget实例。</returns>
public async UniTask<T> CreateWidgetByTypeAsync<T>(Transform parentTrans, bool visible = true) where T : UIWidget, new()
{
return await CreateWidgetByPathAsync<T>(parentTrans, typeof(T).Name, visible);
}
/// <summary>
/// 调整图标数量。
/// </summary>
/// <remarks>常用于Icon创建。</remarks>
/// <param name="listIcon">存放Icon的列表。</param>
/// <param name="number">创建数目。</param>
/// <param name="parentTrans">资源父节点。</param>
/// <param name="prefab">资产副本。</param>
/// <param name="assetPath">资产地址。</param>
/// <typeparam name="T">图标类型。</typeparam>
public void AdjustIconNum<T>(List<T> listIcon, int number, Transform parentTrans, GameObject prefab = null, string assetPath = "")
where T : UIWidget, new()
{
if (listIcon == null)
{
listIcon = new List<T>();
}
if (listIcon.Count < number)
{
int needNum = number - listIcon.Count;
for (int iconIdx = 0; iconIdx < needNum; iconIdx++)
{
T tmpT = prefab == null ? CreateWidgetByType<T>(parentTrans) : CreateWidgetByPrefab<T>(prefab, parentTrans);
listIcon.Add(tmpT);
}
}
else if (listIcon.Count > number)
{
RemoveUnUseItem<T>(listIcon, number);
}
}
/// <summary>
/// 异步调整图标数量。
/// </summary>
/// <param name="listIcon"></param>
/// <param name="tarNum"></param>
/// <param name="parentTrans"></param>
/// <param name="prefab"></param>
/// <param name="assetPath"></param>
/// <param name="maxNumPerFrame"></param>
/// <param name="updateAction"></param>
/// <typeparam name="T"></typeparam>
public void AsyncAdjustIconNum<T>(List<T> listIcon, int tarNum, Transform parentTrans, GameObject prefab = null,
string assetPath = "", int maxNumPerFrame = 5,
Action<T, int> updateAction = null) where T : UIWidget, new()
{
AsyncAdjustIconNumInternal(listIcon, tarNum, parentTrans, maxNumPerFrame, updateAction, prefab, assetPath).Forget();
}
/// <summary>
/// 异步创建接口。
/// </summary>
/// <param name="listIcon"></param>
/// <param name="tarNum"></param>
/// <param name="parentTrans"></param>
/// <param name="maxNumPerFrame"></param>
/// <param name="updateAction"></param>
/// <param name="prefab"></param>
/// <param name="assetPath"></param>
/// <typeparam name="T"></typeparam>
private async UniTaskVoid AsyncAdjustIconNumInternal<T>(List<T> listIcon, int tarNum, Transform parentTrans, int maxNumPerFrame,
Action<T, int> updateAction, GameObject prefab, string assetPath) where T : UIWidget, new()
{
if (listIcon == null)
{
listIcon = new List<T>();
}
int createCnt = 0;
for (int i = 0; i < tarNum; i++)
{
T tmpT = null;
if (i < listIcon.Count)
{
tmpT = listIcon[i];
}
else
{
if (prefab == null)
{
tmpT = await CreateWidgetByPathAsync<T>(parentTrans, assetPath);
}
else
{
tmpT = CreateWidgetByPrefab<T>(prefab, parentTrans);
}
listIcon.Add(tmpT);
}
int index = i;
if (updateAction != null)
{
updateAction(tmpT, index);
}
createCnt++;
if (createCnt >= maxNumPerFrame)
{
createCnt = 0;
await UniTask.Yield();
}
}
if (listIcon.Count > tarNum)
{
RemoveUnUseItem(listIcon, tarNum);
}
}
private void RemoveUnUseItem<T>(List<T> listIcon, int tarNum) where T : UIWidget
{
var removeIcon = new List<T>();
for (int iconIdx = 0; iconIdx < listIcon.Count; iconIdx++)
{
var icon = listIcon[iconIdx];
if (iconIdx >= tarNum)
{
removeIcon.Add(icon);
}
}
for (var index = 0; index < removeIcon.Count; index++)
{
var icon = removeIcon[index];
listIcon.Remove(icon);
icon.OnDestroy();
icon.OnDestroyWidget();
ListChild.Remove(icon);
if (icon.gameObject != null)
{
UnityEngine.Object.Destroy(icon.gameObject);
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 150e58aa8e003994686cd51fa0d192c9

View File

@@ -0,0 +1,674 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using GameLogic;
using TEngine;
using UnityEngine;
using UnityEngine.UI;
namespace GameLogic
{
public sealed partial class UIModule : Singleton<UIModule>, IUpdate
{
private static Transform m_InstanceRoot = null;
private bool m_enableErrorLog = true;
private Camera m_UICamera = null;
private readonly List<UIWindow> _stack = new List<UIWindow>(100);
public const int LAYER_DEEP = 2000;
public const int WINDOW_DEEP = 100;
public const int WINDOW_HIDE_LAYER = 2; // Ignore Raycast
public const int WINDOW_SHOW_LAYER = 5; // UI
public static IUIResourceLoader Resource;
/// <summary>
/// UI根节点。
/// </summary>
public static Transform UIRoot => m_InstanceRoot;
/// <summary>
/// UI根节点。
/// </summary>
public Camera UICamera => m_UICamera;
private ErrorLogger _errorLogger;
protected override void OnInit()
{
var uiRoot = GameObject.Find("UIRoot");
if (uiRoot != null)
{
m_InstanceRoot = uiRoot.GetComponentInChildren<Canvas>()?.transform;
}
else
{
Log.Fatal("UIRoot not found !");
return;
}
Resource = new UIResourceLoader();
UnityEngine.Object.DontDestroyOnLoad(m_InstanceRoot.parent != null ? m_InstanceRoot.parent : m_InstanceRoot);
m_InstanceRoot.gameObject.layer = LayerMask.NameToLayer("UI");
if (Debugger.Instance != null)
{
switch (Debugger.Instance.ActiveWindowType)
{
case DebuggerActiveWindowType.AlwaysOpen:
m_enableErrorLog = true;
break;
case DebuggerActiveWindowType.OnlyOpenWhenDevelopment:
m_enableErrorLog = Debug.isDebugBuild;
break;
case DebuggerActiveWindowType.OnlyOpenInEditor:
m_enableErrorLog = Application.isEditor;
break;
default:
m_enableErrorLog = false;
break;
}
if (m_enableErrorLog)
{
_errorLogger = new ErrorLogger(this);
}
}
}
protected override void OnRelease()
{
if (_errorLogger != null)
{
_errorLogger.Dispose();
_errorLogger = null;
}
CloseAll(isShutDown:true);
if (m_InstanceRoot != null && m_InstanceRoot.parent != null)
{
UnityEngine.Object.Destroy(m_InstanceRoot.parent.gameObject);
}
}
#region
/// <summary>
/// 设置屏幕安全区域(异形屏支持)。
/// </summary>
/// <param name="safeRect">安全区域</param>
public static void ApplyScreenSafeRect(Rect safeRect)
{
CanvasScaler scaler = UIRoot.GetComponentInParent<CanvasScaler>();
if (scaler == null)
{
Log.Error($"Not found {nameof(CanvasScaler)} !");
return;
}
// Convert safe area rectangle from absolute pixels to UGUI coordinates
float rateX = scaler.referenceResolution.x / Screen.width;
float rateY = scaler.referenceResolution.y / Screen.height;
float posX = (int)(safeRect.position.x * rateX);
float posY = (int)(safeRect.position.y * rateY);
float width = (int)(safeRect.size.x * rateX);
float height = (int)(safeRect.size.y * rateY);
float offsetMaxX = scaler.referenceResolution.x - width - posX;
float offsetMaxY = scaler.referenceResolution.y - height - posY;
// 注意:安全区坐标系的原点为左下角
var rectTrans = UIRoot.transform as RectTransform;
if (rectTrans != null)
{
rectTrans.offsetMin = new Vector2(posX, posY); //锚框状态下的屏幕左下角偏移向量
rectTrans.offsetMax = new Vector2(-offsetMaxX, -offsetMaxY); //锚框状态下的屏幕右上角偏移向量
}
}
/// <summary>
/// 模拟IPhoneX异形屏
/// </summary>
public static void SimulateIPhoneXNotchScreen()
{
Rect rect;
if (Screen.height > Screen.width)
{
// 竖屏Portrait
float deviceWidth = 1125;
float deviceHeight = 2436;
rect = new Rect(0f / deviceWidth, 102f / deviceHeight, 1125f / deviceWidth, 2202f / deviceHeight);
}
else
{
// 横屏Landscape
float deviceWidth = 2436;
float deviceHeight = 1125;
rect = new Rect(132f / deviceWidth, 63f / deviceHeight, 2172f / deviceWidth, 1062f / deviceHeight);
}
Rect safeArea = new Rect(Screen.width * rect.x, Screen.height * rect.y, Screen.width * rect.width, Screen.height * rect.height);
ApplyScreenSafeRect(safeArea);
}
#endregion
/// <summary>
/// 获取所有层级下顶部的窗口名称。
/// </summary>
public string GetTopWindow()
{
if (_stack.Count == 0)
{
return string.Empty;
}
UIWindow topWindow = _stack[^1];
return topWindow.WindowName;
}
/// <summary>
/// 获取指定层级下顶部的窗口名称。
/// </summary>
public string GetTopWindow(int layer)
{
UIWindow lastOne = null;
for (int i = 0; i < _stack.Count; i++)
{
if (_stack[i].WindowLayer == layer)
lastOne = _stack[i];
}
if (lastOne == null)
return string.Empty;
return lastOne.WindowName;
}
/// <summary>
/// 是否有任意窗口正在加载。
/// </summary>
public bool IsAnyLoading()
{
for (int i = 0; i < _stack.Count; i++)
{
var window = _stack[i];
if (window.IsLoadDone == false)
return true;
}
return false;
}
/// <summary>
/// 查询窗口是否存在。
/// </summary>
/// <typeparam name="T">界面类型。</typeparam>
/// <returns>是否存在。</returns>
public bool HasWindow<T>()
{
return HasWindow(typeof(T));
}
/// <summary>
/// 查询窗口是否存在。
/// </summary>
/// <param name="type">界面类型。</param>
/// <returns>是否存在。</returns>
public bool HasWindow(Type type)
{
return IsContains(type.FullName);
}
/// <summary>
/// 异步打开窗口。
/// </summary>
/// <param name="userDatas">用户自定义数据。</param>
/// <returns>打开窗口操作句柄。</returns>
public void ShowUIAsync<T>(params System.Object[] userDatas) where T : UIWindow
{
ShowUIImp(typeof(T), true, userDatas);
}
/// <summary>
/// 异步打开窗口。
/// </summary>
/// <param name="type">界面类型。</param>
/// <param name="userDatas">用户自定义数据。</param>
/// <returns>打开窗口操作句柄。</returns>
public void ShowUIAsync(Type type, params System.Object[] userDatas)
{
ShowUIImp(type, true, userDatas);
}
/// <summary>
/// 同步打开窗口。
/// </summary>
/// <typeparam name="T">窗口类。</typeparam>
/// <param name="userDatas">用户自定义数据。</param>
/// <returns>打开窗口操作句柄。</returns>
public void ShowUI<T>(params System.Object[] userDatas) where T : UIWindow
{
ShowUIImp(typeof(T), false, userDatas);
}
/// <summary>
/// 异步打开窗口。
/// </summary>
/// <param name="userDatas">用户自定义数据。</param>
/// <returns>打开窗口操作句柄。</returns>
public async UniTask<UIWindow> ShowUIAsyncAwait<T>(params System.Object[] userDatas) where T : UIWindow
{
return await ShowUIAwaitImp(typeof(T), true, userDatas);
}
/// <summary>
/// 同步打开窗口。
/// </summary>
/// <param name="type"></param>
/// <param name="userDatas"></param>
/// <returns>打开窗口操作句柄。</returns>
public void ShowUI(Type type, params System.Object[] userDatas)
{
ShowUIImp(type, false, userDatas);
}
private void ShowUIImp(Type type, bool isAsync, params System.Object[] userDatas)
{
string windowName = type.FullName;
// 如果窗口已经存在
if (IsContains(windowName))
{
UIWindow window = GetWindow(windowName);
Pop(window); //弹出窗口
Push(window); //重新压入
window.TryInvoke(OnWindowPrepare, userDatas);
}
else
{
UIWindow window = CreateInstance(type);
Push(window); //首次压入
window.InternalLoad(window.AssetName, OnWindowPrepare, isAsync, userDatas).Forget();
}
}
private async UniTask<UIWindow> ShowUIAwaitImp(Type type, bool isAsync, params System.Object[] userDatas)
{
string windowName = type.FullName;
// 如果窗口已经存在
if (IsContains(windowName))
{
UIWindow window = GetWindow(windowName);
Pop(window); //弹出窗口
Push(window); //重新压入
window.TryInvoke(OnWindowPrepare, userDatas);
return window;
}
else
{
UIWindow window = CreateInstance(type);
Push(window); //首次压入
window.InternalLoad(window.AssetName, OnWindowPrepare, isAsync, userDatas).Forget();
float time = 0f;
while (!window.IsLoadDone)
{
time += Time.deltaTime;
if (time > 60f)
{
break;
}
await UniTask.Yield();
}
return window;
}
}
/// <summary>
/// 关闭窗口
/// </summary>
public void CloseUI<T>() where T : UIWindow
{
CloseUI(typeof(T));
}
public void CloseUI(Type type)
{
string windowName = type.FullName;
UIWindow window = GetWindow(windowName);
if (window == null)
return;
window.InternalDestroy();
Pop(window);
OnSortWindowDepth(window.WindowLayer);
OnSetWindowVisible();
}
public void HideUI<T>() where T : UIWindow
{
HideUI(typeof(T));
}
public void HideUI(Type type)
{
string windowName = type.FullName;
UIWindow window = GetWindow(windowName);
if (window == null)
{
return;
}
if (window.HideTimeToClose <= 0)
{
CloseUI(type);
return;
}
window.Visible = false;
window.IsHide = true;
window.HideTimerId = ModuleSystem.GetModule<ITimerModule>().AddTimer((arg) =>
{
CloseUI(type);
},window.HideTimeToClose);
if (window.FullScreen)
{
OnSetWindowVisible();
}
}
/// <summary>
/// 关闭所有窗口。
/// </summary>
public void CloseAll(bool isShutDown = false)
{
for (int i = 0; i < _stack.Count; i++)
{
UIWindow window = _stack[i];
window.InternalDestroy(isShutDown);
}
_stack.Clear();
}
/// <summary>
/// 关闭所有窗口除了。
/// </summary>
public void CloseAllWithOut(UIWindow withOut)
{
for (int i = _stack.Count - 1; i >= 0; i--)
{
UIWindow window = _stack[i];
if (window == withOut)
{
continue;
}
window.InternalDestroy();
_stack.RemoveAt(i);
}
}
/// <summary>
/// 关闭所有窗口除了。
/// </summary>
public void CloseAllWithOut<T>() where T : UIWindow
{
for (int i = _stack.Count - 1; i >= 0; i--)
{
UIWindow window = _stack[i];
if (window.GetType() == typeof(T))
{
continue;
}
window.InternalDestroy();
_stack.RemoveAt(i);
}
}
private void OnWindowPrepare(UIWindow window)
{
OnSortWindowDepth(window.WindowLayer);
window.InternalCreate();
window.InternalRefresh();
OnSetWindowVisible();
}
private void OnSortWindowDepth(int layer)
{
int depth = layer * LAYER_DEEP;
for (int i = 0; i < _stack.Count; i++)
{
if (_stack[i].WindowLayer == layer)
{
_stack[i].Depth = depth;
depth += WINDOW_DEEP;
}
}
}
private void OnSetWindowVisible()
{
bool isHideNext = false;
for (int i = _stack.Count - 1; i >= 0; i--)
{
UIWindow window = _stack[i];
if (isHideNext == false)
{
if (window.IsHide)
{
continue;
}
window.Visible = true;
if (window.IsPrepare && window.FullScreen)
{
isHideNext = true;
}
}
else
{
window.Visible = false;
}
}
}
private UIWindow CreateInstance(Type type)
{
UIWindow window = Activator.CreateInstance(type) as UIWindow;
WindowAttribute attribute = Attribute.GetCustomAttribute(type, typeof(WindowAttribute)) as WindowAttribute;
if (window == null)
throw new GameFrameworkException($"Window {type.FullName} create instance failed.");
if (attribute != null)
{
string assetName = string.IsNullOrEmpty(attribute.Location) ? type.Name : attribute.Location;
window.Init(type.FullName, attribute.WindowLayer, attribute.FullScreen, assetName, attribute.FromResources, attribute.HideTimeToClose);
}
else
{
window.Init(type.FullName, (int)UILayer.UI, fullScreen: window.FullScreen, assetName: type.Name, fromResources: false, hideTimeToClose: 10);
}
return window;
}
/// <summary>
/// 异步获取窗口。
/// </summary>
/// <returns>打开窗口操作句柄。</returns>
public async UniTask<T> GetUIAsyncAwait<T>() where T : UIWindow
{
string windowName = typeof(T).FullName;
var window = GetWindow(windowName);
if (window == null)
{
return null;
}
var ret = window as T;
if (ret == null)
{
return null;
}
if (ret.IsLoadDone)
{
return ret;
}
float time = 0f;
while (!ret.IsLoadDone)
{
time += Time.deltaTime;
if (time > 60f)
{
break;
}
await UniTask.Yield();
}
return ret;
}
/// <summary>
/// 异步获取窗口。
/// </summary>
/// <param name="callback">回调。</param>
/// <returns>打开窗口操作句柄。</returns>
public void GetUIAsync<T>(Action<T> callback) where T : UIWindow
{
string windowName = typeof(T).FullName;
var window = GetWindow(windowName);
if (window == null)
{
return;
}
var ret = window as T;
if (ret == null)
{
return;
}
GetUIAsyncImp(callback).Forget();
async UniTaskVoid GetUIAsyncImp(Action<T> ctx)
{
float time = 0f;
while (!ret.IsLoadDone)
{
time += Time.deltaTime;
if (time > 60f)
{
break;
}
await UniTask.Yield();
}
ctx?.Invoke(ret);
}
}
private UIWindow GetWindow(string windowName)
{
for (int i = 0; i < _stack.Count; i++)
{
UIWindow window = _stack[i];
if (window.WindowName == windowName)
{
return window;
}
}
return null;
}
private bool IsContains(string windowName)
{
for (int i = 0; i < _stack.Count; i++)
{
UIWindow window = _stack[i];
if (window.WindowName == windowName)
{
return true;
}
}
return false;
}
private void Push(UIWindow window)
{
// 如果已经存在
if (IsContains(window.WindowName))
throw new System.Exception($"Window {window.WindowName} is exist.");
// 获取插入到所属层级的位置
int insertIndex = -1;
for (int i = 0; i < _stack.Count; i++)
{
if (window.WindowLayer == _stack[i].WindowLayer)
{
insertIndex = i + 1;
}
}
// 如果没有所属层级,找到相邻层级
if (insertIndex == -1)
{
for (int i = 0; i < _stack.Count; i++)
{
if (window.WindowLayer > _stack[i].WindowLayer)
{
insertIndex = i + 1;
}
}
}
// 如果是空栈或没有找到插入位置
if (insertIndex == -1)
{
insertIndex = 0;
}
// 最后插入到堆栈
_stack.Insert(insertIndex, window);
}
private void Pop(UIWindow window)
{
// 从堆栈里移除
_stack.Remove(window);
}
public void OnUpdate()
{
if (_stack == null)
{
return;
}
int count = _stack.Count;
for (int i = 0; i < _stack.Count; i++)
{
if (_stack.Count != count)
{
break;
}
var window = _stack[i];
window.InternalUpdate();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 75f20a98eacc4715a146a1c22f62decb
timeCreated: 1730998744

View File

@@ -0,0 +1,314 @@
using System.Collections.Generic;
using TEngine;
using UnityEngine;
namespace GameLogic
{
public abstract class UIWidget : UIBase
{
/// <summary>
/// 窗口组件的实例资源对象。
/// </summary>
public override GameObject gameObject { protected set; get; }
/// <summary>
/// 窗口组件矩阵位置组件。
/// </summary>
public override RectTransform rectTransform { protected set; get; }
/// <summary>
/// 窗口位置组件。
/// </summary>
public override Transform transform { protected set; get; }
/// <summary>
/// 窗口组件名称。
/// </summary>
// ReSharper disable once InconsistentNaming
public string name { protected set; get; } = string.Empty;
/// <summary>
/// UI类型。
/// </summary>
public override UIType Type => UIType.Widget;
/// <summary>
/// 所属的窗口。
/// </summary>
public UIWindow OwnerWindow
{
get
{
var parentUI = base.parent;
while (parentUI != null)
{
if (parentUI.Type == UIType.Window)
{
return parentUI as UIWindow;
}
parentUI = parentUI.Parent;
}
return null;
}
}
/// <summary>
/// 窗口可见性
/// </summary>
public bool Visible
{
get => gameObject.activeSelf;
set
{
gameObject.SetActive(value);
OnSetVisible(value);
}
}
internal bool InternalUpdate()
{
if (!IsPrepare)
{
return false;
}
List<UIWidget> listNextUpdateChild = null;
if (ListChild != null && ListChild.Count > 0)
{
listNextUpdateChild = m_listUpdateChild;
var updateListValid = m_updateListValid;
List<UIWidget> listChild = null;
if (!updateListValid)
{
if (listNextUpdateChild == null)
{
listNextUpdateChild = new List<UIWidget>();
m_listUpdateChild = listNextUpdateChild;
}
else
{
listNextUpdateChild.Clear();
}
listChild = ListChild;
}
else
{
listChild = listNextUpdateChild;
}
for (int i = 0; i < listChild.Count; i++)
{
var uiWidget = listChild[i];
if (uiWidget == null)
{
continue;
}
var needValid = uiWidget.InternalUpdate();
if (!updateListValid && needValid)
{
listNextUpdateChild.Add(uiWidget);
}
}
if (!updateListValid)
{
m_updateListValid = true;
}
}
bool needUpdate = false;
if (listNextUpdateChild is not { Count: > 0 })
{
HasOverrideUpdate = true;
OnUpdate();
needUpdate = HasOverrideUpdate;
}
else
{
OnUpdate();
needUpdate = true;
}
return needUpdate;
}
#region Create
/// <summary>
/// 创建窗口内嵌的界面。
/// </summary>
/// <param name="parentUI">父节点UI。</param>
/// <param name="widgetRoot">组件根节点。</param>
/// <param name="visible">是否可见。</param>
/// <returns></returns>
public bool Create(UIBase parentUI, GameObject widgetRoot, bool visible = true)
{
return CreateImp(parentUI, widgetRoot, false, visible);
}
/// <summary>
/// 根据资源名创建
/// </summary>
/// <param name="resPath"></param>
/// <param name="parentUI"></param>
/// <param name="parentTrans"></param>
/// <param name="visible"></param>
/// <returns></returns>
public bool CreateByPath(string resPath, UIBase parentUI, Transform parentTrans = null, bool visible = true)
{
GameObject goInst = UIModule.Resource.LoadGameObject(resPath, parent: parentTrans);
if (goInst == null)
{
return false;
}
if (!Create(parentUI, goInst, visible))
{
return false;
}
goInst.transform.localScale = Vector3.one;
goInst.transform.localPosition = Vector3.zero;
return true;
}
/// <summary>
/// 根据prefab或者模版来创建新的 widget。
/// <remarks>存在父物体得资源故不需要异步加载。</remarks>
/// </summary>
/// <param name="parentUI">父物体UI。</param>
/// <param name="goPrefab">实例化预制体。</param>
/// <param name="parentTrans">实例化父节点。</param>
/// <param name="visible">是否可见。</param>
/// <returns>是否创建成功。</returns>
public bool CreateByPrefab(UIBase parentUI, GameObject goPrefab, Transform parentTrans, bool visible = true)
{
if (parentTrans == null)
{
parentTrans = parentUI.rectTransform;
}
return CreateImp(parentUI, Object.Instantiate(goPrefab, parentTrans), true, visible);
}
private bool CreateImp(UIBase parentUI, GameObject widgetRoot, bool bindGo, bool visible = true)
{
if (!CreateBase(widgetRoot, bindGo))
{
return false;
}
RestChildCanvas(parentUI);
parent = parentUI;
Parent.ListChild.Add(this);
Parent.SetUpdateDirty();
ScriptGenerator();
BindMemberProperty();
RegisterEvent();
OnCreate();
OnRefresh();
IsPrepare = true;
if (!visible)
{
gameObject.SetActive(false);
}
else
{
if (!gameObject.activeSelf)
{
gameObject.SetActive(true);
}
}
return true;
}
protected bool CreateBase(GameObject go, bool bindGo)
{
if (go == null)
{
return false;
}
name = GetType().Name;
transform = go.GetComponent<Transform>();
rectTransform = transform as RectTransform;
gameObject = go;
Log.Assert(rectTransform != null, $"{go.name} ui base element need to be RectTransform");
return true;
}
protected void RestChildCanvas(UIBase parentUI)
{
if (parentUI == null || parentUI.gameObject == null)
{
return;
}
Canvas parentCanvas = parentUI.gameObject.GetComponentInParent<Canvas>();
if (parentCanvas == null)
{
return;
}
if (gameObject != null)
{
var listCanvas = gameObject.GetComponentsInChildren<Canvas>(true);
for (var index = 0; index < listCanvas.Length; index++)
{
var childCanvas = listCanvas[index];
childCanvas.sortingOrder = parentCanvas.sortingOrder + childCanvas.sortingOrder % UIModule.WINDOW_DEEP;
}
}
}
#endregion
#region Destroy
/// <summary>
/// 组件被销毁调用。
/// <remarks>请勿手动调用!</remarks>
/// </summary>
internal void OnDestroyWidget()
{
Parent?.SetUpdateDirty();
RemoveAllUIEvent();
foreach (var uiChild in ListChild)
{
uiChild.OnDestroy();
uiChild.OnDestroyWidget();
}
if (gameObject != null)
{
Object.Destroy(gameObject);
}
}
/// <summary>
/// 主动销毁组件。
/// </summary>
public void Destroy()
{
if (parent != null)
{
parent.ListChild.Remove(this);
OnDestroy();
OnDestroyWidget();
}
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 85d37e6e79fd7c84192b1c3c60496874

View File

@@ -0,0 +1,456 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using TEngine;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace GameLogic
{
public abstract class UIWindow : UIBase
{
#region Propreties
private System.Action<UIWindow> _prepareCallback;
private bool _isCreate = false;
private GameObject _panel;
private Canvas _canvas;
protected Canvas Canvas => _canvas;
private Canvas[] _childCanvas;
private GraphicRaycaster _raycaster;
protected GraphicRaycaster GraphicRaycaster => _raycaster;
private GraphicRaycaster[] _childRaycaster;
public override UIType Type => UIType.Window;
/// <summary>
/// 窗口位置组件。
/// </summary>
public override Transform transform => _panel.transform;
/// <summary>
/// 窗口矩阵位置组件。
/// </summary>
public override RectTransform rectTransform => _panel.transform as RectTransform;
/// <summary>
/// 窗口的实例资源对象。
/// </summary>
public override GameObject gameObject => _panel;
/// <summary>
/// 窗口名称。
/// </summary>
public string WindowName { private set; get; }
/// <summary>
/// 窗口层级。
/// </summary>
public int WindowLayer { private set; get; }
/// <summary>
/// 资源定位地址。
/// </summary>
public string AssetName { private set; get; }
/// <summary>
/// 是否为全屏窗口。
/// </summary>
public virtual bool FullScreen { private set; get; } = false;
/// <summary>
/// 是内部资源无需AB加载。
/// </summary>
public bool FromResources { private set; get; }
/// <summary>
/// 隐藏窗口关闭时间。
/// </summary>
public int HideTimeToClose { get; set; }
public int HideTimerId { get; set; }
/// <summary>
/// 窗口深度值。
/// </summary>
public int Depth
{
get
{
if (_canvas != null)
{
return _canvas.sortingOrder;
}
else
{
return 0;
}
}
set
{
if (_canvas != null)
{
if (_canvas.sortingOrder == value)
{
return;
}
// 设置父类
_canvas.sortingOrder = value;
// 设置子类
int depth = value;
for (int i = 0; i < _childCanvas.Length; i++)
{
var canvas = _childCanvas[i];
if (canvas != _canvas)
{
depth += 5; //注意递增值
canvas.sortingOrder = depth;
}
}
// 虚函数
if (_isCreate)
{
OnSortDepth(value);
}
}
}
}
/// <summary>
/// 窗口可见性
/// </summary>
public bool Visible
{
get
{
if (_canvas != null)
{
return _canvas.gameObject.layer == UIModule.WINDOW_SHOW_LAYER;
}
else
{
return false;
}
}
set
{
if (_canvas != null)
{
int setLayer = value ? UIModule.WINDOW_SHOW_LAYER : UIModule.WINDOW_HIDE_LAYER;
if (_canvas.gameObject.layer == setLayer)
return;
// 显示设置
_canvas.gameObject.layer = setLayer;
for (int i = 0; i < _childCanvas.Length; i++)
{
_childCanvas[i].gameObject.layer = setLayer;
}
// 交互设置
Interactable = value;
// 虚函数
if (_isCreate)
{
OnSetVisible(value);
}
}
}
}
/// <summary>
/// 窗口交互性
/// </summary>
private bool Interactable
{
get
{
if (_raycaster != null)
{
return _raycaster.enabled;
}
else
{
return false;
}
}
set
{
if (_raycaster != null)
{
_raycaster.enabled = value;
for (int i = 0; i < _childRaycaster.Length; i++)
{
_childRaycaster[i].enabled = value;
}
}
}
}
/// <summary>
/// 是否加载完毕。
/// </summary>
internal bool IsLoadDone = false;
/// <summary>
/// UI是否销毁。
/// </summary>
internal bool IsDestroyed = false;
/// <summary>
/// UI是否隐藏标志位。
/// </summary>
public bool IsHide { internal set; get; } = false;
#endregion
public void Init(string name, int layer, bool fullScreen, string assetName, bool fromResources, int hideTimeToClose)
{
WindowName = name;
WindowLayer = layer;
FullScreen = fullScreen;
AssetName = assetName;
FromResources = fromResources;
HideTimeToClose = hideTimeToClose;
}
internal void TryInvoke(System.Action<UIWindow> prepareCallback, System.Object[] userDatas)
{
CancelHideToCloseTimer();
base.userDatas = userDatas;
if (IsPrepare)
{
prepareCallback?.Invoke(this);
}
else
{
_prepareCallback = prepareCallback;
}
}
internal async UniTaskVoid InternalLoad(string location, Action<UIWindow> prepareCallback, bool isAsync, System.Object[] userDatas)
{
_prepareCallback = prepareCallback;
this.userDatas = userDatas;
if (!FromResources)
{
if (isAsync)
{
var uiInstance = await UIModule.Resource.LoadGameObjectAsync(location, parent: UIModule.UIRoot);
Handle_Completed(uiInstance);
}
else
{
var uiInstance = UIModule.Resource.LoadGameObject(location, parent: UIModule.UIRoot);
Handle_Completed(uiInstance);
}
}
else
{
GameObject panel = Object.Instantiate(Resources.Load<GameObject>(location), UIModule.UIRoot);
Handle_Completed(panel);
}
}
internal void InternalCreate()
{
if (_isCreate == false)
{
_isCreate = true;
ScriptGenerator();
BindMemberProperty();
RegisterEvent();
OnCreate();
}
}
internal void InternalRefresh()
{
OnRefresh();
}
internal bool InternalUpdate()
{
if (!IsPrepare || !Visible)
{
return false;
}
List<UIWidget> listNextUpdateChild = null;
if (ListChild != null && ListChild.Count > 0)
{
listNextUpdateChild = m_listUpdateChild;
var updateListValid = m_updateListValid;
List<UIWidget> listChild = null;
if (!updateListValid)
{
if (listNextUpdateChild == null)
{
listNextUpdateChild = new List<UIWidget>();
m_listUpdateChild = listNextUpdateChild;
}
else
{
listNextUpdateChild.Clear();
}
listChild = ListChild;
}
else
{
listChild = listNextUpdateChild;
}
for (int i = 0; i < listChild.Count; i++)
{
var uiWidget = listChild[i];
if (uiWidget == null)
{
continue;
}
var needValid = uiWidget.InternalUpdate();
if (!updateListValid && needValid)
{
listNextUpdateChild.Add(uiWidget);
}
}
if (!updateListValid)
{
m_updateListValid = true;
}
}
bool needUpdate = false;
if (listNextUpdateChild == null || listNextUpdateChild.Count <= 0)
{
HasOverrideUpdate = true;
OnUpdate();
needUpdate = HasOverrideUpdate;
}
else
{
OnUpdate();
needUpdate = true;
}
return needUpdate;
}
internal void InternalDestroy(bool isShutDown = false)
{
_isCreate = false;
RemoveAllUIEvent();
for (int i = 0; i < ListChild.Count; i++)
{
var uiChild = ListChild[i];
uiChild.CallDestroy();
uiChild.OnDestroyWidget();
}
// 注销回调函数
_prepareCallback = null;
OnDestroy();
// 销毁面板对象
if (_panel != null)
{
Object.Destroy(_panel);
_panel = null;
}
IsDestroyed = true;
if (!isShutDown)
{
CancelHideToCloseTimer();
}
}
/// <summary>
/// 处理资源加载完成回调。
/// </summary>
/// <param name="panel">面板资源实例。</param>
private void Handle_Completed(GameObject panel)
{
if (panel == null)
{
return;
}
IsLoadDone = true;
if (IsDestroyed)
{
Object.Destroy(panel);
return;
}
panel.name = GetType().Name;
_panel = panel;
_panel.transform.localPosition = Vector3.zero;
// 获取组件
_canvas = _panel.GetComponent<Canvas>();
if (_canvas == null)
{
throw new Exception($"Not found {nameof(Canvas)} in panel {WindowName}");
}
_canvas.overrideSorting = true;
_canvas.sortingOrder = 0;
_canvas.sortingLayerName = "Default";
// 获取组件
_raycaster = _panel.GetComponent<GraphicRaycaster>();
_childCanvas = _panel.GetComponentsInChildren<Canvas>(true);
_childRaycaster = _panel.GetComponentsInChildren<GraphicRaycaster>(true);
// 通知UI管理器
IsPrepare = true;
_prepareCallback?.Invoke(this);
}
protected virtual void Hide()
{
UIModule.Instance.HideUI(this.GetType());
}
protected virtual void Close()
{
UIModule.Instance.CloseUI(this.GetType());
}
internal void CancelHideToCloseTimer()
{
IsHide = false;
if (HideTimerId > 0)
{
ModuleSystem.GetModule<ITimerModule>().RemoveTimer(HideTimerId);
HideTimerId = 0;
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e69db0620e3c6504e912406b236862a7

View File

@@ -0,0 +1,75 @@
using System;
namespace GameLogic
{
/// <summary>
/// UI层级枚举。
/// </summary>
public enum UILayer : int
{
Bottom = 0,
UI = 1,
Top = 2,
Tips = 3,
System = 4,
}
[AttributeUsage(AttributeTargets.Class)]
public class WindowAttribute : Attribute
{
/// <summary>
/// 窗口层级
/// </summary>
public readonly int WindowLayer;
/// <summary>
/// 资源定位地址。
/// </summary>
public readonly string Location;
/// <summary>
/// 全屏窗口标记。
/// </summary>
public readonly bool FullScreen;
/// <summary>
/// 是内部资源无需AB加载。
/// </summary>
public readonly bool FromResources;
public readonly int HideTimeToClose;
public WindowAttribute(int windowLayer, string location = "", bool fullScreen = false, int hideTimeToClose = 10)
{
WindowLayer = windowLayer;
Location = location;
FullScreen = fullScreen;
HideTimeToClose = hideTimeToClose;
}
public WindowAttribute(UILayer windowLayer, string location = "", bool fullScreen = false, int hideTimeToClose = 10)
{
WindowLayer = (int)windowLayer;
Location = location;
FullScreen = fullScreen;
HideTimeToClose = hideTimeToClose;
}
public WindowAttribute(UILayer windowLayer, bool fromResources, bool fullScreen = false, int hideTimeToClose = 10)
{
WindowLayer = (int)windowLayer;
FromResources = fromResources;
FullScreen = fullScreen;
HideTimeToClose = hideTimeToClose;
}
public WindowAttribute(UILayer windowLayer, bool fromResources, string location, bool fullScreen = false, int hideTimeToClose = 10)
{
WindowLayer = (int)windowLayer;
FromResources = fromResources;
Location = location;
FullScreen = fullScreen;
HideTimeToClose = hideTimeToClose;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fb81c60a8b8a1d34780af88a49c85918

View File

@@ -1,13 +0,0 @@
using TEngine;
namespace GameLogic
{
public class OnEnterGameAppProcedure : ProcedureBase
{
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
{
base.OnEnter(procedureOwner);
Log.Debug("OnEnter GameApp Procedure");
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d724ef97b0234d48a2690c722a441af1
timeCreated: 1696750677

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14d09cd7f31347f591736f8bcbb5e94c
timeCreated: 1735231556

View File

@@ -1,6 +1,6 @@
using System.Diagnostics;
namespace GameBase
namespace GameLogic
{
/// <summary>
/// 全局对象必须继承于此。
@@ -17,7 +17,7 @@ namespace GameBase
if (null == _instance)
{
_instance = new T();
_instance.Init();
_instance.OnInit();
SingletonSystem.Retain(_instance);
}
@@ -32,14 +32,14 @@ namespace GameBase
#if UNITY_EDITOR
string st = new StackTrace().ToString();
// using const string to compare simply
if (!st.Contains("GameBase.Singleton`1[T].get_Instance"))
if (!st.Contains("GameLogic.Singleton`1[T].get_Instance"))
{
UnityEngine.Debug.LogError($"请必须通过Instance方法来实例化{typeof(T).FullName}类");
}
#endif
}
protected virtual void Init()
protected virtual void OnInit()
{
}
@@ -49,11 +49,14 @@ namespace GameBase
public virtual void Release()
{
OnRelease();
if (_instance != null)
{
SingletonSystem.Release(_instance);
_instance = null;
}
}
protected abstract void OnRelease();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d7bb4cd9d323468dbaf9651bffb0c0a1
timeCreated: 1735231561

View File

@@ -1,7 +1,7 @@
using TEngine;
using UnityEngine;
namespace GameBase
namespace GameLogic
{
/// <summary>
/// 全局MonoBehavior必须继承于此
@@ -25,13 +25,13 @@ namespace GameBase
{
return true;
}
Object.Destroy(gameObject);
return false;
}
protected virtual void OnLoad()
{
}
protected virtual void OnDestroy()
@@ -47,10 +47,7 @@ namespace GameBase
/// </summary>
public static bool IsValid
{
get
{
return _instance != null;
}
get { return _instance != null; }
}
public static T Active()
@@ -62,7 +59,7 @@ namespace GameBase
{
if (_instance != null)
{
SingletonSystem.Release(_instance.gameObject);
SingletonSystem.Release(_instance.gameObject, _instance);
_instance = null;
}
}
@@ -87,7 +84,6 @@ namespace GameBase
go = new GameObject(instName);
go.transform.position = Vector3.zero;
}
SingletonSystem.Retain(go);
}
if (go != null)
@@ -101,9 +97,12 @@ namespace GameBase
if (_instance == null)
{
Log.Error($"Can't create SingletonBehaviour<{typeof(T)}>");
Log.Fatal($"Can't create SingletonBehaviour<{typeof(T)}>");
}
SingletonSystem.Retain(go, _instance);
}
return _instance;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0a53eafeed174676b4aeb55ce8576233
timeCreated: 1735231561

View File

@@ -0,0 +1,369 @@
using System;
using System.Collections.Generic;
using TEngine;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
namespace GameLogic
{
public interface ISingleton
{
/// <summary>
/// 激活接口,通常用于在某个时机手动实例化
/// </summary>
void Active();
/// <summary>
/// 释放接口
/// </summary>
void Release();
}
public interface IUpdate
{
/// <summary>
/// 游戏框架模块轮询。
/// </summary>
void OnUpdate();
}
public interface IFixedUpdate
{
/// <summary>
/// 游戏框架模块轮询。
/// </summary>
void OnFixedUpdate();
}
public interface ILateUpdate
{
/// <summary>
/// 游戏框架模块轮询。
/// </summary>
void OnLateUpdate();
}
public interface IDrawGizmos
{
void OnDrawGizmos();
}
public interface IDrawGizmosSelected
{
void OnDrawGizmosSelected();
}
/// <summary>
/// 框架中的全局对象与Unity场景依赖相关的DontDestroyOnLoad需要统一管理方便重启游戏时清除工作
/// </summary>
public static class SingletonSystem
{
private static IUpdateDriver _updateDriver;
private static readonly List<ISingleton> _singletons = new List<ISingleton>();
private static readonly List<IUpdate> _updates = new List<IUpdate>();
private static readonly List<IFixedUpdate> _fixedUpdates = new List<IFixedUpdate>();
private static readonly List<ILateUpdate> _lateUpdates = new List<ILateUpdate>();
#if UNITY_EDITOR
private static readonly List<IDrawGizmos> _drawGizmos = new List<IDrawGizmos>();
private static readonly List<IDrawGizmosSelected> _drawGizmosSelecteds = new List<IDrawGizmosSelected>();
#endif
private static readonly Dictionary<string, GameObject> _gameObjects = new Dictionary<string, GameObject>();
public static void Retain(ISingleton singleton)
{
CheckInit();
_singletons.Add(singleton);
BuildLifeCycle(singleton);
}
public static void Retain(GameObject go, object singleton)
{
CheckInit();
if (_gameObjects.TryAdd(go.name, go))
{
if (Application.isPlaying)
{
Object.DontDestroyOnLoad(go);
}
BuildLifeCycle(singleton);
}
}
private static void BuildLifeCycle(object singleton)
{
Type iUpdate = typeof(IUpdate);
bool needUpdate = iUpdate.IsInstanceOfType(singleton);
if (needUpdate && singleton is IUpdate update)
{
_updates.Add(update);
}
Type iFixedUpdate = typeof(IFixedUpdate);
bool needFixedUpdate = iFixedUpdate.IsInstanceOfType(singleton);
if (needFixedUpdate && singleton is IFixedUpdate fixedUpdate)
{
_fixedUpdates.Add(fixedUpdate);
}
Type iLateUpdate = typeof(ILateUpdate);
bool needLateUpdate = iLateUpdate.IsInstanceOfType(singleton);
if (needLateUpdate && singleton is ILateUpdate lateUpdate)
{
_lateUpdates.Add(lateUpdate);
}
#if UNITY_EDITOR
Type iDrawGizmos = typeof(IDrawGizmos);
bool needDrawGizmos = iDrawGizmos.IsInstanceOfType(singleton);
if (needDrawGizmos && singleton is IDrawGizmos drawGizmos)
{
_drawGizmos.Add(drawGizmos);
}
Type iDrawGizmosSelected = typeof(IDrawGizmosSelected);
bool needDrawGizmosSelected = iDrawGizmosSelected.IsInstanceOfType(singleton);
if (needDrawGizmosSelected && singleton is IDrawGizmosSelected drawGizmosSelected)
{
_drawGizmosSelecteds.Add(drawGizmosSelected);
}
#endif
}
public static void Release(GameObject go, object singleton)
{
if (_gameObjects != null && _gameObjects.ContainsKey(go.name))
{
_gameObjects.Remove(go.name);
Object.Destroy(go);
ReleaseLifeCycle(singleton);
}
}
public static void Release(ISingleton singleton)
{
if (_singletons != null && _singletons.Contains(singleton))
{
_singletons.Remove(singleton);
ReleaseLifeCycle(singleton);
}
}
private static void ReleaseLifeCycle(object singleton)
{
Type iUpdate = typeof(IUpdate);
bool needUpdate = iUpdate.IsInstanceOfType(singleton);
if (needUpdate && singleton is IUpdate update)
{
if (_updates.Contains(update))
{
_updates.Remove(update);
}
}
Type iFixedUpdate = typeof(IFixedUpdate);
bool needFixedUpdate = iFixedUpdate.IsInstanceOfType(singleton);
if (needFixedUpdate && singleton is IFixedUpdate fixedUpdate)
{
if (_fixedUpdates.Contains(fixedUpdate))
{
_fixedUpdates.Remove(fixedUpdate);
}
}
Type iLateUpdate = typeof(ILateUpdate);
bool needLateUpdate = iLateUpdate.IsInstanceOfType(singleton);
if (needLateUpdate && singleton is ILateUpdate lateUpdate)
{
if (_lateUpdates.Contains(lateUpdate))
{
_lateUpdates.Remove(lateUpdate);
}
}
#if UNITY_EDITOR
Type iDrawGizmos = typeof(IDrawGizmos);
bool needDrawGizmos = iDrawGizmos.IsInstanceOfType(singleton);
if (needDrawGizmos && singleton is IDrawGizmos drawGizmos)
{
if (_drawGizmos.Contains(drawGizmos))
{
_drawGizmos.Remove(drawGizmos);
}
}
Type iDrawGizmosSelected = typeof(IDrawGizmosSelected);
bool needDrawGizmosSelected = iDrawGizmosSelected.IsInstanceOfType(singleton);
if (needDrawGizmosSelected && singleton is IDrawGizmosSelected drawGizmosSelected)
{
if (_drawGizmosSelecteds.Contains(drawGizmosSelected))
{
_drawGizmosSelecteds.Remove(drawGizmosSelected);
}
}
#endif
}
public static void Release()
{
if (_gameObjects != null)
{
foreach (var item in _gameObjects)
{
Object.Destroy(item.Value);
}
_gameObjects.Clear();
}
if (_singletons != null)
{
for (int i = _singletons.Count - 1; i >= 0; i--)
{
_singletons[i].Release();
}
_singletons.Clear();
}
Resources.UnloadUnusedAssets();
}
public static GameObject GetGameObject(string name)
{
GameObject go = null;
if (_gameObjects != null)
{
_gameObjects.TryGetValue(name, out go);
}
return go;
}
internal static bool ContainsKey(string name)
{
if (_gameObjects != null)
{
return _gameObjects.ContainsKey(name);
}
return false;
}
public static void Restart()
{
if (Camera.main != null)
{
Camera.main.gameObject.SetActive(false);
}
Release();
SceneManager.LoadScene(0);
}
internal static ISingleton GetSingleton(string name)
{
for (int i = 0; i < _singletons.Count; ++i)
{
if (_singletons[i].ToString() == name)
{
return _singletons[i];
}
}
return null;
}
#region
private static bool m_isInit = false;
private static void CheckInit()
{
if (m_isInit == true)
{
return;
}
m_isInit = true;
_updateDriver ??= ModuleSystem.GetModule<IUpdateDriver>();
_updateDriver.AddUpdateListener(OnUpdate);
_updateDriver.AddFixedUpdateListener(OnFixedUpdate);
_updateDriver.AddLateUpdateListener(OnLateUpdate);
#if UNITY_EDITOR
_updateDriver.AddOnDrawGizmosListener(OnDrawGizmos);
_updateDriver.AddOnDrawGizmosSelectedListener(OnDrawGizmosSelected);
#endif
}
private static void DeInit()
{
if (m_isInit == false)
{
return;
}
m_isInit = false;
_updateDriver ??= ModuleSystem.GetModule<IUpdateDriver>();
_updateDriver.RemoveUpdateListener(OnUpdate);
_updateDriver.RemoveFixedUpdateListener(OnFixedUpdate);
_updateDriver.RemoveLateUpdateListener(OnLateUpdate);
#if UNITY_EDITOR
_updateDriver.RemoveOnDrawGizmosListener(OnDrawGizmos);
_updateDriver.RemoveOnDrawGizmosSelectedListener(OnDrawGizmosSelected);
#endif
}
private static void OnUpdate()
{
foreach (var update in _updates)
{
update.OnUpdate();
}
}
private static void OnFixedUpdate()
{
foreach (var fixedUpdate in _fixedUpdates)
{
fixedUpdate.OnFixedUpdate();
}
}
private static void OnLateUpdate()
{
foreach (var lateUpdate in _lateUpdates)
{
lateUpdate.OnLateUpdate();
}
}
private static void OnDrawGizmos()
{
#if UNITY_EDITOR
foreach (var drawGizmo in _drawGizmos)
{
drawGizmo.OnDrawGizmos();
}
#endif
}
private static void OnDrawGizmosSelected()
{
#if UNITY_EDITOR
foreach (var drawGizmosSelected in _drawGizmosSelecteds)
{
drawGizmosSelected.OnDrawGizmosSelected();
}
#endif
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4f62c29005dd4afcac0324f56012ece8
timeCreated: 1735231561

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: df698de4bdfd42f5957e14bd0bf18bb2
timeCreated: 1741271003

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ef88305cf33b40c4abb95b6f6c0b632a
timeCreated: 1741272372

View File

@@ -0,0 +1,12 @@
using System.Collections;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
namespace GameLogic
{
[Window(UILayer.Bottom, fullScreen: true)]
class BattleMainUI : UIWindow
{
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: bde7ed0ea10cf29448370b39ecd69a97
guid: 7732574955f8c1c4a90f8f1610853321
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 757413612c6b420b8da76dcaf86e50f9
timeCreated: 1741271008

View File

@@ -0,0 +1,13 @@
using UnityEngine.UI;
using TEngine;
using Log = TEngine.Log;
namespace GameLogic
{
[Window(UILayer.UI)]
class LoginUI : UIWindow
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0c5b8cbc255542c8b3b0c19b1f8f0804
timeCreated: 1729498406

View File

@@ -2,9 +2,11 @@
"name": "GameProto",
"rootNamespace": "",
"references": [
"GUID:cbb0d51b565003841ae81cdbaf747114",
"GUID:24c092aee38482f4e80715eaa8148782",
"GUID:f51ebe6a0ceec4240a699833d6309b23"
"GUID:756335c0388f7114790e504ed368ae1d",
"GUID:47f9fc774596be54ebfed7739cd70c86",
"GUID:1aa3e8589868c80499255710874679c0",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:24c092aee38482f4e80715eaa8148782"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8f58f15387c7a6f4fad9857024eb47f7
guid: af760644fe07b7945b1afa1ccb7cff14
AssemblyDefinitionImporter:
externalObjects: {}
userData:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 155fdc354ff753047b6c9b6683f2d173
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 49afb8bbdbfbf014baadbaed2e2bebee
guid: c71653bf190ecf441bc2abdb88928964
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -1,4 +1,3 @@
namespace Luban
{
public abstract class BeanBase

View File

@@ -14,7 +14,6 @@ namespace Luban
{
foreach (var p in o.GetType().GetFields())
{
sb.Append($"{p.Name} = {p.GetValue(o)},");
}