@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user