Files
Northbound/Assets/External/JMO Assets/Cartoon FX Remaster/CFXR Assets/Scripts/CFXR_EmissionBySurface.cs
dal4segno f3923079a4 건설 모드에서 클릭이 잘 작동하지 않는 문제 수정
건설 모드 UI가 클릭되어 건설되지 않는 문제 수정
JMO Asset 위치 조정
Tower, Monster, Creep의 Hit/Destroy FX Prefab 설정 (Template Level)
Tower에 체력바 추가 (최초 건설 시, 체력 변경 시 등장)
Tower 관련 디버깅 로그 정리
건설 토대의 사이즈가 비정상적인 문제 수정
2026-02-25 01:41:58 +09:00

195 lines
8.0 KiB
C#

using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace CartoonFX
{
[RequireComponent(typeof(ParticleSystem))]
public class CFXR_EmissionBySurface : MonoBehaviour
{
public bool active = true;
public float particlesPerUnit = 10;
[Tooltip("This is to avoid slowdowns in the Editor if the value gets too high")] public float maxEmissionRate = 5000;
[HideInInspector] public float density = 0;
bool attachedToEditor;
ParticleSystem ps;
#if UNITY_EDITOR
void OnValidate()
{
this.hideFlags = HideFlags.DontSaveInBuild;
CalculateAndUpdateEmission();
}
internal void AttachToEditor()
{
if (attachedToEditor) return;
EditorApplication.update += OnEditorUpdate;
attachedToEditor = true;
}
internal void DetachFromEditor()
{
if (!attachedToEditor) return;
EditorApplication.update -= OnEditorUpdate;
attachedToEditor = false;
}
void OnEditorUpdate()
{
CalculateAndUpdateEmission();
if (!System.Array.Exists(Selection.gameObjects, item => item == this.gameObject))
{
DetachFromEditor();
}
}
void CalculateAndUpdateEmission()
{
if (!active) return;
if (this == null) return;
if (ps == null) ps = this.GetComponent<ParticleSystem>();
density = CalculateShapeDensity(ps.shape, ps.main.scalingMode == ParticleSystemScalingMode.Shape, this.transform);
if (density == 0) return;
float emissionOverTime = density * particlesPerUnit;
ParticleSystem.EmissionModule emission = ps.emission;
if (Math.Abs(emission.rateOverTime.constant - emissionOverTime) > 0.1f)
{
emission.rateOverTime = Mathf.Min(maxEmissionRate, emissionOverTime);
}
}
float CalculateShapeDensity(ParticleSystem.ShapeModule shapeModule, bool isShapeScaling, Transform transform)
{
float arcPercentage = Mathf.Max(0.01f, shapeModule.arc / 360f);
float thicknessPercentage = Mathf.Max(0.01f, 1.0f - shapeModule.radiusThickness);
float scaleX = shapeModule.scale.x;
float scaleY = shapeModule.scale.y;
float scaleZ = shapeModule.scale.z;
if (isShapeScaling)
{
Vector3 localScale = Quaternion.Euler(shapeModule.rotation) * transform.localScale;
scaleX = scaleX * localScale.x;
scaleY = scaleY * localScale.y;
scaleZ = scaleZ * localScale.z;
}
scaleX = Mathf.Abs(scaleX);
scaleY = Mathf.Abs(scaleY);
scaleZ = Mathf.Abs(scaleZ);
switch (shapeModule.shapeType)
{
case ParticleSystemShapeType.Hemisphere:
case ParticleSystemShapeType.Sphere:
{
float rX = shapeModule.radius * scaleX;
float rY = shapeModule.radius * scaleY;
float rZ = shapeModule.radius * scaleZ;
float rmX = rX * thicknessPercentage;
float rmY = rY * thicknessPercentage;
float rmZ = rZ * thicknessPercentage;
float volume = (rX * rY * rZ - rmX * rmY * rmZ) * Mathf.PI;
if (shapeModule.shapeType == ParticleSystemShapeType.Hemisphere)
{
volume /= 2.0f;
}
return volume * arcPercentage;
}
case ParticleSystemShapeType.Cone:
{
float innerDisk = shapeModule.radius * scaleX * thicknessPercentage * shapeModule.radius * scaleY * thicknessPercentage * Mathf.PI;
float outerDisk = shapeModule.radius *scaleX * shapeModule.radius * scaleY * Mathf.PI;
return outerDisk - innerDisk;
}
case ParticleSystemShapeType.ConeVolume:
{
// cylinder volume, changing the angle doesn't actually extend the area from where the particles are emitted
float innerCylinder = shapeModule.radius * scaleX * thicknessPercentage * shapeModule.radius * scaleY * thicknessPercentage * Mathf.PI * shapeModule.length * scaleZ;
float outerCylinder = shapeModule.radius * scaleX * shapeModule.radius * scaleY * Mathf.PI * shapeModule.length * scaleZ;
return outerCylinder - innerCylinder;
}
case ParticleSystemShapeType.BoxEdge:
case ParticleSystemShapeType.BoxShell:
case ParticleSystemShapeType.Box:
{
return scaleX * scaleY * scaleZ;
}
case ParticleSystemShapeType.Circle:
{
float radiusX = shapeModule.radius * scaleX;
float radiusY = shapeModule.radius * scaleY;
float radiusMinX = radiusX * thicknessPercentage;
float radiusMinY = radiusY * thicknessPercentage;
float area = (radiusX * radiusY - radiusMinX * radiusMinY) * Mathf.PI;
return area * arcPercentage;
}
case ParticleSystemShapeType.SingleSidedEdge:
{
return shapeModule.radius * scaleX;
}
case ParticleSystemShapeType.Donut:
{
float outerDonutVolume = 2 * Mathf.PI * Mathf.PI * shapeModule.donutRadius * shapeModule.donutRadius * shapeModule.radius * arcPercentage;
float innerDonutVolume = 2 * Mathf.PI * Mathf.PI * shapeModule.donutRadius * thicknessPercentage * thicknessPercentage * shapeModule.donutRadius * shapeModule.radius * arcPercentage;
return (outerDonutVolume - innerDonutVolume) * scaleX * scaleY * scaleZ;
}
case ParticleSystemShapeType.Rectangle:
{
return scaleX * scaleY;
}
case ParticleSystemShapeType.Mesh:
case ParticleSystemShapeType.SkinnedMeshRenderer:
case ParticleSystemShapeType.MeshRenderer:
{
Debug.LogWarning( string.Format("[{0}] Calculating volume for a mesh is unsupported.", nameof(CFXR_EmissionBySurface)));
this.active = false;
return 0;
}
case ParticleSystemShapeType.Sprite:
case ParticleSystemShapeType.SpriteRenderer:
{
Debug.LogWarning( string.Format("[{0}] Calculating volume for a sprite is unsupported.", nameof(CFXR_EmissionBySurface)));
this.active = false;
return 0;
}
}
return 0;
}
#endif
}
#if UNITY_EDITOR
[CustomEditor(typeof(CFXR_EmissionBySurface))]
class CFXR_EmissionBySurface_Editor : Editor
{
CFXR_EmissionBySurface Target { get { return target as CFXR_EmissionBySurface; } }
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Space(10);
EditorGUILayout.HelpBox("This Editor script will adapt the particle emission based on its shape density, so that you can resize it to fit a specific situation and the overall number of particles won't change.\n\nYou can scale the object to change the emission area, and you can open the 'Shape' module in the Particle System to visualize the emission area.", MessageType.Info);
EditorGUILayout.HelpBox("Calculated Density: " + Target.density, MessageType.None);
}
void OnEnable()
{
Target.AttachToEditor();
}
void OnDisable()
{
Target.DetachFromEditor();
}
}
#endif
}