spin trails

This commit is contained in:
SoulliesOfficial
2026-07-04 14:27:04 -04:00
parent f9694c5992
commit 6e1ce6f6f7
23 changed files with 76915 additions and 20526 deletions

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using Ichni.RhythmGame.Beatmap;
using UnityEngine;
namespace Ichni.RhythmGame.ThemeBundles.DepartureToMultiverse.Beatmap
{
[System.Serializable]
public class DTMCircleSpinTrails_BM : EnvironmentObject_BM
{
// Circle Spin Trails Settings
public int particleCount = 20;
public float particleLifetime = 5f;
public Gradient colorGradient;
public float orbitalZSpeed = 1f;
public float emissionIntensity = 2f;
public DTMCircleSpinTrails_BM()
{
}
public DTMCircleSpinTrails_BM(
string elementName, Guid elementGuid, List<string> tags, GameElement_BM attachedElement,
string themeBundleName, string objectName, bool isStatic,
int particleCount, float particleLifetime, Gradient colorGradient,
float orbitalZSpeed, float emissionIntensity)
: base(elementName, elementGuid, tags, attachedElement, themeBundleName, objectName, isStatic)
{
this.particleCount = particleCount;
this.particleLifetime = particleLifetime;
this.colorGradient = colorGradient;
this.orbitalZSpeed = orbitalZSpeed;
this.emissionIntensity = emissionIntensity;
}
public override void ExecuteBM()
{
matchedElement = DTMCircleSpinTrails.GenerateElement(
elementName, elementGuid, tags, false,
themeBundleName, objectName, GetElement(attachedElementGuid), isStatic,
particleCount, particleLifetime, colorGradient, orbitalZSpeed, emissionIntensity);
}
public override GameElement DuplicateBM(GameElement parent)
{
return DTMCircleSpinTrails.GenerateElement(
elementName, Guid.NewGuid(), tags, true,
themeBundleName, objectName, parent, isStatic,
particleCount, particleLifetime, colorGradient, orbitalZSpeed, emissionIntensity);
}
}
}

View File

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

View File

