This commit is contained in:
2026-05-17 15:12:16 +07:00
parent 93da00c206
commit bf0ebe447d
902 changed files with 142169 additions and 31515 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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