推进同化RScript

This commit is contained in:
2025-11-24 18:02:57 +08:00
parent fc8f48bc7e
commit 2e0d16db49
17 changed files with 400 additions and 864 deletions

View File

@@ -30,11 +30,6 @@ namespace Demo.Game
VirtualCamera = GetComponent<CinemachineVirtualCamera>();
}
public override IEnumerator LoadScript(string script)
{
yield return base.LoadScript(script);
}
public override IEnumerator UnloadScript()
{
yield return base.UnloadScript();
@@ -44,89 +39,73 @@ namespace Demo.Game
/// <summary>
/// 设置是否为正交相机
/// </summary>
/// <param name="arg"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetOrthographic(string arg)
public void SetOrthographic(bool orthographic)
{
MainCamera.orthographic = ConvertValue<bool>(arg);
MainCamera.orthographic = orthographic;
}
/// <summary>
/// 设置相机视野角度
/// </summary>
/// <param name="arg"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetFieldOfView(string arg)
public void SetFieldOfView(float fieldOfView)
{
MainCamera.fieldOfView = Parse(arg);
MainCamera.fieldOfView = fieldOfView;
}
/// <summary>
/// 设置正交相机的尺寸
/// </summary>
/// <param name="arg"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetOrthographicSize(string arg)
public void SetOrthographicSize(float orthographicSize)
{
MainCamera.orthographicSize = Parse(arg);
MainCamera.orthographicSize = orthographicSize;
}
/// <summary>
/// 设置近裁剪面距离
/// </summary>
/// <param name="arg"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetNearClipPlane(string arg)
public void SetNearClipPlane(float nearClipPlane)
{
MainCamera.nearClipPlane = Parse(arg);
MainCamera.nearClipPlane = nearClipPlane;
}
/// <summary>
/// 设置远裁剪面距离
/// </summary>
/// <param name="arg"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetFarClipPlane(string arg)
public void SetFarClipPlane(float farClipPlane)
{
MainCamera.farClipPlane = Parse(arg);
MainCamera.farClipPlane = farClipPlane;
}
/// <summary>
/// 设置相机深度
/// </summary>
/// <param name="arg"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetDepth(string arg)
public void SetDepth(float depth)
{
MainCamera.depth = Parse(arg);
MainCamera.depth = depth;
}
/// <summary>
/// 设置虚拟相机跟随目标
/// </summary>
/// <param name="targetName">对象相对路径</param>
[Convention.RScript.Variable.Attr.Method]
public void SetVirtualCameraFollow(string targetName)
public void SetVirtualCameraFollow(ScriptableObject target)
{
var target = FindWithPath(targetName, false);
if (target != null)
{
VirtualCamera.Follow = target.transform;
}
VirtualCamera.Follow = target.transform;
}
/// <summary>
/// 设置虚拟相机观察目标
/// </summary>
/// <param name="targetName">对象相对路径</param>
[Convention.RScript.Variable.Attr.Method]
public void SetVirtualCameraLookAt(string targetName)
public void SetVirtualCameraLookAt(ScriptableObject target)
{
var target = FindWithPath(targetName, false);
if (target != null)
{
VirtualCamera.LookAt = target.transform;
}
VirtualCamera.LookAt = target.transform;
}
/// <summary>
@@ -136,16 +115,12 @@ namespace Demo.Game
/// <param name="y">Y轴偏移</param>
/// <param name="z">Z轴偏移</param>
[Convention.RScript.Variable.Attr.Method]
public void SetVirtualCameraFollowOffset(string x, string y, string z)
public void SetVirtualCameraFollowOffset(float x, float y, float z)
{
var body = VirtualCamera.GetCinemachineComponent<Cinemachine.CinemachineTransposer>();
if (body != null)
{
body.m_FollowOffset = new Vector3(
Parse(x),
Parse(y),
Parse(z)
);
body.m_FollowOffset = new Vector3(x, y, z);
}
}
@@ -156,14 +131,14 @@ namespace Demo.Game
/// <param name="y">Y轴阻尼</param>
/// <param name="z">Z轴阻尼</param>
[Convention.RScript.Variable.Attr.Method]
public void SetVirtualCameraFollowDamping(string x, string y, string z)
public void SetVirtualCameraFollowDamping(float x, float y, float z)
{
var body = VirtualCamera.GetCinemachineComponent<Cinemachine.CinemachineTransposer>();
if (body != null)
{
body.m_XDamping = Parse(x);
body.m_YDamping = Parse(y);
body.m_ZDamping = Parse(z);
body.m_XDamping = x;
body.m_YDamping = y;
body.m_ZDamping = z;
}
}
@@ -174,13 +149,13 @@ namespace Demo.Game
/// <param name="y">Y轴阻尼</param>
/// <param name="z">Z轴阻尼</param>
[Convention.RScript.Variable.Attr.Method]
public void SetVirtualCameraLookAtDamping(string x, string y, string z)
public void SetVirtualCameraLookAtDamping(float x, float y, float z)
{
var aim = VirtualCamera.GetCinemachineComponent<Cinemachine.CinemachineComposer>();
if (aim != null)
{
aim.m_HorizontalDamping = Parse(x);
aim.m_VerticalDamping = Parse(y);
aim.m_HorizontalDamping = x;
aim.m_VerticalDamping = y;
}
}
}

View File

