佛山工厂量产随机快速复制

Signed-off-by: TRADER_FOER <lhf190@outlook.com>
This commit is contained in:
2026-07-29 20:14:03 +08:00
parent c828389d0d
commit d662f2fa95
209 changed files with 179128 additions and 3709417 deletions

View File

@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using Ichni.Editor;
using Ichni.Editor.Commands;
using Ichni.RhythmGame.ThemeBundles.DepartureToMultiverse;
using UnityEngine;
@@ -7,7 +9,7 @@ namespace Ichni.RhythmGame
{
public partial class ElementFolder
{
private void QuickCopy(Vector3 unitPositionOffset, Vector3 unitRotationOffset, Vector3 unitScaleOffset, float unitTimeOffset, int iteration = 1)
private void QuickCopy(Vector3 unitPositionOffset, Vector3 unitRotationOffset, Vector3 unitScaleOffset, float unitTimeOffset, int iteration = 1, QuickCopyDisturbanceSettings disturbance = default)
{
if (iteration <= 0) return;
@@ -17,6 +19,14 @@ namespace Ichni.RhythmGame
return;
}
CommandManager.ExecuteCommand(new CreateElementsCommand(() =>
QuickCopyRaw(unitPositionOffset, unitRotationOffset, unitScaleOffset, unitTimeOffset, iteration, disturbance)));
}
private List<GameElement> QuickCopyRaw(Vector3 unitPositionOffset, Vector3 unitRotationOffset, Vector3 unitScaleOffset, float unitTimeOffset, int iteration, QuickCopyDisturbanceSettings disturbance)
{
disturbance = disturbance ?? QuickCopyDisturbanceSettings.None;
List<GameElement> createdFolders = new List<GameElement>();
CopyPasteDeleteModule cpd = EditorManager.instance.operationManager.CopyPasteDeleteModule;
cpd.CopyElement(this);
@@ -24,11 +34,12 @@ namespace Ichni.RhythmGame
{
ElementFolder newFolder = cpd.PasteElementRaw(this, parentElement) as ElementFolder;
if (newFolder == null) continue;
createdFolders.Add(newFolder);
Vector3 positionOffset = unitPositionOffset * i;
Vector3 rotationOffset = unitRotationOffset * i;
Vector3 scaleOffset = unitScaleOffset * i;
float timeOffset = unitTimeOffset * i;
Vector3 positionOffset = unitPositionOffset * i + disturbance.SamplePosition();
Vector3 rotationOffset = unitRotationOffset * i + disturbance.SampleRotation();
Vector3 scaleOffset = unitScaleOffset * i + disturbance.SampleScale();
float timeOffset = unitTimeOffset * i + disturbance.SampleTime();
newFolder.ApplyFolderPositionOffset(positionOffset);
newFolder.ApplyFolderRotationOffset(rotationOffset);
@@ -38,6 +49,7 @@ namespace Ichni.RhythmGame
}
cpd.pastedElementList?.Clear();
return createdFolders;
}
private void ApplyFolderPositionOffset(Vector3 positionOffset)
@@ -196,4 +208,120 @@ namespace Ichni.RhythmGame
}
}
}
internal class QuickCopyDisturbanceSettings
{
public static QuickCopyDisturbanceSettings None => new QuickCopyDisturbanceSettings();
public Vector3 positionLower;
public Vector3 positionUpper;
public Vector3 rotationLower;
public Vector3 rotationUpper;
public Vector3 scaleLower;
public Vector3 scaleUpper;
public float timeLower;
public float timeUpper;
public Vector3 SamplePosition() => SampleVector(positionLower, positionUpper);
public Vector3 SampleRotation() => SampleVector(rotationLower, rotationUpper);
public Vector3 SampleScale() => SampleVector(scaleLower, scaleUpper);
public float SampleTime() => SampleFloat(timeLower, timeUpper);
private static Vector3 SampleVector(Vector3 lower, Vector3 upper)
{
return new Vector3(
SampleFloat(lower.x, upper.x),
SampleFloat(lower.y, upper.y),
SampleFloat(lower.z, upper.z));
}
private static float SampleFloat(float lower, float upper)
{
float min = Mathf.Min(lower, upper);
float max = Mathf.Max(lower, upper);
return Mathf.Approximately(min, max) ? min : UnityEngine.Random.Range(min, max);
}
}
internal static class QuickCopyDisturbanceWindow
{
public static void Show(GameElement owner, string title, QuickCopyDisturbanceSettings settings)
{
InspectorSecondaryWindow window = EditorManager.instance.uiManager.inspector.GenerateSecondaryWindow(owner, title);
IHaveInspection windowInspection = window;
DynamicUIContainer container = windowInspection.GenerateContainer("Settings");
settings ??= QuickCopyDisturbanceSettings.None;
DynamicUIVector3InputField positionLower = windowInspection.GenerateVector3InputField(container.GenerateSubcontainer(1), "Position Lower", settings.positionLower);
DynamicUIVector3InputField positionUpper = windowInspection.GenerateVector3InputField(container.GenerateSubcontainer(1), "Position Upper", settings.positionUpper);
DynamicUIVector3InputField rotationLower = windowInspection.GenerateVector3InputField(container.GenerateSubcontainer(1), "Rotation Lower", settings.rotationLower);
DynamicUIVector3InputField rotationUpper = windowInspection.GenerateVector3InputField(container.GenerateSubcontainer(1), "Rotation Upper", settings.rotationUpper);
DynamicUIVector3InputField scaleLower = windowInspection.GenerateVector3InputField(container.GenerateSubcontainer(1), "Scale Lower", settings.scaleLower);
DynamicUIVector3InputField scaleUpper = windowInspection.GenerateVector3InputField(container.GenerateSubcontainer(1), "Scale Upper", settings.scaleUpper);
DynamicUISubcontainer timeSub = container.GenerateSubcontainer(2);
DynamicUIInputField timeLower = windowInspection.GenerateInputField(timeSub, "Time Lower", settings.timeLower.ToString());
DynamicUIInputField timeUpper = windowInspection.GenerateInputField(timeSub, "Time Upper", settings.timeUpper.ToString());
BindVector3(positionLower, value => settings.positionLower = value);
BindVector3(positionUpper, value => settings.positionUpper = value);
BindVector3(rotationLower, value => settings.rotationLower = value);
BindVector3(rotationUpper, value => settings.rotationUpper = value);
BindVector3(scaleLower, value => settings.scaleLower = value);
BindVector3(scaleUpper, value => settings.scaleUpper = value);
BindFloat(timeLower, value => settings.timeLower = value);
BindFloat(timeUpper, value => settings.timeUpper = value);
}
private static void BindVector3(DynamicUIVector3InputField field, Action<Vector3> applyValue)
{
void UpdateValue(string _)
{
if (TryGetVector3(field, out Vector3 value))
{
applyValue(value);
}
}
field.inputFieldX.onEndEdit.RemoveAllListeners();
field.inputFieldY.onEndEdit.RemoveAllListeners();
field.inputFieldZ.onEndEdit.RemoveAllListeners();
field.inputFieldX.onValueChanged.RemoveAllListeners();
field.inputFieldY.onValueChanged.RemoveAllListeners();
field.inputFieldZ.onValueChanged.RemoveAllListeners();
field.inputFieldX.onValueChanged.AddListener(UpdateValue);
field.inputFieldY.onValueChanged.AddListener(UpdateValue);
field.inputFieldZ.onValueChanged.AddListener(UpdateValue);
}
private static void BindFloat(DynamicUIInputField field, Action<float> applyValue)
{
void UpdateValue(string text)
{
if (float.TryParse(text, out float value))
{
applyValue(value);
}
}
field.inputField.onEndEdit.RemoveAllListeners();
field.inputField.onValueChanged.RemoveAllListeners();
field.inputField.onValueChanged.AddListener(UpdateValue);
}
private static bool TryGetVector3(DynamicUIVector3InputField field, out Vector3 value)
{
if (float.TryParse(field.inputFieldX.text, out float x) &&
float.TryParse(field.inputFieldY.text, out float y) &&
float.TryParse(field.inputFieldZ.text, out float z))
{
value = new Vector3(x, y, z);
return true;
}
value = default;
return false;
}
}
}

