Update
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using UnityEditor;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace DA_Assets.DAI
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[InitializeOnLoad]
|
||||
public class DARunner
|
||||
{
|
||||
/// <summary>
|
||||
/// Thread-safe queue for actions to be executed on the main thread.
|
||||
/// </summary>
|
||||
private static ConcurrentQueue<Action> _executionQueue = new ConcurrentQueue<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Time since the script was reloaded.
|
||||
/// </summary>
|
||||
public static double timeSinceScriptReload => _stopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for update functions.
|
||||
/// </summary>
|
||||
public static Action update;
|
||||
|
||||
/// <summary>
|
||||
/// Delta time between updates.
|
||||
/// </summary>
|
||||
public static float deltaTime => (float)_deltaTime;
|
||||
private static double _deltaTime;
|
||||
|
||||
private static double _previousTime;
|
||||
private static Stopwatch _stopwatch;
|
||||
|
||||
private static int _frequencyMs = 15; // Update frequency in milliseconds.
|
||||
|
||||
private static Thread _thread;
|
||||
private static CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// Static constructor to initialize the DARunner.
|
||||
/// </summary>
|
||||
static DARunner()
|
||||
{
|
||||
update += () =>
|
||||
{
|
||||
while (_executionQueue.TryDequeue(out var action))
|
||||
{
|
||||
try
|
||||
{
|
||||
action?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error executing action on main thread: {ex.Message}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_stopwatch = Stopwatch.StartNew();
|
||||
SynchronizationContext context = SynchronizationContext.Current;
|
||||
|
||||
Update(context);
|
||||
|
||||
// Subscribe to assembly reload events to properly terminate the thread.
|
||||
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before assembly reload to terminate the background thread.
|
||||
/// </summary>
|
||||
private static void OnBeforeAssemblyReload()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_thread?.Join();
|
||||
_thread = null;
|
||||
_cancellationTokenSource = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the background thread that posts updates to the main thread's synchronization context.
|
||||
/// </summary>
|
||||
/// <param name="context">The synchronization context of the main thread.</param>
|
||||
private static void Update(SynchronizationContext context)
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
CancellationToken token = _cancellationTokenSource.Token;
|
||||
|
||||
_thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
double currentTime = _stopwatch.Elapsed.TotalSeconds;
|
||||
_deltaTime = currentTime - _previousTime;
|
||||
_previousTime = currentTime;
|
||||
|
||||
context.Post(_ => update(), null);
|
||||
|
||||
Thread.Sleep(_frequencyMs);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error in background thread: {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues an action to be executed on the main thread.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to execute.</param>
|
||||
public static void ExecuteOnMainThread(Action action)
|
||||
{
|
||||
_executionQueue.Enqueue(action);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf822dab9c6f5304082770e5bdd87c2f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DA_Assets.DAI
|
||||
{
|
||||
[Serializable]
|
||||
public class HamburgerItem
|
||||
{
|
||||
[SerializeField] string id;
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField] UnityEditor.AnimatedValues.AnimBool fade;
|
||||
#endif
|
||||
[SerializeField] UpdateBool checkBoxValue = new UpdateBool(false, false);
|
||||
[SerializeField] GUIContent guiContent;
|
||||
[SerializeField] Action body;
|
||||
[SerializeField] Action<string, bool> checkBoxValueChanged;
|
||||
[SerializeField] Action onButtonClick;
|
||||
[SerializeField] GUIContent buttonGuiContent;
|
||||
|
||||
public string Id { get => id; set => id = value; }
|
||||
#if UNITY_EDITOR
|
||||
public UnityEditor.AnimatedValues.AnimBool Fade { get => fade; set => fade = value; }
|
||||
#endif
|
||||
public UpdateBool CheckBoxValue { get => checkBoxValue; set => checkBoxValue = value; }
|
||||
public GUIContent GUIContent { get => guiContent; set => guiContent = value; }
|
||||
public Action Body { get => body; set => body = value; }
|
||||
public Action<string, bool> CheckBoxValueChanged { get => checkBoxValueChanged; set => checkBoxValueChanged = value; }
|
||||
public Action OnButtonClick { get => onButtonClick; set => onButtonClick = value; }
|
||||
public GUIContent ButtonGuiContent { get => buttonGuiContent; set => buttonGuiContent = value; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40254133d93a7524193a26270d2c5dff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,120 @@
|
||||
using DA_Assets.Extensions;
|
||||
using DA_Assets.Tools;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DA_Assets.DAI
|
||||
{
|
||||
public class MonoBehaviourLinkerRuntime<T3> : LinkerBase<T3> where T3 : UnityEngine.Object { }
|
||||
|
||||
public static class MonoBehaviourLinkerExtensions
|
||||
{
|
||||
public static T2 Link<T2, T3>(this T3 monoBeh, ref T2 linker) where T3 : UnityEngine.Object where T2 : MonoBehaviourLinkerRuntime<T3>
|
||||
{
|
||||
bool needSetDirty = false;
|
||||
bool needInit = false;
|
||||
|
||||
if (linker == null)
|
||||
{
|
||||
needSetDirty = true;
|
||||
needInit = true;
|
||||
|
||||
AttributeValidator.Validate<T2>(typeof(SerializableAttribute));
|
||||
linker = (T2)Activator.CreateInstance(typeof(T2));
|
||||
}
|
||||
|
||||
if (linker.MonoBeh == null)
|
||||
{
|
||||
needSetDirty = true;
|
||||
linker.MonoBeh = monoBeh;
|
||||
}
|
||||
|
||||
if (needInit)
|
||||
{
|
||||
linker.OnLink();
|
||||
}
|
||||
|
||||
if (needSetDirty)
|
||||
{
|
||||
linker.SetDirty();
|
||||
}
|
||||
|
||||
return linker;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class LinkerBase<T3> where T3 : UnityEngine.Object
|
||||
{
|
||||
[SerializeField] protected T3 monoBeh;
|
||||
public T3 MonoBeh
|
||||
{
|
||||
get => monoBeh;
|
||||
set => monoBeh = value;
|
||||
}
|
||||
|
||||
public virtual void OnLink() { }
|
||||
|
||||
public void SetDirty()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
DARunner.ExecuteOnMainThread(() =>
|
||||
{
|
||||
monoBeh.SetDirtyExt();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SetValue<T>(ref T currentValue, T newValue)
|
||||
{
|
||||
if (!EqualityComparer<T>.Default.Equals(currentValue, newValue))
|
||||
{
|
||||
if (IsTypeNestedFromType(typeof(T3), typeof(ScriptableObject)))
|
||||
{
|
||||
SceneBackuper.MakeActiveSceneDirty();
|
||||
}
|
||||
|
||||
monoBeh.SetDirtyExt();
|
||||
currentValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsTypeNestedFromType(Type currentType, Type parentType)
|
||||
{
|
||||
while (currentType != null)
|
||||
{
|
||||
if (currentType == parentType)
|
||||
return true;
|
||||
|
||||
currentType = currentType.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AttributeValidator
|
||||
{
|
||||
public static void Validate<T>(params Type[] atts)
|
||||
where T : class
|
||||
{
|
||||
Type type = typeof(T);
|
||||
|
||||
foreach (Type att in atts)
|
||||
{
|
||||
if (!typeof(Attribute).IsAssignableFrom(att))
|
||||
{
|
||||
Debug.LogError($"Type '{att.Name}' is not an Attribute type.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Attribute attribute = Attribute.GetCustomAttribute(type, att);
|
||||
|
||||
if (attribute == null)
|
||||
{
|
||||
Debug.LogError($"Attribute '{att.Name}' is not applied to '{type.Name}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26e58ddaec469d748a90f80015f3d095
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace DA_Assets.DAI
|
||||
{
|
||||
public class SerializePropertyAttribute : Attribute
|
||||
{
|
||||
public SerializePropertyAttribute(string fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
private string fieldName;
|
||||
public string FieldName => fieldName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5c28f73643561544bc1ffeb95d1a5e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DA_Assets.DAI
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
|
||||
public abstract class PropertyHeader : PropertyAttribute
|
||||
{
|
||||
public GUIContent HeaderLabel { get; protected set; }
|
||||
|
||||
protected PropertyHeader(GUIContent headerLabel)
|
||||
{
|
||||
HeaderLabel = headerLabel;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||
public abstract class CustomInspectorProperty : PropertyAttribute
|
||||
{
|
||||
public ComponentType Type { get; protected set; }
|
||||
public GUIContent Label { get; protected set; }
|
||||
public float MinValue { get; protected set; }
|
||||
public float MaxValue { get; protected set; }
|
||||
|
||||
protected CustomInspectorProperty(ComponentType type, GUIContent label, float minValue = 0, float maxValue = 1)
|
||||
{
|
||||
Type = type;
|
||||
Label = label;
|
||||
MinValue = minValue;
|
||||
MaxValue = maxValue;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ComponentType
|
||||
{
|
||||
Toggle,
|
||||
EnumField,
|
||||
FloatField,
|
||||
Vector4Field,
|
||||
TextField,
|
||||
IntField,
|
||||
Vector2Field,
|
||||
Vector2IntField,
|
||||
ColorField,
|
||||
SliderField
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UpdateBool
|
||||
{
|
||||
[SerializeField] bool _value;
|
||||
[SerializeField] bool _temp;
|
||||
|
||||
public UpdateBool(bool value, bool temp)
|
||||
{
|
||||
_value = value;
|
||||
_temp = temp;
|
||||
}
|
||||
|
||||
public bool Value { get => _value; set => _value = value; }
|
||||
public bool Temp { get => _temp; set => _temp = value; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b818ea9ca4e968242b9089946222b9f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user