Compare commits

...

2 Commits

Author SHA1 Message Date
yulong 9df0b196f6 1 2024-08-22 18:17:00 +08:00
yulong c53b3e138e 提交 2024-08-22 18:13:09 +08:00
2477 changed files with 394902 additions and 394 deletions

21
.gitignore vendored
View File

@ -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

8
Assets/Editor.meta Normal file
View File

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

View File

@ -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
}
}
}

View File

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

8
Assets/Framework.meta Normal file
View File

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

View File

@ -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}");
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,30 @@

namespace MotionFramework
{
/// <summary>
/// 日志等级
/// </summary>
public enum ELogLevel
{
/// <summary>
/// 信息
/// </summary>
Log,
/// <summary>
/// 警告
/// </summary>
Warning,
/// <summary>
/// 错误
/// </summary>
Error,
/// <summary>
/// 异常
/// </summary>
Exception,
}
}

View File

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

View File

@ -0,0 +1,17 @@
using System;
namespace MotionFramework
{
public interface IActivatorServices
{
/// <summary>
/// 创建实例
/// </summary>
object CreateInstance(Type classType);
/// <summary>
/// 获取特性
/// </summary>
Attribute GetAttribute(Type classType);
}
}

View File

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

View File

@ -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();
}
}

View File

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

View File

@ -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);
}
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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}");
}
}
}

View File

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

View File

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

View File

@ -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;
}
}

View File

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

View File

@ -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();
}
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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();
}
}
}

View File

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

View File

@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MotionFramework.Experimental.Animation
{
public enum EAnimStates
{
Playing,
Paused,
}
}

View File

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

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 27404a5600794902a08f7d7710e766b3
timeCreated: 1716521294

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 436369bf8c5b45a084984e639d160e1d
timeCreated: 1716536171

View File

@ -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;
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f83adf796757446ba4c3b2d84fc9012e
timeCreated: 1716536226

View File

@ -0,0 +1,4 @@
public interface ICameraController
{
void ControlCamera();
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 117f0a495e484ef0b45eb7155a72b5ef
timeCreated: 1716536177

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fde27b4e1f5e4fa3ab0b3e7f1249b66a
timeCreated: 1716536117

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e00183c47e5a46dbb1eafcfd793004f2
timeCreated: 1716530957

View File

@ -0,0 +1,11 @@
using System;
using UnityEngine;
public interface IRaycastable
{
event Action<RaycastHit> OnRaycastHit;
void PerformRaycast();
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5ff7acc217c44ea0adf9478b454b7e1d
timeCreated: 1716536124

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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);
}
}
}

View File

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

View File

@ -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();
}
}
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -0,0 +1,9 @@

namespace MotionFramework.Console
{
public interface IConsoleWindow
{
void OnGUI();
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,9 @@

namespace MotionFramework.Reference
{
public interface IReference
{
void OnRelease();
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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);
}
}
}

View File

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

View File

@ -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);
}
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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;
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,12 @@

namespace UnityEngine
{
public static partial class UnityEngine_Object_Extension
{
public static bool IsDestroyed(this UnityEngine.Object o)
{
return o == null;
}
}
}

View File

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

View File

@ -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