2025-09-25 19:04:05 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Collections.Generic;
|
2025-10-08 12:06:37 +08:00
|
|
|
|
using System.Data;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
using Convention;
|
2025-10-14 22:57:48 +08:00
|
|
|
|
using Convention.RScript;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
using Convention.WindowsUI.Variant;
|
|
|
|
|
|
using Demo.Game;
|
2025-10-08 12:06:37 +08:00
|
|
|
|
using Flee.PublicTypes;
|
2025-10-06 17:43:21 +08:00
|
|
|
|
using Sirenix.OdinInspector;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
using Unity.Profiling;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Demo
|
|
|
|
|
|
{
|
2025-10-27 20:24:15 +08:00
|
|
|
|
public static class ScriptUtility
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
|
|
|
|
|
public static void OpenScriptFile(ScriptableObject so)
|
|
|
|
|
|
{
|
|
|
|
|
|
string path = so.ScriptPath;
|
2025-10-02 11:11:42 +08:00
|
|
|
|
var ScriptEditor = so.GetRoot().RootGameController.WhichOpenScript;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Diagnostics.Process.Start(string.Format($"{ScriptEditor}", $"\"{path}\""));
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"Cannt open {path}: {string.Format($"{ScriptEditor}", $"\"{path}\"")}", so);
|
|
|
|
|
|
Debug.LogException(ex, so);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public interface IScriptableObject
|
|
|
|
|
|
{
|
|
|
|
|
|
ScriptableObject SharedInterfaceScriptObject { get; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region ScriptableObject Inside
|
|
|
|
|
|
|
|
|
|
|
|
public partial class ScriptableObject : IScriptableObject
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 用于开放给倒置接口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public ScriptableObject SharedInterfaceScriptObject => this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public partial class ScriptableObject
|
|
|
|
|
|
{
|
|
|
|
|
|
// 时间点系统
|
|
|
|
|
|
|
|
|
|
|
|
public static Dictionary<string, float> TimePointDelta = new();
|
|
|
|
|
|
public static Dictionary<string, float> TimePoints = new();
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 重设指定时间线
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="id">时间线ID,若不存在则创建</param>
|
|
|
|
|
|
/// <param name="delta">当每次调用NextTimePoint函数时使用的单位值</param>
|
|
|
|
|
|
/// <param name="value">初始化时间</param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void ResetTimePoint(string id, string delta, string value)
|
|
|
|
|
|
{
|
|
|
|
|
|
TimePointDelta[id] = float.Parse(delta);
|
|
|
|
|
|
TimePoints[id] = float.Parse(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 推动时间线前进
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="id">时间线ID</param>
|
|
|
|
|
|
/// <param name="times">前进次数,最终时间的增量为前进次数乘该时间线的单位值</param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void NextTimePoint(string id, string times)
|
|
|
|
|
|
{
|
|
|
|
|
|
TimePoints[id] += TimePointDelta[id] * float.Parse(times);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置时间线的值
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="id">时间线ID</param>
|
|
|
|
|
|
/// <param name="value">次数,时间线的值将被设置为次数乘该时间线的单位值</param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void SetTimePoint(string id, string value)
|
|
|
|
|
|
{
|
|
|
|
|
|
TimePoints[id] = TimePointDelta[id] * float.Parse(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 12:06:37 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 上下文系统
|
|
|
|
|
|
/// </summary>
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public partial class ScriptableObject
|
|
|
|
|
|
{
|
2025-10-06 17:43:21 +08:00
|
|
|
|
[Content] public Dictionary<string, float> ScriptContextSpace = new();
|
|
|
|
|
|
|
|
|
|
|
|
public bool GetCompleteScriptContext(string key, out float value)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-06 17:43:21 +08:00
|
|
|
|
if (ScriptContextSpace.TryGetValue(key, out value) == false)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-06 17:43:21 +08:00
|
|
|
|
if (Parent == null)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
return Parent.GetCompleteScriptContext(key, out value);
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
2025-10-06 17:43:21 +08:00
|
|
|
|
return true;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 12:06:37 +08:00
|
|
|
|
public void GetCompleteScriptContext(ref Dictionary<string, float> context)
|
|
|
|
|
|
{
|
|
|
|
|
|
var current = this;
|
|
|
|
|
|
while (current != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var key in current.ScriptContextSpace.Keys)
|
|
|
|
|
|
{
|
|
|
|
|
|
context[key] = current.ScriptContextSpace[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
current = current.Parent;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置局部上下文变量,将会传递给子物体使用
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="name">字符串</param>
|
|
|
|
|
|
/// <param name="value">浮点数</param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void SetContext(string name, string value)
|
|
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
ScriptContextSpace[name] = Parse(value);
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 12:06:37 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数值解析工具
|
|
|
|
|
|
/// </summary>
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public partial class ScriptableObject
|
|
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
private ExpressionContext ExpressionParserCreater()
|
|
|
|
|
|
{
|
|
|
|
|
|
ExpressionContext context = new();
|
|
|
|
|
|
context.Imports.AddType(typeof(Mathf));
|
|
|
|
|
|
Dictionary<string, float> vars = new();
|
|
|
|
|
|
GetCompleteScriptContext(ref vars);
|
|
|
|
|
|
foreach (var item in vars)
|
|
|
|
|
|
{
|
|
|
|
|
|
context.Variables[item.Key] = item.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
return context;
|
|
|
|
|
|
}
|
2025-10-01 20:51:48 +08:00
|
|
|
|
public static float OneBarTime = 1;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-10-15 01:16:16 +08:00
|
|
|
|
/// 从字符串解析为浮点数
|
2025-09-25 19:04:05 +08:00
|
|
|
|
/// <list type="bullet">从时间点列表<see cref="TimePoints"/>中获取</list>
|
|
|
|
|
|
/// <list type="bullet">或是从上下文变量<see cref="GetCompleteScriptContext"/>中获取</list>
|
2025-10-15 01:16:16 +08:00
|
|
|
|
/// <list type="bullet">或是从数据驱动对象<see cref="DDT"/>中获取</list>
|
|
|
|
|
|
/// <list type="bullet">或是通过计算表达式值获取</list>
|
2025-09-25 19:04:05 +08:00
|
|
|
|
/// <list type="bullet">或是直接调用<see cref="float.Parse(string)"/></list>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="value"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public float Parse(string value)
|
|
|
|
|
|
{
|
|
|
|
|
|
value = value.Trim();
|
2025-10-08 12:06:37 +08:00
|
|
|
|
if(value.StartsWith("\"")&&value.EndsWith("\""))
|
|
|
|
|
|
{
|
|
|
|
|
|
value = value[1..^1];
|
|
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
|
if (TimePoints.TryGetValue(value, out var result))
|
|
|
|
|
|
return result;
|
2025-10-06 17:43:21 +08:00
|
|
|
|
if (GetCompleteScriptContext(value, out result))
|
2025-09-25 19:04:05 +08:00
|
|
|
|
return result;
|
|
|
|
|
|
if(value.EndsWith(']'))
|
|
|
|
|
|
{
|
|
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
Regex regex = new(@"^(.+)\[(.+)\]$");
|
2025-10-01 20:51:48 +08:00
|
|
|
|
var match = regex.Match(value);
|
|
|
|
|
|
if (match.Success)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
return (FindWithPath(match.Groups[1].Value) as DDT).Datas[(int)this.Parse(match.Groups[2].Value)];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
{
|
|
|
|
|
|
Regex regex = new(@"^(.+)\[\]$");
|
|
|
|
|
|
var match = regex.Match(value);
|
|
|
|
|
|
if (match.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (FindWithPath(match.Groups[1].Value) as DDT).Datas.Count;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-01 20:51:48 +08:00
|
|
|
|
throw new ArgumentException("value is end by ']' but not match on any invlid parse");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (value.EndsWith('}'))
|
|
|
|
|
|
{
|
|
|
|
|
|
{
|
|
|
|
|
|
Regex regex = new(@"^\{\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\}$");
|
|
|
|
|
|
var match = regex.Match(value);
|
|
|
|
|
|
if (match.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
int barSplitTimes = int.Parse(match.Groups[1].Value);
|
|
|
|
|
|
int barCount = int.Parse(match.Groups[2].Value);
|
|
|
|
|
|
int tickCount = int.Parse(match.Groups[3].Value);
|
|
|
|
|
|
return (barCount + tickCount / (float)barSplitTimes) * OneBarTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
throw new ArgumentException("value is end by '}' but not match on any invlid parse");
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
2025-10-07 01:45:52 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
if (float.TryParse(value, out var _result))
|
|
|
|
|
|
return _result;
|
|
|
|
|
|
else
|
|
|
|
|
|
return ExpressionParserCreater().CompileGeneric<float>(value).Evaluate();
|
2025-10-07 01:45:52 +08:00
|
|
|
|
}
|
2025-10-08 12:06:37 +08:00
|
|
|
|
catch(Exception ex)
|
2025-10-07 01:45:52 +08:00
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
throw new FormatException($"{value} is not support any Parser", ex);
|
2025-10-07 01:45:52 +08:00
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
2025-10-08 12:06:37 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected T ConvertValue<T>(string str)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ConventionUtility.convert_xvalue<T>(str);
|
|
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 12:06:37 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 先天支持的工具函数
|
|
|
|
|
|
/// </summary>
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public partial class ScriptableObject
|
|
|
|
|
|
{
|
2025-10-05 20:18:48 +08:00
|
|
|
|
[Content, SerializeField] private Vector3
|
|
|
|
|
|
EnterGameLocalPosition = Vector3.zero,
|
|
|
|
|
|
EnterGameEulerAngles = Vector3.zero,
|
|
|
|
|
|
EnterGameLocalScaling = Vector3.one;
|
2025-10-08 12:06:37 +08:00
|
|
|
|
[Content, SerializeField] private bool IsSetObjectDisable = false;
|
2025-10-05 20:18:48 +08:00
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置坐标
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="x"></param>
|
|
|
|
|
|
/// <param name="y"></param>
|
|
|
|
|
|
/// <param name="z"></param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void SetLocalPosition(string x, string y, string z)
|
|
|
|
|
|
{
|
2025-10-05 20:18:48 +08:00
|
|
|
|
EnterGameLocalPosition = new(Parse(x), Parse(y), Parse(z));
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置欧拉角
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="x"></param>
|
|
|
|
|
|
/// <param name="y"></param>
|
|
|
|
|
|
/// <param name="z"></param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void SetLocalEulerAngles(string x, string y, string z)
|
|
|
|
|
|
{
|
2025-10-05 20:18:48 +08:00
|
|
|
|
EnterGameEulerAngles = new(Parse(x), Parse(y), Parse(z));
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置缩放
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="x"></param>
|
|
|
|
|
|
/// <param name="y"></param>
|
|
|
|
|
|
/// <param name="z"></param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void SetLocalScaling(string x, string y, string z)
|
|
|
|
|
|
{
|
2025-10-05 20:18:48 +08:00
|
|
|
|
EnterGameLocalScaling = new(Parse(x), Parse(y), Parse(z));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 关闭该物体,
|
|
|
|
|
|
/// 在面对如多Game场景时关闭某些GameWorld中默认存在的全局灯光等场景时非常有用
|
|
|
|
|
|
/// </summary>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void SetObjectDisable()
|
|
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
IsSetObjectDisable = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ResetScriptableObjectEnterGameStatus()
|
|
|
|
|
|
{
|
|
|
|
|
|
transform.localPosition = EnterGameLocalPosition;
|
|
|
|
|
|
transform.localEulerAngles = EnterGameEulerAngles;
|
|
|
|
|
|
transform.localScale = EnterGameLocalScaling;
|
|
|
|
|
|
if (IsSetObjectDisable)
|
|
|
|
|
|
{
|
|
|
|
|
|
gameObject.SetActive(false);
|
|
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 12:06:37 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// UpdatePerFrame相关
|
|
|
|
|
|
/// </summary>
|
2025-10-04 23:09:46 +08:00
|
|
|
|
public partial class ScriptableObject
|
|
|
|
|
|
{
|
|
|
|
|
|
[Content] public int UpdatePerFrame = 1;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 指定多少个<see cref="TickType.Update"/>状态的<see cref="UpdateTicks(float, float, TickType)"/>执行一次更新,不会影响到子物体
|
|
|
|
|
|
/// 属于性能优化的高级选项
|
|
|
|
|
|
/// </summary>
|
2025-10-05 20:18:48 +08:00
|
|
|
|
/// <param name="frame">每frame帧更新一次, 等于0代表不会在<see cref="TickType.Update"/>状态运行</param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-10-04 23:09:46 +08:00
|
|
|
|
public void SetUpdatePerFrame(string frame)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdatePerFrame = Mathf.Max(1, int.Parse(frame));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
|
/// <summary>
|
2025-10-08 12:06:37 +08:00
|
|
|
|
/// EnableScript相关
|
2025-09-25 19:04:05 +08:00
|
|
|
|
/// </summary>
|
2025-10-08 12:06:37 +08:00
|
|
|
|
public partial class ScriptableObject
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
|
|
|
|
|
private bool isEnableScript = false;
|
|
|
|
|
|
|
2025-10-13 23:54:29 +08:00
|
|
|
|
public string SourcePath = "";
|
|
|
|
|
|
public string ScriptName = "";
|
|
|
|
|
|
public string ScriptPath;
|
|
|
|
|
|
public string ScriptTypename;
|
|
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
// Hierarchy
|
|
|
|
|
|
|
|
|
|
|
|
public PropertiesWindow.ItemEntry MyHierarchyItem;
|
|
|
|
|
|
public static PropertiesWindow.ItemEntry AllScriptableObjectCounterHierarchyItem;
|
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public void EnableScript(string sourcePath, string scriptPath, string scriptType, ScriptableObject parent)
|
|
|
|
|
|
{
|
2025-10-13 23:54:29 +08:00
|
|
|
|
#if UNITY_EDITOR||Using_ProfilerMarker
|
2025-10-10 01:01:36 +08:00
|
|
|
|
s_PreparePerfMarker = new(ScriptName);
|
2025-09-25 19:04:05 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
if (isEnableScript)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"ScriptableObject is currently enableScript, start coroutine {nameof(UnloadScript)} to disable", this);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.SourcePath = sourcePath;
|
|
|
|
|
|
this.ScriptPath = scriptPath;
|
|
|
|
|
|
this.ScriptTypename = scriptType;
|
|
|
|
|
|
this.Parent = parent;
|
|
|
|
|
|
|
|
|
|
|
|
this.name = ScriptName;
|
|
|
|
|
|
|
|
|
|
|
|
isEnableScript = true;
|
|
|
|
|
|
// 只有RootObject的parent会是空的
|
|
|
|
|
|
if (parent != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
MyHierarchyItem = parent.MyHierarchyItem.GetHierarchyItem().CreateSubPropertyItem(1)[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
MyHierarchyItem = HierarchyWindow.instance.CreateRootItemEntryWithBinders(1)[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
MyHierarchyItem.GetHierarchyItem().title = this.ScriptName + $"<{scriptType}>";
|
|
|
|
|
|
MyHierarchyItem.GetHierarchyItem().target = this;
|
|
|
|
|
|
MyHierarchyItem.GetHierarchyItem().ButtonGameObject.GetComponent<Editor.UI.RightClick>().ScriptObjectMenu = OnHierarchyItemRightClick;
|
2025-10-06 17:43:21 +08:00
|
|
|
|
//var parentHierarchyItem = MyHierarchyItem.GetParent();
|
|
|
|
|
|
//if (parentHierarchyItem != null)
|
|
|
|
|
|
// parentHierarchyItem.GetPropertyListItem().RefreshChilds();
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 12:06:37 +08:00
|
|
|
|
public bool EnsureEnableScript()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isEnableScript == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("ScriptableObject is currently disableScript", this);
|
|
|
|
|
|
}
|
|
|
|
|
|
return isEnableScript;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-13 23:54:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 脚本解析与Update执行
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public partial class ScriptableObject
|
|
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
public static Dictionary<string, Type> FastScriptableObjectTypen = new();
|
2025-10-14 22:57:48 +08:00
|
|
|
|
public static int AllScriptableObjectCounter = 0;
|
|
|
|
|
|
|
2025-10-08 12:06:37 +08:00
|
|
|
|
|
2025-10-13 23:54:29 +08:00
|
|
|
|
#if UNITY_EDITOR||Using_ProfilerMarker
|
2025-10-08 12:06:37 +08:00
|
|
|
|
public ProfilerMarker s_PreparePerfMarker;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
#region LoadSubScript
|
2025-10-08 12:06:37 +08:00
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
private static ScriptableObject LastLoadedScriptableObject;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
|
2025-10-12 00:54:25 +08:00
|
|
|
|
public IEnumerator DoLoadSubScriptAsync([In] string type, [In] string path, [Opt] Action<ScriptableObject> callback)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
|
|
|
|
|
// 判断类型是否合法
|
|
|
|
|
|
if (DefaultInstantiate.GetScriptableObjectInstantiate().TryGetValue(type, out var creater) == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"{type} is not exist or {type}'s Instantiater is not valid", this);
|
2025-10-06 16:09:52 +08:00
|
|
|
|
callback?.Invoke(null);
|
|
|
|
|
|
yield break;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 生成对象
|
|
|
|
|
|
var child = creater();
|
|
|
|
|
|
// 路径预处理
|
2025-10-06 17:43:21 +08:00
|
|
|
|
if (path.Replace('\\', '/').ToLower().StartsWith(RootObjectQuickPath))
|
|
|
|
|
|
path = $"{new ToolFile(GetRoot().SourcePath) | path[RootObjectQuickPath.Length..]}";
|
2025-09-25 19:04:05 +08:00
|
|
|
|
// 获取文件
|
|
|
|
|
|
ToolFile file;
|
|
|
|
|
|
if (File.Exists(path))
|
|
|
|
|
|
file = new(path);
|
|
|
|
|
|
else
|
2025-10-06 16:09:52 +08:00
|
|
|
|
file = new ToolFile(SourcePath) | path;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
// 找不到脚本
|
|
|
|
|
|
if (file.Exists() == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"{file}<{path}> is not found", this);
|
2025-10-06 16:09:52 +08:00
|
|
|
|
callback?.Invoke(null);
|
|
|
|
|
|
yield break;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
child.ScriptName = file.GetName(true);
|
|
|
|
|
|
child.transform.SetParent(this.transform);
|
|
|
|
|
|
child.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
|
|
|
|
|
child.transform.localScale = Vector3.one;
|
|
|
|
|
|
child.EnableScript(file.GetCurrentDirName(), Path.Combine(file.GetCurrentDirName(), file.GetName(false)), type, this);
|
|
|
|
|
|
|
|
|
|
|
|
// Add Child
|
|
|
|
|
|
Childs.Add(child);
|
|
|
|
|
|
|
|
|
|
|
|
// Load Child Script
|
2025-10-06 16:09:52 +08:00
|
|
|
|
yield return child.LoadScript(file.LoadAsText());
|
2025-09-25 19:04:05 +08:00
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
LastLoadedScriptableObject = child;
|
2025-10-06 16:09:52 +08:00
|
|
|
|
callback?.Invoke(child);
|
|
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
|
|
2025-11-11 14:30:44 +08:00
|
|
|
|
public IEnumerator DoGenerateSubScriptAsync([In] string type, string name, [Opt] Action<ScriptableObject> callback)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 判断类型是否合法
|
|
|
|
|
|
if (DefaultInstantiate.GetScriptableObjectInstantiate().TryGetValue(type, out var creater) == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"{type} is not exist or {type}'s Instantiater is not valid", this);
|
|
|
|
|
|
callback?.Invoke(null);
|
|
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 生成对象
|
|
|
|
|
|
var child = creater();
|
|
|
|
|
|
child.ScriptName = name;
|
|
|
|
|
|
child.transform.SetParent(this.transform);
|
|
|
|
|
|
child.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
|
|
|
|
|
child.transform.localScale = Vector3.one;
|
|
|
|
|
|
child.EnableScript("", "", type, this);
|
|
|
|
|
|
|
|
|
|
|
|
// Add Child
|
|
|
|
|
|
Childs.Add(child);
|
|
|
|
|
|
|
|
|
|
|
|
// Load Child Script
|
|
|
|
|
|
yield return child.LoadScript("");
|
|
|
|
|
|
|
|
|
|
|
|
LastLoadedScriptableObject = child;
|
|
|
|
|
|
callback?.Invoke(child);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建子脚本
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="type">指定类型</param>
|
|
|
|
|
|
public IEnumerator NewSubScript([In] string type)
|
|
|
|
|
|
{
|
|
|
|
|
|
return DoGenerateSubScriptAsync(type, $"New {type}", null);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-06 16:09:52 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 加载子脚本
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="type">指定类型</param>
|
|
|
|
|
|
/// <param name="path">指定脚本,可用决定路径或与当前脚本目录的相对路径</param>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-10-06 16:09:52 +08:00
|
|
|
|
public IEnumerator LoadSubScript([In] string type, [In] string path)
|
|
|
|
|
|
{
|
2025-10-12 00:54:25 +08:00
|
|
|
|
return DoLoadSubScriptAsync(type, path, null);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取已加载的脚本对象
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>无法找到时将返回空</returns>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-10-14 22:57:48 +08:00
|
|
|
|
public ScriptableObject GetLoadedObject(string path)
|
|
|
|
|
|
{
|
|
|
|
|
|
return FindWithPath(path, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取父脚本对象
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-10-14 22:57:48 +08:00
|
|
|
|
public ScriptableObject GetParentObject()
|
|
|
|
|
|
{
|
|
|
|
|
|
return Parent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取上一个加载的脚本对象
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-10-14 22:57:48 +08:00
|
|
|
|
public ScriptableObject GetLastLoadedScriptableObject()
|
|
|
|
|
|
{
|
|
|
|
|
|
return LastLoadedScriptableObject;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
private IEnumerator ParseScript2Expr(string script)
|
|
|
|
|
|
{
|
|
|
|
|
|
RScriptEngine engine = new();
|
|
|
|
|
|
RScriptImportClass importClass = new()
|
|
|
|
|
|
{
|
|
|
|
|
|
typeof(Mathf),
|
|
|
|
|
|
};
|
|
|
|
|
|
RScriptVariables variables = new()
|
|
|
|
|
|
{
|
|
|
|
|
|
{ "this", new() { data = this, type = this.GetType() } },
|
|
|
|
|
|
{ "self", new() { data = this, type = this.GetType() } }
|
|
|
|
|
|
};
|
2025-10-18 18:22:27 +08:00
|
|
|
|
foreach (var type in DefaultInstantiate.GetScriptableObjectInstantiate().Keys)
|
|
|
|
|
|
{
|
|
|
|
|
|
variables.Add(type, new(type.GetType(), type));
|
|
|
|
|
|
}
|
2025-10-14 22:57:48 +08:00
|
|
|
|
return engine.RunAsync(script, importClass, variables);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Content] private bool IsEnableUpdate = false;
|
|
|
|
|
|
|
|
|
|
|
|
public enum TickType
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
Reset,
|
|
|
|
|
|
Pause,
|
|
|
|
|
|
Start,
|
|
|
|
|
|
Update
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
[Content, SerializeField] private int ScriptUpdateCounter = 0;
|
|
|
|
|
|
public void ScriptUpdate(float currentTime, float deltaTime, TickType tickType)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
if (IsEnableUpdate == false)
|
|
|
|
|
|
return;
|
|
|
|
|
|
if (gameObject.activeInHierarchy == false)
|
|
|
|
|
|
return;
|
|
|
|
|
|
#if UNITY_EDITOR||Using_ProfilerMarker
|
|
|
|
|
|
s_PreparePerfMarker.Begin(this);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
if (tickType == TickType.Reset)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
ResetEnterGameStatus();
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
2025-10-14 22:57:48 +08:00
|
|
|
|
// UpdateTicks
|
|
|
|
|
|
if (UpdatePerFrame > 0)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
if (ScriptUpdateCounter % UpdatePerFrame == 0)
|
|
|
|
|
|
UpdateTicks(currentTime, deltaTime, tickType);
|
|
|
|
|
|
ScriptUpdateCounter += tickType == TickType.Update ? 1 : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Childs UpdateTicks
|
|
|
|
|
|
foreach (var child in Childs)
|
|
|
|
|
|
{
|
|
|
|
|
|
child.ScriptUpdate(currentTime, deltaTime, tickType);
|
|
|
|
|
|
}
|
|
|
|
|
|
#if UNITY_EDITOR||Using_ProfilerMarker
|
|
|
|
|
|
s_PreparePerfMarker.End();
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected virtual void UpdateTicks(float currentTime, float deltaTime, TickType tickType)
|
|
|
|
|
|
{
|
2025-09-25 19:04:05 +08:00
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// <para>使用<see cref="ScriptableCallAttribute"/>标记可编辑脚本所能够调用的函数,并附加注释</para>
|
|
|
|
|
|
/// <para>使用<see cref="DefaultScriptAttribute"/>标记派生类,并附加默认模板</para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public partial class ScriptableObject : SerializedMonoBehaviour, IHierarchyItemClickEventListener
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
public static bool IsAutoPlay = false;
|
|
|
|
|
|
|
|
|
|
|
|
public ScriptableObject Parent;
|
|
|
|
|
|
|
2025-10-15 01:16:16 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取根脚本对象
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2025-10-27 20:24:15 +08:00
|
|
|
|
[Convention.RScript.Variable.Attr.Method]
|
2025-10-14 22:57:48 +08:00
|
|
|
|
public RootObject GetRoot()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Parent == null)
|
|
|
|
|
|
return this as RootObject;
|
|
|
|
|
|
if (Parent is RootObject result)
|
|
|
|
|
|
return result;
|
2025-10-15 01:16:16 +08:00
|
|
|
|
else
|
|
|
|
|
|
return Parent.GetRoot();
|
2025-10-14 22:57:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public List<ScriptableObject> Childs = new();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Cache
|
|
|
|
|
|
|
|
|
|
|
|
public const string RootObjectQuickPath = "project/";
|
|
|
|
|
|
|
|
|
|
|
|
public ScriptableObject FindWithPath(string path, bool isMustExist = true)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(path))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isMustExist)
|
|
|
|
|
|
throw new Exception("path is null or empty");
|
|
|
|
|
|
else
|
|
|
|
|
|
return null;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
// GetParent
|
|
|
|
|
|
ScriptableObject result = Parent;
|
|
|
|
|
|
if (path.Replace('\\','/').ToLower().StartsWith(RootObjectQuickPath))
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
result = GetRoot();
|
|
|
|
|
|
path = path[RootObjectQuickPath.Length..];
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
2025-10-14 22:57:48 +08:00
|
|
|
|
|
|
|
|
|
|
if (Parent == null)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
throw new InvalidOperationException($"Root is nosupport to {nameof(FindWithPath)}");
|
|
|
|
|
|
}
|
2025-10-07 01:45:52 +08:00
|
|
|
|
|
2025-10-14 22:57:48 +08:00
|
|
|
|
// Find
|
|
|
|
|
|
var components = path.Split('/', '\\');
|
|
|
|
|
|
components[^1] = new ToolFile(components[^1]).GetFilename(true);
|
|
|
|
|
|
foreach (var component in components)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (component == "..")
|
|
|
|
|
|
result = result.Parent;
|
|
|
|
|
|
else if (component == "." || string.IsNullOrEmpty(component))
|
2025-10-08 12:06:37 +08:00
|
|
|
|
continue;
|
2025-10-14 22:57:48 +08:00
|
|
|
|
else
|
2025-10-07 01:45:52 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
int index = 0;
|
|
|
|
|
|
string targetScriptObjectPath = component;
|
|
|
|
|
|
Regex regex = new(@"^(.*)\[(\d*)\]$");
|
|
|
|
|
|
var match = regex.Match(component);
|
|
|
|
|
|
if (match.Success)
|
2025-10-07 01:45:52 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
targetScriptObjectPath = match.Groups[1].Value;
|
|
|
|
|
|
index = int.Parse(match.Groups[2].Value);
|
2025-10-07 01:45:52 +08:00
|
|
|
|
}
|
2025-10-14 22:57:48 +08:00
|
|
|
|
var target = result.Childs.FirstOrDefault(x =>
|
2025-10-06 16:09:52 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
bool stats = x.ScriptName == targetScriptObjectPath;
|
|
|
|
|
|
if (index == 0)
|
|
|
|
|
|
return stats;
|
|
|
|
|
|
else if (stats)
|
|
|
|
|
|
index--;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (target != null)
|
|
|
|
|
|
result = target;
|
2025-10-06 16:09:52 +08:00
|
|
|
|
else
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
if (isMustExist)
|
|
|
|
|
|
throw new Exception($"{component} in {path} is not found");
|
2025-10-07 01:45:52 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-10-14 22:57:48 +08:00
|
|
|
|
Debug.Log($"{component} in {path} is not found", this);
|
|
|
|
|
|
return null;
|
2025-10-07 01:45:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
2025-10-14 22:57:48 +08:00
|
|
|
|
return result;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public virtual IEnumerator LoadScript(string script)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (EnsureEnableScript() == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
2025-10-08 00:35:50 +08:00
|
|
|
|
// 增数
|
|
|
|
|
|
{
|
|
|
|
|
|
AllScriptableObjectCounter++;
|
|
|
|
|
|
AllScriptableObjectCounterHierarchyItem.GetHierarchyItem().text = $"ScriptableObjectCount: {AllScriptableObjectCounter}";
|
|
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
|
yield return ParseScript2Expr(script);
|
|
|
|
|
|
IsEnableUpdate = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Reset()
|
|
|
|
|
|
{
|
|
|
|
|
|
StartCoroutine(UnloadScript());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual IEnumerator UnloadScript()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (EnsureEnableScript())
|
|
|
|
|
|
{
|
|
|
|
|
|
this.name = "<Unloading>";
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var child in Childs)
|
|
|
|
|
|
{
|
|
|
|
|
|
yield return child.UnloadScript();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
// 清理各种状态
|
|
|
|
|
|
IsEnableUpdate = false;
|
|
|
|
|
|
if (MyHierarchyItem != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 卸载UI
|
|
|
|
|
|
MyHierarchyItem.Release();
|
|
|
|
|
|
MyHierarchyItem = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.isEnableScript = false;
|
|
|
|
|
|
this.Parent = null;
|
|
|
|
|
|
this.name = "<Unload>";
|
|
|
|
|
|
// 清理Cache
|
2025-10-08 12:06:37 +08:00
|
|
|
|
//
|
2025-10-08 00:35:50 +08:00
|
|
|
|
// 减数
|
|
|
|
|
|
AllScriptableObjectCounter--;
|
|
|
|
|
|
AllScriptableObjectCounterHierarchyItem.GetHierarchyItem().text = $"ScriptableObjectCount: {AllScriptableObjectCounter}";
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 12:06:37 +08:00
|
|
|
|
public virtual void ResetEnterGameStatus()
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
2025-10-08 12:06:37 +08:00
|
|
|
|
ResetScriptableObjectEnterGameStatus();
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual void OnHierarchyItemRightClick(RectTransform item)
|
|
|
|
|
|
{
|
|
|
|
|
|
DefaultInstantiate.OpenInstantiateMenu(this, item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual void OnHierarchyItemClick(HierarchyItem item)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
public interface IAssetBundleLoader : IScriptableObject
|
|
|
|
|
|
{
|
|
|
|
|
|
public RootObject GetRoot();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static class AssetBundlesLoadHelper
|
|
|
|
|
|
{
|
|
|
|
|
|
public struct Entry
|
|
|
|
|
|
{
|
|
|
|
|
|
public AssetBundle assetBundle;
|
|
|
|
|
|
public int referenceCounter;
|
|
|
|
|
|
public PropertiesWindow.ItemEntry entry;
|
|
|
|
|
|
|
|
|
|
|
|
public Entry(AssetBundle assetBundle, int referenceCounter = 0, PropertiesWindow.ItemEntry entry = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.assetBundle = assetBundle;
|
|
|
|
|
|
this.referenceCounter = referenceCounter;
|
|
|
|
|
|
this.entry = entry;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static Dictionary<string, Entry> AssetBundleCounter = new();
|
|
|
|
|
|
private static Dictionary<string, IEnumerator> AssetBundleLoading = new();
|
|
|
|
|
|
private static PropertiesWindow.ItemEntry AssetBundlesItemEntry = null;
|
|
|
|
|
|
|
2025-10-06 16:09:52 +08:00
|
|
|
|
public static IEnumerator LoadAssetBundleAsync(string ab, Action<AssetBundle> callback)
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (AssetBundleCounter.TryGetValue(ab, out var result))
|
|
|
|
|
|
{
|
|
|
|
|
|
result.referenceCounter++;
|
2025-10-06 16:09:52 +08:00
|
|
|
|
callback?.Invoke(result.assetBundle);
|
2025-09-25 19:04:05 +08:00
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (AssetBundleLoading.TryGetValue(ab, out var tir))
|
|
|
|
|
|
{
|
|
|
|
|
|
yield return tir;
|
|
|
|
|
|
if (AssetBundleCounter.TryGetValue(ab, out result))
|
|
|
|
|
|
{
|
|
|
|
|
|
result.referenceCounter++;
|
2025-10-06 16:09:52 +08:00
|
|
|
|
callback?.Invoke(result.assetBundle);
|
2025-09-25 19:04:05 +08:00
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
var file = new ToolFile(PlatformIndicator.StreamingAssetsPath) | "AssetBundle/";
|
|
|
|
|
|
#if PLATFORM_WINDOWS
|
|
|
|
|
|
file = file | "Windows";
|
|
|
|
|
|
#endif
|
|
|
|
|
|
file = file | ab;
|
2025-10-01 20:51:48 +08:00
|
|
|
|
#if ENABLE_CLASS_Interaction
|
2025-09-29 16:38:09 +08:00
|
|
|
|
var downloader = new Interaction(file.GetFullPath());
|
2025-10-01 20:51:48 +08:00
|
|
|
|
#else
|
|
|
|
|
|
var downloader = new ToolFile(file.GetFullPath());
|
|
|
|
|
|
#endif
|
2025-09-25 19:04:05 +08:00
|
|
|
|
if (AssetBundlesItemEntry == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hierarchy = HierarchyWindow.instance;
|
|
|
|
|
|
AssetBundlesItemEntry = hierarchy.CreateRootItemEntryWithBinders(nameof(AssetBundlesLoadHelper))[0];
|
|
|
|
|
|
AssetBundlesItemEntry.ref_value.GetComponent<HierarchyItem>().title = nameof(AssetBundlesLoadHelper);
|
|
|
|
|
|
}
|
|
|
|
|
|
var abLoadingItem = AssetBundlesItemEntry.ref_value.GetComponent<HierarchyItem>().CreateSubPropertyItem(1)[0];
|
|
|
|
|
|
var loadingHierarchyItem = abLoadingItem.ref_value.GetComponent<HierarchyItem>();
|
|
|
|
|
|
loadingHierarchyItem.title = $"{ab}<Loading>";
|
2025-09-29 16:38:09 +08:00
|
|
|
|
var ir = downloader.LoadAsAssetBundle(p =>
|
2025-09-25 19:04:05 +08:00
|
|
|
|
{
|
|
|
|
|
|
loadingHierarchyItem.title = $"{ab}<Loading{(int)(p * 100)}%>";
|
|
|
|
|
|
}, x =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (x != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
loadingHierarchyItem.title = $"{ab}";
|
|
|
|
|
|
var assets = x.GetAllAssetNames();
|
|
|
|
|
|
var subAssetsItems = loadingHierarchyItem.CreateSubPropertyItem(assets.Length);
|
|
|
|
|
|
for (int i = 0, e = assets.Length; i < e; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
subAssetsItems[i].ref_value.GetComponent<HierarchyItem>().title = Path.GetFileName(assets[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
AssetBundleCounter[ab] = new(x, 1, abLoadingItem);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
loadingHierarchyItem.title = $"{ab}<Failed>";
|
|
|
|
|
|
}
|
|
|
|
|
|
AssetBundleLoading.Remove(ab);
|
2025-10-06 16:09:52 +08:00
|
|
|
|
callback?.Invoke(x);
|
2025-09-25 19:04:05 +08:00
|
|
|
|
});
|
|
|
|
|
|
AssetBundleLoading.Add(ab, ir);
|
|
|
|
|
|
yield return ir;
|
|
|
|
|
|
AssetBundleLoading.Remove(ab);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-06 16:09:52 +08:00
|
|
|
|
public static IEnumerator LoadAssetBundle(this IAssetBundleLoader self, string ab, Action<AssetBundle> callback)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log($"{self.SharedInterfaceScriptObject.ScriptName}.{nameof(LoadAssetBundle)}({ab})", self.SharedInterfaceScriptObject);
|
|
|
|
|
|
yield return LoadAssetBundleAsync(ab, callback);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
|
public static IEnumerator UnloadAssetBundle(this IAssetBundleLoader self, string ab)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Editor中暂时忽略卸载功能
|
|
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public abstract class TimelineScriptObject : ScriptableObject
|
|
|
|
|
|
{
|
|
|
|
|
|
public static PropertiesWindow TimelineWindow;
|
|
|
|
|
|
private PropertiesWindow.ItemEntry MyTimelineEntry;
|
|
|
|
|
|
private Editor.UI.TimelineItem MyTimelineItem;
|
|
|
|
|
|
//[SerializeField] private bool IsTimelineItemShow = false;
|
|
|
|
|
|
private static List<TimelineScriptObject> TimelineScriptObjectWhichOnShow = new();
|
|
|
|
|
|
|
|
|
|
|
|
private static UnityEngine.UI.Image CacheLastFocusImage;
|
|
|
|
|
|
private static Color CacheLastFocusImageOriginColor = new(1, 1, 1, 0.01f);
|
|
|
|
|
|
private static Color FocusImageColor = new Color(1f, 47f / 51f, 0.0156862754f, 0.1f);
|
|
|
|
|
|
|
|
|
|
|
|
public override IEnumerator LoadScript(string script)
|
|
|
|
|
|
{
|
|
|
|
|
|
MyTimelineEntry = TimelineWindow.CreateRootItemEntries(1)[0];
|
|
|
|
|
|
MyTimelineItem = MyTimelineEntry.ref_value.GetComponent<Editor.UI.TimelineItem>();
|
|
|
|
|
|
yield return base.LoadScript(script);
|
|
|
|
|
|
MyTimelineItem.title = ScriptName;
|
|
|
|
|
|
MyTimelineItem.AddListener(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
HierarchyWindow.instance.MakeFocusOn(MyHierarchyItem.GetHierarchyItem());
|
|
|
|
|
|
if (CacheLastFocusImage != null)
|
|
|
|
|
|
CacheLastFocusImage.color = CacheLastFocusImageOriginColor;
|
|
|
|
|
|
CacheLastFocusImage = MyHierarchyItem.GetHierarchyItem().ButtonGameObject.GetComponent<UnityEngine.UI.Image>();
|
|
|
|
|
|
CacheLastFocusImage.color = FocusImageColor;
|
|
|
|
|
|
});
|
|
|
|
|
|
SetupTimelineItem(MyTimelineItem);
|
|
|
|
|
|
// 暂时的逻辑是总是展示的
|
|
|
|
|
|
{
|
|
|
|
|
|
TimelineScriptObjectWhichOnShow.Add(this);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnHierarchyItemClick(HierarchyItem item)
|
|
|
|
|
|
{
|
2025-10-27 20:24:15 +08:00
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//protected virtual void ShowTimelineItemWithChilds()
|
|
|
|
|
|
//{
|
|
|
|
|
|
// ScriptableObject parent = this;
|
|
|
|
|
|
// List<ScriptableObject> childs = this.Childs;
|
|
|
|
|
|
// if (parent is TimelineScriptObject ptso)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// ptso.IsTimelineItemShow = true;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// foreach (var child in childs)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// if (child is TimelineScriptObject tso)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// tso.IsTimelineItemShow = true;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.UpdateTicks(currentTime, deltaTime, tickType);
|
2025-10-27 20:24:15 +08:00
|
|
|
|
{
|
2025-09-25 19:04:05 +08:00
|
|
|
|
MyTimelineItem.ResizeOnTimeline();
|
2025-10-27 20:24:15 +08:00
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected abstract void SetupTimelineItem(Editor.UI.TimelineItem item);
|
|
|
|
|
|
|
|
|
|
|
|
protected virtual Color GetTimelineItemColor()
|
|
|
|
|
|
{
|
|
|
|
|
|
static float Foo(uint hash)
|
|
|
|
|
|
{
|
|
|
|
|
|
float a= (hash % 10) * 0.1f;
|
|
|
|
|
|
return a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var a1 = (uint)this.GetType().GetHashCode();
|
|
|
|
|
|
var a2 = (uint)a1.ToString().GetHashCode();
|
|
|
|
|
|
var a3 = (uint)(a2.ToString().GetHashCode() >> 5);
|
|
|
|
|
|
var color = new Vector3(Foo(a1), Foo(a2), Foo(a3)).normalized;
|
|
|
|
|
|
return new(color.x, color.y, color.z);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override IEnumerator UnloadScript()
|
|
|
|
|
|
{
|
|
|
|
|
|
yield return base.UnloadScript();
|
|
|
|
|
|
MyTimelineItem.RawButton.onClick.RemoveAllListeners();
|
|
|
|
|
|
MyTimelineEntry.Release();
|
|
|
|
|
|
MyTimelineEntry = null;
|
|
|
|
|
|
MyTimelineItem = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|