View File

@@ -58,6 +58,7 @@ namespace Ichni.RhythmGame
var inspectorMain = EditorManager.instance.uiManager.inspector;
IHaveInspection qcWindow = inspectorMain.GenerateSecondaryWindow(this, elementName + "'s Quick Copy");
var qcContainer = qcWindow.GenerateContainer();
QuickCopyDisturbanceSettings disturbance = QuickCopyDisturbanceSettings.None;
var positionSub = qcContainer.GenerateSubcontainer(3);
var xField = qcWindow.GenerateInputField(positionSub, "X offset", "0");
@@ -78,14 +79,19 @@ namespace Ichni.RhythmGame
var timeField = qcWindow.GenerateInputField(timeSub, "Time offset", "0");
var iterationField = qcWindow.GenerateInputField(timeSub, "Iteration", "1");
qcWindow.GenerateButton(this, timeSub, "Copy", () =>
var actionSub = qcContainer.GenerateSubcontainer(2);
qcWindow.GenerateButton(this, actionSub, "Copy", () =>
{
Vector3 positionOffset = new Vector3(xField.GetValue<float>(), yField.GetValue<float>(), zField.GetValue<float>());
Vector3 rotationOffset = new Vector3(rxField.GetValue<float>(), ryField.GetValue<float>(), rzField.GetValue<float>());
Vector3 scaleOffset = new Vector3(sxField.GetValue<float>(), syField.GetValue<float>(), szField.GetValue<float>());
float timeOffset = timeField.GetValue<float>();
int iteration = iterationField.GetValue<int>();
QuickCopy(positionOffset, rotationOffset, scaleOffset, timeOffset, iteration);
QuickCopy(positionOffset, rotationOffset, scaleOffset, timeOffset, iteration, disturbance);
});
qcWindow.GenerateButton(this, actionSub, "Disturbance", () =>
{
QuickCopyDisturbanceWindow.Show(this, elementName + "'s Quick Copy Disturbance", disturbance);
});
})
.Span(3)

