[增加]1. 增加初始化版本基础

This commit is contained in:
Blank 2024-08-14 15:06:22 +08:00
commit 4903db5fbd
31 changed files with 1544 additions and 0 deletions

8
Editor.meta Normal file
View File

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

View File

@ -0,0 +1,19 @@
{
"name": "GameFrameX.UI.Editor",
"references": [
"GameFrameX.Editor",
"GameFrameX.UI.Runtime",
"GameFrameX.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 594836a3547f8684b87c3ad614e8c3c1
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,22 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFrameX.Editor;
using GameFrameX.UI.Runtime;
using UnityEditor;
namespace GameFrameX.UI.Editor
{
[CustomEditor(typeof(UIComponent))]
internal sealed class UIComponentInspector : ComponentTypeComponentInspector
{
protected override void RefreshTypeNames()
{
RefreshComponentTypeNames(typeof(IUIManager));
}
}
}

View File

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

201
LICENSE.md Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2023] [ALianBlank of copyright owner][alianblank@outlook.com][https://alianblank.com/]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

7
LICENSE.md.meta Normal file
View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e8e6d4d0681133a4db1d37468165aa77
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

19
README.md Normal file
View File

@ -0,0 +1,19 @@
## HOMEPAGE
GameFrameX 的 UI 资源组件
**UI 资源组件 (UI Component)** - 提供UI相关的接口。
# 使用文档(文档编写于GPT4)
## 注意事项
# 使用方式(任选其一)
1. 直接在 `manifest.json` 的文件中的 `dependencies` 节点下添加以下内容
```json
{"com.gameframex.unity.ui": "https://github.com/gameframex/com.gameframex.unity.ui.git"}
```
2. 在Unity 的`Packages Manager` 中使用`Git URL` 的方式添加库,地址为https://github.com/gameframex/com.gameframex.unity.ui.git
3. 直接下载仓库放置到Unity 项目的`Packages` 目录下。会自动加载识别

7
README.md.meta Normal file
View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e9d16ed3054820345a199b386c358106
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Runtime.meta Normal file
View File

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

View File

@ -0,0 +1,19 @@
{
"name": "GameFrameX.UI.Runtime",
"rootNamespace": "GameFrameX.UI.Runtime",
"references": [
"GameFrameX.Runtime",
"GameFrameX.Asset.Runtime",
"YooAsset.Runtime",
"UniTask.Runtime"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 00aa3e3791886d34b82527d486ecc1b7
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
using UnityEngine;
using UnityEngine.Scripting;
namespace GameFrameX.UI.Runtime
{
[Preserve]
public class GameFrameXUICroppingHelper : MonoBehaviour
{
[Preserve]
private void Start()
{
_ = typeof(IUIManager);
_ = typeof(UIManager);
_ = typeof(UI);
_ = typeof(UIComponent);
_ = typeof(UILayer);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ff72df8c11c54aa6ab023d704f0a6dca
timeCreated: 1723608483

20
Runtime/IUIManager.cs Normal file
View File

@ -0,0 +1,20 @@
namespace GameFrameX.UI.Runtime
{
/// <summary>
/// UGUI接口。
/// </summary>
public interface IUIManager
{
/// <summary>
/// 打开UI界面
/// </summary>
/// <param name="panelName"></param>
void Show(string panelName);
/// <summary>
/// 关闭UI界面
/// </summary>
/// <param name="panelName"></param>
void Hide(string panelName);
}
}

View File

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

479
Runtime/UI.cs Normal file
View File

@ -0,0 +1,479 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GameFrameX.ObjectPool;
using UnityEngine;
namespace GameFrameX.UI.Runtime
{
public abstract class UI : ObjectBase, IDisposable
{
/// <summary>
/// 用户自定义数据
/// </summary>
protected object UserData { get; private set; }
/// <summary>
/// 界面显示之前触发
/// </summary>
public Action<UI> OnShowBeforeAction { get; set; }
/// <summary>
/// 界面显示之后触发
/// </summary>
public Action<UI> OnShowAfterAction { get; set; }
/// <summary>
/// 界面隐藏之前触发
/// </summary>
public Action<UI> OnHideBeforeAction { get; set; }
/// <summary>
/// 界面隐藏之后触发
/// </summary>
public Action<UI> OnHideAfterAction { get; set; }
/// <summary>
/// 记录初始化UI是否是显示的状态
/// </summary>
protected bool IsInitVisible { get; }
public UI(GameObject owner, object userData = null, bool isRoot = false)
{
UserData = userData;
Owner = owner;
IsInitVisible = Owner.activeSelf;
IsRoot = isRoot;
InitView();
// 在初始化的时候先隐藏UI。后续由声明周期控制
// if (parent == null)
// {
// SetVisibleWithNoNotify(false);
// }
Init();
// parent?.Add(this);
if (owner.name.IsNullOrWhiteSpace())
{
Name = GetType().Name;
}
else
{
Name = owner.name;
}
}
protected virtual void InitView()
{
}
/// <summary>
/// 界面添加到UI系统之前执行
/// </summary>
protected virtual void Init()
{
// Log.Info("Init " + Name);
}
/// <summary>
/// 界面显示后执行
/// </summary>
protected virtual void OnShow()
{
// Log.Info("OnShow " + Name);
}
/// <summary>
/// 界面显示之后执行,设置数据和多语言建议在这里设置
/// </summary>
public virtual void Refresh()
{
// Log.Info("Refresh " + Name);
}
/// <summary>
/// 界面隐藏之前执行
/// </summary>
protected virtual void OnHide()
{
// Log.Info("OnHide " + Name);
}
/// <summary>
/// UI 对象销毁之前执行
/// </summary>
protected virtual void OnDispose()
{
// Log.Info("OnDispose " + Name);
}
/// <summary>
/// 显示UI
/// </summary>
public virtual void Show(object userData = null)
{
UserData = userData;
// Log.Info("Show " + Name);
if (Visible)
{
OnShowBeforeAction?.Invoke(this);
OnShow();
OnShowAfterAction?.Invoke(this);
Refresh();
return;
}
Visible = true;
}
/// <summary>
/// 隐藏UI
/// </summary>
public virtual void Hide()
{
// Log.Info("Hide " + Name);
if (!Visible)
{
OnHideBeforeAction?.Invoke(this);
OnHide();
OnHideAfterAction?.Invoke(this);
return;
}
Visible = false;
}
/// <summary>
/// UI 对象
/// </summary>
public GameObject Owner { get; }
/// <summary>
/// 是否是UI根
/// </summary>
public bool IsRoot { get; private set; }
/// <summary>
/// UI 变换对象
/// </summary>
public Transform Transform
{
get { return Owner?.transform; }
}
/// <summary>
/// UI 名称
/// </summary>
public sealed override string Name
{
get
{
if (Owner == null)
{
return string.Empty;
}
return Owner.name;
}
protected set
{
if (Owner == null)
{
return;
}
if (Owner.name.IsNotNullOrWhiteSpace() && Owner.name == value)
{
return;
}
Owner.name = value;
}
}
protected override void Release(bool isShutdown)
{
OnShowBeforeAction = null;
OnShowAfterAction = null;
OnHideBeforeAction = null;
OnHideAfterAction = null;
Parent = null;
}
/// <summary>
/// 设置UI的显示状态不发出事件
/// </summary>
/// <param name="value"></param>
public virtual void SetVisibleWithNoNotify(bool value)
{
if (Owner.activeSelf == value)
{
return;
}
Owner.SetActive(value);
}
/// <summary>
/// 获取UI是否显示
/// </summary>
public bool IsVisible
{
get { return Visible; }
}
protected virtual bool Visible
{
get
{
if (Owner == null)
{
return false;
}
return Owner.activeSelf;
}
set
{
if (Owner == null)
{
return;
}
if (Owner.activeSelf == value)
{
return;
}
if (value == false)
{
OnHideBeforeAction?.Invoke(this);
foreach (var child in this.Children)
{
child.Value.Visible = value;
}
OnHide();
OnHideAfterAction?.Invoke(this);
}
Owner.SetActive(value);
if (value)
{
OnShowBeforeAction?.Invoke(this);
foreach (var child in Children)
{
child.Value.Visible = value;
}
OnShow();
OnShowAfterAction?.Invoke(this);
Refresh();
}
}
}
/// <summary>
/// 界面对象是否为空
/// </summary>
public bool IsEmpty
{
get { return Owner == null; }
}
protected readonly Dictionary<string, UI> Children = new Dictionary<string, UI>();
/// <summary>
/// 是否从对象池获取
/// </summary>
protected bool IsFromPool { get; set; }
protected bool IsDisposed;
/// <summary>
/// 销毁UI对象
/// </summary>
public virtual void Dispose()
{
if (IsDisposed)
{
return;
}
IsDisposed = true;
// 删除所有的孩子
DisposeChildren();
// 删除自己的UI
/*if (!IsRoot)
{
RemoveFromParent();
}*/
// 释放UI
OnDispose();
// 删除自己的UI
/*if (!IsRoot)
{
GObject.DestroyObject();
}*/
Release(false);
// isFromFGUIPool = false;
}
/// <summary>
/// 添加UI对象到子级列表
/// </summary>
/// <param name="ui"></param>
/// <param name="index">添加到的目标UI层级索引位置</param>
/// <exception cref="Exception"></exception>
public void Add(UI ui, int index = -1)
{
if (ui == null || ui.IsEmpty)
{
throw new Exception($"ui can not be empty");
}
if (string.IsNullOrWhiteSpace(ui.Name))
{
throw new Exception($"ui.Name can not be empty");
}
if (Children.ContainsKey(ui.Name))
{
throw new Exception($"ui.Name({ui.Name}) already exist");
}
AddInner(ui, index);
}
protected virtual void AddInner(UI ui, int index = -1)
{
Children.Add(ui.Name, ui);
if (index < 0 || index > Children.Count)
{
ui.Transform.SetParent(Transform);
}
else
{
ui.Transform.SetParent(Transform);
ui.Transform.SetSiblingIndex(index);
}
ui.Parent = this;
if (ui.IsInitVisible)
{
// 显示UI
ui.Show(ui.UserData);
}
}
/// <summary>
/// UI 父级对象
/// </summary>
public UI Parent { get; protected set; }
/// <summary>
/// 设置当前UI对象为全屏
/// </summary>
public abstract void MakeFullScreen();
/// <summary>
/// 将自己从父级UI对象删除
/// </summary>
public virtual void RemoveFromParent()
{
Parent?.Remove(Name);
}
/// <summary>
/// 删除指定UI名称的UI对象
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public virtual bool Remove(string name)
{
if (Children.TryGetValue(name, out var ui))
{
Children.Remove(name);
if (ui != null)
{
ui.RemoveChildren();
ui.Hide();
return true;
}
}
return false;
}
/// <summary>
/// 销毁所有自己对象
/// </summary>
public void DisposeChildren()
{
if (Children.Count > 0)
{
var children = GetAll();
foreach (var child in children)
{
child.Dispose();
}
Children.Clear();
}
}
/// <summary>
/// 删除所有子级UI对象
/// </summary>
public void RemoveChildren()
{
if (Children.Count > 0)
{
var children = GetAll();
foreach (var child in children)
{
child.RemoveFromParent();
}
Children.Clear();
}
}
/// <summary>
/// 根据 UI名称 获取子级UI对象
/// </summary>
/// <param name="name">UI名称</param>
/// <returns></returns>
public UI Get(string name)
{
if (Children.TryGetValue(name, out var child))
{
return child;
}
return null;
}
/// <summary>
/// 获取所有的子级UI非递归
/// </summary>
/// <returns></returns>
public UI[] GetAll()
{
return Children.Values.ToArray();
}
}
}

3
Runtime/UI.cs.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ddd408eb103c29b43bcefdc5c970cc6f
timeCreated: 1722830399

454
Runtime/UIComponent.cs Normal file
View File

@ -0,0 +1,454 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using GameFrameX.Asset.Runtime;
using UnityEngine;
using GameFrameX.Runtime;
namespace GameFrameX.UI.Runtime
{
/// <summary>
/// UI组件
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("Game Framework/UI")]
public abstract class UIComponent : GameFrameworkComponent
{
private IUIManager m_UIManager;
protected IAssetManager AssetManager;
protected UI Root;
protected UI HiddenRoot;
protected UI FloorRoot;
protected UI NormalRoot;
protected UI FixedRoot;
protected UI WindowRoot;
protected UI TipRoot;
protected UI BlackBoardRoot;
protected UI DialogueRoot;
protected UI GuideRoot;
protected UI LoadingRoot;
protected UI NotifyRoot;
protected UI SystemRoot;
private readonly Dictionary<UILayer, Dictionary<string, UI>> m_UILayerDictionary = new Dictionary<UILayer, Dictionary<string, UI>>(16);
private void Start()
{
AssetManager = GameFrameworkEntry.GetModule<IAssetManager>();
if (AssetManager == null)
{
Log.Fatal("Asset manager is invalid.");
return;
}
}
protected override void Awake()
{
ImplementationComponentType = Type.GetType(componentType);
InterfaceComponentType = typeof(IUIManager);
base.Awake();
m_UIManager = GameFrameworkEntry.GetModule<IUIManager>();
if (m_UIManager == null)
{
Log.Fatal("UI manager is invalid.");
return;
}
Root = CreateRootNode();
GameFrameworkGuard.NotNull(Root, nameof(Root));
Root.Show();
HiddenRoot = CreateNode(Root, UILayer.Hidden);
FloorRoot = CreateNode(Root, UILayer.Floor);
NormalRoot = CreateNode(Root, UILayer.Normal);
FixedRoot = CreateNode(Root, UILayer.Fixed);
WindowRoot = CreateNode(Root, UILayer.Window);
TipRoot = CreateNode(Root, UILayer.Tip);
BlackBoardRoot = CreateNode(Root, UILayer.BlackBoard);
DialogueRoot = CreateNode(Root, UILayer.Dialogue);
GuideRoot = CreateNode(Root, UILayer.Guide);
LoadingRoot = CreateNode(Root, UILayer.Loading);
NotifyRoot = CreateNode(Root, UILayer.Notify);
SystemRoot = CreateNode(Root, UILayer.System);
m_UILayerDictionary[UILayer.Hidden] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Floor] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Normal] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Fixed] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Window] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Tip] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.BlackBoard] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Dialogue] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Guide] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Loading] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.Notify] = new Dictionary<string, UI>(64);
m_UILayerDictionary[UILayer.System] = new Dictionary<string, UI>(64);
}
/// <summary>
/// 创建根节点
/// </summary>
/// <returns></returns>
protected abstract UI CreateRootNode();
/// <summary>
/// 创建UI节点
/// </summary>
/// <param name="root">根节点</param>
/// <param name="layer">UI层级</param>
/// <returns></returns>
protected abstract UI CreateNode(object root, UILayer layer);
/// <summary>
/// 添加全屏UI对象
/// </summary>
/// <param name="creator">UI创建器</param>
/// <param name="descFilePath">UI目录</param>
/// <param name="layer">目标层级</param>
/// <param name="userData">用户自定义数据</param>
/// <typeparam name="T"></typeparam>
/// <returns>返回创建后的UI对象</returns>
public T AddToFullScreen<T>(System.Func<object, T> creator, string descFilePath, UILayer layer, object userData = null) where T : UI
{
return Add<T>(creator, descFilePath, layer, true, userData);
}
/// <summary>
/// 异步添加UI 对象
/// </summary>
/// <param name="creator">UI创建器</param>
/// <param name="descFilePath">UI目录</param>
/// <param name="layer">目标层级</param>
/// <param name="isFullScreen">是否全屏</param>
/// <param name="userData">用户自定义数据</param>
/// <typeparam name="T"></typeparam>
/// <returns>返回创建后的UI对象</returns>
public abstract UniTask<T> AddAsync<T>(System.Func<object, T> creator, string descFilePath, UILayer layer, bool isFullScreen = false, object userData = null) where T : UI;
/// <summary>
/// 添加UI对象
/// </summary>
/// <param name="creator">UI创建器</param>
/// <param name="descFilePath">UI目录</param>
/// <param name="layer">目标层级</param>
/// <param name="isFullScreen">是否全屏</param>
/// <param name="userData">用户自定义数据</param>
/// <typeparam name="T"></typeparam>
/// <returns>返回创建后的UI对象</returns>
/// <exception cref="ArgumentNullException">创建器不存在,引发参数异常</exception>
public virtual T Add<T>(System.Func<object, T> creator, string descFilePath, UILayer layer, bool isFullScreen = false, object userData = null) where T : UI
{
GameFrameworkGuard.NotNull(creator, nameof(creator));
GameFrameworkGuard.NotNull(descFilePath, nameof(descFilePath));
var ui = creator(userData);
Add(ui, layer);
if (isFullScreen)
{
ui.MakeFullScreen();
}
return ui;
}
/// <summary>
/// 从UI管理列表中删除所有的UI
/// </summary>
public void RemoveAll()
{
var tempKv = new Dictionary<string, UI>(32);
foreach (var kv in m_UILayerDictionary)
{
tempKv.Clear();
foreach (var fui in kv.Value)
{
tempKv[fui.Key] = fui.Value;
}
foreach (var fui in tempKv)
{
Remove(fui.Key);
fui.Value.Dispose();
}
kv.Value.Clear();
}
tempKv.Clear();
m_UILayerDictionary.Clear();
}
protected UI Add(UI ui, UILayer layer)
{
GameFrameworkGuard.NotNull(ui, nameof(ui));
if (m_UILayerDictionary[layer].ContainsKey(ui.Name))
{
return m_UILayerDictionary[layer][ui.Name];
}
m_UILayerDictionary[layer][ui.Name] = ui;
switch (layer)
{
case UILayer.Hidden:
HiddenRoot.Add(ui);
break;
case UILayer.Floor:
FloorRoot.Add(ui);
break;
case UILayer.Normal:
NormalRoot.Add(ui);
break;
case UILayer.Fixed:
FixedRoot.Add(ui);
break;
case UILayer.Window:
WindowRoot.Add(ui);
break;
case UILayer.Tip:
TipRoot.Add(ui);
break;
case UILayer.BlackBoard:
BlackBoardRoot.Add(ui);
break;
case UILayer.Dialogue:
DialogueRoot.Add(ui);
break;
case UILayer.Guide:
GuideRoot.Add(ui);
break;
case UILayer.Loading:
LoadingRoot.Add(ui);
break;
case UILayer.Notify:
NotifyRoot.Add(ui);
break;
case UILayer.System:
SystemRoot.Add(ui);
break;
}
return ui;
}
/// <summary>
/// 根据UI名称从UI管理列表中移除
/// </summary>
/// <param name="uiName"></param>
/// <returns></returns>
public bool Remove(string uiName)
{
GameFrameworkGuard.NotNullOrEmpty(uiName, nameof(uiName));
if (SystemRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.System].Remove(uiName);
return true;
}
if (NotifyRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Notify].Remove(uiName);
return true;
}
if (HiddenRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Hidden].Remove(uiName);
return true;
}
if (FloorRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Floor].Remove(uiName);
return true;
}
if (NormalRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Normal].Remove(uiName);
return true;
}
if (FixedRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Fixed].Remove(uiName);
return true;
}
if (WindowRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Window].Remove(uiName);
return true;
}
if (TipRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Tip].Remove(uiName);
return true;
}
if (BlackBoardRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.BlackBoard].Remove(uiName);
return true;
}
if (DialogueRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Dialogue].Remove(uiName);
return true;
}
if (GuideRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Guide].Remove(uiName);
return true;
}
if (LoadingRoot.Remove(uiName))
{
m_UILayerDictionary[UILayer.Loading].Remove(uiName);
return true;
}
return false;
}
/// <summary>
/// 根据UI名称和层级从UI管理列表中移除
/// </summary>
/// <param name="uiName">UI名称</param>
/// <param name="layer">层级</param>
/// <returns></returns>
public void Remove(string uiName, UILayer layer)
{
GameFrameworkGuard.NotNullOrEmpty(uiName, nameof(uiName));
switch (layer)
{
case UILayer.Hidden:
HiddenRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Hidden].Remove(uiName);
break;
case UILayer.Floor:
FloorRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Floor].Remove(uiName);
break;
case UILayer.Normal:
NormalRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Normal].Remove(uiName);
break;
case UILayer.Fixed:
FixedRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Fixed].Remove(uiName);
break;
case UILayer.Window:
WindowRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Window].Remove(uiName);
break;
case UILayer.Tip:
TipRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Tip].Remove(uiName);
break;
case UILayer.BlackBoard:
BlackBoardRoot.Remove(uiName);
m_UILayerDictionary[UILayer.BlackBoard].Remove(uiName);
break;
case UILayer.Dialogue:
DialogueRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Dialogue].Remove(uiName);
break;
case UILayer.Guide:
GuideRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Guide].Remove(uiName);
break;
case UILayer.Loading:
LoadingRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Loading].Remove(uiName);
break;
case UILayer.Notify:
NotifyRoot.Remove(uiName);
m_UILayerDictionary[UILayer.Notify].Remove(uiName);
break;
case UILayer.System:
SystemRoot.Remove(uiName);
m_UILayerDictionary[UILayer.System].Remove(uiName);
break;
}
}
/// <summary>
/// 判断UI名称是否在UI管理列表
/// </summary>
/// <param name="uiName">UI名称</param>
/// <returns></returns>
public bool Has(string uiName)
{
GameFrameworkGuard.NotNullOrEmpty(uiName, nameof(uiName));
return Get(uiName) != null;
}
/// <summary>
/// 判断UI是否在UI管理列表如果存在则返回对象不存在返回空值
/// </summary>
/// <param name="uiName">UI名称</param>
/// <param name="fui"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public bool Has<T>(string uiName, out T fui) where T : UI
{
GameFrameworkGuard.NotNullOrEmpty(uiName, nameof(uiName));
var ui = Get(uiName);
fui = ui as T;
return fui != null;
}
/// <summary>
/// 根据UI名称获取UI对象
/// </summary>
/// <param name="uiName">UI名称</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Get<T>(string uiName) where T : UI
{
T fui = default;
GameFrameworkGuard.NotNullOrEmpty(uiName, nameof(uiName));
foreach (var kv in m_UILayerDictionary)
{
if (kv.Value.TryGetValue(uiName, out var ui))
{
fui = ui as T;
break;
}
}
return fui;
}
/// <summary>
/// 根据UI名称获取UI对象
/// </summary>
/// <param name="uiName"></param>
/// <returns></returns>
public UI Get(string uiName)
{
GameFrameworkGuard.NotNullOrEmpty(uiName, nameof(uiName));
foreach (var kv in m_UILayerDictionary)
{
if (kv.Value.TryGetValue(uiName, out var ui))
{
return ui;
}
}
return null;
}
protected virtual void OnDestroy()
{
RemoveAll();
Root?.Dispose();
Root = null;
}
}
}