@@ -6,8 +6,6 @@ namespace Demo.Game
{
public class DDT : ScriptableObject
{
public string BindingDataJson => $"{ScriptPath}.json";
public static DDT Make()
{
return new GameObject().AddComponent<DDT>();
@@ -15,12 +13,6 @@ namespace Demo.Game
public List<float> Datas = new();
[Convention.RScript.Variable.Attr.Method]
public void Add(string value)
{
Datas.Add(Parse(value));
}
[Convention.RScript.Variable.Attr.Method]
public void Add(float value)
{
@@ -32,28 +24,5 @@ namespace Demo.Game
{
Datas.Add((barCount + tickCount / (float)barSplitTimes) * OneBarTime);
}
/// <summary>
/// 从特定的json中读取数据, 并调用<see cref="Add(string)"/>
/// </summary>
[Convention.RScript.Variable.Attr.Method]
public void Load()
{
var file = new ToolFile(BindingDataJson);
if (file.Exists() == false)
{
file.MustExistsPath()
.SaveAsJson<List<string>>(new());
Datas.Clear();
}
else
{
Datas.Clear();
foreach (var item in file.LoadAsJson<List<string>>())
{
Add(item);
}
}
}
}
}

View File

@@ -205,10 +205,11 @@ namespace Demo.Game
rootGameObject.transform.SetParent(transform);
rootGameObject.ScriptName = rootObject.GetName(true);
rootGameObject.audioSystem = MainAudio;
rootGameObject.EnableScript(content.RootSourceDir, rootObject.GetFullPath(), this);
rootGameObject.EnableScript(content.RootSourceDir, this);
try
{
yield return rootGameObject.LoadScript(rootObject.LoadAsText());
yield return rootGameObject.ParseScript2Expr(rootObject.LoadAsText());
yield return rootGameObject.ApplyScript();
}
finally
{

View File

@@ -1,10 +1,8 @@
using Convention;
using Convention.WindowsUI.Variant;
using System;
using System.Collections;
using System.Collections.Generic;
using Convention;
using Convention.VFX;
using Convention.WindowsUI.Variant;
using Demo.Editor.UI;
using UnityEngine;
using UnityEngine.InputSystem;
@@ -16,19 +14,15 @@ namespace Demo.Game
[Content] public GameController RootGameController;
public override IEnumerator LoadScript(string script)
public string SourcePath;
protected override IEnumerator DoSomethingDuringApplyScript()
{
try
yield return base.DoSomethingDuringApplyScript();
ScriptUpdate(RootGameController.SongOffset, 0.01f, TickType.Reset);
if (RootGameController.IsMain)
{
yield return base.LoadScript(script);
}
finally
{
ScriptUpdate(RootGameController.SongOffset, 0.01f, TickType.Start);
if (RootGameController.IsMain)
{
Keyboard.current.onTextInput += InputCatchChar;
}
Keyboard.current.onTextInput += InputCatchChar;
}
}
@@ -41,14 +35,15 @@ namespace Demo.Game
yield return base.UnloadScript();
}
public void EnableScript(string sourcePath, string scriptPath, GameController parent)
public void EnableScript(string sourcePath, GameController parent)
{
AllScriptableObjectCounter = 0;
if (AllScriptableObjectCounterHierarchyItem == null)
{
AllScriptableObjectCounterHierarchyItem = HierarchyWindow.instance.CreateRootItemEntryWithBinders(typeof(ScriptableObject))[0];
}
base.EnableScript(sourcePath, scriptPath, nameof(RootObject), null);
SourcePath = sourcePath;
base.EnableScript(null);
RootGameController = parent;
}

View File

@@ -1,3 +1,9 @@
using Convention;
using Convention.RScript;
using Convention.WindowsUI.Variant;
using Demo.Game;
using Flee.PublicTypes;
using Sirenix.OdinInspector;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -6,35 +12,11 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Convention;
using Convention.RScript;
using Convention.WindowsUI.Variant;
using Demo.Game;
using Flee.PublicTypes;
using Sirenix.OdinInspector;
using Unity.Profiling;
using UnityEngine;
namespace Demo
{
public static class ScriptUtility
{
public static void OpenScriptFile(ScriptableObject so)
{
string path = so.ScriptPath;
var ScriptEditor = so.GetRoot().RootGameController.WhichOpenScript;
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; }
@@ -50,191 +32,8 @@ namespace Demo
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>
[Convention.RScript.Variable.Attr.Method]
public void ResetTimePoint(string id, float delta, float value)
{
TimePointDelta[id] = delta;
TimePoints[id] = value;
}
/// <summary>
/// 推动时间线前进
/// </summary>
/// <param name="id">时间线ID</param>
/// <param name="times">前进次数,最终时间的增量为前进次数乘该时间线的单位值</param>
[Convention.RScript.Variable.Attr.Method]
public void NextTimePoint(string id, float times)
{
TimePoints[id] += TimePointDelta[id] * times;
}
/// <summary>
/// 设置时间线的值
/// </summary>
/// <param name="id">时间线ID</param>
/// <param name="value">次数,时间线的值将被设置为次数乘该时间线的单位值</param>
[Convention.RScript.Variable.Attr.Method]
public void SetTimePoint(string id, float value)
{
TimePoints[id] = TimePointDelta[id] * value;
}
}
/// <summary>
/// 上下文系统
/// </summary>
public partial class ScriptableObject
{
[Content] public Dictionary<string, float> ScriptContextSpace = new();
public bool GetCompleteScriptContext(string key, out float value)
{
if (ScriptContextSpace.TryGetValue(key, out value) == false)
{
if (Parent == null)
return false;
return Parent.GetCompleteScriptContext(key, out value);
}
return true;
}
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;
}
}
/// <summary>
/// 设置局部上下文变量,将会传递给子物体使用
/// </summary>
/// <param name="name">字符串</param>
/// <param name="value">浮点数</param>
[Convention.RScript.Variable.Attr.Method]
public void SetContext(string name, float value)
{
ScriptContextSpace[name] = value;
}
}
/// <summary>
/// 数值解析工具
/// </summary>
public partial class ScriptableObject
{
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;
}
public static float OneBarTime = 1;
/// <summary>
/// 从字符串解析为浮点数
/// <list type="bullet">从时间点列表<see cref="TimePoints"/>中获取</list>
/// <list type="bullet">或是从上下文变量<see cref="GetCompleteScriptContext"/>中获取</list>
/// <list type="bullet">或是从数据驱动对象<see cref="DDT"/>中获取</list>
/// <list type="bullet">或是通过计算表达式值获取</list>
/// <list type="bullet">或是直接调用<see cref="float.Parse(string)"/></list>
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[Convention.RScript.Variable.Attr.Method]
public float Parse(string value)
{
value = value.Trim();
if(value.StartsWith("\"")&&value.EndsWith("\""))
{
value = value[1..^1];
}
if (TimePoints.TryGetValue(value, out var result))
return result;
if (GetCompleteScriptContext(value, out result))
return result;
if(value.EndsWith(']'))
{
{
Regex regex = new(@"^(.+)\[(.+)\]$");
var match = regex.Match(value);
if (match.Success)
{
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;
}
}
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");
}
try
{
if (float.TryParse(value, out var _result))
return _result;
else
return ExpressionParserCreater().CompileGeneric<float>(value).Evaluate();
}
catch(Exception ex)
{
throw new FormatException($"{value} is not support any Parser", ex);
}
}
protected T ConvertValue<T>(string str)
{
return ConventionUtility.convert_xvalue<T>(str);
}
}
/// <summary>
/// 先天支持的工具函数
/// 初始化与对象设置
/// </summary>
public partial class ScriptableObject
{
@@ -243,6 +42,7 @@ namespace Demo
EnterGameEulerAngles = Vector3.zero,
EnterGameLocalScaling = Vector3.one;
[Content, SerializeField] private bool IsSetObjectDisable = false;
[Content] public int UpdatePerFrame = 1;
/// <summary>
/// 设置坐标
@@ -288,25 +88,6 @@ namespace Demo
IsSetObjectDisable = true;
}
private void ResetScriptableObjectEnterGameStatus()
{
transform.localPosition = EnterGameLocalPosition;
transform.localEulerAngles = EnterGameEulerAngles;
transform.localScale = EnterGameLocalScaling;
if (IsSetObjectDisable)
{
gameObject.SetActive(false);
}
}
}
/// <summary>
/// UpdatePerFrame相关
/// </summary>
public partial class ScriptableObject
{
[Content] public int UpdatePerFrame = 1;
/// <summary>
/// 指定多少个<see cref="TickType.Update"/>状态的<see cref="UpdateTicks(float, float, TickType)"/>执行一次更新,不会影响到子物体
/// 属于性能优化的高级选项
@@ -317,26 +98,144 @@ namespace Demo
{
UpdatePerFrame = Mathf.Max(1, frame);
}
private void ScriptableObjectDoReset()
{
transform.localPosition = EnterGameLocalPosition;
transform.localEulerAngles = EnterGameEulerAngles;
transform.localScale = EnterGameLocalScaling;
gameObject.SetActive(IsSetObjectDisable == false);
}
}
/// <summary>
/// EnableScript相关
/// 跨脚本上下文变量
/// </summary>
public partial class ScriptableObject
{
public Dictionary<string, object> ScriptableObjectContents = new();
public object GetContent(string key)
{
if (ScriptableObjectContents.TryGetValue(key, out var result))
{
return result;
}
if (Parent != null)
{
return Parent.GetContent(key);
}
return null;
}
public void SetContent(string key, object value)
{
ScriptableObjectContents[key] = value;
}
}
/// <summary>
/// 基础信息, 父子关系与EnableScript
/// </summary>
public partial class ScriptableObject
{
public static bool IsAutoPlay = false;
public static float OneBarTime = 60;
private bool isEnableScript = false;
public string SourcePath = "";
public string ScriptName = "";
public string ScriptPath;
public string ScriptTypename;
// Hierarchy
public ScriptableObject Parent;
public List<ScriptableObject> Childs = new();
public PropertiesWindow.ItemEntry MyHierarchyItem;
public static PropertiesWindow.ItemEntry AllScriptableObjectCounterHierarchyItem;
/// <summary>
/// 获取根脚本对象
/// </summary>
/// <returns></returns>
[Convention.RScript.Variable.Attr.Method]
public RootObject GetRoot()
{
if (Parent == null)
return this as RootObject;
if (Parent is RootObject result)
return result;
else
return Parent.GetRoot();
}
public void EnableScript(string sourcePath, string scriptPath, string scriptType, ScriptableObject parent)
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;
}
// GetParent
ScriptableObject result = Parent;
if (path.Replace('\\', '/').ToLower().StartsWith(RootObjectQuickPath))
{
result = GetRoot();
path = path[RootObjectQuickPath.Length..];
}
if (Parent == null)
{
throw new InvalidOperationException($"Root is nosupport to {nameof(FindWithPath)}");
}
// 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))
continue;
else
{
int index = 0;
string targetScriptObjectPath = component;
Regex regex = new(@"^(.*)\[(\d*)\]$");
var match = regex.Match(component);
if (match.Success)
{
targetScriptObjectPath = match.Groups[1].Value;
index = int.Parse(match.Groups[2].Value);
}
var target = result.Childs.FirstOrDefault(x =>
{
bool stats = x.ScriptName == targetScriptObjectPath;
if (index == 0)
return stats;
else if (stats)
index--;
return false;
});
if (target != null)
result = target;
else
{
if (isMustExist)
throw new Exception($"{component} in {path} is not found");
else
{
Debug.Log($"{component} in {path} is not found", this);
return null;
}
}
}
}
return result;
}
public void EnableScript(ScriptableObject parent)
{
#if UNITY_EDITOR||Using_ProfilerMarker
s_PreparePerfMarker = new(ScriptName);
@@ -346,9 +245,6 @@ namespace Demo
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;
@@ -363,14 +259,16 @@ namespace Demo
{
MyHierarchyItem = HierarchyWindow.instance.CreateRootItemEntryWithBinders(1)[0];
}
MyHierarchyItem.GetHierarchyItem().title = this.ScriptName + $"<{scriptType}>";
MyHierarchyItem.GetHierarchyItem().title = this.ScriptName + $"<{this.GetType()}>";
MyHierarchyItem.GetHierarchyItem().target = this;
MyHierarchyItem.GetHierarchyItem().ButtonGameObject.GetComponent<Editor.UI.RightClick>().ScriptObjectMenu = OnHierarchyItemRightClick;
//var parentHierarchyItem = MyHierarchyItem.GetParent();
//if (parentHierarchyItem != null)
// parentHierarchyItem.GetPropertyListItem().RefreshChilds();
}
#region Hierarchy
public PropertiesWindow.ItemEntry MyHierarchyItem;
public static PropertiesWindow.ItemEntry AllScriptableObjectCounterHierarchyItem;
public bool EnsureEnableScript()
{
if (isEnableScript == false)
@@ -379,6 +277,8 @@ namespace Demo
}
return isEnableScript;
}
#endregion
}
/// <summary>
@@ -396,115 +296,55 @@ namespace Demo
#region LoadSubScript
private static ScriptableObject LastLoadedScriptableObject;
public IEnumerator DoLoadSubScriptAsync([In] string type, [In] string path, [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();
// 路径预处理
if (path.Replace('\\', '/').ToLower().StartsWith(RootObjectQuickPath))
path = $"{new ToolFile(GetRoot().SourcePath) | path[RootObjectQuickPath.Length..]}";
// 获取文件
ToolFile file;
if (File.Exists(path))
file = new(path);
else
file = new ToolFile(SourcePath) | path;
// 找不到脚本
if (file.Exists() == false)
{
Debug.LogError($"{file}<{path}> is not found", this);
callback?.Invoke(null);
yield break;
}
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
yield return child.LoadScript(file.LoadAsText());
LastLoadedScriptableObject = child;
callback?.Invoke(child);
}
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);
}
public ScriptableObject DoGenerateSubScript([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);
return null;
}
// 生成对象
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
ConventionUtility.StartCoroutine(child.LoadScript(""));
LastLoadedScriptableObject = child;
callback?.Invoke(child);
return child;
}
/// <summary>
/// 创建无初始化脚本且无启动的子脚本对象
/// 创建基于子脚本的实例对象
/// </summary>
/// <param name="type"></param>
/// <param name="name"></param>
/// <returns></returns>
[Convention.RScript.Variable.Attr.Method]
public ScriptableObject NewSubScriptWithoutInit([In] string type, [In]string name)
public ScriptableObject NewSubScript([In] string type, [In] string name, [In] string path)
{
// 判断类型是否合法
if (DefaultInstantiate.GetScriptableObjectInstantiate().TryGetValue(type, out var creater) == false)
{
Debug.LogError($"{type} is not exist or {type}'s Instantiater is not valid", this);
return null;
}
// 生成对象
var child = creater();
// 获取文件
var file = new ToolFile(GetRoot().SourcePath);
file = file | path;
// 找不到脚本
if (file.Exists() == false)
{
Debug.LogError($"{file}<{path}> is not found", this);
return null;
}
child.ScriptName = name;
child.transform.SetParent(this.transform);
child.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
child.transform.localScale = Vector3.one;
child.EnableScript(this);
// Add Child
Childs.Add(child);
// Load Child Script
ConventionUtility.StartCoroutine(child.ParseScript2Expr(file.LoadAsText()));
return child;
}
/// <summary>
/// 创建不基于子脚本的实例对象
/// </summary>
/// <param name="type"></param>
/// <param name="name"></param>
/// <returns></returns>
[Convention.RScript.Variable.Attr.Method]
public ScriptableObject NewSubScript([In] string type, string name)
{
// 判断类型是否合法
if (DefaultInstantiate.GetScriptableObjectInstantiate().TryGetValue(type, out var creater) == false)
@@ -518,58 +358,14 @@ namespace Demo
child.transform.SetParent(this.transform);
child.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
child.transform.localScale = Vector3.one;
child.EnableScript("", "", type, this);
child.EnableScript(this);
// Add Child
Childs.Add(child);
return child;
}
/// <summary>
/// 将无初始化创建的脚本对象确认完全加载并设置为最后添加的脚本对象
/// </summary>
/// <returns></returns>
[Convention.RScript.Variable.Attr.Method]
public ScriptableObject ApplyLoad()
{
// Load Child Script
ConventionUtility.StartCoroutine(this.LoadScript(""));
LastLoadedScriptableObject = this;
return this;
}
/// <summary>
/// 创建不需要脚本语句的子脚本对象
/// </summary>
/// <param name="type">指定类型</param>
[Convention.RScript.Variable.Attr.Method]
public ScriptableObject NewSubScript([In] string type, [In] string name)
{
return DoGenerateSubScript(type, name, null);
}
/// <summary>
/// 创建不需要脚本语句的子脚本对象
/// </summary>
/// <param name="type">指定类型</param>
[Convention.RScript.Variable.Attr.Method]
public ScriptableObject NewSubScript([In] string type)
{
return NewSubScript(type, $"New {type}");
}
/// <summary>
/// 加载子脚本
/// </summary>
/// <param name="type">指定类型</param>
/// <param name="path">指定脚本,可用决定路径或与当前脚本目录的相对路径</param>
[Convention.RScript.Variable.Attr.Method]
public IEnumerator LoadSubScript([In] string type, [In] string path)
{
return DoLoadSubScriptAsync(type, path, null);
}
/// <summary>
/// 获取已加载的脚本对象
/// </summary>
@@ -590,19 +386,9 @@ namespace Demo
return Parent;
}
/// <summary>
/// 获取上一个加载的脚本对象
/// </summary>
/// <returns></returns>
[Convention.RScript.Variable.Attr.Method]
public ScriptableObject GetLastLoadedScriptableObject()
{
return LastLoadedScriptableObject;
}
#endregion
private IEnumerator ParseScript2Expr(string script)
public IEnumerator ParseScript2Expr(string script)
{
RScriptEngine engine = new();
RScriptImportClass importClass = new()
@@ -674,123 +460,26 @@ namespace Demo
/// </summary>
public partial class ScriptableObject : SerializedMonoBehaviour, IHierarchyItemClickEventListener
{
public static bool IsAutoPlay = false;
public ScriptableObject Parent;
/// <summary>
/// 获取根脚本对象
/// </summary>
/// <returns></returns>
[Convention.RScript.Variable.Attr.Method]
public RootObject GetRoot()
protected virtual IEnumerator DoSomethingDuringApplyScript()
{
if (Parent == null)
return this as RootObject;
if (Parent is RootObject result)
return result;
else
return Parent.GetRoot();
yield break;
}
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;
}
// GetParent
ScriptableObject result = Parent;
if (path.Replace('\\','/').ToLower().StartsWith(RootObjectQuickPath))
{
result = GetRoot();
path = path[RootObjectQuickPath.Length..];
}
if (Parent == null)
{
throw new InvalidOperationException($"Root is nosupport to {nameof(FindWithPath)}");
}
// 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))
continue;
else
{
int index = 0;
string targetScriptObjectPath = component;
Regex regex = new(@"^(.*)\[(\d*)\]$");
var match = regex.Match(component);
if (match.Success)
{
targetScriptObjectPath = match.Groups[1].Value;
index = int.Parse(match.Groups[2].Value);
}
var target = result.Childs.FirstOrDefault(x =>
{
bool stats = x.ScriptName == targetScriptObjectPath;
if (index == 0)
return stats;
else if (stats)
index--;
return false;
});
if (target != null)
result = target;
else
{
if (isMustExist)
throw new Exception($"{component} in {path} is not found");
else
{
Debug.Log($"{component} in {path} is not found", this);
return null;
}
}
}
}
return result;
}
public virtual IEnumerator LoadScript(string script)
public IEnumerator ApplyScript()
{
if (EnsureEnableScript() == false)
{
yield break;
}
yield return DoSomethingDuringApplyScript();
// 增数
{
AllScriptableObjectCounter++;
AllScriptableObjectCounterHierarchyItem.GetHierarchyItem().text = $"ScriptableObjectCount: {AllScriptableObjectCounter}";
}
yield return ParseScript2Expr(script);
IsEnableUpdate = true;
}
private void Reset()
{
StartCoroutine(UnloadScript());
}
public virtual IEnumerator UnloadScript()
{
if (EnsureEnableScript())
@@ -827,7 +516,7 @@ namespace Demo
public virtual void ResetEnterGameStatus()
{
ResetScriptableObjectEnterGameStatus();
ScriptableObjectDoReset();
}
public virtual void OnHierarchyItemRightClick(RectTransform item)
@@ -956,14 +645,18 @@ namespace Demo
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);
private static Color FocusImageColor = new(1f, 47f / 51f, 0.0156862754f, 0.1f);
public override IEnumerator LoadScript(string script)
protected override IEnumerator DoSomethingDuringApplyScript()
{
MyTimelineEntry = TimelineWindow.CreateRootItemEntries(1)[0];
MyTimelineItem = MyTimelineEntry.ref_value.GetComponent<Editor.UI.TimelineItem>();
yield return base.LoadScript(script);
yield return base.DoSomethingDuringApplyScript();
if(MyTimelineEntry==null)
{
MyTimelineEntry = TimelineWindow.CreateRootItemEntries(1)[0];
MyTimelineItem = MyTimelineEntry.ref_value.GetComponent<Editor.UI.TimelineItem>();
}
MyTimelineItem.title = ScriptName;
MyTimelineItem.RawButton.onClick.RemoveAllListeners();
MyTimelineItem.AddListener(() =>
{
HierarchyWindow.instance.MakeFocusOn(MyHierarchyItem.GetHierarchyItem());
@@ -979,28 +672,20 @@ namespace Demo
}
}
public override IEnumerator UnloadScript()
{
yield return base.UnloadScript();
MyTimelineItem.RawButton.onClick.RemoveAllListeners();
MyTimelineEntry.Release();
MyTimelineEntry = null;
MyTimelineItem = null;
}
public override void OnHierarchyItemClick(HierarchyItem item)
{
}
//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);
@@ -1025,14 +710,5 @@ namespace Demo
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;
}
}
}

