using System;
using System.Collections.Generic;
using System.Linq;
using Cielonos.MainGame.Characters;
using Sirenix.OdinInspector;
using SLSUtilities.LeanPoolAssistance;
using UnityEngine;
namespace Cielonos.MainGame
{
[Serializable]
[InlineProperty]
public class MeshEffectRendererInfo
{
public Renderer renderer;
public float referenceScale = 1f;
}
public class MeshEffect : PooledObject
{
[System.Serializable]
[InlineProperty]
public class AdaptiveParameter
{
// Renamed for compactness as requested: C = Constant, R2C = RandomTwoConstants
public enum ParamMode { Constant, RandomRanged }
[HorizontalGroup("Line", Width = 120)]
[HideLabel]
public ParamMode mode = ParamMode.Constant;
[HorizontalGroup("Line", Width = 100)]
[ShowIf("mode", ParamMode.Constant)]
[LabelText("Constant")]
[LabelWidth(60)]
public float constantValue = 1f;
[HorizontalGroup("Line", Width = 100)]
[ShowIf("mode", ParamMode.RandomRanged)]
[LabelText("Min")]
[LabelWidth(30)]
public float constantMin = 1f;
[HorizontalGroup("Line", Width = 100)]
[ShowIf("mode", ParamMode.RandomRanged)]
[LabelText("Max")]
[LabelWidth(30)]
public float constantMax = 2f;
[HideInInspector]
public bool isCurveDetected = false;
[ShowIf("isCurveDetected")]
[InfoBox("Original source used Curves. Converted to Constant (Lossy).", InfoMessageType.Warning)]
private bool ShowCurveWarning => isCurveDetected;
public AdaptiveParameter(float defaultVal)
{
mode = ParamMode.Constant;
constantValue = defaultVal;
}
public ParticleSystem.MinMaxCurve GetScaledCurve(float scale)
{
if (mode == ParamMode.Constant)
{
return new ParticleSystem.MinMaxCurve(constantValue * scale);
}
else
{
return new ParticleSystem.MinMaxCurve(constantMin * scale, constantMax * scale);
}
}
public void PullFrom(ParticleSystem.MinMaxCurve source)
{
isCurveDetected = false;
switch (source.mode)
{
case ParticleSystemCurveMode.Constant:
mode = ParamMode.Constant;
constantValue = source.constant;
break;
case ParticleSystemCurveMode.TwoConstants:
mode = ParamMode.RandomRanged;
constantMin = source.constantMin;
constantMax = source.constantMax;
break;
case ParticleSystemCurveMode.Curve:
case ParticleSystemCurveMode.TwoCurves:
isCurveDetected = true;
mode = ParamMode.Constant;
constantValue = source.curveMultiplier;
break;
}
}
}
[System.Serializable]
public class ParticleSetting
{
[Required]
[OnValueChanged("PullFromParticleSystem")]
public ParticleSystem particleSystem;
[LabelText("Start Size"), LabelWidth(80)]
public AdaptiveParameter baseStartSize = new AdaptiveParameter(0.5f);
[LabelText("Emission"), LabelWidth(80)]
public AdaptiveParameter baseEmissionRate = new AdaptiveParameter(10f);
[HorizontalGroup("Sensitivity")]
[LabelText("Size Sens."), LabelWidth(70)]
[Range(0f, 1f)]
public float sizeScaleSensitivity = 0f;
[HorizontalGroup("Sensitivity")]
[LabelText("Emission Sens."), LabelWidth(90)]
[Range(0f, 1f)]
public float emissionScaleSensitivity = 1f;
///
/// Automatically pull values from the assigned ParticleSystem.
/// 自动读取粒子系统参数
///
public void PullFromParticleSystem()
{
if (particleSystem != null)
{
baseStartSize.PullFrom(particleSystem.main.startSize);
baseEmissionRate.PullFrom(particleSystem.emission.rateOverTime);
}
}
}
[Title("Mesh Effect Configuration")]
[Button("Add All Child Particles", ButtonSizes.Medium), PropertyOrder(-1)]
[Tooltip("Automatically find all ParticleSystems in children and add them to the list.")]
public void AutoPopulateChildren()
{
var systems = GetComponentsInChildren(true);
foreach (var ps in systems)
{
if (particleSettings.Any(p => p.particleSystem == ps)) continue;
var newSetting = new ParticleSetting();
newSetting.particleSystem = ps;
newSetting.PullFromParticleSystem();
particleSettings.Add(newSetting);
}
}
[ListDrawerSettings(ShowFoldout = true)]
public List particleSettings = new List();
[Title("Global Adjustments")]
[Tooltip("Global scale multiplier for the entire effect. \n全局系数,用于整体调整效果强弱")]
public float globalMultiplier = 1f;
[Tooltip("Reference scale for calculating 'One Unit'. Usually set to 1 or the average size of a character. \n参考尺寸,用于定义“单位大小”。通常设为1或者标准角色的大小")]
public float referenceScale = 1f;
public Renderer targetRenderer;
private void Reset()
{
isAutoDespawn = false;
}
public void AttachTo(MeshEffectRendererInfo target)
{
if (target == null) return;
targetRenderer = target.renderer;
transform.SetParent(targetRenderer.transform, true);
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
referenceScale = target.referenceScale;
ApplyEffectSettings();
}
public void StopAllParticles()
{
foreach (var setting in particleSettings.Where(setting => setting.particleSystem != null))
{
setting.particleSystem.Stop();
}
}
private void ApplyEffectSettings()
{
if (targetRenderer == null) return;
// Calculate Scale Factor based on Bounds
Vector3 size = targetRenderer.bounds.size;
float averageSize = (size.x + size.y + size.z) / 3f;
float scaleRatio = (averageSize / referenceScale);
if (scaleRatio <= 0.001f) scaleRatio = 1f;
foreach (var setting in particleSettings)
{
if (setting.particleSystem == null) continue;
var main = setting.particleSystem.main;
var emission = setting.particleSystem.emission;
var shape = setting.particleSystem.shape;
// 1. Adaptive Shape Setup
shape.enabled = true;
if (targetRenderer is SkinnedMeshRenderer smr)
{
shape.shapeType = ParticleSystemShapeType.SkinnedMeshRenderer;
shape.skinnedMeshRenderer = smr;
}
else if (targetRenderer is MeshRenderer mr)
{
shape.shapeType = ParticleSystemShapeType.MeshRenderer;
shape.meshRenderer = mr;
}
// 2. Calculate Adaptive Parameters
// Size Logic
float sizeFactor = Mathf.Lerp(1f, scaleRatio, setting.sizeScaleSensitivity);
float finalSizeScale = sizeFactor * globalMultiplier;
main.startSize = setting.baseStartSize.GetScaledCurve(finalSizeScale);
// Emission Logic
float areaRatio = Mathf.Pow(scaleRatio, 2);
float emissionFactor = Mathf.Lerp(1f, areaRatio, setting.emissionScaleSensitivity);
float finalEmissionScale = emissionFactor * globalMultiplier;
emission.rateOverTime = setting.baseEmissionRate.GetScaledCurve(finalEmissionScale);
}
}
}
}