This commit is contained in:
SoulliesOfficial
2026-03-30 03:23:36 -04:00
parent 7533e7031d
commit 7954ec800c
56 changed files with 23688 additions and 18808 deletions

View File

@@ -97,7 +97,7 @@ namespace Ichni.Editor
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, colorParameterName, emissionColor));
colorPreview.color = emissionColor;
//connectedBaseElement.Refresh();
connectedBaseElement.Refresh();
}
public void SliderChange(float value)

View File

@@ -197,10 +197,12 @@ namespace Ichni.Editor
return highest;
}
private static float SmartInferHeight(Type t)
private static float SmartInferHeight(FieldDef fd)
{
if (t == typeof(Color)) return 240f;
// 多数基础组件默认为 100 单位高度
// EmissionColorPicker 有 Toggle/RGB滑块/Intensity输入框高度明显大于 BaseColorPicker
if (fd.Attr is DynamicUIEmissionColorAttribute) return 320f;
if (fd.ValueType == typeof(Color)) return 280f;
// 多数基础组件默认为 100
return 100f;
}
@@ -209,7 +211,7 @@ namespace Ichni.Editor
float highest = 100f;
foreach (var f in fields)
{
float height = f.Attr.Height > 0 ? f.Attr.Height : SmartInferHeight(f.ValueType);
float height = f.Attr.Height > 0 ? f.Attr.Height : SmartInferHeight(f);
if (height > highest) highest = height;
}
return highest;

View File

@@ -0,0 +1,58 @@
using UnityEngine;
namespace Ichni.RhythmGame.Beatmap
{
public class SpeedLinesEffect_BM : EffectBase_BM
{
// --- 时间控制 ---
public float duration;
public AnimationCurve intensityCurve;
// --- Lines 设置 ---
public Color color = new Color(1f, 1f, 1f, 0.5f);
public float peakRemap = 0f;
public float speedLinesTiling = 200f;
public float speedLinesRadialScale = 0.1f;
public float speedLinesPower = 0f;
public float speedLinesAnimation = 3f;
// --- Radial Mask 设置 ---
public float maskScale = 1f;
public float maskHardness = 0f;
public float maskPower = 5f;
public SpeedLinesEffect_BM() { }
public SpeedLinesEffect_BM(float duration, AnimationCurve intensityCurve,
Color color, float peakRemap = 0f,
float speedLinesTiling = 200f, float speedLinesRadialScale = 0.1f,
float speedLinesPower = 0f, float speedLinesAnimation = 3f,
float maskScale = 1f, float maskHardness = 0f, float maskPower = 5f)
{
this.effectTime = duration;
this.duration = duration;
this.intensityCurve = intensityCurve;
this.color = color;
this.peakRemap = peakRemap;
this.speedLinesTiling = speedLinesTiling;
this.speedLinesRadialScale = speedLinesRadialScale;
this.speedLinesPower = speedLinesPower;
this.speedLinesAnimation = speedLinesAnimation;
this.maskScale = maskScale;
this.maskHardness = maskHardness;
this.maskPower = maskPower;
}
public override EffectBase ConvertToGameType(GameElement attachedGameElement)
{
return new SpeedLinesEffect(
duration, intensityCurve,
color, peakRemap,
speedLinesTiling, speedLinesRadialScale, speedLinesPower, speedLinesAnimation,
maskScale, maskHardness, maskPower)
{
attachedGameElement = attachedGameElement,
};
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1d3ceadc5c586f14db585dad8293db73

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
using Ichni.RhythmGame.ThemeBundles.DepartureToMultiverse;
using Sirenix.OdinInspector;
using UnityEngine;
@@ -146,6 +147,11 @@ namespace Ichni.RhythmGame
note.AddinNoteManager(false);
note.Refresh();
}
else if (element is DTMTrail dtmTrail)
{
dtmTrail.ApplyTimeOffset(offset);
dtmTrail.Refresh();
}
// GameElement 本身具有层级结构,所有的 GameElement 都可能会携带子物体(如 EnvironmentObjectTrack 等等)
if (element.childElementList != null && element.childElementList.Count > 0)

View File

@@ -0,0 +1,49 @@
using Ichni.Editor;
using UnityEngine;
namespace Ichni.RhythmGame
{
public partial class SpeedLinesEffect
{
#region [] Inspector Setup
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
// --- 基础时间控制AnimationCurve 需手动生成,不在 AutoBuild 支持范围内)---
var container = inspector.GenerateContainer("Speed Lines");
var timeSettings = container.GenerateSubcontainer(3);
inspector.GenerateInputField(this, timeSettings, "Effect Time", nameof(effectTime));
inspector.GenerateButton(this, timeSettings, "Intensity Curve", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Intensity Curve", nameof(intensityCurve))
.SetAsCustomCurve();
});
var colorSettings = container.GenerateSubcontainer(1, 240f);
// ColorBaseColorPicker含 Alpha 作为透明度控制)
inspector.GenerateBaseColorPicker(this, colorSettings, "Color", nameof(color));
// --- Lines 参数组 ---
var linesSettings = container.GenerateSubcontainer(3);
inspector.GenerateInputField(this, linesSettings, "Peak Remap", nameof(peakRemap));
inspector.GenerateInputField(this, linesSettings, "Tiling", nameof(speedLinesTiling));
inspector.GenerateInputField(this, linesSettings, "Radial Scale", nameof(speedLinesRadialScale));
inspector.GenerateInputField(this, linesSettings, "Power", nameof(speedLinesPower));
inspector.GenerateInputField(this, linesSettings, "Animation Speed", nameof(speedLinesAnimation));
// --- Radial Mask 参数组 ---
var maskSettings = container.GenerateSubcontainer(3);
inspector.GenerateInputField(this, maskSettings, "Mask Scale", nameof(maskScale));
inspector.GenerateInputField(this, maskSettings, "Mask Hardness", nameof(maskHardness));
inspector.GenerateInputField(this, maskSettings, "Mask Power", nameof(maskPower));
SetRemove(timeSettings);
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 42c333b2a607c454bbec97aeaf5ad6d8

View File

@@ -44,6 +44,7 @@ namespace Ichni.RhythmGame
{ "Pixelate", () => new PixelateEffect(1, 320, 180, CustomCurvePresets.Instant()) },
{ "LowPassFilter", () => new LowPassFilterEffect(1, 10, CustomCurvePresets.Parabolic(1, 0, 1)) },
{ "HighPassFilter", () => new HighPassFilterEffect(1, 22000, CustomCurvePresets.Parabolic(1, 0, 1)) },
{ "SpeedLines", () => new SpeedLinesEffect(1f, CustomCurvePresets.Parabolic(1, 0, 1), new Color(1f, 1f, 1f, 0.5f), peakRemap: 0f) },
{ "DTM_RippleEffect", () => new DTMRippleEffect(0.65f, Color.white, 0) }
};
#endregion

View File

@@ -134,5 +134,11 @@ namespace Ichni.RhythmGame
public interface IHaveTimeDurationSubmodule
{
public TimeDurationSubmodule timeDurationSubmodule { get; set; }
public virtual void ApplyTimeOffset(float offset)
{
timeDurationSubmodule.startTime += offset;
timeDurationSubmodule.endTime += offset;
}
}
}

View File

@@ -49,6 +49,12 @@ namespace Ichni.RhythmGame
timeDurationSubmodule = new TimeDurationSubmodule(this);
colorSubmodule = new ColorSubmodule(this);
}
public virtual void ApplyTimeOffset(float offset)
{
(this as IHaveTimeDurationSubmodule).ApplyTimeOffset(offset);
}
#endregion
}
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using DG.Tweening;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
using Ichni.RhythmGame.Beatmap;
using UnityEngine;
namespace Ichni.RhythmGame