View File

@@ -17,46 +17,17 @@ namespace Demo.Game
[Content, SerializeField] private string project;
[Content, SerializeField] private GameController SubWorldGameController;
public override IEnumerator LoadScript(string script)
protected override IEnumerator DoSomethingDuringApplyScript()
{
yield return base.LoadScript(script);
/*
// Load
var content = GameContent.instance;
// Always
content.IsCreateNewProject = false;
// Push Content
var oldContentRootSourceDir = content.RootSourceDir;
var oldSetupSongDuration = content.SetupSongDuration;
var oldSetSongCurrentTime = content.SetSongCurrentTime;
// Setting New
content.RootSourceDir = Path.Combine(PlatformIndicator.StreamingAssetsPath, project) + "/";
content.SetupSongDuration = (x, y) => { };
content.SetSongCurrentTime = x => { };
yield return base.DoSomethingDuringApplyScript();
var ir = SceneManager.LoadSceneAsync(Editor.EditorController.SceneName, LoadSceneMode.Additive);
ir.completed += x =>
{
SubWorldGameController = (from controller in FindObjectsOfType<GameController>()
where controller.RootSourcePath == project
select controller).First();
ConventionUtility.StartCoroutine(SubWorldGameController.GameInitBySubWorld(GetRoot().InputCatch));
};
yield return ir;
// Pull Content
content.RootSourceDir = oldContentRootSourceDir;
content.SetupSongDuration = oldSetupSongDuration;
content.SetSongCurrentTime = oldSetSongCurrentTime;
*/
var ir = SceneManager.LoadSceneAsync(Editor.EditorController.SceneName, LoadSceneMode.Additive);
IEnumerator after = null;
ir.completed += x =>
{
SubWorldGameController = (from controller in FindObjectsOfType<GameController>()
where controller.RootSourcePath == project
select controller).First();
after = SubWorldGameController.GameInitBySubWorld(GetRoot().InputCatch);
};
yield return ir;
yield return after;
}
public override IEnumerator UnloadScript()

View File

@@ -20,7 +20,7 @@ namespace Demo.Game
}
public int Content = 0;
public List<UpdatementEntry> Entries = new();
public readonly List<UpdatementEntry> Entries = new();
protected abstract void UpdateData(DataType data);
protected abstract DataType Lerp(DataType begin, DataType end, float t);
@@ -40,26 +40,15 @@ namespace Demo.Game
});
}
/// <summary>
/// 添加数据
/// </summary>
/// <param name="time"></param>
/// <param name="position"></param>
/// <param name="curveType"></param>
public void ManualAddEntry(string time, DataType position, MathExtension.EaseCurveType curveType)
{
ManualAddEntry(time, position, curveType);
}
private void UpdateEntry(int start, float percent)
{
UpdatementEntry head = Entries[start], tail = Entries[Mathf.Min(start + 1, Entries.Count - 1)];
UpdateData(Lerp(head.Position, tail.Position, MathExtension.Evaluate(Mathf.Clamp01(percent), head.easeCurveType)));
}
public override IEnumerator LoadScript(string script)
protected override IEnumerator DoSomethingDuringApplyScript()
{
yield return base.LoadScript(script);
yield return base.DoSomethingDuringApplyScript();
Entries.Sort((x, y) => x.TimePoint.CompareTo(y.TimePoint));
if (UpdateTarget == null)
{
@@ -76,9 +65,10 @@ namespace Demo.Game
public override IEnumerator UnloadScript()
{
Content = 0;
Entries = new();
Entries.Clear();
yield return base.UnloadScript();
}
protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType)
{
base.UpdateTicks(currentTime, deltaTime, tickType);
@@ -140,16 +130,12 @@ namespace Demo.Game
/// <summary>
/// 设置更新对象
/// </summary>
/// <param name="path">脚本的相对路径</param>
[Convention.RScript.Variable.Attr.Method]
public void SetUpdateTarget(string path)
public void SetUpdateTarget(ScriptableObject target)
{
var temp = FindWithPath(path);
if (temp != null)
UpdateTarget = temp.gameObject;
else
Debug.LogWarning($"{path}' is not found", this);
UpdateTarget = target.gameObject;
}
/// <summary>
///
/// </summary>
@@ -165,7 +151,6 @@ namespace Demo.Game
public interface ILocalUpdatement<DataType>: IScriptableObject
{
int Content { get; set; }
public List<ILocalUpdatementExtension.UpdatementEntry<DataType>> Entries { get; set; }
void UpdateData(DataType data);
@@ -182,11 +167,11 @@ namespace Demo.Game
public MathExtension.EaseCurveType easeCurveType = MathExtension.EaseCurveType.Linear;
}
public static void AddEntry<DataType>(this ILocalUpdatement<DataType> self, string time, DataType position, MathExtension.EaseCurveType curveType)
public static void AddEntry<DataType>(this ILocalUpdatement<DataType> self, float time, DataType position, MathExtension.EaseCurveType curveType)
{
self.Entries.Add(new()
{
TimePoint = self.SharedInterfaceScriptObject.Parse(time),
TimePoint = time,
Position = position,
easeCurveType = curveType
});

View File

@@ -5,55 +5,40 @@ using UnityEngine;
namespace Demo.Game
{
public class LookAtAnchor : Updatement<string>
public class LookAtAnchor : Updatement<ScriptableObject>
{
public static LookAtAnchor Make()
{
return new GameObject().AddComponent<LookAtAnchor>();
}
protected override string Lerp(string begin, string end, float t)
protected override ScriptableObject Lerp(ScriptableObject begin, ScriptableObject end, float t)
{
return begin;
}
[Content, SerializeField] private string Cache;
[Content] public ScriptableObject LookAtObject;
[Content] public bool IsEnableUpdateEveryTick = false;
protected override void UpdateData(string data)
protected override void UpdateData(ScriptableObject data)
{
if (Cache != data)
if (data != LookAtObject)
{
LookAtObject = FindWithPath(data, false);
Cache = data;
Foo();
LookAtObject = data;
transform.LookAt(LookAtObject.transform);
}
if (IsEnableUpdateEveryTick)
{
Foo();
}
void Foo()
else if (IsEnableUpdateEveryTick)
{
if (LookAtObject != null)
transform.LookAt(LookAtObject.transform);
}
}
public override IEnumerator UnloadScript()
{
Cache = null;
yield return base.UnloadScript();
}
/// <summary>
/// 在指定时刻切换面向的物体,并尝试一次更新
/// </summary>
/// <param name="time"></param>
/// <param name="target">对象相对路径,不存在时将解除锁定</param>
[Convention.RScript.Variable.Attr.Method]
public void Add(string time, string target)
public void Add(float time, ScriptableObject target)
{
ManualAddEntry(time, target, default);
}

View File

@@ -5,30 +5,30 @@ using UnityEngine;
namespace Demo.Game
{
public class MaterialUpdatement : Updatement<string>, IAssetBundleLoader
public class MaterialUpdatement : Updatement<Material>, IAssetBundleLoader
{
public static MaterialUpdatement Make()
{
return new GameObject().AddComponent<MaterialUpdatement>();
}
public string MaterialAssetBundlePath;
public AssetBundle MaterialAssetBundle;
public string MaterialAssetBundlePath = null;
public AssetBundle MaterialAssetBundle = null;
protected override string Lerp(string begin, string end, float t)
protected override Material Lerp(Material begin, Material end, float t)
{
return begin;
}
[Content, SerializeField] private string Cache;
[Content, SerializeField] private Material Cache;
protected override void UpdateData(string data)
protected override void UpdateData(Material data)
{
if (string.IsNullOrEmpty(MaterialAssetBundlePath))
return;
if (Cache != data && Parent.TryGetComponent<MeshRenderer>(out var meshRenderer))
{
meshRenderer.material = MaterialAssetBundle.LoadAsset<Material>(data);
meshRenderer.material = data;
Cache = data;
}
}
@@ -38,7 +38,7 @@ namespace Demo.Game
Cache = null;
if (string.IsNullOrEmpty(MaterialAssetBundlePath) == false)
yield return this.UnloadAssetBundle(MaterialAssetBundlePath);
MaterialAssetBundlePath = "";
MaterialAssetBundlePath = null;
yield return base.UnloadScript();
}
@@ -59,12 +59,16 @@ namespace Demo.Game
/// <summary>
/// 在指定时刻切换父物体上的MeshRenderer.material
/// </summary>
/// <param name="time"></param>
/// <param name="material"></param>
[Convention.RScript.Variable.Attr.Method]
public void Add(string time, string material)
public IEnumerator Add(float time, string material)
{
ManualAddEntry(time, material, default);
var ir = MaterialAssetBundle.LoadAssetAsync<Material>(material);
ir.completed += x =>
{
var mat = ir.asset as Material;
ManualAddEntry(time, mat, default);
};
yield return ir;
}
}
}

View File

@@ -22,12 +22,37 @@ namespace Demo.Game
public abstract class BasicSplineRenderer : Updatement<SplineClipDuration>, IAssetBundleLoader, IDependOnSplineCore
{
[Content] public SplineCore MySplineCore { get; set; }
[Content] public MeshRenderer MyMeshRenderer;
[Content] private MeshFilter m_MeshFilter;
[Content] private MeshRenderer m_MyMeshRenderer;
[Header("LineRenderer.Material")]
[Content] public string LinesAssetBundlePath;
[Content] public AssetBundle LinesAssetBundle;
[Content] public string LineMaterial;
[Content] public Material MyLineMaterial;
[Content] public Material LineDefaultMaterial;
public override void ResetEnterGameStatus()
{
base.ResetEnterGameStatus();
MyMeshRenderer.material = LineDefaultMaterial;
}
public MeshFilter MyMeshFilter
{
get
{
if (m_MeshFilter == null)
m_MeshFilter = this.GetOrAddComponent<MeshFilter>();
return m_MeshFilter;
}
}
public MeshRenderer MyMeshRenderer
{
get
{
if (m_MyMeshRenderer == null)
m_MyMeshRenderer = this.GetOrAddComponent<MeshRenderer>();
return m_MyMeshRenderer;
}
}
public abstract Vector3 EvaluateClipFromPosition(float time);
@@ -37,26 +62,6 @@ namespace Demo.Game
public abstract SplineSample EvaluateClipTo(float time);
public override IEnumerator LoadScript(string script)
{
yield return base.LoadScript(script);
// Bind and Init Spline
var splineGameObject = MySplineCore.gameObject;
this.GetOrAddComponent<MeshFilter>();
MyMeshRenderer = this.GetOrAddComponent<MeshRenderer>();
MyMeshRenderer.enabled = true;
if (string.IsNullOrEmpty(LinesAssetBundlePath) == false)
{
var ir = LinesAssetBundle.LoadAssetAsync<Material>(LineMaterial);
ir.completed += x =>
{
MyLineMaterial = ir.asset as Material;
};
yield return ir;
}
MyMeshRenderer.material = MyLineMaterial;
}
public override IEnumerator UnloadScript()
{
if (string.IsNullOrEmpty(LinesAssetBundlePath) == false)
@@ -73,15 +78,15 @@ namespace Demo.Game
/// <param name="to"></param>
/// <param name="curveType">可取值为30种缓动曲线</param>
[Convention.RScript.Variable.Attr.Method]
public void Add(string time, float from, float to, string curveType)
public void Add(float time, float from, float to, string curveType)
{
ManualAddEntry(time, new(from, to), Enum.Parse<MathExtension.EaseCurveType>(curveType));
}
[Convention.RScript.Variable.Attr.Method]
public IEnumerator LoadSpline(string path)
public void LoadSpline(string path)
{
yield return this.LoadSplineTool(path);
this.LoadSplineTool(path);
}
@@ -97,14 +102,15 @@ namespace Demo.Game
/// <param name="ab"></param>
/// <param name="material"></param>
[Convention.RScript.Variable.Attr.Method]
public IEnumerator LoadMaterial(string ab, string material)
public void LoadMaterial(string ab, string material)
{
yield return this.LoadAssetBundle(ab, x =>
{
LinesAssetBundlePath = ab;
LinesAssetBundle = x;
LineMaterial = material;
});
MyMeshRenderer.enabled = true;
LinesAssetBundlePath = ab;
this.LoadAssetBundle(ab, x =>
{
LinesAssetBundle = x;
LineDefaultMaterial = LinesAssetBundle.LoadAsset<Material>(material);
});
}
protected override SplineClipDuration Lerp(SplineClipDuration begin, SplineClipDuration end, float t)
@@ -117,11 +123,9 @@ namespace Demo.Game
{
[Content] public TMeshGenerator MyMeshGenerator;
public override IEnumerator LoadScript(string script)
protected override IEnumerator DoSomethingDuringApplyScript()
{
yield return base.LoadScript(script);
// Setup Mesh Generater
MyMeshRenderer.material = MyLineMaterial;
yield return base.DoSomethingDuringApplyScript();
MyMeshGenerator = this.GetOrAddComponent<TMeshGenerator>();
MyMeshGenerator.spline = MySplineCore.MySplineComputer;
SetupMeshGenerator(MyMeshGenerator);
@@ -142,9 +146,9 @@ namespace Demo.Game
/// </summary>
/// <param name="mode">Clip, UniformClip, Clamp, UniformClamp</param>
[Convention.RScript.Variable.Attr.Method]
public void SetUVMode(string mode)
public void SetUVMode(MeshGenerator.UVMode mode)
{
MyUVMode = Enum.Parse<MeshGenerator.UVMode>(mode);
MyUVMode = mode;
}
#endregion

View File

@@ -27,37 +27,37 @@ namespace Demo.Game
/// </summary>
/// <param name="path">对象路径, 不存在时则立刻加载</param>
[Convention.RScript.Variable.Attr.Method]
public IEnumerator LoadSpline(string path)
public void LoadSpline(string path)
{
yield return this.LoadSplineTool(path);
this.LoadSplineTool(path);
}
/// <summary>
/// 必须先执行LoadSpline加载样条线
/// </summary>
/// <param name="value">百分比所在位置,取值范围是[01]</param>
/// <param name="offset">百分比所在位置,取值范围是[01]</param>
[Convention.RScript.Variable.Attr.Method]
public void EvaluatePosition(string value)
public void EvaluatePosition(float offset)
{
MySplineOffset = Parse(value);
MySplineOffset = offset;
Updater = () => transform.position = MySplineCore.MySplineComputer.EvaluatePosition(MySplineOffset);
}
/// <summary>
/// 绑定到样条线渲染器上(必须已经加载),
/// 绑定到样条线渲染器上
/// 并设置跟随指定时间的时刻渲染器所生成的头部
/// </summary>
/// <param name="path">对象路径, 不存在时则立刻加载</param>
/// <param name="splineRenderer">样条线渲染器对象</param>
/// <param name="time">时刻</param>
/// <param name="isFollowPosition">是否跟随位置, 默认开启</param>
/// <param name="isFollowRotation">是否跟随旋转, 默认开启</param>
[Convention.RScript.Variable.Attr.Method]
public void LoadSplineRenderer(string path, string time, string isFollowPosition = "true", string isFollowRotation = "true")
public void LoadSplineRenderer(BasicSplineRenderer splineRenderer, float time, bool isFollowPosition = true, bool isFollowRotation = true)
{
MySplineRenderer = this.LoadSplineRendererTool(path);
MySplineOffset = Parse(time);
bool bIsFollowPosition = ConvertValue<bool>(isFollowPosition);
bool bIsFollowRotation = ConvertValue<bool>(isFollowRotation);
MySplineRenderer = splineRenderer;
MySplineOffset = time;
bool bIsFollowPosition = isFollowPosition;
bool bIsFollowRotation = isFollowRotation;
if (bIsFollowPosition && bIsFollowRotation)
{
Updater = () =>

View File

@@ -22,7 +22,7 @@ namespace Demo.Game
public interface IDependOnSplineCore : IScriptableObject
{
SplineCore MySplineCore { get; set; }
IEnumerator LoadSpline(string path);
void LoadSpline(string path);
}
public static class DependOnSplineCoreUtility
@@ -38,19 +38,13 @@ namespace Demo.Game
/// 加载并绑定到新样条线
/// </summary>
/// <param name="path">对象相对路径,若对象不存在则作为脚本相对路径加载</param>
public static IEnumerator LoadSplineTool(this IDependOnSplineCore self, string path)
public static SplineCore LoadSplineTool(this IDependOnSplineCore self, string path)
{
var spline = self.SharedInterfaceScriptObject.FindWithPath(path, false);
if (spline == null)
yield return self.SharedInterfaceScriptObject.DoLoadSubScriptAsync(nameof(SplineCore), path, x => spline = x);
if (spline is SplineCore sc)
{
self.MySplineCore = sc;
}
else
{
Debug.LogWarning($"{path} is not a SplineCore", self.SharedInterfaceScriptObject);
}
spline = self.SharedInterfaceScriptObject.NewSubScript(nameof(SplineCore), new ToolFile(path).GetFilename(true), path);
self.MySplineCore = (SplineCore)spline;
return self.MySplineCore;
}
}
@@ -61,18 +55,31 @@ namespace Demo.Game
return new GameObject("", typeof(SplineComputer)).AddComponent<SplineCore>();
}
[Content] public SplineComputer MySplineComputer;
[Content] private SplineComputer m_MySplineComputer;
[Content] public int NodeContent = 0;
[Content] public List<SplineNode> MySplineNodes = new();
[Content] public readonly List<SplineNode> MySplineNodes = new();
[Content] public SplineComputer.SampleMode MySampleMode = default;
[Content] public Spline.Type MyType = default;
public bool IsClose = false;
public override IEnumerator LoadScript(string script)
public SplineComputer MySplineComputer
{
get
{
if(m_MySplineComputer==null)
m_MySplineComputer= GetComponent<SplineComputer>();
return m_MySplineComputer;
}
}
/// <summary>
/// <see cref="SplineCore"/>需要在子<see cref="SplineNode"/>都添加后再应用脚本才能使得节点生效
/// </summary>
/// <returns></returns>
protected override IEnumerator DoSomethingDuringApplyScript()
{
MySplineComputer = GetComponent<SplineComputer>();
yield return base.LoadScript(script);
yield return base.DoSomethingDuringApplyScript();
NodeContent = 0;
MySplineComputer.SetPoints(new SplinePoint[MySplineNodes.Count]);
foreach (SplineNode node in MySplineNodes)
@@ -86,13 +93,12 @@ namespace Demo.Game
MySplineComputer.Break();
MySplineComputer.sampleMode = MySampleMode;
MySplineComputer.type = MyType;
yield return null;
MySplineComputer.Rebuild();
}
protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType)
{
if (tickType == TickType.Start || tickType == TickType.Reset)
if (tickType != TickType.Update)
MySplineComputer.Rebuild();
}
@@ -109,9 +115,9 @@ namespace Demo.Game
/// </summary>
/// <param name="mode">CatmullRom, BSpline, Bezier, Linear </param>
[Convention.RScript.Variable.Attr.Method]
public void SetType(string mode)
public void SetType(Spline.Type mode)
{
MyType = Enum.Parse<Spline.Type>(mode);
MyType = mode;
}
/// <summary>
@@ -119,9 +125,9 @@ namespace Demo.Game
/// </summary>
/// <param name="mode">Default, Uniform, Optimized</param>
[Convention.RScript.Variable.Attr.Method]
public void SetSampleMode(string mode)
public void SetSampleMode(SplineComputer.SampleMode mode)
{
MySampleMode = Enum.Parse<SplineComputer.SampleMode>(mode);
MySampleMode = mode;
}
@@ -135,47 +141,13 @@ namespace Demo.Game
}
/// <summary>
/// 加载并加入节点
/// 加入节点或者添加节点组件后加入节点
/// </summary>
/// <param name="path">脚本位置</param>
[Convention.RScript.Variable.Attr.Method]
public IEnumerator LoadNode(string path)
{
yield return DoLoadSubScriptAsync(nameof(SplineNode), path, node =>
{
if (node is SplineNode _node)
{
MySplineNodes.Add(_node);
}
else
{
Debug.LogError($"{path} is not {nameof(SplineNode)}", this);
}
});
}
/// <summary>
/// 加入节点脚本对象
/// </summary>
[Convention.RScript.Variable.Attr.Method]
public void AddNode(ScriptableObject node)
public void LoadNode(ScriptableObject node)
{
MySplineNodes.Add(node.GetOrAddComponent<SplineNode>());
}
/// <summary>
/// 加入已加载的节点如果目标脚本不是SplineNode
/// 那么为其添加SplineNode组件
/// </summary>
/// <param name="path">脚本位置</param>
[Convention.RScript.Variable.Attr.Method]
public void AddNode(string path)
{
var node = FindWithPath(path);
if (node != null)
{
AddNode(node);
}
}
}
}

View File

@@ -11,17 +11,22 @@ namespace Demo.Game
return new GameObject("", typeof(Node)).AddComponent<SplineNode>();
}
public Node MyNode;
private Node m_MyNode;
public float NodeSize = 1;
public Color NodeColor = Color.white;
public bool IsSetupNodeRotation = false;
public Vector3 NodeRotation = Vector3.zero;
public int MyNodeContent = 0;
public override IEnumerator LoadScript(string script)
public Node MyNode
{
MyNode = GetComponent<Node>();
yield return base.LoadScript(script);
get
{
if (m_MyNode == null)
m_MyNode = GetComponent<Node>();
return m_MyNode;
}
}
public void AddTo(SplineCore core)
@@ -38,9 +43,9 @@ namespace Demo.Game
/// </summary>
/// <param name="size"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetNodeSize(string size)
public void SetNodeSize(float size)
{
NodeSize = Parse(size);
NodeSize = size;
}
/// <summary>
@@ -51,9 +56,9 @@ namespace Demo.Game
/// <param name="b"></param>
/// <param name="a"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetNodeColor(string r, string g, string b, string a)
public void SetNodeColor(float r, float g, float b, float a)
{
NodeColor = new(Parse(r), Parse(g), Parse(b), Parse(a));
NodeColor = new(r, g, b, a);
}
/// <summary>
@@ -63,10 +68,10 @@ namespace Demo.Game
/// <param name="y"></param>
/// <param name="z"></param>
[Convention.RScript.Variable.Attr.Method]
public void SetNodeRotation(string x, string y, string z)
public void SetNodeRotation(float x, float y, float z)
{
IsSetupNodeRotation = true;
this.transform.localEulerAngles = NodeRotation = new(Parse(x), Parse(y), Parse(z));
this.transform.localEulerAngles = NodeRotation = new(x, y, z);
}
}
}

View File

@@ -31,13 +31,11 @@ namespace Demo.Game
/// <param name="x">x</param>
/// <param name="y">y</param>
/// <param name="z">z</param>
/// <param name="curveType">可取值为30种缓动曲线</param>
/// <param name="curveType">缓动曲线</param>
[Convention.RScript.Variable.Attr.Method]
public void Add(string time, string x, string y, string z, string curveType)
public void Add(float time, float x, float y, float z, MathExtension.EaseCurveType curveType)
{
ManualAddEntry(time,
new(float.Parse(x), float.Parse(y), float.Parse(z)),
Enum.Parse<MathExtension.EaseCurveType>(curveType));
ManualAddEntry(time, new(x, y, z), curveType);
}
}
}

