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); } } } }