View File

@@ -2,6 +2,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Ichni.Editor;
using Ichni.Editor.Commands;
using UnityEngine;
namespace Ichni.RhythmGame
@@ -17,20 +18,30 @@ namespace Ichni.RhythmGame
/// <param name="unitRotationOffset">单位旋转偏移(欧拉角)</param>
/// <param name="unitScaleOffset">单位缩放偏移</param>
/// <param name="iteration">迭代次数即产生几个粘贴的Track</param>
private void QuickCopy(Vector3 unitPositionOffset, float unitTimeOffset, bool includeAnimations = true, int iteration = 1, bool MovingTrack = false, Vector3 unitRotationOffset = default, Vector3 unitScaleOffset = default)
private void QuickCopy(Vector3 unitPositionOffset, float unitTimeOffset, bool includeAnimations = true, int iteration = 1, bool MovingTrack = false, Vector3 unitRotationOffset = default, Vector3 unitScaleOffset = default, QuickCopyDisturbanceSettings disturbance = default)
{
if (iteration <= 0) return;
CommandManager.ExecuteCommand(new CreateElementsCommand(() =>
QuickCopyRaw(unitPositionOffset, unitTimeOffset, includeAnimations, iteration, MovingTrack, unitRotationOffset, unitScaleOffset, disturbance)));
}
private List<GameElement> QuickCopyRaw(Vector3 unitPositionOffset, float unitTimeOffset, bool includeAnimations, int iteration, bool MovingTrack, Vector3 unitRotationOffset, Vector3 unitScaleOffset, QuickCopyDisturbanceSettings disturbance)
{
disturbance = disturbance ?? QuickCopyDisturbanceSettings.None;
List<GameElement> createdTracks = new List<GameElement>();
CopyPasteDeleteModule cpd = EditorManager.instance.operationManager.CopyPasteDeleteModule;
cpd.CopyElement(this);
for (int i = 1; i <= iteration; i++)
{
cpd.PasteElementRaw(this, parentElement);
Track newTrack = cpd.pastedElementList[0] as Track;
Vector3 positionOffset = unitPositionOffset * i;
float timeOffset = unitTimeOffset * i;
Vector3 rotationOffset = unitRotationOffset * i;
Vector3 scaleOffset = unitScaleOffset * i;
Track newTrack = cpd.pastedElementList?.FirstOrDefault() as Track;
if (newTrack == null) continue;
createdTracks.Add(newTrack);
Vector3 positionOffset = unitPositionOffset * i + disturbance.SamplePosition();
float timeOffset = unitTimeOffset * i + disturbance.SampleTime();
Vector3 rotationOffset = unitRotationOffset * i + disturbance.SampleRotation();
Vector3 scaleOffset = unitScaleOffset * i + disturbance.SampleScale();
//以下对Track的所有有效的Submodule和子GameElement进行偏移 TODO: 需要思考是否有统一时间偏移的方法?
if (MovingTrack)
@@ -87,6 +98,7 @@ namespace Ichni.RhythmGame
}
cpd.pastedElementList.Clear();
return createdTracks;
}
/// <summary>