View File

@@ -28,13 +28,11 @@ namespace Demo.Game
/// <param name="x">x</param>
/// <param name="y">y</param>
/// <param name="z">z</param>
/// <param name="curveType">可取值为30种缓动曲线</param>
/// <param name="curveType">缓动曲线</param>
[Convention.RScript.Variable.Attr.Method]
public void Add(string time, string x, string y, string z, string curveType)
public void Add(float time, float x, float y, float z, MathExtension.EaseCurveType curveType)
{
ManualAddEntry(time,
new(float.Parse(x), float.Parse(y), float.Parse(z)),
Enum.Parse<MathExtension.EaseCurveType>(curveType));
ManualAddEntry(time, new(x, y, z), curveType);
}
}
}

View File

@@ -28,13 +28,11 @@ namespace Demo.Game
/// <param name="x">x</param>
/// <param name="y">y</param>
/// <param name="z">z</param>
/// <param name="curveType">可取值为30种缓动曲线</param>
/// <param name="curveType">缓动曲线</param>
[Convention.RScript.Variable.Attr.Method]
public void Add(string time, string x, string y, string z, string curveType)
public void Add(float time, float x, float y, float z, MathExtension.EaseCurveType curveType)
{
ManualAddEntry(time,
new(float.Parse(x), float.Parse(y), float.Parse(z)),
Enum.Parse<MathExtension.EaseCurveType>(curveType));
ManualAddEntry(time, new(x, y, z), curveType);
}
}
}