Compare commits
2 Commits
c28b27513e
...
9df0b196f6
Author | SHA1 | Date |
---|---|---|
|
9df0b196f6 | |
|
c53b3e138e |
|
@ -3,3 +3,24 @@
|
|||
/Logs
|
||||
/Temp
|
||||
/UserSettings
|
||||
/.vs
|
||||
/.vsconfig
|
||||
/Assembly-CSharp.csproj
|
||||
/Assembly-CSharp-Editor.csproj
|
||||
/Assembly-CSharp-firstpass.csproj
|
||||
/ignore.conf
|
||||
/Manual.docx
|
||||
/MotionFramework.csproj
|
||||
/NaughtyAttributes.Core.csproj
|
||||
/NaughtyAttributes.Editor.csproj
|
||||
/NaughtyAttributes.Test.csproj
|
||||
/packages.config
|
||||
/U3D_TobaccoWarehouseISMDTSystem.sln
|
||||
/UniTask.Addressables.csproj
|
||||
/UniTask.csproj
|
||||
/UniTask.DOTween.csproj
|
||||
/UniTask.Editor.csproj
|
||||
/UniTask.Linq.csproj
|
||||
/UniTask.TextMeshPro.csproj
|
||||
/Краткое описание.docx
|
||||
/obj
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f4a3ce4290fe60641955eb4a6d7cf61c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,44 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
public class BulkRenamer : EditorWindow
|
||||
{
|
||||
private int fixedRow = 1; // Row to use for all names
|
||||
private int fixedLayer = 1; // Layer to use for all names
|
||||
private int startingColumn = 1; // Starting column number
|
||||
|
||||
[MenuItem("Tools/Bulk Renamer")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<BulkRenamer>("Bulk Renamer");
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Bulk Renamer Settings", EditorStyles.boldLabel);
|
||||
fixedRow = EditorGUILayout.IntField("Fixed Row", fixedRow);
|
||||
startingColumn = EditorGUILayout.IntField("Starting Column", startingColumn);
|
||||
fixedLayer = EditorGUILayout.IntField("Fixed Layer", fixedLayer);
|
||||
|
||||
if (GUILayout.Button("Rename Selected Objects"))
|
||||
{
|
||||
RenameObjects();
|
||||
}
|
||||
}
|
||||
|
||||
void RenameObjects()
|
||||
{
|
||||
if (Selection.gameObjects.Length == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("No objects selected", "Please select at least one object in the scene.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
int currentColumn = startingColumn;
|
||||
foreach (GameObject obj in Selection.gameObjects)
|
||||
{
|
||||
obj.name = $"{fixedRow}-{currentColumn}-{fixedLayer}";
|
||||
currentColumn++; // Only increment column
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3480beb204ff1a84c8c039548c95ac2d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f3d83d63307c65b4a86e4328b6436709
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Framework.Scripts.Runtime.Engine.Engine.Camera;
|
||||
using UnityEngine;
|
||||
|
||||
// 添加游戏开发中常用的游戏模块的命名空间
|
||||
using MotionFramework;
|
||||
using MotionFramework.Console;
|
||||
using MotionFramework.Event;
|
||||
using MotionFramework.Scripts.Runtime.Engine.Engine.Network.WebRequest;
|
||||
using MotionFramework.Utility;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class GameLauncher : MonoBehaviour
|
||||
{
|
||||
public string sceneName;
|
||||
void Awake()
|
||||
{
|
||||
// 初始化框架
|
||||
MotionEngine.Initialize(this, HandleMotionFrameworkLog);
|
||||
}
|
||||
void Start()
|
||||
{
|
||||
// 创建游戏模块
|
||||
CreateGameModules();
|
||||
|
||||
SceneManager.LoadScene(sceneName);
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
// 更新框架
|
||||
MotionEngine.Update();
|
||||
}
|
||||
|
||||
private async void CreateGameModules()
|
||||
{
|
||||
//webrequest管理器
|
||||
MotionEngine.CreateModule<WebRequestManager>();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void HandleMotionFrameworkLog(ELogLevel logLevel, string log)
|
||||
{
|
||||
if (logLevel == ELogLevel.Log)
|
||||
{
|
||||
UnityEngine.Debug.Log(log);
|
||||
}
|
||||
else if (logLevel == ELogLevel.Error)
|
||||
{
|
||||
UnityEngine.Debug.LogError(log);
|
||||
}
|
||||
else if (logLevel == ELogLevel.Warning)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning(log);
|
||||
}
|
||||
else if (logLevel == ELogLevel.Exception)
|
||||
{
|
||||
UnityEngine.Debug.LogError(log);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"{logLevel}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9285dff9c8040aa4488bf8974674f237
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2f4cf2a58e574d74aac5310ccd75525a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 388029185fa707340a861f57cd175df2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a5ef71c752c838c459544a86dd58463c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 427646cfb10656d498f5e8d094a680f2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 03ce8fe28e97d2f40b814dcae8cad75d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
|
||||
namespace MotionFramework
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志等级
|
||||
/// </summary>
|
||||
public enum ELogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// 信息
|
||||
/// </summary>
|
||||
Log,
|
||||
|
||||
/// <summary>
|
||||
/// 警告
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// 异常
|
||||
/// </summary>
|
||||
Exception,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae9a7933bf5cb574ab1fa7b757931fcc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
|
||||
namespace MotionFramework
|
||||
{
|
||||
public interface IActivatorServices
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建实例
|
||||
/// </summary>
|
||||
object CreateInstance(Type classType);
|
||||
|
||||
/// <summary>
|
||||
/// 获取特性
|
||||
/// </summary>
|
||||
Attribute GetAttribute(Type classType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: beee09ffd49d7ef49a983e15cca8fe5e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
|
||||
namespace MotionFramework
|
||||
{
|
||||
public interface IModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建模块
|
||||
/// </summary>
|
||||
void OnCreate(System.Object createParam);
|
||||
|
||||
/// <summary>
|
||||
/// 轮询模块
|
||||
/// </summary>
|
||||
void OnUpdate();
|
||||
|
||||
/// <summary>
|
||||
/// 销毁模块
|
||||
/// </summary>
|
||||
void OnDestroy();
|
||||
|
||||
/// <summary>
|
||||
/// GUI绘制
|
||||
/// </summary>
|
||||
void OnGUI();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f4757101f8553d49ad6da27c94c50c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace MotionFramework
|
||||
{
|
||||
/// <summary>
|
||||
/// 同步其它线程里的回调到主线程里
|
||||
/// 注意:Unity3D中需要设置Scripting Runtime Version为.NET4.6
|
||||
/// </summary>
|
||||
public sealed class MainThreadSyncContext : SynchronizationContext
|
||||
{
|
||||
private readonly ConcurrentQueue<Action> _safeQueue = new ConcurrentQueue<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// 更新同步队列
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_safeQueue.TryDequeue(out Action action) == false)
|
||||
return;
|
||||
action.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向同步队列里投递一个回调方法
|
||||
/// </summary>
|
||||
public override void Post(SendOrPostCallback callback, object state)
|
||||
{
|
||||
Action action = new Action(() => { callback(state); });
|
||||
_safeQueue.Enqueue(action);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8dc088445ce08dc4e81bfe04a381b449
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
|
||||
namespace MotionFramework
|
||||
{
|
||||
public abstract class ModuleSingleton<T> where T : class, IModule
|
||||
{
|
||||
private static T _instance;
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
MotionLog.Error($"{typeof(T)} is not create. Use {nameof(MotionEngine)}.{nameof(MotionEngine.CreateModule)} create.");
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
protected ModuleSingleton()
|
||||
{
|
||||
if (_instance != null)
|
||||
throw new System.Exception($"{typeof(T)} instance already created.");
|
||||
_instance = this as T;
|
||||
}
|
||||
protected void DestroySingleton()
|
||||
{
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b6c8230c0d097534e950f4e84ce01e96
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,267 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MotionFramework
|
||||
{
|
||||
public static class MotionEngine
|
||||
{
|
||||
private class ModuleWrapper
|
||||
{
|
||||
public int Priority { private set; get; }
|
||||
public IModule Module { private set; get; }
|
||||
|
||||
public ModuleWrapper(IModule module, int priority)
|
||||
{
|
||||
Module = module;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<ModuleWrapper> _coms = new List<ModuleWrapper>(100);
|
||||
private static MonoBehaviour _behaviour;
|
||||
private static bool _isDirty = false;
|
||||
private static long _frame = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化框架
|
||||
/// </summary>
|
||||
public static void Initialize(MonoBehaviour behaviour, Action<ELogLevel, string> logCallback)
|
||||
{
|
||||
if (behaviour == null)
|
||||
throw new Exception("MotionFramework behaviour is null.");
|
||||
if (_behaviour != null)
|
||||
throw new Exception($"{nameof(MotionEngine)} is already initialized.");
|
||||
|
||||
UnityEngine.Object.DontDestroyOnLoad(behaviour.gameObject);
|
||||
_behaviour = behaviour;
|
||||
|
||||
// 注册日志回调
|
||||
if (logCallback != null)
|
||||
MotionLog.RegisterCallback(logCallback);
|
||||
|
||||
behaviour.StartCoroutine(CheckFrame());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测MotionEngine更新方法
|
||||
/// </summary>
|
||||
private static IEnumerator CheckFrame()
|
||||
{
|
||||
var wait = new WaitForSeconds(1f);
|
||||
yield return wait;
|
||||
|
||||
// 说明:初始化之后,如果忘记更新MotionEngine,这里会抛出异常
|
||||
if (_frame == 0)
|
||||
throw new Exception($"Please call update method : MotionEngine.Update");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新框架
|
||||
/// </summary>
|
||||
public static void Update()
|
||||
{
|
||||
_frame++;
|
||||
|
||||
// 如果有新模块需要重新排序
|
||||
if (_isDirty)
|
||||
{
|
||||
_isDirty = false;
|
||||
_coms.Sort((left, right) =>
|
||||
{
|
||||
if (left.Priority > right.Priority)
|
||||
return -1;
|
||||
else if (left.Priority == right.Priority)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
||||
// 轮询所有模块
|
||||
for (int i = 0; i < _coms.Count; i++)
|
||||
{
|
||||
_coms[i].Module.OnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制所有模块的GUI内容
|
||||
/// </summary>
|
||||
internal static void DrawModulesGUIContent()
|
||||
{
|
||||
for (int i = 0; i < _coms.Count; i++)
|
||||
{
|
||||
_coms[i].Module.OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询游戏模块是否存在
|
||||
/// </summary>
|
||||
public static bool Contains<T>() where T : class, IModule
|
||||
{
|
||||
System.Type type = typeof(T);
|
||||
return Contains(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询游戏模块是否存在
|
||||
/// </summary>
|
||||
public static bool Contains(System.Type moduleType)
|
||||
{
|
||||
for (int i = 0; i < _coms.Count; i++)
|
||||
{
|
||||
if (_coms[i].Module.GetType() == moduleType)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建游戏模块
|
||||
/// </summary>
|
||||
/// <typeparam name="T">模块类</typeparam>
|
||||
/// <param name="priority">运行时的优先级,优先级越大越早执行。如果没有设置优先级,那么会按照添加顺序执行</param>
|
||||
public static T CreateModule<T>(int priority = 0) where T : class, IModule
|
||||
{
|
||||
return CreateModule<T>(null, priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建游戏模块
|
||||
/// </summary>
|
||||
/// <typeparam name="T">模块类</typeparam>
|
||||
/// <param name="createParam">创建参数</param>
|
||||
/// <param name="priority">运行时的优先级,优先级越大越早执行。如果没有设置优先级,那么会按照添加顺序执行</param>
|
||||
public static T CreateModule<T>(System.Object createParam, int priority = 0) where T : class, IModule
|
||||
{
|
||||
if (priority < 0)
|
||||
throw new Exception("The priority can not be negative");
|
||||
|
||||
if (Contains(typeof(T)))
|
||||
throw new Exception($"Game module {typeof(T)} is already existed");
|
||||
|
||||
// 如果没有设置优先级
|
||||
if (priority == 0)
|
||||
{
|
||||
int minPriority = GetMinPriority();
|
||||
priority = --minPriority;
|
||||
}
|
||||
|
||||
MotionLog.Log($"Create game module : {typeof(T)}");
|
||||
T module = Activator.CreateInstance<T>();
|
||||
ModuleWrapper wrapper = new ModuleWrapper(module, priority);
|
||||
wrapper.Module.OnCreate(createParam);
|
||||
_coms.Add(wrapper);
|
||||
_isDirty = true;
|
||||
return module;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 销毁模块
|
||||
/// </summary>
|
||||
/// <typeparam name="T">模块类</typeparam>
|
||||
public static bool DestroyModule<T>()
|
||||
{
|
||||
var moduleType = typeof(T);
|
||||
for (int i = 0; i < _coms.Count; i++)
|
||||
{
|
||||
if (_coms[i].Module.GetType() == moduleType)
|
||||
{
|
||||
_coms[i].Module.OnDestroy();
|
||||
_coms.RemoveAt(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取游戏模块
|
||||
/// </summary>
|
||||
/// <typeparam name="T">模块类</typeparam>
|
||||
public static T GetModule<T>() where T : class, IModule
|
||||
{
|
||||
System.Type type = typeof(T);
|
||||
for (int i = 0; i < _coms.Count; i++)
|
||||
{
|
||||
if (_coms[i].Module.GetType() == type)
|
||||
return _coms[i].Module as T;
|
||||
}
|
||||
|
||||
MotionLog.Warning($"Not found game module {type}");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前模块里最小的优先级
|
||||
/// </summary>
|
||||
private static int GetMinPriority()
|
||||
{
|
||||
int minPriority = 0;
|
||||
for (int i = 0; i < _coms.Count; i++)
|
||||
{
|
||||
if (_coms[i].Priority < minPriority)
|
||||
minPriority = _coms[i].Priority;
|
||||
}
|
||||
return minPriority; //小于等于零
|
||||
}
|
||||
|
||||
#region 协程相关
|
||||
/// <summary>
|
||||
/// 开启一个协程
|
||||
/// </summary>
|
||||
public static Coroutine StartCoroutine(IEnumerator coroutine)
|
||||
{
|
||||
if (_behaviour == null)
|
||||
throw new Exception($"{nameof(MotionEngine)} is not initialize. Use MotionEngine.Initialize");
|
||||
return _behaviour.StartCoroutine(coroutine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止一个协程
|
||||
/// </summary>
|
||||
public static void StopCoroutine(Coroutine coroutine)
|
||||
{
|
||||
if (_behaviour == null)
|
||||
throw new Exception($"{nameof(MotionEngine)} is not initialize. Use MotionEngine.Initialize");
|
||||
_behaviour.StopCoroutine(coroutine);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开启一个协程
|
||||
/// </summary>
|
||||
public static void StartCoroutine(string methodName)
|
||||
{
|
||||
if (_behaviour == null)
|
||||
throw new Exception($"{nameof(MotionEngine)} is not initialize. Use MotionEngine.Initialize");
|
||||
_behaviour.StartCoroutine(methodName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止一个协程
|
||||
/// </summary>
|
||||
public static void StopCoroutine(string methodName)
|
||||
{
|
||||
if (_behaviour == null)
|
||||
throw new Exception($"{nameof(MotionEngine)} is not initialize. Use MotionEngine.Initialize");
|
||||
_behaviour.StopCoroutine(methodName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 停止所有协程
|
||||
/// </summary>
|
||||
public static void StopAllCoroutines()
|
||||
{
|
||||
if (_behaviour == null)
|
||||
throw new Exception($"{nameof(MotionEngine)} is not initialize. Use MotionEngine.Initialize");
|
||||
_behaviour.StopAllCoroutines();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b799b83d73a9cb543b2504ccb1cf796a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
|
||||
namespace MotionFramework
|
||||
{
|
||||
internal static class MotionLog
|
||||
{
|
||||
private static System.Action<ELogLevel, string> _callback;
|
||||
|
||||
/// <summary>
|
||||
/// 监听日志
|
||||
/// </summary>
|
||||
public static void RegisterCallback(System.Action<ELogLevel, string> callback)
|
||||
{
|
||||
_callback += callback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
public static void Log(string info)
|
||||
{
|
||||
_callback?.Invoke(ELogLevel.Log, $"[MotionLog] {info}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 警告
|
||||
/// </summary>
|
||||
public static void Warning(string info)
|
||||
{
|
||||
_callback?.Invoke(ELogLevel.Warning, $"[MotionLog] {info}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
public static void Error(string info)
|
||||
{
|
||||
_callback?.Invoke(ELogLevel.Error, $"[MotionLog] {info}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异常
|
||||
/// </summary>
|
||||
public static void Exception(string info)
|
||||
{
|
||||
_callback?.Invoke(ELogLevel.Exception, $"[MotionLog] {info}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dbad3cd04905bab4fb6391f5184bcfa0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2d31ab63c6e8982469ffce74914f4a4b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using MotionFramework.Experimental.Animation;
|
||||
|
||||
[RequireComponent(typeof(Animator))]
|
||||
public class AnimBehaviour : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public class AnimationWrapper
|
||||
{
|
||||
public int Layer;
|
||||
public WrapMode Mode;
|
||||
public AnimationClip Clip;
|
||||
}
|
||||
|
||||
private AnimPlayable _animPlayable;
|
||||
private Animator _animator;
|
||||
|
||||
[SerializeField]
|
||||
protected AnimationWrapper[] _animations;
|
||||
|
||||
[SerializeField]
|
||||
protected bool _playAutomatically = true;
|
||||
|
||||
[SerializeField]
|
||||
protected bool _animatePhysics = false;
|
||||
|
||||
/// <summary>
|
||||
/// 自动播放动画
|
||||
/// </summary>
|
||||
public bool PlayAutomatically
|
||||
{
|
||||
get
|
||||
{
|
||||
return _playAutomatically;
|
||||
}
|
||||
set
|
||||
{
|
||||
_playAutomatically = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 物理更新模式
|
||||
/// </summary>
|
||||
public bool AnimatePhysics
|
||||
{
|
||||
get
|
||||
{
|
||||
return _animatePhysics;
|
||||
}
|
||||
set
|
||||
{
|
||||
_animatePhysics = value;
|
||||
_animator.updateMode = _animatePhysics ? AnimatorUpdateMode.AnimatePhysics : AnimatorUpdateMode.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
_animator = GetComponent<Animator>();
|
||||
_animator.updateMode = _animatePhysics ? AnimatorUpdateMode.AnimatePhysics : AnimatorUpdateMode.Normal;
|
||||
|
||||
_animPlayable = new AnimPlayable();
|
||||
_animPlayable.Create(_animator);
|
||||
|
||||
// 添加列表动作
|
||||
for (int i = 0; i < _animations.Length; i++)
|
||||
{
|
||||
var wrapper = _animations[i];
|
||||
if (wrapper == null || wrapper.Clip == null)
|
||||
continue;
|
||||
|
||||
wrapper.Clip.wrapMode = wrapper.Mode;
|
||||
_animPlayable.AddAnimation(wrapper.Clip.name, wrapper.Clip, wrapper.Layer);
|
||||
}
|
||||
}
|
||||
public void OnEnable()
|
||||
{
|
||||
_animPlayable.PlayGraph();
|
||||
|
||||
if (PlayAutomatically)
|
||||
{
|
||||
var wrapper = GetDefaultWrapper();
|
||||
if (wrapper != null)
|
||||
{
|
||||
Play(wrapper.Clip.name, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
_animPlayable.Update(float.MaxValue);
|
||||
}
|
||||
public void OnDisable()
|
||||
{
|
||||
_animPlayable.StopGraph();
|
||||
}
|
||||
public void OnDestroy()
|
||||
{
|
||||
_animPlayable.Destroy();
|
||||
}
|
||||
public void Update()
|
||||
{
|
||||
_animPlayable.Update(Time.deltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取动画状态
|
||||
/// </summary>
|
||||
public AnimState GetState(string name)
|
||||
{
|
||||
return _animPlayable.GetAnimState(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动画是否在播放中
|
||||
/// </summary>
|
||||
public bool IsPlaying(string name)
|
||||
{
|
||||
return _animPlayable.IsPlaying(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含动画片段
|
||||
/// </summary>
|
||||
public bool IsContains(string name)
|
||||
{
|
||||
return _animPlayable.IsContains(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放动画
|
||||
/// </summary>
|
||||
public void Play(string name, float fadeLength = 0.25f)
|
||||
{
|
||||
_animPlayable.Play(name, fadeLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止动画
|
||||
/// </summary>
|
||||
public void Stop(string name)
|
||||
{
|
||||
_animPlayable.Stop(name);
|
||||
}
|
||||
|
||||
private AnimationWrapper GetDefaultWrapper()
|
||||
{
|
||||
for (int i = 0; i < _animations.Length; i++)
|
||||
{
|
||||
var wrapper = _animations[i];
|
||||
if (wrapper == null || wrapper.Clip == null)
|
||||
continue;
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef52174816df6104587b15c5b250201a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,196 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Playables;
|
||||
using UnityEngine.Animations;
|
||||
|
||||
namespace MotionFramework.Experimental.Animation
|
||||
{
|
||||
public sealed class AnimMixer : AnimNode
|
||||
{
|
||||
private const float HIDE_DURATION = 0.25f;
|
||||
private readonly List<AnimState> _states = new List<AnimState>(10);
|
||||
private AnimationMixerPlayable _mixer;
|
||||
private bool _isQuiting = false;
|
||||
|
||||
/// <summary>
|
||||
/// 动画层级
|
||||
/// </summary>
|
||||
public int Layer { private set; get; }
|
||||
|
||||
|
||||
public AnimMixer(PlayableGraph graph, int layer) : base(graph)
|
||||
{
|
||||
Layer = layer;
|
||||
|
||||
_mixer = AnimationMixerPlayable.Create(graph);
|
||||
SetSourcePlayable(_mixer);
|
||||
}
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
for (int i = 0; i < _states.Count; i++)
|
||||
{
|
||||
var state = _states[i];
|
||||
if (state != null)
|
||||
state.Update(deltaTime);
|
||||
}
|
||||
|
||||
bool isAllDone = true;
|
||||
for (int i = 0; i < _states.Count; i++)
|
||||
{
|
||||
var state = _states[i];
|
||||
if (state != null)
|
||||
{
|
||||
if (state.IsDone == false)
|
||||
isAllDone = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 当子节点都已经完成的时候断开连接
|
||||
if (isAllDone && _isQuiting == false)
|
||||
{
|
||||
_isQuiting = true;
|
||||
StartWeightFade(0, HIDE_DURATION);
|
||||
}
|
||||
if (_isQuiting)
|
||||
{
|
||||
if (Mathf.Approximately(Weight, 0f))
|
||||
DisconnectMixer();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放指定动画
|
||||
/// </summary>
|
||||
public void Play(AnimState animState, float fadeDuration)
|
||||
{
|
||||
// 重新激活混合器
|
||||
_isQuiting = false;
|
||||
StartWeightFade(1f, 0);
|
||||
|
||||
if (IsContains(animState) == false)
|
||||
{
|
||||
// 优先插入到一个空位
|
||||
int index = _states.FindIndex(s => s == null);
|
||||
if (index == -1)
|
||||
{
|
||||
// Increase input count
|
||||
int inputCount = _mixer.GetInputCount();
|
||||
_mixer.SetInputCount(inputCount + 1);
|
||||
|
||||
animState.Connect(_mixer, inputCount);
|
||||
_states.Add(animState);
|
||||
}
|
||||
else
|
||||
{
|
||||
animState.Connect(_mixer, index);
|
||||
_states[index] = animState;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _states.Count; i++)
|
||||
{
|
||||
var state = _states[i];
|
||||
if (state == null)
|
||||
continue;
|
||||
|
||||
if (state == animState)
|
||||
{
|
||||
state.StartWeightFade(1f, fadeDuration);
|
||||
state.PlayNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
state.StartWeightFade(0f, fadeDuration);
|
||||
state.PauseNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止指定动画,恢复为初始状态
|
||||
/// </summary>
|
||||
public void Stop(string name)
|
||||
{
|
||||
AnimState state = FindState(name);
|
||||
if (state == null)
|
||||
return;
|
||||
|
||||
state.PauseNode();
|
||||
state.ResetNode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停所有动画
|
||||
/// </summary>
|
||||
public void PauseAll()
|
||||
{
|
||||
for (int i = 0; i < _states.Count; i++)
|
||||
{
|
||||
var state = _states[i];
|
||||
if (state == null)
|
||||
continue;
|
||||
state.PauseNode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含该动画
|
||||
/// </summary>
|
||||
public bool IsContains(AnimNode node)
|
||||
{
|
||||
foreach (var state in _states)
|
||||
{
|
||||
if (state == node)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除一个动画
|
||||
/// </summary>
|
||||
public void RemoveState(string name)
|
||||
{
|
||||
var state = FindState(name);
|
||||
if (state == null)
|
||||
return;
|
||||
|
||||
_states[state.InputPort] = null;
|
||||
state.Destroy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定的动画
|
||||
/// </summary>
|
||||
/// <returns>如果没有返回NULL</returns>
|
||||
private AnimState FindState(string name)
|
||||
{
|
||||
foreach (var state in _states)
|
||||
{
|
||||
if (state != null && state.Name == name)
|
||||
return state;
|
||||
}
|
||||
|
||||
MotionLog.Warning($"Animation state doesn't exist : {name}");
|
||||
return null;
|
||||
}
|
||||
|
||||
private void DisconnectMixer()
|
||||
{
|
||||
for (int i = 0; i < _states.Count; i++)
|
||||
{
|
||||
var state = _states[i];
|
||||
if (state == null)
|
||||
continue;
|
||||
|
||||
state.Disconnect();
|
||||
_states[i] = null;
|
||||
}
|
||||
|
||||
Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b33204afd07c00c4f8b425276778b8bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,221 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Playables;
|
||||
using UnityEngine.Animations;
|
||||
|
||||
namespace MotionFramework.Experimental.Animation
|
||||
{
|
||||
public abstract class AnimNode
|
||||
{
|
||||
private readonly PlayableGraph _graph;
|
||||
private Playable _source;
|
||||
private Playable _parent;
|
||||
|
||||
private float _fadeSpeed = 0f;
|
||||
private float _fadeWeight = 0f;
|
||||
private bool _isFading = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经连接
|
||||
/// </summary>
|
||||
public bool IsConnect { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 输入端口
|
||||
/// </summary>
|
||||
public int InputPort { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经完成
|
||||
/// If the duration of the playable is set, when the time of the playable reaches its duration during playback this flag will be set to true.
|
||||
/// </summary>
|
||||
public bool IsDone
|
||||
{
|
||||
get
|
||||
{
|
||||
return _source.IsDone();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否有效
|
||||
/// if the Playable is properly constructed by the PlayableGraph and has not been destroyed, false otherwise.
|
||||
/// </summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return _source.IsValid();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否正在播放中
|
||||
/// </summary>
|
||||
public bool IsPlaying
|
||||
{
|
||||
get
|
||||
{
|
||||
return _source.GetPlayState() == PlayState.Playing;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间轴
|
||||
/// </summary>
|
||||
public float Time
|
||||
{
|
||||
set
|
||||
{
|
||||
_source.SetTime(value);
|
||||
}
|
||||
get
|
||||
{
|
||||
return (float)_source.GetTime();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放速度
|
||||
/// </summary>
|
||||
public float Speed
|
||||
{
|
||||
set
|
||||
{
|
||||
_source.SetSpeed(value);
|
||||
}
|
||||
get
|
||||
{
|
||||
return (float)_source.GetSpeed();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 权重值
|
||||
/// </summary>
|
||||
public float Weight
|
||||
{
|
||||
set
|
||||
{
|
||||
_parent.SetInputWeight(InputPort, value);
|
||||
}
|
||||
get
|
||||
{
|
||||
return _parent.GetInputWeight(InputPort);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public AnimNode(PlayableGraph graph)
|
||||
{
|
||||
_graph = graph;
|
||||
}
|
||||
public virtual void Update(float deltaTime)
|
||||
{
|
||||
if (_isFading)
|
||||
{
|
||||
Weight = Mathf.MoveTowards(Weight, _fadeWeight, _fadeSpeed * deltaTime);
|
||||
if (Mathf.Approximately(Weight, _fadeWeight))
|
||||
{
|
||||
_isFading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public virtual void Destroy()
|
||||
{
|
||||
if (IsValid)
|
||||
{
|
||||
_graph.DestroySubgraph(_source);
|
||||
}
|
||||
}
|
||||
public virtual void PlayNode()
|
||||
{
|
||||
// NOTE : When playing, the local time of this Playable will be updated during the evaluation of the PlayableGraph.
|
||||
_source.Play();
|
||||
|
||||
// NOTE : Changes a flag indicating that a playable has completed its operation.
|
||||
// Playable that reach the end of their duration are automatically marked as done.
|
||||
_source.SetDone(false);
|
||||
}
|
||||
public virtual void PauseNode()
|
||||
{
|
||||
// NOTE : When paused, the local time of this Playable will not be updated during the evaluation of the PlayableGraph.
|
||||
_source.Pause();
|
||||
|
||||
// NOTE : Changes a flag indicating that a playable has completed its operation.
|
||||
// Playable that reach the end of their duration are automatically marked as done.
|
||||
_source.SetDone(true);
|
||||
}
|
||||
public virtual void ResetNode()
|
||||
{
|
||||
_fadeSpeed = 0;
|
||||
_fadeWeight = 0;
|
||||
_isFading = false;
|
||||
|
||||
Time = 0;
|
||||
Speed = 1;
|
||||
Weight = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接到父节点
|
||||
/// </summary>
|
||||
/// <param name="parent">父节点对象</param>
|
||||
/// <param name="inputPort">父节点上的输入端口</param>
|
||||
public void Connect(Playable parent, int parentInputPort)
|
||||
{
|
||||
if (IsConnect)
|
||||
throw new System.Exception("AnimNode is connected.");
|
||||
|
||||
_parent = parent;
|
||||
InputPort = parentInputPort;
|
||||
|
||||
// 重置节点
|
||||
ResetNode();
|
||||
|
||||
// 连接
|
||||
_graph.Connect(_source, 0, parent, parentInputPort);
|
||||
IsConnect = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同父节点断开连接
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
if (IsConnect == false)
|
||||
throw new System.Exception("AnimNode is disconnected.");
|
||||
|
||||
// 断开
|
||||
_graph.Disconnect(_parent, InputPort);
|
||||
IsConnect = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始权重值过渡
|
||||
/// </summary>
|
||||
/// <param name="destWeight">目标权重值</param>
|
||||
/// <param name="fadeDuration">过渡时间</param>
|
||||
public void StartWeightFade(float destWeight, float fadeDuration)
|
||||
{
|
||||
if (fadeDuration <= 0)
|
||||
{
|
||||
Weight = destWeight;
|
||||
_isFading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//注意:保持统一的渐变速度
|
||||
_fadeSpeed = 1f / fadeDuration;
|
||||
_fadeWeight = destWeight;
|
||||
_isFading = true;
|
||||
}
|
||||
|
||||
protected void SetSourcePlayable(Playable playable)
|
||||
{
|
||||
_source = playable;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b6928afd60ab2ed489e492e00d9ddaba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,228 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Playables;
|
||||
using UnityEngine.Animations;
|
||||
|
||||
namespace MotionFramework.Experimental.Animation
|
||||
{
|
||||
public class AnimPlayable
|
||||
{
|
||||
private readonly List<AnimState> _states = new List<AnimState>(10);
|
||||
private readonly List<AnimMixer> _mixers = new List<AnimMixer>(10);
|
||||
|
||||
private PlayableGraph _graph;
|
||||
private AnimationPlayableOutput _output;
|
||||
private AnimationLayerMixerPlayable _mixerRoot;
|
||||
|
||||
public void Create(Animator animator)
|
||||
{
|
||||
string name = animator.gameObject.name;
|
||||
_graph = PlayableGraph.Create(name);
|
||||
_graph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
|
||||
|
||||
_mixerRoot = AnimationLayerMixerPlayable.Create(_graph);
|
||||
_output = AnimationPlayableOutput.Create(_graph, name, animator);
|
||||
_output.SetSourcePlayable(_mixerRoot);
|
||||
}
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
_graph.Evaluate(deltaTime);
|
||||
|
||||
// 更新所有层级
|
||||
for (int i = 0; i < _mixers.Count; i++)
|
||||
{
|
||||
var mixer = _mixers[i];
|
||||
if(mixer.IsConnect)
|
||||
mixer.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
public void Destroy()
|
||||
{
|
||||
_graph.Destroy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the graph
|
||||
/// </summary>
|
||||
public void PlayGraph()
|
||||
{
|
||||
_graph.Play();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the graph
|
||||
/// </summary>
|
||||
public void StopGraph()
|
||||
{
|
||||
_graph.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测动画是否正在播放
|
||||
/// </summary>
|
||||
/// <param name="name">动画名称</param>
|
||||
public bool IsPlaying(string name)
|
||||
{
|
||||
AnimState state = GetAnimState(name);
|
||||
if (state == null)
|
||||
return false;
|
||||
|
||||
return state.IsConnect && state.IsPlaying;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放一个动画
|
||||
/// </summary>
|
||||
/// <param name="name">动画名称</param>
|
||||
/// <param name="fadeLength">融合时间</param>
|
||||
public void Play(string name, float fadeLength)
|
||||
{
|
||||
var animState = GetAnimState(name);
|
||||
if (animState == null)
|
||||
{
|
||||
MotionLog.Warning($"Not found animation {name}");
|
||||
return;
|
||||
}
|
||||
|
||||
int layer = animState.Layer;
|
||||
var animMixer = GetAnimMixer(layer);
|
||||
if (animMixer == null)
|
||||
animMixer = CreateAnimMixer(layer);
|
||||
|
||||
if(animMixer.IsConnect == false)
|
||||
animMixer.Connect(_mixerRoot, animMixer.Layer);
|
||||
|
||||
animMixer.Play(animState, fadeLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止一个动画
|
||||
/// </summary>
|
||||
/// <param name="name">动画名称</param>
|
||||
public void Stop(string name)
|
||||
{
|
||||
var animState = GetAnimState(name);
|
||||
if (animState == null)
|
||||
{
|
||||
MotionLog.Warning($"Not found animation {name}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (animState.IsConnect == false)
|
||||
return;
|
||||
|
||||
var animMixer = GetAnimMixer(animState.Layer);
|
||||
if (animMixer == null)
|
||||
throw new System.Exception("Should never get here.");
|
||||
|
||||
animMixer.Stop(animState.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个动画片段
|
||||
/// </summary>
|
||||
/// <param name="name">动画名称</param>
|
||||
/// <param name="clip">动画片段</param>
|
||||
/// <param name="layer">动画层级</param>
|
||||
public bool AddAnimation(string name, AnimationClip clip, int layer = 0)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new System.ArgumentException("Name is null or empty.");
|
||||
if (clip == null)
|
||||
throw new System.ArgumentNullException();
|
||||
if (layer < 0)
|
||||
throw new System.Exception("Layer must be greater than zero.");
|
||||
|
||||
if (IsContains(name))
|
||||
{
|
||||
MotionLog.Warning($"Animation already exists : {name}");
|
||||
return false;
|
||||
}
|
||||
|
||||
AnimState stateNode = new AnimState(_graph, clip, name, layer);
|
||||
_states.Add(stateNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除一个动画片段
|
||||
/// </summary>
|
||||
/// <param name="name">动画名称</param>
|
||||
public bool RemoveAnimation(string name)
|
||||
{
|
||||
if (IsContains(name) == false)
|
||||
{
|
||||
MotionLog.Warning($"Not found Animation : {name}");
|
||||
return false;
|
||||
}
|
||||
|
||||
AnimState animState = GetAnimState(name);
|
||||
AnimMixer animMixer = GetAnimMixer(animState.Layer);
|
||||
if(animMixer != null)
|
||||
animMixer.RemoveState(animState.Name);
|
||||
|
||||
animState.Destroy();
|
||||
_states.Remove(animState);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个动画状态
|
||||
/// </summary>
|
||||
/// <param name="name">动画名称</param>
|
||||
public AnimState GetAnimState(string name)
|
||||
{
|
||||
for (int i = 0; i < _states.Count; i++)
|
||||
{
|
||||
if (_states[i].Name == name)
|
||||
return _states[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含一个动画状态
|
||||
/// </summary>
|
||||
/// <param name="name">动画名称</param>
|
||||
public bool IsContains(string name)
|
||||
{
|
||||
for (int i = 0; i < _states.Count; i++)
|
||||
{
|
||||
if (_states[i].Name == name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private AnimMixer GetAnimMixer(int layer)
|
||||
{
|
||||
for (int i = 0; i < _mixers.Count; i++)
|
||||
{
|
||||
if (_mixers[i].Layer == layer)
|
||||
return _mixers[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private AnimMixer CreateAnimMixer(int layer)
|
||||
{
|
||||
// Increase input count
|
||||
int inputCount = _mixerRoot.GetInputCount();
|
||||
if(layer == 0 && inputCount == 0)
|
||||
{
|
||||
_mixerRoot.SetInputCount(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (layer > inputCount - 1)
|
||||
{
|
||||
_mixerRoot.SetInputCount(layer + 1);
|
||||
}
|
||||
}
|
||||
|
||||
var animMixer = new AnimMixer(_graph, layer);
|
||||
_mixers.Add(animMixer);
|
||||
return animMixer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 772b8a9edd8466646bab12d6f5ba1084
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,99 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Playables;
|
||||
using UnityEngine.Animations;
|
||||
|
||||
namespace MotionFramework.Experimental.Animation
|
||||
{
|
||||
public sealed class AnimState : AnimNode
|
||||
{
|
||||
public readonly string Name;
|
||||
private readonly AnimationClip _clip;
|
||||
public AnimationClipPlayable _clipPlayable;
|
||||
|
||||
/// <summary>
|
||||
/// 动画层级
|
||||
/// </summary>
|
||||
public int Layer = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 动画长度
|
||||
/// </summary>
|
||||
public float ClipLength
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_clip == null)
|
||||
return 0f;
|
||||
if (Speed == 0f)
|
||||
return Mathf.Infinity;
|
||||
return _clip.length / Speed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 归一化时间轴
|
||||
/// </summary>
|
||||
public float NormalizedTime
|
||||
{
|
||||
set
|
||||
{
|
||||
if (_clip == null)
|
||||
return;
|
||||
Time = _clip.length * value;
|
||||
}
|
||||
|
||||
get
|
||||
{
|
||||
if (_clip == null)
|
||||
return 1f;
|
||||
return Time / _clip.length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动画模式
|
||||
/// </summary>
|
||||
public WrapMode WrapMode
|
||||
{
|
||||
set
|
||||
{
|
||||
if (_clip != null)
|
||||
_clip.wrapMode = value;
|
||||
}
|
||||
get
|
||||
{
|
||||
if (_clip == null)
|
||||
return WrapMode.Default;
|
||||
return _clip.wrapMode;
|
||||
}
|
||||
}
|
||||
|
||||
public AnimState(PlayableGraph graph, AnimationClip clip, string name, int layer) : base(graph)
|
||||
{
|
||||
_clip = clip;
|
||||
Name = name;
|
||||
Layer = layer;
|
||||
|
||||
_clipPlayable = AnimationClipPlayable.Create(graph, clip);
|
||||
_clipPlayable.SetApplyFootIK(false);
|
||||
_clipPlayable.SetApplyPlayableIK(false);
|
||||
SetSourcePlayable(_clipPlayable);
|
||||
|
||||
if (clip.wrapMode == WrapMode.Once)
|
||||
{
|
||||
_clipPlayable.SetDuration(clip.length);
|
||||
}
|
||||
}
|
||||
public override void PlayNode()
|
||||
{
|
||||
if (_clip.wrapMode == WrapMode.Once || _clip.wrapMode == WrapMode.ClampForever)
|
||||
{
|
||||
Time = 0;
|
||||
}
|
||||
|
||||
base.PlayNode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 606e5729d04cc8742a38f58e595c02f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MotionFramework.Experimental.Animation
|
||||
{
|
||||
public enum EAnimStates
|
||||
{
|
||||
Playing,
|
||||
Paused,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5660d9e37e5c2a44f818506c9fe76624
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 27404a5600794902a08f7d7710e766b3
|
||||
timeCreated: 1716521294
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 436369bf8c5b45a084984e639d160e1d
|
||||
timeCreated: 1716536171
|
|
@ -0,0 +1,236 @@
|
|||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Framework.Scripts.Runtime.Engine.Engine.Camera.CameraControl
|
||||
{
|
||||
public class CameraControlComponent : ICameraController
|
||||
{
|
||||
public enum CameraEnum
|
||||
{
|
||||
TopView,
|
||||
FrontView
|
||||
}
|
||||
|
||||
//相机跟随的目标物体,一般是一个空物体
|
||||
[SerializeField] private GameObject target;
|
||||
|
||||
[HorizontalLine(color: EColor.Blue)] [SerializeField] [Label("启用旋转")]
|
||||
private bool enableMouseFunctions = true;
|
||||
|
||||
[SerializeField] [Label("启用鼠标碰到UI禁用脚本")]
|
||||
private bool enableMouseOverUI = true;
|
||||
|
||||
[SerializeField] private CameraEnum cameraEnum;
|
||||
|
||||
private float middleSpeed = 2;
|
||||
|
||||
private int MouseWheelSensitivity = 20; //滚轮灵敏度设置
|
||||
|
||||
private int MouseZoomMin = 10; //相机距离最小值
|
||||
|
||||
private int MouseZoomMax = 9999; //相机距离最大值
|
||||
|
||||
private float xSpeed = 250.0f; //旋转视角时相机x轴转速
|
||||
|
||||
private float ySpeed = 120.0f; //旋转视角时相机y轴转速
|
||||
|
||||
private float Distance = 20; //相机和target之间的距离,因为相机的Z轴总是指向target,也就是相机z轴方向上的距离
|
||||
|
||||
//[BoxGroup("参数")][SerializeField][Label("角度最小限制")] private int yMinLimit = -360;
|
||||
//[BoxGroup("参数")][SerializeField][Label("角度最大限制")] private int yMaxLimit = 360;
|
||||
|
||||
private Vector2 minMaxSlider;
|
||||
|
||||
private float x = 0.0f; //存储相机的euler角
|
||||
private float y = 0.0f; //存储相机的euler角
|
||||
private Vector3 targetOnScreenPosition; //目标的屏幕坐标,第三个值为z轴距离
|
||||
|
||||
private Quaternion storeRotation; //存储相机的姿态四元数
|
||||
private Vector3 CameraTargetPosition; //target的位置
|
||||
private Vector3 initPosition; //平移时用于存储平移的起点位置
|
||||
private Vector3 cameraX; //相机的x轴方向向量
|
||||
private Vector3 cameraY; //相机的y轴方向向量
|
||||
private Vector3 cameraZ; //相机的z轴方向向量
|
||||
|
||||
private Vector3 initScreenPos; //中键刚按下时鼠标的屏幕坐标(第三个值其实没什么用)
|
||||
private Vector3 curScreenPos; //当前鼠标的屏幕坐标(第三个值其实没什么用)
|
||||
|
||||
|
||||
private Vector2 jilu;
|
||||
|
||||
private Transform cameraTransform;
|
||||
|
||||
public void Init(Transform cam, Transform target)
|
||||
{
|
||||
cameraTransform = cam;
|
||||
this.target = target.gameObject;
|
||||
|
||||
cameraEnum = CameraEnum.FrontView;
|
||||
Vector3 angles = cameraTransform.eulerAngles;
|
||||
x = angles.y;
|
||||
y = angles.x;
|
||||
CameraTargetPosition = target.transform.position;
|
||||
|
||||
Distance = Vector3.Distance(cameraTransform.position, target.transform.position);
|
||||
|
||||
// 给摄像机一个初始的位置和角度
|
||||
SetCameraPositionAndRotation();
|
||||
}
|
||||
|
||||
public void MoveTargetTo(Vector3 newPosition, float newfloat)
|
||||
{
|
||||
target.transform.DOMove(newPosition, 2).OnUpdate(() =>
|
||||
{
|
||||
Vector3 mPosition = storeRotation * new Vector3(0.0F, 0.0F, -Distance) + target.transform.position;
|
||||
cameraTransform.position = mPosition;
|
||||
CameraTargetPosition = target.transform.position;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void SetCameraPositionAndRotation()
|
||||
{
|
||||
//Vector3 position = rotation * new Vector3(0.0f, 0.0f, -11) + target.transform.position;
|
||||
|
||||
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
|
||||
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
|
||||
|
||||
y = ClampAngle(y, minMaxSlider.x, minMaxSlider.y);
|
||||
|
||||
storeRotation = Quaternion.Euler(y, x, 0);
|
||||
var position = storeRotation * new Vector3(0.0f, 0.0f, -Distance) + CameraTargetPosition;
|
||||
|
||||
cameraTransform.rotation = storeRotation;
|
||||
cameraTransform.position = position;
|
||||
}
|
||||
|
||||
|
||||
//将angle限制在min~max之间
|
||||
static float ClampAngle(float angle, float min, float max)
|
||||
{
|
||||
if (angle < -360)
|
||||
angle += 360;
|
||||
if (angle > 360)
|
||||
angle -= 360;
|
||||
return Mathf.Clamp(angle, min, max);
|
||||
}
|
||||
|
||||
|
||||
public GameObject GetTarget()
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
public void UpdateValue(Vector2 minMax)
|
||||
{
|
||||
minMaxSlider = minMax;
|
||||
}
|
||||
|
||||
|
||||
public void ControlCamera()
|
||||
{
|
||||
if (enableMouseOverUI)
|
||||
{
|
||||
if (EventSystem.current.IsPointerOverGameObject())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (enableMouseFunctions)
|
||||
{
|
||||
//鼠标右键旋转功能
|
||||
if (Input.GetMouseButton(1))
|
||||
{
|
||||
//如果是顶视图,只能左右旋转,不能上下
|
||||
if (cameraEnum == CameraEnum.FrontView)
|
||||
{
|
||||
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
|
||||
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
|
||||
|
||||
y = ClampAngle(y, minMaxSlider.x, minMaxSlider.y);
|
||||
|
||||
storeRotation = Quaternion.Euler(y, x, 0);
|
||||
var position = storeRotation * new Vector3(0.0f, 0.0f, -Distance) + CameraTargetPosition;
|
||||
|
||||
// Debug.Log(storeRotation);
|
||||
cameraTransform.rotation = storeRotation;
|
||||
cameraTransform.position = position;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
|
||||
//y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
|
||||
|
||||
//y = ClampAngle(y, minMaxSlider.x, minMaxSlider.y);
|
||||
|
||||
storeRotation = Quaternion.Euler(90, x, 0);
|
||||
var position = storeRotation * new Vector3(0.0f, 0.0f, -Distance) + CameraTargetPosition;
|
||||
|
||||
cameraTransform.rotation = storeRotation;
|
||||
cameraTransform.position = position;
|
||||
}
|
||||
}
|
||||
else if (Input.GetAxis("Mouse ScrollWheel") != 0) //鼠标滚轮缩放功能
|
||||
{
|
||||
if (Distance >= MouseZoomMin && Distance <= MouseZoomMax)
|
||||
{
|
||||
Distance -= Input.GetAxis("Mouse ScrollWheel") * MouseWheelSensitivity;
|
||||
}
|
||||
|
||||
if (Distance < MouseZoomMin)
|
||||
{
|
||||
Distance = MouseZoomMin;
|
||||
}
|
||||
|
||||
if (Distance > MouseZoomMax)
|
||||
{
|
||||
Distance = MouseZoomMax;
|
||||
}
|
||||
|
||||
cameraTransform.position = storeRotation * new Vector3(0.0F, 0.0F, -Distance) + CameraTargetPosition;
|
||||
}
|
||||
|
||||
//鼠标中键平移
|
||||
if (Input.GetMouseButtonDown(2))
|
||||
{
|
||||
cameraX = cameraTransform.right;
|
||||
cameraY = cameraTransform.up;
|
||||
cameraZ = cameraTransform.forward;
|
||||
|
||||
initScreenPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, targetOnScreenPosition.z);
|
||||
Debug.Log("downOnce");
|
||||
|
||||
//targetOnScreenPosition.z为目标物体到相机xmidbuttonDownPositiony平面的法线距离
|
||||
if (UnityEngine.Camera.main != null) targetOnScreenPosition = UnityEngine.Camera.main.WorldToScreenPoint(CameraTargetPosition);
|
||||
initPosition = CameraTargetPosition;
|
||||
}
|
||||
|
||||
if (Input.GetMouseButton(2))
|
||||
{
|
||||
curScreenPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, targetOnScreenPosition.z);
|
||||
//0.01这个系数是控制平移的速度,要根据相机和目标物体的distance来灵活选择
|
||||
Vector3 vv = initPosition - (0.01f * middleSpeed) * ((curScreenPos.x - initScreenPos.x) * cameraX +
|
||||
(curScreenPos.y - initScreenPos.y) * cameraY);
|
||||
target.transform.position = new Vector3(vv.x, vv.y, vv.z);
|
||||
|
||||
//重新计算位置
|
||||
Vector3 mPosition = storeRotation * new Vector3(0.0F, 0.0F, -Distance) + target.transform.position;
|
||||
cameraTransform.position = mPosition;
|
||||
|
||||
// //用这个会让相机的平移变得更平滑,但是可能在你buttonup时未使相机移动到应到的位置,导致再进行旋转与缩放操作时出现短暂抖动
|
||||
//transform.position=Vector3.Lerp(transform.position,mPosition,Time.deltaTime*moveSpeed);
|
||||
}
|
||||
|
||||
if (Input.GetMouseButtonUp(2))
|
||||
{
|
||||
Debug.Log("upOnce");
|
||||
//平移结束把cameraTargetPosition的位置更新一下,不然会影响缩放与旋转功能
|
||||
CameraTargetPosition = target.transform.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f83adf796757446ba4c3b2d84fc9012e
|
||||
timeCreated: 1716536226
|
|
@ -0,0 +1,4 @@
|
|||
public interface ICameraController
|
||||
{
|
||||
void ControlCamera();
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 117f0a495e484ef0b45eb7155a72b5ef
|
||||
timeCreated: 1716536177
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fde27b4e1f5e4fa3ab0b3e7f1249b66a
|
||||
timeCreated: 1716536117
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using MotionFramework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Framework.Scripts.Runtime.Engine.Engine.Camera
|
||||
{
|
||||
|
||||
public class CameraRaycastComponent : IRaycastable
|
||||
{
|
||||
public LayerMask RaycastLayerMask = ~0;
|
||||
public float RaycastDistance = 100f;
|
||||
|
||||
public event Action<RaycastHit> OnRaycastHit;
|
||||
private UnityEngine.Camera camera;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
camera=UnityEngine.Camera.main;
|
||||
}
|
||||
|
||||
public void PerformRaycast()
|
||||
{
|
||||
|
||||
if (camera == null)
|
||||
{
|
||||
Debug.LogError("没有找到主摄像机");
|
||||
return;
|
||||
}
|
||||
|
||||
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, RaycastDistance, RaycastLayerMask))
|
||||
{
|
||||
OnRaycastHit?.Invoke(hit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e00183c47e5a46dbb1eafcfd793004f2
|
||||
timeCreated: 1716530957
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
public interface IRaycastable
|
||||
{
|
||||
|
||||
|
||||
event Action<RaycastHit> OnRaycastHit;
|
||||
|
||||
void PerformRaycast();
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5ff7acc217c44ea0adf9478b454b7e1d
|
||||
timeCreated: 1716536124
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e64000d37ca5cca4eb4908b53f9be554
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace MotionFramework.Console
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ConsoleAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 标题名称
|
||||
/// </summary>
|
||||
public string Title;
|
||||
|
||||
/// <summary>
|
||||
/// 显示顺序
|
||||
/// </summary>
|
||||
public int Order;
|
||||
|
||||
public ConsoleAttribute(string title, int order)
|
||||
{
|
||||
Title = title;
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f02d224d0e39bf24db2e41f0f982b510
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,139 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MotionFramework.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MotionFramework.Console
|
||||
{
|
||||
public static class ConsoleGUI
|
||||
{
|
||||
private static bool _initGlobalStyle = false;
|
||||
|
||||
public static GUIStyle HorizontalScrollbarStyle { private set; get; }
|
||||
public static GUIStyle HorizontalScrollbarThumbStyle { private set; get; }
|
||||
public static GUIStyle VerticalScrollbarStyle { private set; get; }
|
||||
public static GUIStyle VerticalScrollbarThumbStyle { private set; get; }
|
||||
public static GUIStyle XStyle { private set; get; }
|
||||
public static GUIStyle ToolbarStyle { private set; get; }
|
||||
public static GUIStyle ButtonStyle { private set; get; }
|
||||
public static GUIStyle ToogleStyle1 { private set; get; }
|
||||
public static GUIStyle ToogleStyle2 { private set; get; }
|
||||
public static GUIStyle TextFieldStyle { private set; get; }
|
||||
public static GUIStyle LableStyle { private set; get; }
|
||||
public static GUIStyle RichLabelStyle { private set; get; }
|
||||
public static int RichLabelFontSize { private set; get; }
|
||||
|
||||
private static GUIStyle _cachedHorizontalScrollbarThumb;
|
||||
private static GUIStyle _cachedVerticalScrollbarThumb;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一些高度和字体大小固定的控件样式
|
||||
/// 控制台的标准分辨率为 : 1920X1080
|
||||
/// </summary>
|
||||
internal static void InitGlobalStyle()
|
||||
{
|
||||
if (_initGlobalStyle == false)
|
||||
{
|
||||
_initGlobalStyle = true;
|
||||
|
||||
float scale;
|
||||
if (Screen.height > Screen.width)
|
||||
{
|
||||
// 竖屏Portrait
|
||||
scale = Screen.width / 1080f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 横屏Landscape
|
||||
scale = Screen.width / 1920f;
|
||||
}
|
||||
|
||||
HorizontalScrollbarStyle = new GUIStyle(GUI.skin.horizontalScrollbar);
|
||||
HorizontalScrollbarStyle.fixedHeight = (int)(30 * scale);
|
||||
HorizontalScrollbarThumbStyle = new GUIStyle(GUI.skin.horizontalScrollbarThumb);
|
||||
HorizontalScrollbarThumbStyle.fixedHeight = (int)(30 * scale);
|
||||
|
||||
VerticalScrollbarStyle = new GUIStyle(GUI.skin.verticalScrollbar);
|
||||
VerticalScrollbarStyle.fixedWidth = (int)(30 * scale);
|
||||
VerticalScrollbarThumbStyle = new GUIStyle(GUI.skin.verticalScrollbarThumb);
|
||||
VerticalScrollbarThumbStyle.fixedWidth = (int)(30 * scale);
|
||||
|
||||
XStyle = new GUIStyle(GUI.skin.button);
|
||||
XStyle.fontSize = (int)(38 * scale);
|
||||
XStyle.fixedWidth = (int)(40 * scale);
|
||||
XStyle.fixedHeight = (int)(40 * scale);
|
||||
|
||||
ToolbarStyle = new GUIStyle(GUI.skin.button);
|
||||
ToolbarStyle.fontSize = (int)(28 * scale);
|
||||
ToolbarStyle.fixedHeight = (int)(40 * scale);
|
||||
|
||||
ButtonStyle = new GUIStyle(GUI.skin.button);
|
||||
ButtonStyle.fontSize = (int)(28 * scale);
|
||||
ButtonStyle.fixedHeight = (int)(40 * scale);
|
||||
|
||||
ToogleStyle1 = new GUIStyle(GUI.skin.button);
|
||||
ToogleStyle1.fontSize = (int)(26 * scale);
|
||||
ToogleStyle1.fixedHeight = (int)(35 * scale);
|
||||
|
||||
ToogleStyle2 = new GUIStyle(GUI.skin.box);
|
||||
ToogleStyle2.fontSize = (int)(26 * scale);
|
||||
ToogleStyle2.fixedHeight = (int)(35 * scale);
|
||||
|
||||
TextFieldStyle = new GUIStyle(GUI.skin.textField);
|
||||
TextFieldStyle.fontSize = (int)(22 * scale);
|
||||
TextFieldStyle.fixedHeight = (int)(30 * scale);
|
||||
|
||||
LableStyle = new GUIStyle(GUI.skin.label);
|
||||
LableStyle.fontSize = (int)(24 * scale);
|
||||
|
||||
RichLabelStyle = GUIStyle.none;
|
||||
RichLabelStyle.richText = true;
|
||||
RichLabelFontSize = (int)(24 * scale);
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector2 BeginScrollView(Vector2 pos, float offset = 0f)
|
||||
{
|
||||
// 设置滑动条皮肤
|
||||
_cachedHorizontalScrollbarThumb = GUI.skin.horizontalScrollbarThumb;
|
||||
_cachedVerticalScrollbarThumb = GUI.skin.verticalScrollbarThumb;
|
||||
GUI.skin.horizontalScrollbarThumb = HorizontalScrollbarThumbStyle;
|
||||
GUI.skin.verticalScrollbarThumb = VerticalScrollbarThumbStyle;
|
||||
|
||||
float scrollWidth = Screen.safeArea.width - VerticalScrollbarStyle.fixedWidth;
|
||||
float scrollHeight = Screen.safeArea.height - ToolbarStyle.fixedHeight * 2 - offset;
|
||||
return GUILayout.BeginScrollView(pos, HorizontalScrollbarStyle, VerticalScrollbarStyle, GUILayout.Width(scrollWidth), GUILayout.Height(scrollHeight));
|
||||
}
|
||||
public static void EndScrollView()
|
||||
{
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
// 还原滑动条皮肤
|
||||
GUI.skin.horizontalScrollbarThumb = _cachedHorizontalScrollbarThumb;
|
||||
GUI.skin.verticalScrollbarThumb = _cachedVerticalScrollbarThumb;
|
||||
}
|
||||
public static bool Toggle(string name, bool checkFlag)
|
||||
{
|
||||
GUIStyle style = checkFlag ? ToogleStyle1 : ToogleStyle2;
|
||||
if (GUILayout.Button(name, style))
|
||||
{
|
||||
checkFlag = !checkFlag;
|
||||
}
|
||||
return checkFlag;
|
||||
}
|
||||
public static void Lable(string text)
|
||||
{
|
||||
GUILayout.Label($"<size={RichLabelFontSize}><color=white>{text}</color></size>", RichLabelStyle);
|
||||
}
|
||||
public static void RedLable(string text)
|
||||
{
|
||||
GUILayout.Label($"<size={RichLabelFontSize}><color=red>{text}</color></size>", RichLabelStyle);
|
||||
}
|
||||
public static void YellowLable(string text)
|
||||
{
|
||||
GUILayout.Label($"<size={RichLabelFontSize}><color=yellow>{text}</color></size>", RichLabelStyle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5fd3df705aafda04ba9590b482ab54a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,170 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using MotionFramework.Utility;
|
||||
|
||||
namespace MotionFramework.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// 控制台
|
||||
/// </summary>
|
||||
public static class DeveloperConsole
|
||||
{
|
||||
private class WindowWrapper : IComparer<WindowWrapper>, IComparable<WindowWrapper>
|
||||
{
|
||||
public Type ClassType;
|
||||
public string Title;
|
||||
public int Priority;
|
||||
public IConsoleWindow Instance;
|
||||
|
||||
public int CompareTo(WindowWrapper other)
|
||||
{
|
||||
return Compare(this, other);
|
||||
}
|
||||
public int Compare(WindowWrapper a, WindowWrapper b)
|
||||
{
|
||||
return a.Priority.CompareTo(b.Priority);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控制台节点列表
|
||||
/// </summary>
|
||||
private readonly static List<WindowWrapper> _wrappers = new List<WindowWrapper>();
|
||||
|
||||
// FPS相关
|
||||
private static FPSCounter _fpsCounter = null;
|
||||
private static int _lastFrame = 0;
|
||||
|
||||
// GUI相关
|
||||
private static bool _visible = false;
|
||||
private static int _showIndex = 0;
|
||||
private static Texture _bgTexture;
|
||||
private static string[] _toolbarTitles;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化控制台
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">扩展的控制台窗口所在的程序集</param>
|
||||
public static void Initialize(bool showFPS = true, string assemblyName = AssemblyUtility.UnityDefaultAssemblyName)
|
||||
{
|
||||
if (showFPS)
|
||||
{
|
||||
_fpsCounter = new FPSCounter();
|
||||
}
|
||||
|
||||
// 加载背景纹理
|
||||
string textureName = "console_background";
|
||||
_bgTexture = Resources.Load<Texture>(textureName);
|
||||
if (_bgTexture == null)
|
||||
UnityEngine.Debug.LogWarning($"Not found {textureName} texture in Resources folder.");
|
||||
|
||||
// 获取所有控制台窗口类
|
||||
List<Type> types = AssemblyUtility.GetAssignableAttributeTypes(AssemblyUtility.MotionFrameworkAssemblyName, typeof(IConsoleWindow), typeof(ConsoleAttribute));
|
||||
List<Type> temps = AssemblyUtility.GetAssignableAttributeTypes(assemblyName, typeof(IConsoleWindow), typeof(ConsoleAttribute));
|
||||
types.AddRange(temps);
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
{
|
||||
ConsoleAttribute attribute = (ConsoleAttribute)Attribute.GetCustomAttribute(types[i], typeof(ConsoleAttribute));
|
||||
WindowWrapper wrapper = new WindowWrapper()
|
||||
{
|
||||
ClassType = types[i],
|
||||
Title = attribute.Title,
|
||||
Priority = attribute.Order,
|
||||
};
|
||||
_wrappers.Add(wrapper);
|
||||
}
|
||||
|
||||
// 根据优先级排序
|
||||
_wrappers.Sort();
|
||||
|
||||
// 创建实例类
|
||||
for (int i = 0; i < _wrappers.Count; i++)
|
||||
{
|
||||
WindowWrapper wrapper = _wrappers[i];
|
||||
wrapper.Instance = (IConsoleWindow)Activator.CreateInstance(wrapper.ClassType);
|
||||
}
|
||||
|
||||
// 标题列表
|
||||
List<string> titles = new List<string>();
|
||||
for (int i = 0; i < _wrappers.Count; i++)
|
||||
{
|
||||
titles.Add(_wrappers[i].Title);
|
||||
}
|
||||
_toolbarTitles = titles.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制控制台
|
||||
/// 注意:该接口必须在OnGUI函数内调用
|
||||
/// </summary>
|
||||
public static void Draw()
|
||||
{
|
||||
if (_fpsCounter != null)
|
||||
{
|
||||
if (_lastFrame != Time.frameCount)
|
||||
{
|
||||
_lastFrame = Time.frameCount;
|
||||
_fpsCounter.Update();
|
||||
}
|
||||
}
|
||||
|
||||
// 注意:GUI接口只能在OnGUI内部使用
|
||||
ConsoleGUI.InitGlobalStyle();
|
||||
|
||||
float posX = Screen.safeArea.x;
|
||||
float posY = Screen.height - Screen.safeArea.height - Screen.safeArea.y;
|
||||
|
||||
if (_visible == false)
|
||||
{
|
||||
float wdith = ConsoleGUI.XStyle.fixedWidth;
|
||||
float height = ConsoleGUI.XStyle.fixedHeight;
|
||||
|
||||
// 显示按钮
|
||||
if (GUI.Button(new Rect(posX + 10, posY + 10, wdith, height), "X", ConsoleGUI.XStyle))
|
||||
_visible = true;
|
||||
|
||||
// FPS
|
||||
if (_fpsCounter != null)
|
||||
{
|
||||
int fps = _fpsCounter.GetFPS();
|
||||
string text = $"<size={ConsoleGUI.RichLabelFontSize * 2}><color=red>{fps}</color></size>";
|
||||
GUI.Label(new Rect(posX + wdith * 1.5f, posY + 5, wdith * 2, height * 2), text, ConsoleGUI.RichLabelStyle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Rect windowRect = new Rect(posX, posY, Screen.safeArea.width, Screen.safeArea.height);
|
||||
GUI.Window(0, windowRect, DrawWindow, string.Empty);
|
||||
}
|
||||
}
|
||||
private static void DrawWindow(int windowID)
|
||||
{
|
||||
// 绘制背景
|
||||
if (_visible && _bgTexture != null)
|
||||
GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), _bgTexture, ScaleMode.StretchToFill, true);
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
// 隐藏按钮
|
||||
if (GUILayout.Button("X", ConsoleGUI.ButtonStyle, GUILayout.Width(ConsoleGUI.ButtonStyle.fixedHeight)))
|
||||
_visible = false;
|
||||
|
||||
// 绘制按钮栏
|
||||
_showIndex = GUILayout.Toolbar(_showIndex, _toolbarTitles, ConsoleGUI.ToolbarStyle);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
// 绘制选中窗口
|
||||
for (int i = 0; i < _wrappers.Count; i++)
|
||||
{
|
||||
if (_showIndex != i)
|
||||
continue;
|
||||
WindowWrapper wrapper = _wrappers[i];
|
||||
wrapper.Instance.OnGUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 08697fd1e8d875c4888cba0db1280b60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,42 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace MotionFramework.Console
|
||||
{
|
||||
public class FPSCounter
|
||||
{
|
||||
private const float UpdateInterval = 1.0f;
|
||||
|
||||
private bool _isStart = false;
|
||||
private float _lastInterval;
|
||||
private int _frames;
|
||||
private float _ms;
|
||||
private float _fps;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_isStart == false)
|
||||
{
|
||||
_isStart = true;
|
||||
_lastInterval = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
++_frames;
|
||||
float timeNow = Time.realtimeSinceStartup;
|
||||
if (timeNow > _lastInterval + UpdateInterval)
|
||||
{
|
||||
_fps = _frames / (timeNow - _lastInterval);
|
||||
_ms = 1000.0f / Mathf.Max(_fps, 0.00001f);
|
||||
_frames = 0;
|
||||
_lastInterval = timeNow;
|
||||
}
|
||||
}
|
||||
public int GetFPS()
|
||||
{
|
||||
return Mathf.CeilToInt(_fps);
|
||||
}
|
||||
public float GetMS()
|
||||
{
|
||||
return _ms;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 269834f2912e01245a28761bc1787ffc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
namespace MotionFramework.Console
|
||||
{
|
||||
public interface IConsoleWindow
|
||||
{
|
||||
void OnGUI();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e825e215c03a3a54d9ee86a64d99c51a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a5df74ec92889b640bf662d3a2991bb2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8fe81b14a1faa17408f8bcc7b18f11f6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
namespace MotionFramework.Reference
|
||||
{
|
||||
public interface IReference
|
||||
{
|
||||
void OnRelease();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b7a4e9d2f217d14fa3fd2c5a2de2f8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MotionFramework.Reference
|
||||
{
|
||||
internal class ReferenceCollector
|
||||
{
|
||||
private readonly Stack<IReference> _collector;
|
||||
|
||||
/// <summary>
|
||||
/// 引用类型
|
||||
/// </summary>
|
||||
public Type ClassType { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 内部缓存总数
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return _collector.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 外部使用总数
|
||||
/// </summary>
|
||||
public int SpawnCount { private set; get; }
|
||||
|
||||
|
||||
public ReferenceCollector(Type type, int capacity)
|
||||
{
|
||||
ClassType = type;
|
||||
|
||||
// 创建缓存池
|
||||
_collector = new Stack<IReference>(capacity);
|
||||
|
||||
// 检测是否继承了专属接口
|
||||
Type temp = type.GetInterface(nameof(IReference));
|
||||
if (temp == null)
|
||||
throw new Exception($"{type.Name} must inherit from {nameof(IReference)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 申请引用对象
|
||||
/// </summary>
|
||||
public IReference Spawn()
|
||||
{
|
||||
IReference item;
|
||||
if (_collector.Count > 0)
|
||||
{
|
||||
item = _collector.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = Activator.CreateInstance(ClassType) as IReference;
|
||||
}
|
||||
SpawnCount++;
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收引用对象
|
||||
/// </summary>
|
||||
public void Release(IReference item)
|
||||
{
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
if (item.GetType() != ClassType)
|
||||
throw new Exception($"Invalid type {item.GetType()}");
|
||||
|
||||
if (_collector.Contains(item))
|
||||
throw new Exception($"The item {item.GetType()} already exists.");
|
||||
|
||||
SpawnCount--;
|
||||
item.OnRelease();
|
||||
_collector.Push(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空集合
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_collector.Clear();
|
||||
SpawnCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0c5b53630547ac94d9281a9abb0814c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,115 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MotionFramework.Reference
|
||||
{
|
||||
/// <summary>
|
||||
/// 引用池
|
||||
/// </summary>
|
||||
public static class ReferencePool
|
||||
{
|
||||
private static readonly Dictionary<Type, ReferenceCollector> _collectors = new Dictionary<Type, ReferenceCollector>();
|
||||
|
||||
/// <summary>
|
||||
/// 对象池初始容量
|
||||
/// </summary>
|
||||
public static int InitCapacity { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// 对象池的数量
|
||||
/// </summary>
|
||||
public static int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _collectors.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有对象池
|
||||
/// </summary>
|
||||
public static void ClearAll()
|
||||
{
|
||||
_collectors.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 申请引用对象
|
||||
/// </summary>
|
||||
public static IReference Spawn(Type type)
|
||||
{
|
||||
if (_collectors.ContainsKey(type) == false)
|
||||
{
|
||||
_collectors.Add(type, new ReferenceCollector(type, InitCapacity));
|
||||
}
|
||||
return _collectors[type].Spawn();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 申请引用对象
|
||||
/// </summary>
|
||||
public static T Spawn<T>() where T : class, IReference, new()
|
||||
{
|
||||
Type type = typeof(T);
|
||||
return Spawn(type) as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收引用对象
|
||||
/// </summary>
|
||||
public static void Release(IReference item)
|
||||
{
|
||||
Type type = item.GetType();
|
||||
if (_collectors.ContainsKey(type) == false)
|
||||
{
|
||||
_collectors.Add(type, new ReferenceCollector(type, InitCapacity));
|
||||
}
|
||||
_collectors[type].Release(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量回收列表集合
|
||||
/// </summary>
|
||||
public static void Release<T>(List<T> items) where T : class, IReference, new()
|
||||
{
|
||||
Type type = typeof(T);
|
||||
if (_collectors.ContainsKey(type) == false)
|
||||
{
|
||||
_collectors.Add(type, new ReferenceCollector(type, InitCapacity));
|
||||
}
|
||||
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
_collectors[type].Release(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量回收数组集合
|
||||
/// </summary>
|
||||
public static void Release<T>(T[] items) where T : class, IReference, new()
|
||||
{
|
||||
Type type = typeof(T);
|
||||
if (_collectors.ContainsKey(type) == false)
|
||||
{
|
||||
_collectors.Add(type, new ReferenceCollector(type, InitCapacity));
|
||||
}
|
||||
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
_collectors[type].Release(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#region 调试专属方法
|
||||
internal static Dictionary<Type, ReferenceCollector> GetAllCollectors
|
||||
{
|
||||
get { return _collectors; }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3f4f4ee7b0e49ee459b54961143b5847
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3f00e9242af88e04b905a41cfdf8c50d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,141 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
public static class AssemblyUtility
|
||||
{
|
||||
public const string MotionFrameworkAssemblyName = "MotionFramework";
|
||||
public const string MotionFrameworkAssemblyEditorName = "MotionFramework.Editor";
|
||||
public const string UnityDefaultAssemblyName = "Assembly-CSharp";
|
||||
public const string UnityDefaultAssemblyEditorName = "Assembly-CSharp-Editor";
|
||||
|
||||
|
||||
private static readonly Dictionary<string, List<Type>> _cache = new Dictionary<string, List<Type>>();
|
||||
|
||||
static AssemblyUtility()
|
||||
{
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取程序集
|
||||
/// </summary>
|
||||
public static Assembly GetAssembly(string assemblyName)
|
||||
{
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
if (assembly.GetName().Name == assemblyName)
|
||||
return assembly;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取程序集里的所有类型
|
||||
/// </summary>
|
||||
private static List<Type> GetTypes(string assemblyName)
|
||||
{
|
||||
if (_cache.ContainsKey(assemblyName))
|
||||
return _cache[assemblyName];
|
||||
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
if (assembly.GetName().Name == assemblyName)
|
||||
{
|
||||
List<Type> types = assembly.GetTypes().ToList();
|
||||
_cache.Add(assemblyName, types);
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
// 注意:如果没有找到程序集返回空列表
|
||||
UnityEngine.Debug.LogWarning($"Not found assembly : {assemblyName}");
|
||||
return new List<Type>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取带继承关系的所有类的类型
|
||||
/// <param name="parentType">父类类型</param>
|
||||
/// </summary>
|
||||
public static List<Type> GetAssignableTypes(string assemblyName, System.Type parentType)
|
||||
{
|
||||
List<Type> result = new List<Type>();
|
||||
List<Type> cacheTypes = GetTypes(assemblyName);
|
||||
for (int i = 0; i < cacheTypes.Count; i++)
|
||||
{
|
||||
Type type = cacheTypes[i];
|
||||
|
||||
// 判断继承关系
|
||||
if (parentType.IsAssignableFrom(type))
|
||||
{
|
||||
if (type.Name == parentType.Name)
|
||||
continue;
|
||||
result.Add(type);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取带属性标签的所有类的类型
|
||||
/// <param name="attributeType">属性类型</param>
|
||||
/// </summary>
|
||||
public static List<Type> GetAttributeTypes(string assemblyName, System.Type attributeType)
|
||||
{
|
||||
List<Type> result = new List<Type>();
|
||||
List<Type> cacheTypes = GetTypes(assemblyName);
|
||||
for (int i = 0; i < cacheTypes.Count; i++)
|
||||
{
|
||||
System.Type type = cacheTypes[i];
|
||||
|
||||
// 判断属性标签
|
||||
if (Attribute.IsDefined(type, attributeType))
|
||||
{
|
||||
result.Add(type);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取带继承关系和属性标签的所有类的类型
|
||||
/// </summary>
|
||||
/// <param name="parentType">父类类型</param>
|
||||
/// <param name="attributeType">属性类型</param>
|
||||
public static List<Type> GetAssignableAttributeTypes(string assemblyName, System.Type parentType, System.Type attributeType, bool checkError = true)
|
||||
{
|
||||
List<Type> result = new List<Type>();
|
||||
List<Type> cacheTypes = GetTypes(assemblyName);
|
||||
for (int i = 0; i < cacheTypes.Count; i++)
|
||||
{
|
||||
Type type = cacheTypes[i];
|
||||
|
||||
// 判断属性标签
|
||||
if (Attribute.IsDefined(type, attributeType))
|
||||
{
|
||||
// 判断继承关系
|
||||
if (parentType.IsAssignableFrom(type))
|
||||
{
|
||||
if (type.Name == parentType.Name)
|
||||
continue;
|
||||
result.Add(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkError)
|
||||
throw new Exception($"class {type} must inherit from {parentType}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6f5e6f29b65bf0449a7d33dbe1c04380
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
public static class FileUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// 读取文件
|
||||
/// </summary>
|
||||
public static string ReadFile(string filePath)
|
||||
{
|
||||
if (File.Exists(filePath) == false)
|
||||
return string.Empty;
|
||||
return File.ReadAllText(filePath, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建文件
|
||||
/// </summary>
|
||||
public static void CreateFile(string filePath, string content)
|
||||
{
|
||||
// 删除旧文件
|
||||
if (File.Exists(filePath))
|
||||
File.Delete(filePath);
|
||||
|
||||
// 创建文件夹路径
|
||||
CreateFileDirectory(filePath);
|
||||
|
||||
// 创建新文件
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(content);
|
||||
using (FileStream fs = File.Create(filePath))
|
||||
{
|
||||
fs.Write(bytes, 0, bytes.Length);
|
||||
fs.Flush();
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建文件的文件夹路径
|
||||
/// </summary>
|
||||
public static void CreateFileDirectory(string filePath)
|
||||
{
|
||||
// 获取文件的文件夹路径
|
||||
string directory = Path.GetDirectoryName(filePath);
|
||||
CreateDirectory(directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建文件夹路径
|
||||
/// </summary>
|
||||
public static void CreateDirectory(string directory)
|
||||
{
|
||||
// If the directory doesn't exist, create it.
|
||||
if (Directory.Exists(directory) == false)
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件大小(字节数)
|
||||
/// </summary>
|
||||
public static long GetFileSize(string filePath)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(filePath);
|
||||
return fileInfo.Length;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b11b6a7c3e2ac3b42934279f2611bd51
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,170 @@
|
|||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
public static class HashUtility
|
||||
{
|
||||
private static string ToString(byte[] hashBytes)
|
||||
{
|
||||
string result = BitConverter.ToString(hashBytes);
|
||||
result = result.Replace("-", "");
|
||||
return result.ToLower();
|
||||
}
|
||||
|
||||
#region SHA1
|
||||
/// <summary>
|
||||
/// 获取字符串的Hash值
|
||||
/// </summary>
|
||||
public static string StringSHA1(string str)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(str);
|
||||
return BytesSHA1(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件的Hash值
|
||||
/// </summary>
|
||||
public static string FileSHA1(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return StreamSHA1(fs);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MotionLog.Exception(e.ToString());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据流的Hash值
|
||||
/// </summary>
|
||||
public static string StreamSHA1(Stream stream)
|
||||
{
|
||||
// 说明:创建的是SHA1类的实例,生成的是160位的散列码
|
||||
HashAlgorithm hash = HashAlgorithm.Create();
|
||||
byte[] hashBytes = hash.ComputeHash(stream);
|
||||
return ToString(hashBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字节数组的Hash值
|
||||
/// </summary>
|
||||
public static string BytesSHA1(byte[] buffer)
|
||||
{
|
||||
// 说明:创建的是SHA1类的实例,生成的是160位的散列码
|
||||
HashAlgorithm hash = HashAlgorithm.Create();
|
||||
byte[] hashBytes = hash.ComputeHash(buffer);
|
||||
return ToString(hashBytes);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region MD5
|
||||
/// <summary>
|
||||
/// 获取字符串的MD5
|
||||
/// </summary>
|
||||
public static string StringMD5(string str)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(str);
|
||||
return BytesMD5(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件的MD5
|
||||
/// </summary>
|
||||
public static string FileMD5(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return StreamMD5(fs);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MotionLog.Exception(e.ToString());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据流的MD5
|
||||
/// </summary>
|
||||
public static string StreamMD5(Stream stream)
|
||||
{
|
||||
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
|
||||
byte[] hashBytes = provider.ComputeHash(stream);
|
||||
return ToString(hashBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字节数组的MD5
|
||||
/// </summary>
|
||||
public static string BytesMD5(byte[] buffer)
|
||||
{
|
||||
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
|
||||
byte[] hashBytes = provider.ComputeHash(buffer);
|
||||
return ToString(hashBytes);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CRC32
|
||||
/// <summary>
|
||||
/// 获取字符串的CRC32
|
||||
/// </summary>
|
||||
public static string StringCRC32(string str)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(str);
|
||||
return BytesCRC32(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件的CRC32
|
||||
/// </summary>
|
||||
public static string FileCRC32(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return StreamCRC32(fs);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MotionLog.Exception(e.ToString());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据流的CRC32
|
||||
/// </summary>
|
||||
public static string StreamCRC32(Stream stream)
|
||||
{
|
||||
CRC32Algorithm hash = new CRC32Algorithm();
|
||||
byte[] hashBytes = hash.ComputeHash(stream);
|
||||
return ToString(hashBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字节数组的CRC32
|
||||
/// </summary>
|
||||
public static string BytesCRC32(byte[] buffer)
|
||||
{
|
||||
CRC32Algorithm hash = new CRC32Algorithm();
|
||||
byte[] hashBytes = hash.ComputeHash(buffer);
|
||||
return ToString(hashBytes);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 24be65499552d3e46a4b8611ae81b9f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3123d2179297db341a92247c7ed04a4d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,70 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
public struct BitMask32
|
||||
{
|
||||
private int _mask;
|
||||
|
||||
public static implicit operator int(BitMask32 mask) { return mask._mask; }
|
||||
public static implicit operator BitMask32(int mask) { return new BitMask32(mask); }
|
||||
|
||||
public BitMask32(int mask)
|
||||
{
|
||||
_mask = mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开位
|
||||
/// </summary>
|
||||
public void Open(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 31)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
else
|
||||
_mask |= 1 << bit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭位
|
||||
/// </summary>
|
||||
public void Close(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 31)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
else
|
||||
_mask &= ~(1 << bit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 位取反
|
||||
/// </summary>
|
||||
public void Reverse(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 31)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
else
|
||||
_mask ^= 1 << bit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 所有位取反
|
||||
/// </summary>
|
||||
public void Inverse()
|
||||
{
|
||||
_mask = ~_mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比对位值
|
||||
/// </summary>
|
||||
public bool Test(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 31)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
else
|
||||
return (_mask & (1 << bit)) != 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 229afadde794cc744a32a2ec829a67f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,74 @@
|
|||
//--------------------------------------------------
|
||||
// Motion Framework
|
||||
// Copyright©2020-2020 何冠峰
|
||||
// Licensed under the MIT license
|
||||
//--------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
public struct BitMask64
|
||||
{
|
||||
private long _mask;
|
||||
|
||||
public static implicit operator long(BitMask64 mask) { return mask._mask; }
|
||||
public static implicit operator BitMask64(long mask) { return new BitMask64(mask); }
|
||||
|
||||
public BitMask64(long mask)
|
||||
{
|
||||
_mask = mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开位
|
||||
/// </summary>
|
||||
public void Open(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 63)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
else
|
||||
_mask |= 1L << bit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭位
|
||||
/// </summary>
|
||||
public void Close(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 63)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
else
|
||||
_mask &= ~(1L << bit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 位取反
|
||||
/// </summary>
|
||||
public void Reverse(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 63)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
else
|
||||
_mask ^= 1L << bit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 所有位取反
|
||||
/// </summary>
|
||||
public void Inverse()
|
||||
{
|
||||
_mask = ~_mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比对位值
|
||||
/// </summary>
|
||||
public bool Test(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 63)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
else
|
||||
return (_mask & (1L << bit)) != 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 292cc03e5ad0d724188fbfa83506e5e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,247 @@
|
|||
//--------------------------------------------------
|
||||
// This is .NET safe implementation of Crc32 algorithm.
|
||||
// Copyright©2016-2017 Max Vysokikh
|
||||
// Copyright©2020-2020 何冠峰
|
||||
// Licensed under the MIT license
|
||||
//--------------------------------------------------
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
internal class SafeProxy
|
||||
{
|
||||
private const uint Poly = 0xedb88320u;
|
||||
private readonly uint[] _table = new uint[16 * 256];
|
||||
|
||||
internal SafeProxy()
|
||||
{
|
||||
Init(Poly);
|
||||
}
|
||||
public void Init(uint poly)
|
||||
{
|
||||
var table = _table;
|
||||
for (uint i = 0; i < 256; i++)
|
||||
{
|
||||
uint res = i;
|
||||
for (int t = 0; t < 16; t++)
|
||||
{
|
||||
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? poly ^ (res >> 1) : (res >> 1);
|
||||
table[(t * 256) + i] = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
public uint Append(uint crc, byte[] input, int offset, int length)
|
||||
{
|
||||
uint crcLocal = uint.MaxValue ^ crc;
|
||||
|
||||
uint[] table = _table;
|
||||
while (length >= 16)
|
||||
{
|
||||
var a = table[(3 * 256) + input[offset + 12]]
|
||||
^ table[(2 * 256) + input[offset + 13]]
|
||||
^ table[(1 * 256) + input[offset + 14]]
|
||||
^ table[(0 * 256) + input[offset + 15]];
|
||||
|
||||
var b = table[(7 * 256) + input[offset + 8]]
|
||||
^ table[(6 * 256) + input[offset + 9]]
|
||||
^ table[(5 * 256) + input[offset + 10]]
|
||||
^ table[(4 * 256) + input[offset + 11]];
|
||||
|
||||
var c = table[(11 * 256) + input[offset + 4]]
|
||||
^ table[(10 * 256) + input[offset + 5]]
|
||||
^ table[(9 * 256) + input[offset + 6]]
|
||||
^ table[(8 * 256) + input[offset + 7]];
|
||||
|
||||
var d = table[(15 * 256) + ((byte)crcLocal ^ input[offset])]
|
||||
^ table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])]
|
||||
^ table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])]
|
||||
^ table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])];
|
||||
|
||||
crcLocal = d ^ c ^ b ^ a;
|
||||
offset += 16;
|
||||
length -= 16;
|
||||
}
|
||||
|
||||
while (--length >= 0)
|
||||
crcLocal = table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8;
|
||||
|
||||
return crcLocal ^ uint.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of CRC-32.
|
||||
/// This class supports several convenient static methods returning the CRC as UInt32.
|
||||
/// </summary>
|
||||
public class CRC32Algorithm : HashAlgorithm
|
||||
{
|
||||
private uint _currentCrc;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CRC32Algorithm"/> class.
|
||||
/// </summary>
|
||||
public CRC32Algorithm()
|
||||
{
|
||||
#if !NETCORE13
|
||||
HashSizeValue = 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets internal state of the algorithm. Used internally.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
_currentCrc = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends CRC-32 from given buffer
|
||||
/// </summary>
|
||||
protected override void HashCore(byte[] input, int offset, int length)
|
||||
{
|
||||
_currentCrc = AppendInternal(_currentCrc, input, offset, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes CRC-32 from <see cref="HashCore"/>
|
||||
/// </summary>
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
if(BitConverter.IsLittleEndian)
|
||||
return new[] { (byte)_currentCrc, (byte)(_currentCrc >> 8), (byte)(_currentCrc >> 16), (byte)(_currentCrc >> 24) };
|
||||
else
|
||||
return new[] { (byte)(_currentCrc >> 24), (byte)(_currentCrc >> 16), (byte)(_currentCrc >> 8), (byte)_currentCrc };
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Computes CRC-32 from multiple buffers.
|
||||
/// Call this method multiple times to chain multiple buffers.
|
||||
/// </summary>
|
||||
/// <param name="initial">
|
||||
/// Initial CRC value for the algorithm. It is zero for the first buffer.
|
||||
/// Subsequent buffers should have their initial value set to CRC value returned by previous call to this method.
|
||||
/// </param>
|
||||
/// <param name="input">Input buffer with data to be checksummed.</param>
|
||||
/// <param name="offset">Offset of the input data within the buffer.</param>
|
||||
/// <param name="length">Length of the input data in the buffer.</param>
|
||||
/// <returns>Accumulated CRC-32 of all buffers processed so far.</returns>
|
||||
public static uint Append(uint initial, byte[] input, int offset, int length)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
if (offset < 0 || length < 0 || offset + length > input.Length)
|
||||
throw new ArgumentOutOfRangeException("length");
|
||||
return AppendInternal(initial, input, offset, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes CRC-32 from multiple buffers.
|
||||
/// Call this method multiple times to chain multiple buffers.
|
||||
/// </summary>
|
||||
/// <param name="initial">
|
||||
/// Initial CRC value for the algorithm. It is zero for the first buffer.
|
||||
/// Subsequent buffers should have their initial value set to CRC value returned by previous call to this method.
|
||||
/// </param>
|
||||
/// <param name="input">Input buffer containing data to be checksummed.</param>
|
||||
/// <returns>Accumulated CRC-32 of all buffers processed so far.</returns>
|
||||
public static uint Append(uint initial, byte[] input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException();
|
||||
return AppendInternal(initial, input, 0, input.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes CRC-32 from input buffer.
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer with data to be checksummed.</param>
|
||||
/// <param name="offset">Offset of the input data within the buffer.</param>
|
||||
/// <param name="length">Length of the input data in the buffer.</param>
|
||||
/// <returns>CRC-32 of the data in the buffer.</returns>
|
||||
public static uint Compute(byte[] input, int offset, int length)
|
||||
{
|
||||
return Append(0, input, offset, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes CRC-32 from input buffer.
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing data to be checksummed.</param>
|
||||
/// <returns>CRC-32 of the buffer.</returns>
|
||||
public static uint Compute(byte[] input)
|
||||
{
|
||||
return Append(0, input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes CRC-32 from input buffer and writes it after end of data (buffer should have 4 bytes reserved space for it). Can be used in conjunction with <see cref="IsValidWithCrcAtEnd(byte[],int,int)"/>
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer with data to be checksummed.</param>
|
||||
/// <param name="offset">Offset of the input data within the buffer.</param>
|
||||
/// <param name="length">Length of the input data in the buffer.</param>
|
||||
/// <returns>CRC-32 of the data in the buffer.</returns>
|
||||
public static uint ComputeAndWriteToEnd(byte[] input, int offset, int length)
|
||||
{
|
||||
if (length + 4 > input.Length)
|
||||
throw new ArgumentOutOfRangeException("length", "Length of data should be less than array length - 4 bytes of CRC data");
|
||||
var crc = Append(0, input, offset, length);
|
||||
var r = offset + length;
|
||||
input[r] = (byte)crc;
|
||||
input[r + 1] = (byte)(crc >> 8);
|
||||
input[r + 2] = (byte)(crc >> 16);
|
||||
input[r + 3] = (byte)(crc >> 24);
|
||||
return crc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes CRC-32 from input buffer - 4 bytes and writes it as last 4 bytes of buffer. Can be used in conjunction with <see cref="IsValidWithCrcAtEnd(byte[])"/>
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer with data to be checksummed.</param>
|
||||
/// <returns>CRC-32 of the data in the buffer.</returns>
|
||||
public static uint ComputeAndWriteToEnd(byte[] input)
|
||||
{
|
||||
if (input.Length < 4)
|
||||
throw new ArgumentOutOfRangeException("input", "Input array should be 4 bytes at least");
|
||||
return ComputeAndWriteToEnd(input, 0, input.Length - 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates correctness of CRC-32 data in source buffer with assumption that CRC-32 data located at end of buffer in reverse bytes order. Can be used in conjunction with <see cref="ComputeAndWriteToEnd(byte[],int,int)"/>
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer with data to be checksummed.</param>
|
||||
/// <param name="offset">Offset of the input data within the buffer.</param>
|
||||
/// <param name="lengthWithCrc">Length of the input data in the buffer with CRC-32 bytes.</param>
|
||||
/// <returns>Is checksum valid.</returns>
|
||||
public static bool IsValidWithCrcAtEnd(byte[] input, int offset, int lengthWithCrc)
|
||||
{
|
||||
return Append(0, input, offset, lengthWithCrc) == 0x2144DF1C;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates correctness of CRC-32 data in source buffer with assumption that CRC-32 data located at end of buffer in reverse bytes order. Can be used in conjunction with <see cref="ComputeAndWriteToEnd(byte[],int,int)"/>
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer with data to be checksummed.</param>
|
||||
/// <returns>Is checksum valid.</returns>
|
||||
public static bool IsValidWithCrcAtEnd(byte[] input)
|
||||
{
|
||||
if (input.Length < 4)
|
||||
throw new ArgumentOutOfRangeException("input", "Input array should be 4 bytes at least");
|
||||
return Append(0, input, 0, input.Length) == 0x2144DF1C;
|
||||
}
|
||||
|
||||
|
||||
private static readonly SafeProxy _proxy = new SafeProxy();
|
||||
private static uint AppendInternal(uint initial, byte[] input, int offset, int length)
|
||||
{
|
||||
if (length > 0)
|
||||
{
|
||||
return _proxy.Append(initial, input, offset, length);
|
||||
}
|
||||
else
|
||||
return initial;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6b096abda3d32ea44b9870c8fa2a6560
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,110 @@
|
|||
//--------------------------------------------------
|
||||
// Motion Framework
|
||||
// Copyright©2020-2020 何冠峰
|
||||
// Licensed under the MIT license
|
||||
//--------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
[Serializable]
|
||||
public struct ObfuscateDouble : IFormattable, IEquatable<ObfuscateDouble>, IComparable<ObfuscateDouble>, IComparable<double>, IComparable
|
||||
{
|
||||
private static long GlobalSeed = DateTime.Now.Ticks;
|
||||
|
||||
[SerializeField]
|
||||
private long _seed;
|
||||
[SerializeField]
|
||||
private long _data;
|
||||
|
||||
public ObfuscateDouble(double value)
|
||||
{
|
||||
_seed = GlobalSeed++;
|
||||
_data = 0;
|
||||
Value = value;
|
||||
}
|
||||
internal double Value
|
||||
{
|
||||
get
|
||||
{
|
||||
var v = _data ^ _seed;
|
||||
return ConvertValue(v);
|
||||
}
|
||||
set
|
||||
{
|
||||
var v = ConvertValue(value);
|
||||
_data = v ^ _seed;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ObfuscateDouble && Equals((ObfuscateDouble)obj);
|
||||
}
|
||||
|
||||
public string ToString(string format)
|
||||
{
|
||||
return Value.ToString(format);
|
||||
}
|
||||
public string ToString(IFormatProvider provider)
|
||||
{
|
||||
return Value.ToString(provider);
|
||||
}
|
||||
public string ToString(string format, IFormatProvider provider)
|
||||
{
|
||||
return Value.ToString(format, provider);
|
||||
}
|
||||
|
||||
public bool Equals(ObfuscateDouble obj)
|
||||
{
|
||||
return obj.Value.Equals(Value);
|
||||
}
|
||||
public int CompareTo(ObfuscateDouble other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
public int CompareTo(double other)
|
||||
{
|
||||
return Value.CompareTo(other);
|
||||
}
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return Value.CompareTo(obj);
|
||||
}
|
||||
|
||||
#region 运算符重载
|
||||
public static implicit operator double(ObfuscateDouble value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
public static implicit operator ObfuscateDouble(double value)
|
||||
{
|
||||
return new ObfuscateDouble(value);
|
||||
}
|
||||
public static explicit operator ObfuscateDouble(ObfuscateFloat value)
|
||||
{
|
||||
return (float)value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
unsafe static long ConvertValue(double value)
|
||||
{
|
||||
double* ptr = &value;
|
||||
return *((long*)ptr);
|
||||
}
|
||||
unsafe static double ConvertValue(long value)
|
||||
{
|
||||
long* ptr = &value;
|
||||
return *((double*)ptr);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1b8a64fba6ba07a45b9c3f5355dd76bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,106 @@
|
|||
//--------------------------------------------------
|
||||
// Motion Framework
|
||||
// Copyright©2020-2020 何冠峰
|
||||
// Licensed under the MIT license
|
||||
//--------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
[Serializable]
|
||||
public struct ObfuscateFloat : IFormattable, IEquatable<ObfuscateFloat>, IComparable<ObfuscateFloat>, IComparable<float>, IComparable
|
||||
{
|
||||
private static int GlobalSeed = (int)DateTime.Now.Ticks;
|
||||
|
||||
[SerializeField]
|
||||
private int _seed;
|
||||
[SerializeField]
|
||||
private int _data;
|
||||
|
||||
public ObfuscateFloat(float value)
|
||||
{
|
||||
_seed = GlobalSeed++;
|
||||
_data = 0;
|
||||
Value = value;
|
||||
}
|
||||
internal float Value
|
||||
{
|
||||
get
|
||||
{
|
||||
var v = _data ^ _seed;
|
||||
return ConvertValue(v);
|
||||
}
|
||||
set
|
||||
{
|
||||
var v = ConvertValue(value);
|
||||
_data = v ^ _seed;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ObfuscateFloat && Equals((ObfuscateFloat)obj);
|
||||
}
|
||||
|
||||
public string ToString(string format)
|
||||
{
|
||||
return Value.ToString(format);
|
||||
}
|
||||
public string ToString(IFormatProvider provider)
|
||||
{
|
||||
return Value.ToString(provider);
|
||||
}
|
||||
public string ToString(string format, IFormatProvider provider)
|
||||
{
|
||||
return Value.ToString(format, provider);
|
||||
}
|
||||
|
||||
public bool Equals(ObfuscateFloat obj)
|
||||
{
|
||||
return obj.Value.Equals(Value);
|
||||
}
|
||||
public int CompareTo(ObfuscateFloat other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
public int CompareTo(float other)
|
||||
{
|
||||
return Value.CompareTo(other);
|
||||
}
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return Value.CompareTo(obj);
|
||||
}
|
||||
|
||||
#region 运算符重载
|
||||
public static implicit operator float(ObfuscateFloat value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
public static implicit operator ObfuscateFloat(float value)
|
||||
{
|
||||
return new ObfuscateFloat(value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
unsafe static int ConvertValue(float value)
|
||||
{
|
||||
float* ptr = &value;
|
||||
return *((int*)ptr);
|
||||
}
|
||||
unsafe static float ConvertValue(int value)
|
||||
{
|
||||
int* ptr = &value;
|
||||
return *((float*)ptr);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3b3ab743f82f4314587e8def6386768f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,110 @@
|
|||
//--------------------------------------------------
|
||||
// Motion Framework
|
||||
// Copyright©2020-2020 何冠峰
|
||||
// Licensed under the MIT license
|
||||
//--------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
[Serializable]
|
||||
public struct ObfuscateInt : IFormattable, IEquatable<ObfuscateInt>, IComparable<ObfuscateInt>, IComparable<int>, IComparable
|
||||
{
|
||||
private static int GlobalSeed = (int)DateTime.Now.Ticks;
|
||||
|
||||
[SerializeField]
|
||||
private int _seed;
|
||||
[SerializeField]
|
||||
private int _data;
|
||||
|
||||
public ObfuscateInt(int value)
|
||||
{
|
||||
_seed = GlobalSeed++;
|
||||
_data = 0;
|
||||
Value = value;
|
||||
}
|
||||
internal int Value
|
||||
{
|
||||
get
|
||||
{
|
||||
var v = _data ^ _seed;
|
||||
return v;
|
||||
}
|
||||
set
|
||||
{
|
||||
_data = value ^ _seed;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ObfuscateInt && Equals((ObfuscateInt)obj);
|
||||
}
|
||||
|
||||
public string ToString(string format)
|
||||
{
|
||||
return Value.ToString(format);
|
||||
}
|
||||
public string ToString(IFormatProvider provider)
|
||||
{
|
||||
return Value.ToString(provider);
|
||||
}
|
||||
public string ToString(string format, IFormatProvider provider)
|
||||
{
|
||||
return Value.ToString(format, provider);
|
||||
}
|
||||
|
||||
public bool Equals(ObfuscateInt obj)
|
||||
{
|
||||
return Value.Equals(obj.Value);
|
||||
}
|
||||
public int CompareTo(ObfuscateInt other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
public int CompareTo(int other)
|
||||
{
|
||||
return Value.CompareTo(other);
|
||||
}
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return Value.CompareTo(obj);
|
||||
}
|
||||
|
||||
#region 运算符重载
|
||||
public static implicit operator int(ObfuscateInt value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
public static implicit operator ObfuscateInt(int value)
|
||||
{
|
||||
return new ObfuscateInt(value);
|
||||
}
|
||||
public static implicit operator ObfuscateFloat(ObfuscateInt value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
public static implicit operator ObfuscateDouble(ObfuscateInt value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
public static ObfuscateInt operator ++(ObfuscateInt value)
|
||||
{
|
||||
return value.Value + 1;
|
||||
}
|
||||
public static ObfuscateInt operator --(ObfuscateInt value)
|
||||
{
|
||||
return value.Value - 1;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 490fab43fb3ac0749afa102b9ba57dc8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,102 @@
|
|||
//--------------------------------------------------
|
||||
// Motion Framework
|
||||
// Copyright©2020-2020 何冠峰
|
||||
// Licensed under the MIT license
|
||||
//--------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
[Serializable]
|
||||
public struct ObfuscateLong : IFormattable, IEquatable<ObfuscateLong>, IComparable<ObfuscateLong>, IComparable<long>, IComparable
|
||||
{
|
||||
private static long GlobalSeed = DateTime.Now.Ticks;
|
||||
|
||||
[SerializeField]
|
||||
private long _seed;
|
||||
[SerializeField]
|
||||
private long _data;
|
||||
|
||||
public ObfuscateLong(long value)
|
||||
{
|
||||
_seed = GlobalSeed++;
|
||||
_data = 0;
|
||||
Value = value;
|
||||
}
|
||||
internal long Value
|
||||
{
|
||||
get
|
||||
{
|
||||
var v = _data ^ _seed;
|
||||
return v;
|
||||
}
|
||||
set
|
||||
{
|
||||
_data = value ^ _seed;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ObfuscateLong && Equals((ObfuscateLong)obj);
|
||||
}
|
||||
|
||||
public string ToString(string format)
|
||||
{
|
||||
return Value.ToString(format);
|
||||
}
|
||||
public string ToString(IFormatProvider provider)
|
||||
{
|
||||
return Value.ToString(provider);
|
||||
}
|
||||
public string ToString(string format, IFormatProvider provider)
|
||||
{
|
||||
return Value.ToString(format, provider);
|
||||
}
|
||||
|
||||
public bool Equals(ObfuscateLong obj)
|
||||
{
|
||||
return Value.Equals(obj.Value);
|
||||
}
|
||||
public int CompareTo(ObfuscateLong other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
public int CompareTo(long other)
|
||||
{
|
||||
return Value.CompareTo(other);
|
||||
}
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return Value.CompareTo(obj);
|
||||
}
|
||||
|
||||
#region 运算符重载
|
||||
public static implicit operator long(ObfuscateLong value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
public static implicit operator ObfuscateLong(long value)
|
||||
{
|
||||
return new ObfuscateLong(value);
|
||||
}
|
||||
public static ObfuscateLong operator ++(ObfuscateLong value)
|
||||
{
|
||||
return value.Value + 1;
|
||||
}
|
||||
public static ObfuscateLong operator --(ObfuscateLong value)
|
||||
{
|
||||
return value.Value - 1;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 64d0fb04b6975a44180f9068d44b7973
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,203 @@
|
|||
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// 综合计时器
|
||||
/// </summary>
|
||||
public sealed class Timer
|
||||
{
|
||||
/// <summary>
|
||||
/// 延迟后,触发一次
|
||||
/// </summary>
|
||||
public static Timer CreateOnceTimer(float delay)
|
||||
{
|
||||
return new Timer(delay, -1, -1, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟后,永久性的间隔触发
|
||||
/// </summary>
|
||||
/// <param name="delay">延迟时间</param>
|
||||
/// <param name="interval">间隔时间</param>
|
||||
public static Timer CreatePepeatTimer(float delay, float interval)
|
||||
{
|
||||
return new Timer(delay, interval, -1, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟后,在一段时间内间隔触发
|
||||
/// </summary>
|
||||
/// <param name="delay">延迟时间</param>
|
||||
/// <param name="interval">间隔时间</param>
|
||||
/// <param name="duration">触发周期</param>
|
||||
public static Timer CreatePepeatTimer(float delay, float interval, float duration)
|
||||
{
|
||||
return new Timer(delay, interval, duration, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟后,间隔触发一定次数
|
||||
/// </summary>
|
||||
/// <param name="delay">延迟时间</param>
|
||||
/// <param name="interval">间隔时间</param>
|
||||
/// <param name="maxTriggerCount">最大触发次数</param>
|
||||
public static Timer CreatePepeatTimer(float delay, float interval, long maxTriggerCount)
|
||||
{
|
||||
return new Timer(delay, interval, -1, maxTriggerCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟后,在一段时间内触发
|
||||
/// </summary>
|
||||
/// <param name="delay">延迟时间</param>
|
||||
/// <param name="duration">触发周期</param>
|
||||
public static Timer CreateDurationTimer(float delay, float duration)
|
||||
{
|
||||
return new Timer(delay, -1, duration, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟后,永久触发
|
||||
/// </summary>
|
||||
public static Timer CreateForeverTimer(float delay)
|
||||
{
|
||||
return new Timer(delay, -1, -1, -1);
|
||||
}
|
||||
|
||||
|
||||
private readonly float _intervalTime;
|
||||
private readonly float _durationTime;
|
||||
private readonly long _maxTriggerCount;
|
||||
|
||||
// 需要重置的变量
|
||||
private float _delayTimer = 0;
|
||||
private float _durationTimer = 0;
|
||||
private float _intervalTimer = 0;
|
||||
private long _triggerCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 延迟时间
|
||||
/// </summary>
|
||||
public float DelayTime { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经结束
|
||||
/// </summary>
|
||||
public bool IsOver { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经暂停
|
||||
/// </summary>
|
||||
public bool IsPause { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 延迟剩余时间
|
||||
/// </summary>
|
||||
public float Remaining
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsOver)
|
||||
return 0f;
|
||||
else
|
||||
return System.Math.Max(0f, DelayTime - _delayTimer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计时器
|
||||
/// </summary>
|
||||
/// <param name="delay">延迟时间</param>
|
||||
/// <param name="interval">间隔时间</param>
|
||||
/// <param name="duration">运行时间</param>
|
||||
/// <param name="maxTriggerTimes">最大触发次数</param>
|
||||
public Timer(float delay, float interval, float duration, long maxTriggerCount)
|
||||
{
|
||||
DelayTime = delay;
|
||||
_intervalTime = interval;
|
||||
_durationTime = duration;
|
||||
_maxTriggerCount = maxTriggerCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停计时器
|
||||
/// </summary>
|
||||
public void Pause()
|
||||
{
|
||||
IsPause = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复计时器
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
{
|
||||
IsPause = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束计时器
|
||||
/// </summary>
|
||||
public void Kill()
|
||||
{
|
||||
IsOver = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置计时器
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_delayTimer = 0;
|
||||
_durationTimer = 0;
|
||||
_intervalTimer = 0;
|
||||
_triggerCount = 0;
|
||||
IsOver = false;
|
||||
IsPause = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新计时器
|
||||
/// </summary>
|
||||
public bool Update(float deltaTime)
|
||||
{
|
||||
if (IsOver || IsPause)
|
||||
return false;
|
||||
|
||||
_delayTimer += deltaTime;
|
||||
if (_delayTimer < DelayTime)
|
||||
return false;
|
||||
|
||||
if(_intervalTime > 0)
|
||||
_intervalTimer += deltaTime;
|
||||
if (_durationTime > 0)
|
||||
_durationTimer += deltaTime;
|
||||
|
||||
// 检测间隔执行
|
||||
if (_intervalTime > 0)
|
||||
{
|
||||
if (_intervalTimer < _intervalTime)
|
||||
return false;
|
||||
_intervalTimer = 0;
|
||||
}
|
||||
|
||||
// 检测结束条件
|
||||
if (_durationTime > 0)
|
||||
{
|
||||
if (_durationTimer >= _durationTime)
|
||||
Kill();
|
||||
}
|
||||
|
||||
// 检测结束条件
|
||||
if (_maxTriggerCount > 0)
|
||||
{
|
||||
_triggerCount++;
|
||||
if (_triggerCount >= _maxTriggerCount)
|
||||
Kill();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c8072e60d9a07e4ba0634322357d446
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,56 @@
|
|||
//--------------------------------------------------
|
||||
// Motion Framework
|
||||
// Copyright©2021-2021 何冠峰
|
||||
// Licensed under the MIT license
|
||||
//--------------------------------------------------
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace MotionFramework.Utility
|
||||
{
|
||||
public static class ProfilerUtility
|
||||
{
|
||||
private static string _watchName;
|
||||
private static long _limitMilliseconds;
|
||||
private static Stopwatch _watch;
|
||||
|
||||
/// <summary>
|
||||
/// 开启性能测试
|
||||
/// </summary>
|
||||
/// <param name="name">测试名称</param>
|
||||
/// <param name="limitMilliseconds">极限毫秒数</param>
|
||||
[Conditional("DEBUG")]
|
||||
public static void BeginWatch(string name, long limitMilliseconds = long.MaxValue)
|
||||
{
|
||||
if (_watch != null)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Last watch is not end : {_watchName}");
|
||||
}
|
||||
|
||||
_watchName = name;
|
||||
_limitMilliseconds = limitMilliseconds;
|
||||
_watch = new Stopwatch();
|
||||
_watch.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束性能测试
|
||||
/// 说明:当耗费的毫秒数超过极限值则输出为警告
|
||||
/// </summary>
|
||||
[Conditional("DEBUG")]
|
||||
public static void EndWatch()
|
||||
{
|
||||
if (_watch != null)
|
||||
{
|
||||
_watch.Stop();
|
||||
string logInfo = $"[Profiler] {_watchName} took {_watch.ElapsedMilliseconds} ms";
|
||||
if (_watch.ElapsedMilliseconds > _limitMilliseconds)
|
||||
UnityEngine.Debug.LogWarning(logInfo);
|
||||
else
|
||||
UnityEngine.Debug.Log(logInfo);
|
||||
_watch = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1ec9a246cc4236d4e83d331746929371
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: af1f88536c0900042ae072d86f818806
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e19b500d8bb26ff4d9a183fd9bc8074d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
|
||||
namespace UnityEngine
|
||||
{
|
||||
public static partial class UnityEngine_Object_Extension
|
||||
{
|
||||
public static bool IsDestroyed(this UnityEngine.Object o)
|
||||
{
|
||||
return o == null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e3170b32d0ce75e42b20609930ba11d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
|
||||
namespace UnityEngine
|
||||
{
|
||||
public static partial class UnityEngine_RectTransform_Extension
|
||||
{
|
||||
public static void SetSizeDeltaWidth(this RectTransform thisObj, float width)
|
||||
{
|
||||
Vector2 size = thisObj.sizeDelta;
|
||||
size.x = width;
|
||||
thisObj.sizeDelta = size;
|
||||
}
|
||||
public static void SetSizeDeltaHeight(this RectTransform thisObj, float height)
|
||||
{
|
||||
Vector2 size = thisObj.sizeDelta;
|
||||
size.y = height;
|
||||
thisObj.sizeDelta = size;
|
||||
}
|
||||
|
||||
public static void SetAnchoredPositionX(this RectTransform thisObj, float pos)
|
||||
{
|
||||
Vector3 temp = thisObj.anchoredPosition;
|
||||
temp.x = pos;
|
||||
thisObj.anchoredPosition = temp;
|
||||
}
|
||||
public static void SetAnchoredPositionY(this RectTransform thisObj, float pos)
|
||||
{
|
||||
Vector3 temp = thisObj.anchoredPosition;
|
||||
temp.y = pos;
|
||||
thisObj.anchoredPosition = temp;
|
||||
}
|
||||
public static void SetAnchoredPositionZ(this RectTransform thisObj, float pos)
|
||||
{
|
||||
Vector3 temp = thisObj.anchoredPosition;
|
||||
temp.z = pos;
|
||||
thisObj.anchoredPosition = temp;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue