fastTracker

Signed-off-by: TRAfoer <lhf190@outlook.com>
This commit is contained in:
2026-01-07 23:48:28 +08:00
parent 797a5f7141
commit a31269c632
256 changed files with 33278 additions and 1889654 deletions

View File

@@ -164,8 +164,16 @@ namespace Ichni.Editor
public List<IBaseElement> GetMachedElements(TagMatcher o)
{
return EditorManager.instance.beatmapContainer.gameElementList
.Where(p => o.Match(p)).Cast<IBaseElement>().ToList();
List<IBaseElement> matchedElements = new();
EditorManager.instance.beatmapContainer.gameElementList.Where(p => o.Match(p)).ForEach(i =>
{
foreach (var u in i.submoduleList)
{
matchedElements.Add(u);
}
matchedElements.Add(i);
});
return matchedElements.Cast<IBaseElement>().ToList();
}
public List<TagMatcher> GetMatcher(GameElement baseElement)
@@ -319,6 +327,7 @@ namespace Ichni.Editor
// 2. 如果必须检查,保留上面那行。
// 使用 ReflectionHelper 处理赋值 (自动处理 Struct 回写和嵌套)
ReflectionHelper.SetDeepValue(baseElement, parameterName, value);
}
};

View File

@@ -19,10 +19,11 @@ namespace Ichni.RhythmGame
public float songLength;
public float songTime;
public float songBeat => songTime / 60 * bpm;
public float songBeat => beatManager is null ? songTime / 60 * bpm : beatManager.GetBeatFromTime(songTime);
public float offset = 0f;//设定偏移
public BaseElement_BM matchedBM { get; set; }
public BeatManager beatManager = null;
public SongInformation(string songName, float bpm, float delay, float offset)
{
this.songName = songName;
@@ -52,6 +53,7 @@ namespace Ichni.RhythmGame
EditorManager.instance.uiManager.inspector.ClearInspector();
SetUpInspector();
});
beatManager = new BeatManager(this);
}
private AudioClip LoadMP3(string filepath)//猜猜我从哪里偷的
@@ -101,6 +103,24 @@ namespace Ichni.RhythmGame
}
}
public class BeatManager
{
private SongInformation songInformation;
public BeatManager(SongInformation songInformation)
{
this.songInformation = songInformation;
}
public float GetBeatFromTime(float time)
{
return time / 60 * songInformation.bpm;
}
public float GetTimeFromBeat(float beat)
{
return beat * 60 / songInformation.bpm;
}
}
namespace Beatmap
{

View File

@@ -105,11 +105,12 @@ namespace Ichni.RhythmGame
public abstract partial class GameElement //存档,删除,复制,粘贴
{
public Action refreshAction = new Action(() => { });
public virtual void Refresh()
{
if (connectedTab != null) connectedTab.tabButtonText.text = this.elementName;
gameObject.name = elementName;
refreshAction?.Invoke();
}
/// <summary>

View File

@@ -107,6 +107,22 @@ namespace Ichni.RhythmGame
matchedBM = new SkyboxSubsetter_BM(elementName, elementGuid, tags, parentElement.matchedBM as GameElement_BM,
skyBoxThemeBundleList, skyboxNameList, blendTimeList, blendSpeedList);
}
/// <summary>
/// 刷新 skyboxMaterialList 和 skyboxBlender.skyboxMaterials使其与 theme/name 列表同步。
/// </summary>
public void RefreshSkyboxes()
{
skyboxMaterialList.Clear();
skyboxBlender.skyboxMaterials.Clear();
for (int i = 0; i < skyBoxThemeBundleList.Count && i < skyboxNameList.Count; i++)
{
var mat = ThemeBundleManager.instance.GetObject<Material>(skyBoxThemeBundleList[i], skyboxNameList[i]);
skyboxMaterialList.Add(mat);
skyboxBlender.skyboxMaterials.Add(mat);
}
skyboxBlender.InspectorAndAwakeChanges();
}
}
public partial class SkyboxSubsetter
@@ -134,19 +150,55 @@ namespace Ichni.RhythmGame
DynamicUISubcontainer materialSettings = container.GenerateSubcontainer(3);
// 新增显示skybox配置情况//这他妈是什么
DynamicUISubcontainer Textsettings = container.GenerateSubcontainer(2);
for (int i = 0; i < (skyBoxThemeBundleList?.Count ?? 0); i++)
{
try
{
DynamicUISubcontainer Textsettings = container.GenerateSubcontainer(3);
// 安全访问元素:检查索引是否在所有列表的有效范围内
string bundleName = i < skyBoxThemeBundleList.Count ? skyBoxThemeBundleList[i] : "<Missing Bundle>";
string name = i < skyboxNameList.Count ? skyboxNameList[i] : "<Missing Name>";
if (i > 0)
{
inspector.GenerateInputField(this, Textsettings, "Time", $"{nameof(blendTimeList)}.{i - 1}");
inspector.GenerateInputField(this, Textsettings, "Speed", $"{nameof(blendSpeedList)}.{i - 1}");
}
else
{
inspector.GenerateHintText(this, Textsettings, "The First");
inspector.GenerateHintText(this, Textsettings, "0");
}
inspector.GenerateHintText(this, Textsettings, $"{i + 1}. [{bundleName}] {name}\n");
inspector.GenerateHintText(this, Textsettings, $"{i + 1}. {name}");
// inspector.GenerateInputField(this, Textsettings, "B", $"skyBoxThemeBundleList.{i}").AddListenerFunction(RefreshSkyboxes);
// inspector.GenerateInputField(this, Textsettings, "N", $"skyboxNameList.{i}").AddListenerFunction(RefreshSkyboxes);
inspector.GenerateDropdown(this, Textsettings, "B", themeBundleListForSelection, $"{nameof(skyBoxThemeBundleList)}.{i}")
.AddListenerFunction(() => inspectorMain.SetInspector(this));
List<string> skyboxNameListForSelection = new List<string>();
string selectedThemeBundleL = skyBoxThemeBundleList[i];
if (selectedThemeBundleL != String.Empty && ThemeBundleManager.instance.TryGetThemeBundle(selectedThemeBundleL, out ThemeBundle themeBundleLoop))
{
skyboxNameListForSelection = themeBundleLoop.assetList_Material.ConvertAll(x => x.name);
var objectNameDropdown =
inspector.GenerateDropdown(this, Textsettings, "Material Name", skyboxNameListForSelection, $"{nameof(skyboxNameList)}.{i}")
.AddListenerFunction(() => inspectorMain.SetInspector(this))
.AddListenerFunction(RefreshSkyboxes);
}
// else
// {
// var objectNameDropdown =
// inspector.GenerateDropdown(this, Textsettings, "Material Name", new List<string>(), $"{nameof(skyboxNameList)}.{i}");
// objectNameDropdown.dropdown.interactable = false;
// } // 如果没有选择主题包,则材质名称下拉框不可用
// inspector.GenerateDropdown(this, Textsettings, "N", skyboxNameListForSelection, $"skyboxNameList.{i}")
// .AddListenerFunction(RefreshSkyboxes);
// 创建局部变量解决闭包问题
int index = i;
inspector.GenerateButton(this, Textsettings, "Remove Skybox", () =>
{
try
@@ -179,11 +231,16 @@ namespace Ichni.RhythmGame
Debug.LogError($"Error during removal: {ex.Message}");
}
});
}
catch (Exception ex)
{
Debug.LogError($"Error generating UI for index {i}: {ex.Message}");
}
}
Debug.Log((mainSettings == null) + " " + (themeBundleListForSelection == null) + " " + (selectedThemeBundle == null));

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ichni.RhythmGame;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
using Lean.Pool;
@@ -187,15 +188,15 @@ namespace Ichni.RhythmGame
string.Empty, string.Empty, false, 0, 1, false, 10, Vector3.right, 10, 5, true, Vector3.zero);
}); //Particle Tracker
var objectTrackerButton = inspector.GenerateButton(this, particleSubcontainer, "Object Tracker",
() =>
{
ObjectTracker.GenerateElement("New Object Tracker", Guid.NewGuid(), new List<string>(), true, this,
string.Empty, string.Empty, 10, Vector2.zero, Vector2.zero, string.Empty,
false, Vector3.zero,Vector3.zero, string.Empty,
false, Vector3.zero, Vector3.zero, string.Empty);
}); //Object Tracker
var objectTrackerButton = inspector.GenerateButton(this, particleSubcontainer, "Object Tracker",
() =>
{
ObjectTracker.GenerateElement("New Object Tracker", Guid.NewGuid(), new List<string>(), true, this,
string.Empty, string.Empty, 10, Vector2.zero, Vector2.zero, string.Empty,
false, Vector3.zero, Vector3.zero, string.Empty,
false, Vector3.zero, Vector3.zero, string.Empty);
}); //Object Tracker
StandardInspectionElement.GenerateForTransform(this, generateContainer); //关于有Transform的元素
inspector.GenerateButton(this, particleSubcontainer, "Track Global Color Change",
() => { TrackGlobalColorChange.GenerateElement("New Track Global Color Change", Guid.NewGuid(), new List<string>(), true, this, new FlexibleFloat(true), new FlexibleFloat(true), new FlexibleFloat(true), new FlexibleFloat(true)); }); //变量容器
@@ -339,6 +340,12 @@ namespace Ichni.RhythmGame
}
);
var fastNoteTrackerButton = inspector.GenerateButton(this, SamplerSubcontainer, "Fast Note Tracker", () =>
{
FastNoteTracker fastNoteTracker = FastNoteTracker.GenerateElement("Fast Note Tracker", Guid.NewGuid(), new List<string>(), true, this);
}
);
}
}

View File

@@ -64,6 +64,12 @@ namespace Ichni.RhythmGame
// return isGoWithZ ? GetSplineZPercent(Mathf.Clamp01(per)) : Mathf.Clamp01(per);
return Mathf.Clamp01(per);
}
public float GetTrackPercentRaw(float songTimeInTime)
{
float per = AnimationCurveEvaluator.Evaluate(animationCurveType, (songTimeInTime - trackStartTime) / trackTotalTime);
// return isGoWithZ ? GetSplineZPercent(Mathf.Clamp01(per)) : Mathf.Clamp01(per);
return per;
}
// private float GetSplineZPercent(float percent)
// {
// var nodeList = track.trackPathSubmodule.pathNodeList;

View File

@@ -17,7 +17,7 @@ namespace Ichni.RhythmGame
private List<string> objectNameList;
public string themeBundleName;
public string objectName;
public float playTime;
public float stopTime;
@@ -25,17 +25,17 @@ namespace Ichni.RhythmGame
public Vector2 positionOffsetMin = Vector2.zero;
public Vector2 positionOffsetMax = Vector2.zero;
public string customPositionRuleName;
public bool applyRotationOffset = false;
public Vector3 rotationOffsetMin = Vector3.zero;
public Vector3 rotationOffsetMax = Vector3.zero;
public string customRotationRuleName;
public bool applyScaleOffset = false;
public Vector3 scaleOffsetMin = Vector3.one;
public Vector3 scaleOffsetMax = Vector3.one;
public string customScaleRuleName;
public static ObjectTracker GenerateElement(string elementName, Guid id, List<string> tags,
bool isFirstGenerated, Track track, string themeBundleName, string objectName, int spawnCount,
Vector2 positionOffsetMin, Vector2 positionOffsetMax, string customPositionRuleName,
@@ -44,7 +44,7 @@ namespace Ichni.RhythmGame
{
ObjectTracker objectTracker = Instantiate(EditorManager.instance.basePrefabs.objectTracker, track.transform).GetComponent<ObjectTracker>();
objectTracker.objectPrefab = ThemeBundleManager.instance.GetObject<GameObject>(themeBundleName, objectName);
objectTracker.objectController.objects = new[] { objectTracker.objectPrefab };
objectTracker.Initialize(elementName, id, tags, isFirstGenerated, track);
objectTracker.track = track;
@@ -83,12 +83,12 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Object Tracker");
var generate = container.GenerateSubcontainer(3);
var themeBundleDropdown =
var themeBundleDropdown =
inspector.GenerateDropdown(this, generate, "Theme Bundle", themeBundleList, nameof(themeBundleName))
.AddListenerFunction(() => inspectorMain.SetInspector(this));
if (themeBundleName != String.Empty && ThemeBundleManager.instance.TryGetThemeBundle(themeBundleName, out ThemeBundle themeBundle))
@@ -100,13 +100,14 @@ namespace Ichni.RhythmGame
}
else
{
var objectNameDropdown =
var objectNameDropdown =
inspector.GenerateDropdown(this, generate, "Object Name", new List<string>(), nameof(objectName));
objectNameDropdown.dropdown.interactable = false;
} // 如果没有选择主题包,则物体名称下拉框不可用
var setButton = inspector.GenerateButton(this, generate, "Generate", () =>
{
objectController.Clear();
this.objectPrefab = ThemeBundleManager.instance.GetObject<GameObject>(themeBundleName, objectName);
this.objectController.objects = new[] { this.objectPrefab };
this.objectController.Spawn();
@@ -116,33 +117,33 @@ namespace Ichni.RhythmGame
{
setButton.button.interactable = false;
}
var spawnSettings = container.GenerateSubcontainer(3);
inspector.GenerateInputField(this, spawnSettings, "Spawn Count", nameof(spawnCount))
.AddListenerFunction(()=> objectController.spawnCount = spawnCount);
.AddListenerFunction(() => objectController.spawnCount = spawnCount);
inspector.GenerateInputField(this, spawnSettings, "Play Time", nameof(playTime));
inspector.GenerateInputField(this, spawnSettings, "Stop Time", nameof(stopTime));
var posSettings = container.GenerateSubcontainer(1);
inspector.GenerateVector2InputField(this, posSettings, "Position Offset Min", nameof(positionOffsetMin))
.AddListenerFunction(()=> objectController.minOffset = positionOffsetMin);
.AddListenerFunction(() => objectController.minOffset = positionOffsetMin);
inspector.GenerateVector2InputField(this, posSettings, "Position Offset Max", nameof(positionOffsetMax))
.AddListenerFunction(()=> objectController.maxOffset = positionOffsetMax);
.AddListenerFunction(() => objectController.maxOffset = positionOffsetMax);
//inspector.GenerateInputField(this, posSettings, "Custom Position Rule Name", nameof(customPositionRuleName));
var rotSettings = container.GenerateSubcontainer(1);
inspector.GenerateToggle(this, rotSettings, "Apply Rotation Offset", nameof(applyRotationOffset))
.AddListenerFunction(()=> objectController.applyRotation = applyRotationOffset);
.AddListenerFunction(() => objectController.applyRotation = applyRotationOffset);
inspector.GenerateVector3InputField(this, rotSettings, "Rotation Offset Min", nameof(rotationOffsetMin))
.AddListenerFunction(()=> objectController.minRotation = rotationOffsetMin);
.AddListenerFunction(() => objectController.minRotation = rotationOffsetMin);
inspector.GenerateVector3InputField(this, rotSettings, "Rotation Offset Max", nameof(rotationOffsetMax))
.AddListenerFunction(()=> objectController.maxRotation = rotationOffsetMax);
.AddListenerFunction(() => objectController.maxRotation = rotationOffsetMax);
//inspector.GenerateInputField(this, rotSettings, "Custom Rotation Rule Name", nameof(customRotationRuleName));
var scaleSettings = container.GenerateSubcontainer(1);
var scaleSettings = container.GenerateSubcontainer(1);
inspector.GenerateToggle(this, scaleSettings, "Apply Scale Offset", nameof(applyScaleOffset))
.AddListenerFunction(()=> objectController.applyScale = applyScaleOffset);
.AddListenerFunction(() => objectController.applyScale = applyScaleOffset);
inspector.GenerateVector3InputField(this, scaleSettings, "Scale Offset Min", nameof(scaleOffsetMin))
.AddListenerFunction(()=> objectController.minScaleMultiplier = scaleOffsetMin);
.AddListenerFunction(() => objectController.minScaleMultiplier = scaleOffsetMin);
inspector.GenerateVector3InputField(this, scaleSettings, "Scale Offset Max", nameof(scaleOffsetMax))
.AddListenerFunction(()=> objectController.maxScaleMultiplier = scaleOffsetMax);
.AddListenerFunction(() => objectController.maxScaleMultiplier = scaleOffsetMax);
//inspector.GenerateInputField(this, scaleSettings, "Custom Scale Rule Name", nameof(customScaleRuleName));
}
@@ -158,7 +159,7 @@ namespace Ichni.RhythmGame
applyScaleOffset, scaleOffsetMin, scaleOffsetMax, customScaleRuleName);
}
}
public partial class ObjectTracker
{
public void SetSpawnSettings(int spawnCount,
@@ -178,19 +179,19 @@ namespace Ichni.RhythmGame
this.scaleOffsetMin = scaleOffsetMin;
this.scaleOffsetMax = scaleOffsetMax;
this.customScaleRuleName = customScaleRuleName;
objectController.spawnCount = spawnCount;
objectController.minOffset = positionOffsetMin;
objectController.maxOffset = positionOffsetMax;
objectController.applyRotation = applyRotationOffset;
objectController.minRotation = rotationOffsetMin;
objectController.maxRotation = rotationOffsetMax;
objectController.applyScale = applyScaleOffset;
objectController.minScaleMultiplier = scaleOffsetMin;
objectController.maxScaleMultiplier = scaleOffsetMax;
objectController.Spawn();
}
}
@@ -201,7 +202,7 @@ namespace Ichni.RhythmGame
{
public string themeBundleName;
public string objectName;
public float playTime;
public float stopTime;
@@ -209,12 +210,12 @@ namespace Ichni.RhythmGame
public Vector2 positionOffsetMin;
public Vector2 positionOffsetMax;
public string customPositionRuleName;
public bool applyRotationOffset;
public Vector3 rotationOffsetMin;
public Vector3 rotationOffsetMax;
public string customRotationRuleName;
public bool applyScaleOffset;
public Vector3 scaleOffsetMin;
public Vector3 scaleOffsetMax;
@@ -236,7 +237,7 @@ namespace Ichni.RhythmGame
{
this.themeBundleName = themeBundleName;
this.objectName = objectName;
this.playTime = playTime;
this.stopTime = stopTime;
@@ -244,12 +245,12 @@ namespace Ichni.RhythmGame
this.positionOffsetMin = positionOffsetMin;
this.positionOffsetMax = positionOffsetMax;
this.customPositionRuleName = customPositionRuleName;
this.applyRotationOffset = applyRotationOffset;
this.rotationOffsetMin = rotationOffsetMin;
this.rotationOffsetMax = rotationOffsetMax;
this.customRotationRuleName = customRotationRuleName;
this.applyScaleOffset = applyScaleOffset;
this.scaleOffsetMin = scaleOffsetMin;
this.scaleOffsetMax = scaleOffsetMax;
@@ -257,7 +258,7 @@ namespace Ichni.RhythmGame
}
public override void ExecuteBM()
{
{
matchedElement = ObjectTracker.GenerateElement(
elementName, elementGuid, tags, false,
GetElement(attachedElementGuid) as Track,