View File

@@ -135,6 +135,7 @@ namespace Ichni.RhythmGame
{
IHaveInspection qcWindow = inspectorMain.GenerateSecondaryWindow(this, elementName + "'s Quick Copy");
var qcContainer = qcWindow.GenerateContainer();
QuickCopyDisturbanceSettings disturbance = QuickCopyDisturbanceSettings.None;
// 位置偏移
var posSub = qcContainer.GenerateSubcontainer(3);
@@ -160,10 +161,11 @@ namespace Ichni.RhythmGame
var iterationField = qcWindow.GenerateInputField(timeSub, "Iteration", "1");
// 选项 & 按钮
var toggleSub = qcContainer.GenerateSubcontainer(3);
var toggleSub = qcContainer.GenerateSubcontainer(2);
var includeAnimationToggle = qcWindow.GenerateToggle(null, toggleSub, "Include Animation", string.Empty);
var movingTrackToggle = qcWindow.GenerateToggle(null, toggleSub, "Move Track", string.Empty);
qcWindow.GenerateButton(this, toggleSub, "Copy", () =>
var actionSub = qcContainer.GenerateSubcontainer(2);
qcWindow.GenerateButton(this, actionSub, "Copy", () =>
{
Vector3 positionOffset = new Vector3(xField.GetValue<float>(), yField.GetValue<float>(), zField.GetValue<float>());
Vector3 rotationOffset = new Vector3(rotXField.GetValue<float>(), rotYField.GetValue<float>(), rotZField.GetValue<float>());
@@ -172,7 +174,11 @@ namespace Ichni.RhythmGame
int iteration = iterationField.GetValue<int>();
bool includeAnimation = includeAnimationToggle.toggle.isOn;
bool movingTrack = movingTrackToggle.toggle.isOn;
this.QuickCopy(positionOffset, timeOffset, includeAnimation, iteration, movingTrack, rotationOffset, scaleOffset);
this.QuickCopy(positionOffset, timeOffset, includeAnimation, iteration, movingTrack, rotationOffset, scaleOffset, disturbance);
});
qcWindow.GenerateButton(this, actionSub, "Disturbance", () =>
{
QuickCopyDisturbanceWindow.Show(this, elementName + "'s Quick Copy Disturbance", disturbance);
});
})
.Button("Whole Track Move", () =>

View File

@@ -2,6 +2,7 @@ using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
namespace Ichni.RhythmGame
{
@@ -27,6 +28,13 @@ namespace Ichni.RhythmGame
case PathNode pathNode1 when !pathNode1.isShowingSphere:
return;
}
if (Keyboard.current.shiftKey.isPressed && elementToSelect.connectedTab != null)
{
EditorManager.instance.operationManager.AddSelectRangeToElement(elementToSelect);
EditorManager.instance.uiManager.inspector.SetInspector(elementToSelect);
return;
}
EditorManager.instance.uiManager.hierarchy.FindTab(elementToSelect);
}
@@ -40,4 +48,4 @@ namespace Ichni.RhythmGame
{
public SelectSubmodule selectSubmodule { get; set; }
}
}
}