View File

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

68
Runtime/UILayer.cs Normal file
View File

@ -0,0 +1,68 @@
namespace GameFrameX.UI.Runtime
{
/// <summary>
/// UI层级
/// </summary>
public enum UILayer
{
/// <summary>
/// 隐藏
/// </summary>
Hidden = 20,
/// <summary>
/// 底板
/// </summary>
Floor = 15,
/// <summary>
/// 正常
/// </summary>
Normal = 10,
/// <summary>
/// 固定
/// </summary>
Fixed = 0,
/// <summary>
/// 窗口
/// </summary>
Window = -10,
/// <summary>
/// 提示
/// </summary>
Tip = -15,
/// <summary>
/// 引导
/// </summary>
Guide = -20,
/// <summary>
/// 引导
/// </summary>
BlackBoard = -22,
/// <summary>
/// 引导
/// </summary>
Dialogue = -23,
/// <summary>
/// Loading
/// </summary>
Loading = -25,
/// <summary>
/// 通知
/// </summary>
Notify = -30,
/// <summary>
/// 系统顶级
/// </summary>
System = -35,
}
}

3
Runtime/UILayer.cs.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 766aaa212162db545a7a4dcaacca6e22
timeCreated: 1722830399

29
Runtime/UIManager.cs Normal file
View File