View File

@@ -0,0 +1,128 @@
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
using SLSUtilities.Rendering.PostProcessing;
using UnityEngine;
namespace Ichni.RhythmGame
{
/// <summary>
/// 控制 SpeedLines 后处理效果的特效脚本。
/// 通过 intensityCurve + peakRemap 在持续时间内驱动 speedLinesRemap数值越低效果越强
/// </summary>
public partial class SpeedLinesEffect : EffectBase
{
#region [] Property Caches
// --- 时间控制 ---
public AnimationCurve intensityCurve; // 0→1 映射到 remappeakRemap→1f1f=隐去,越小越强)
// --- Lines 设置 ---
public Color color; // 包含 AlphaAlpha 越大越不透明
public float peakRemap; // 效果最强时 speedLinesRemap 的目标值(默认 0越低越密集
public float speedLinesTiling; // 线条分块数量,默认 200
public float speedLinesRadialScale; // 放射缩放,默认 0.1
public float speedLinesPower; // 线条锐利度,默认 0
public float speedLinesAnimation; // 移动速度,默认 3
// --- Radial Mask 设置 ---
public float maskScale; // 遮罩比例,默认 1
public float maskHardness; // 遮罩边缘硬度,默认 0
public float maskPower; // 遮罩幂次,默认 5
private SpeedLines _speedLinesVolume;
#endregion
#region [] Generation & Initialization
public SpeedLinesEffect(float effectTime, AnimationCurve intensityCurve,
Color color, float peakRemap = 0f,
float speedLinesTiling = 200f, float speedLinesRadialScale = 0.1f,
float speedLinesPower = 0f, float speedLinesAnimation = 3f,
float maskScale = 1f, float maskHardness = 0f, float maskPower = 5f)
: base(effectTime)
{
this.intensityCurve = intensityCurve;
this.color = color;
this.peakRemap = peakRemap;
this.speedLinesTiling = speedLinesTiling;
this.speedLinesRadialScale = speedLinesRadialScale;
this.speedLinesPower = speedLinesPower;
this.speedLinesAnimation = speedLinesAnimation;
this.maskScale = maskScale;
this.maskHardness = maskHardness;
this.maskPower = maskPower;
}
public override void CopyParametersFrom(EffectBase other)
{
if (other is SpeedLinesEffect o)
{
this.intensityCurve = o.intensityCurve != null ? new AnimationCurve(o.intensityCurve.keys) : null;
this.color = o.color;
this.peakRemap = o.peakRemap;
this.speedLinesTiling = o.speedLinesTiling;
this.speedLinesRadialScale = o.speedLinesRadialScale;
this.speedLinesPower = o.speedLinesPower;
this.speedLinesAnimation = o.speedLinesAnimation;
this.maskScale = o.maskScale;
this.maskHardness = o.maskHardness;
this.maskPower = o.maskPower;
}
}
private void PrepareHandle()
{
if (_speedLinesVolume == null && PostProcessingManager.GlobalVolume != null)
{
PostProcessingManager.GlobalVolume.profile.TryGet(out _speedLinesVolume);
}
}
#endregion
#region [] Update & Processing
public override void PreExecute()
{
PrepareHandle();
if (_speedLinesVolume == null) return;
// 静态参数在预执行时写入一次
_speedLinesVolume.color.value = color;
_speedLinesVolume.speedLinesTiling.value = speedLinesTiling;
_speedLinesVolume.speedLinesRadialScale.value = speedLinesRadialScale;
_speedLinesVolume.speedLinesPower.value = speedLinesPower;
_speedLinesVolume.speedLinesAnimation.value = speedLinesAnimation;
_speedLinesVolume.maskScale.value = maskScale;
_speedLinesVolume.maskHardness.value = maskHardness;
_speedLinesVolume.maskPower.value = maskPower;
}
public override void Execute()
{
if (_speedLinesVolume == null) return;
// intensityCurve 0→1将 remap 从 1f无效果插值到 peakRemap最强效果
float t = intensityCurve != null ? intensityCurve.Evaluate(effectProgressPercent) : effectProgressPercent;
_speedLinesVolume.speedLinesRemap.value = Mathf.Lerp(1f, peakRemap, t);
}
public override void Adjust() { ResetEffect(); }
public override void Recover() { ResetEffect(); }
public override void Disrupt() { ResetEffect(); }
private void ResetEffect()
{
if (_speedLinesVolume != null)
_speedLinesVolume.speedLinesRemap.value = 1f; // Remap=1 时 IsActive() 返回 false
}
#endregion
#region [] Serialize & BM
public override EffectBase_BM ConvertToBM()
{
return new SpeedLinesEffect_BM(
effectTime, intensityCurve,
color, peakRemap,
speedLinesTiling, speedLinesRadialScale, speedLinesPower, speedLinesAnimation,
maskScale, maskHardness, maskPower);
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9d88500debd393041bf1b0dfdeddef23

View File

@@ -213,11 +213,16 @@ namespace Ichni.RhythmGame
EffectBase effectBase = generateEffects[i];
if (effectBase is NoteGenerateEffect ge)
{
// 必须先重置状态,否则当 nowEffectState 处于 After 时,
// 下一次 UpdateEffect 的 "After && nowEffectState != After" 条件为 false 而跳过 Adjust()
// 导致 SetActive(false) 的 noteMain 永远无法被 Adjust() 重新激活。
ge.nowEffectState = EffectBase.EffectState.Before;
ge.Recover();
beyondTime = Mathf.Max(beyondTime, ge.generateTime);
}
else
{
effectBase.nowEffectState = EffectBase.EffectState.Before;
effectBase.Recover();
}
}

View File

@@ -122,7 +122,14 @@ namespace Ichni.Editor
{
isRotatingSceneCamera = true;
// 初始化当前旋转角度,防止旋转跳变
_currentRotation = sceneCameraTransform.eulerAngles;
// 注意Unity 的 eulerAngles 返回 0~360需要将 X 分量规范化到 -180~180
// 避免 DOTween/PanelDrawer 残留的 (90,0,0) 被直接拾取后造成瞬间俯视
Vector3 rawAngles = sceneCameraTransform.eulerAngles;
_currentRotation = new Vector3(
rawAngles.x > 180f ? rawAngles.x - 360f : rawAngles.x,
rawAngles.y > 180f ? rawAngles.y - 360f : rawAngles.y,
0f
);
}
else if (mouse.rightButton.wasReleasedThisFrame)
{