@@ -0,0 +1,54 @@
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
using Ichni.RhythmGame.ThemeBundles.DepartureToMultiverse.Beatmap;
namespace Ichni.RhythmGame.ThemeBundles.DepartureToMultiverse
{
public partial class DTMCircleSpinTrails
{
#region [Editor] Inspection & Save
public override void SaveBM()
{
matchedBM = new DTMCircleSpinTrails_BM(
elementName, elementGuid, tags,
parentElement.matchedBM as GameElement_BM,
themeBundleName, objectName, isStatic,
particleCount, particleLifetime, colorGradient, orbitalZSpeed, emissionIntensity);
}
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Circle Spin Trails")
.InputField(nameof(particleCount), "Particle Count")
.OnChanged(ApplyParticleSettings)
.Slider(nameof(particleLifetime), 0.1f, 20f, "Particle Lifetime")
.OnChanged(ApplyParticleSettings)
.Slider(nameof(orbitalZSpeed), -10f, 10f, "Orbital Z Speed")
.OnChanged(ApplyParticleSettings)
.Slider(nameof(emissionIntensity), 0f, 10f, "Emission Intensity")
.OnChanged(ApplyParticleSettings)
.Section("Circle Spin Trails Color")
.Button("Gradient Color Keys", () =>
{
var window = insp.GenerateCompositeParameterWindow(this, "Gradient Color Keys", nameof(colorGradient));
window.SetAsGradientColorKeys();
window.closeButton.onClick.AddListener(ApplyParticleSettings);
})
.Button("Gradient Alpha Keys", () =>
{
var window = insp.GenerateCompositeParameterWindow(this, "Gradient Alpha Keys", nameof(colorGradient));
window.SetAsGradientAlphaKeys();
window.closeButton.onClick.AddListener(ApplyParticleSettings);
})
.Section("Circle Spin Trails Actions")
.Button("Apply Settings", ApplyParticleSettings)
.Build();
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7902bc73b942a4f458cafc93892bae52

View File

@@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Ichni.RhythmGame.ThemeBundles.DepartureToMultiverse
{
/// <summary>
/// 圆形轨迹流星环境物体。单个 ParticleSystem粒子沿 Velocity over Lifetime 的 Orbital Z 轨道旋转,
/// 并借助 Trail 模块拖出圆形轨迹。颜色由 Gradient 随机取色,发光强度以 HDR 倍率应用到粒子材质。
/// </summary>
public partial class DTMCircleSpinTrails : EnvironmentObject
{
#region [] Constants
private const string EnableEmissionProperty = "_EnableEmission";
private const string EmissionColorProperty = "_EmissionColor";
private const int DefaultParticleCount = 20;
private const float DefaultParticleLifetime = 5f;
private const float DefaultOrbitalZSpeed = 1f;
private const float DefaultEmissionIntensity = 2f;
private const float MinLifetime = 0.01f;
#endregion
#region [] Exposed Fields
[Header("Core References")]
[Tooltip("流星效果所使用的粒子系统")]
public ParticleSystem trailParticleSystem;
[Header("Circle Spin Trails Settings")]
[Tooltip("同时存在的流星(粒子)数量")]
public int particleCount = DefaultParticleCount;
[Tooltip("单个粒子的存活时间(秒)")]
public float particleLifetime = DefaultParticleLifetime;
[Tooltip("粒子随机取色所使用的渐变")]
public Gradient colorGradient;
[Tooltip("轨道旋转速度Velocity over Lifetime 的 Orbital Z")]
public float orbitalZSpeed = DefaultOrbitalZSpeed;
[Tooltip("发光强度HDR 倍率,应用到粒子材质的 _EmissionColor")]
public float emissionIntensity = DefaultEmissionIntensity;
#endregion
#region [] Runtime Caches
private ParticleSystemRenderer particleRenderer;
#endregion
// 颜色由本元素的 Gradient 独立管理,因此不暴露 ColorSubmodule 的拾色器
public override bool haveBaseColor => false;
public override bool haveEmissionColor => false;
#region [] Default Helpers
/// <summary>返回默认的多彩渐变(青 → 品红 → 黄),用于流星随机取色。</summary>
private static Gradient DefaultColorGradient()
{
Gradient gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[]
{
new GradientColorKey(Color.cyan, 0f),
new GradientColorKey(Color.magenta, 0.5f),
new GradientColorKey(Color.yellow, 1f)
},
new GradientAlphaKey[]
{
new GradientAlphaKey(1f, 0f),
new GradientAlphaKey(1f, 1f)
});
return gradient;
}
#endregion
#region [] Lifecycle & Factory
public static DTMCircleSpinTrails GenerateElement(
string elementName, Guid id, List<string> tags,
bool isFirstGenerated, string themeBundleName, string objectName,
GameElement parentElement, bool isStatic,
int particleCount, float particleLifetime, Gradient colorGradient,
float orbitalZSpeed, float emissionIntensity)
{
DTMCircleSpinTrails spinTrails = EnvironmentObject.GenerateElement(
elementName, id, tags, isFirstGenerated,
themeBundleName, objectName, parentElement, isStatic)
.GetComponent<DTMCircleSpinTrails>();
spinTrails.particleCount = particleCount;
spinTrails.particleLifetime = particleLifetime;
spinTrails.colorGradient = colorGradient ?? DefaultColorGradient();
spinTrails.orbitalZSpeed = orbitalZSpeed;
spinTrails.emissionIntensity = emissionIntensity;
return spinTrails;
}
public override void FirstSetUpObject(bool isFirstGenerated)
{
if (trailParticleSystem == null)
trailParticleSystem = GetComponent<ParticleSystem>();
particleRenderer = trailParticleSystem != null
? trailParticleSystem.GetComponent<ParticleSystemRenderer>()
: null;
// 兼容旧存档中未序列化的渐变字段
if (colorGradient == null) colorGradient = DefaultColorGradient();
// 实例化材质,避免污染共享资源(从 AssetBundle 加载后必须重新初始化 Shader
if (particleRenderer != null) particleRenderer.InitializeShader();
ApplyParticleSettings();
}
public override void AfterInitialize()
{
base.AfterInitialize();
ApplyParticleSettings();
}
public override void Refresh()
{
base.Refresh();
ApplyParticleSettings();
}
public override void OnDirtyRefresh(Dictionary<string, bool> flags)
{
ApplyParticleSettings();
}
#endregion
#region [] Particle Settings
/// <summary>
/// 将当前字段配置写入粒子系统与材质:数量、存活时间、随机颜色、轨道旋转与发光强度。
/// </summary>
public void ApplyParticleSettings()
{
if (trailParticleSystem == null) return;
float safeLifetime = Mathf.Max(particleLifetime, MinLifetime);
int safeCount = Mathf.Max(particleCount, 0);
// 数量与存活时间:以 rateOverTime = count / lifetime 维持稳态粒子数量maxParticles 作为硬上限
ParticleSystem.MainModule main = trailParticleSystem.main;
main.startLifetime = safeLifetime;
main.maxParticles = safeCount;
main.startColor = new ParticleSystem.MinMaxGradient(colorGradient)
{
mode = ParticleSystemGradientMode.RandomColor
};
ParticleSystem.EmissionModule emission = trailParticleSystem.emission;
emission.enabled = true;
emission.rateOverTime = new ParticleSystem.MinMaxCurve(safeCount / safeLifetime);
// 轨道旋转Velocity over Lifetime 的 Orbital Z
ParticleSystem.VelocityOverLifetimeModule velocity = trailParticleSystem.velocityOverLifetime;
velocity.enabled = true;
velocity.orbitalZ = new ParticleSystem.MinMaxCurve(orbitalZSpeed);
// 发光强度HDR 倍率写入材质 _EmissionColor逐粒子随机色由顶点色再相乘着色
if (particleRenderer != null && particleRenderer.material != null)
{
float hdrScale = Mathf.Pow(2f, emissionIntensity);
Color emissionColor = Color.white * hdrScale;
emissionColor.a = 1f;
particleRenderer.material.SetFloat(EnableEmissionProperty, 1f);
particleRenderer.material.SetColor(EmissionColorProperty, emissionColor);
}
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 30fd0b949c7a57141a3320bac21f1e9e