@ -0,0 +1,29 @@
namespace GameFrameX.UI.Runtime
{
public sealed class UIManager : GameFrameworkModule, IUIManager
{
public void Hide(string panelName)
{
}
public void Show(string panelName)
{
}
/// <summary>
/// 全局配置管理器轮询。
/// </summary>
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
protected override void Update(float elapseSeconds, float realElapseSeconds)
{
}
/// <summary>
/// 关闭并清理全局配置管理器。
/// </summary>
protected override void Shutdown()
{
}
}
}

11
Runtime/UIManager.cs.meta Normal file
View File

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

8
Tests.meta Normal file
View File

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

View File

@ -0,0 +1,17 @@
{
"name": "GameFrameX.UI.Tests",
"references": [
"GameFrameX.UI.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1ac48a5b181db134281ee44850277a1a
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

25
Tests/UnitTests.cs Normal file
View File

@ -0,0 +1,25 @@
using System;
using NUnit.Framework;
namespace GameFrameX.UI.Tests
{
internal class UnitTests
{
private DateTime dateTime, dateTime1;
[SetUp]
public void Setup()
{
dateTime = DateTime.Now;
dateTime1 = DateTime.Now.AddHours(1);
}
[Test]
public void Test1()
{
Assert.That(dateTime1.Year, Is.EqualTo(dateTime.Year));
Assert.That(dateTime1.Month, Is.EqualTo(dateTime.Month));
Assert.That(dateTime1.Day, Is.EqualTo(dateTime.Day));
}
}
}

11
Tests/UnitTests.cs.meta Normal file
View File

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

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "com.gameframex.unity.ui",
"displayName": "Game Frame X UI",
"category": "Game Framework X",
"description": "Game Frame X UI Component",
"version": "1.0.0",
"unity": "2017.1",
"keywords": [
"Game Framework X"
],
"repository": {
"name": "com.gameframex.unity.ui",
"url": "https://github.com/gameframex/com.gameframex.unity.ui.git",
"type": "git"
},
"author": {
"name": "Blank",
"email": "alianblank@outlook.com",
"url": "https://alianblank.com/"
},
"dependencies": {
}
}

7
package.json.meta Normal file
View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 72fe41e134403ba4196ccd3fdc89941c
PackageManifestImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: