using System; using System.Collections.Generic; using System.Linq; using Superlazy; using UnityEngine; public class EffectView : MonoBehaviour { private static readonly Queue unused = new Queue(); private static readonly Dictionary used = new Dictionary(); private static Transform unusedRoot; public static void InitGlobal() { unusedRoot = new GameObject("UnusedEffectView").transform; unusedRoot.gameObject.SetActive(false); } public static EffectView MakeUnitEffect(UnitView unit, string effectName, SLEntity effectContext) { var instance = GetEffect(effectName); instance.transform.SetParent(unit.Zone.Root, false); //// TODO: 추후 개선이 가능할지 모르겠지만 이펙트뷰 레벨에서 유닛을 가져다 쓰는게 맞는지 고민 //if (effectContext["CellEffect"]) //{ // instance.transform.position = unit.Position.ToEntity().PositionToCell().CellToPosition().ToVector3(); //} //else { instance.transform.position = unit.Position; } instance.transform.rotation = unit.Rotation; instance.transform.localScale = unit.EffectScale; instance.OnStart(effectContext.Override(), unit.Zone, unit); instance.gameObject.SetActive(true); return instance; } public static EffectView MakeEffect(IZoneView zone, string effectName, SLEntity effectContext/*, IWorld world*/, Vector3 position, Quaternion rotation, Vector3 scale) { var instance = GetEffect(effectName); instance.transform.SetParent(zone.Root, false); instance.transform.SetPositionAndRotation(position, rotation); instance.transform.localScale = scale; instance.OnStart(effectContext.Override(), zone, null); instance.gameObject.SetActive(true); return instance; } private static EffectView GetEffect(string effectName) { EffectView newEffect; if (unused != null && unused.Count != 0) { newEffect = unused.Dequeue(); } else { var newObject = new GameObject(); newObject.SetActive(false); newEffect = newObject.AddComponent(); } if (newEffect.baseRoot == null) { newEffect.baseRoot = SLResources.CreateInstance(effectName, newEffect.gameObject.transform); newEffect.Init(); } newEffect.baseRoot.gameObject.SetActive(true); newEffect.gameObject.name = effectName; return newEffect; } public static void ClearEffect() { foreach (var key in used.Keys.ToList()) { used[key].EndEffect(true); } used.Clear(); } public static void RemoveEffect(string effectID) { if (used.ContainsKey(effectID)) { used[effectID].EndEffect(); } } private SLResourceObject baseRoot; private SLEntity context; private UnitView unit; private Vector3 offset; private Vector3 rotationOffset; private Animator[] anims; private Renderer[] renderers; private ParticleSystem[] particles; private TrailRenderer[] trails; private Transform bindTransform; private void Init() { anims = GetComponentsInChildren(); renderers = GetComponentsInChildren(); particles = GetComponentsInChildren(); trails = GetComponentsInChildren(); } public void EndEffect(bool force = false) { if (gameObject.activeSelf == false) { SLLog.Error($"Already Destroy Effect {context["EffectID"]}"); return; } bindTransform = null; if (used.ContainsKey(context["EffectID"])) { used.Remove(context["EffectID"]); } if (unusedRoot) { transform.localScale = Vector3.one; baseRoot.Destroy(force); baseRoot = null; gameObject.SetActive(false); unused.Enqueue(this); transform.SetParent(unusedRoot); } else { Destroy(gameObject); } } private void OnStart(SLEntity context, IZoneView zone, UnitView unit) { this.context = context.Override(); this.unit = unit; if (this.context["Scale"] != false) { transform.localScale *= this.context["Scale"]; } if (this.context["ForceY"]) { transform.Translate(0, -transform.localPosition.y + context["ForceY"], 0); } if (this.context["ForceRotate"]) { transform.localRotation = Quaternion.Euler(this.context["ForceRotate"].ToVector3()); } offset = this.context["Offset"].ToVector3() * transform.localScale.x; rotationOffset = this.context["RotationOffset"].ToVector3(); if (unit != null) { if (context["Bind"]["Height"]) { enabled = true; var bindHeight = unit.Entity[context["Bind"]["Height"]]; offset += Vector3.up * bindHeight; } if (context["Bind"]["Part"]) { enabled = true; var part = unit.Entity[context["Bind"]["Part"]]; bindTransform = unit.transform.GetComponentsInChildren().FirstOrDefault(t => t.name == part["Transform"]); offset += part["Offset"].ToVector3(); transform.localScale *= part["Scale"]; } if (context["Bind"]["Transform"]) { enabled = true; bindTransform = unit.transform.GetComponentsInChildren().FirstOrDefault(t => t.name == context["Bind"]["Transform"]); } if (context["Bind"]["ShadowScale"]) { enabled = true; var scaleDelta = unit.Entity["ShadowRadius"] / SLSystem.Data["Default"]["DefaultShadowScale"]; transform.localScale *= scaleDelta; } if (bindTransform == null) { bindTransform = unit.transform.Find("OffsetRoot"); } foreach (var r in renderers) { r.gameObject.layer = unit.Zone.Layer; } foreach (var p in particles) { p.gameObject.layer = unit.Zone.Layer; } foreach (var t in trails) { t.gameObject.layer = unit.Zone.Layer; } } transform.Translate(offset, Space.Self); transform.Rotate(rotationOffset, Space.Self); foreach (var p in particles) { p.Clear(); } foreach (var t in trails) { t.Clear(); } if (context["EffectID"] == false) { context["EffectID"] = context["Effect"]; var index = 1; while (used.ContainsKey(context["EffectID"])) { context["EffectID"] = context["Effect"].ToString() + index; ++index; } } used.Add(context["EffectID"], this); //string layer = "Default"; //if (this.context["Layer"]) //{ // layer = this.context["Layer"]; //} //gameObject.SetLayerRecursively(layer); } private void Update() { if (unit != null) { if (unit.isActiveAndEnabled == false) { EndEffect(); return; } if (context["Bind"]["TopPosition"]) { transform.position = unit.Entity["TopPosition"].ToVector3(); transform.Translate(offset, Space.Self); } if (context["Bind"]["Position"]) { transform.position = bindTransform.position; transform.Translate(offset, Space.Self); } if (context["Bind"]["Rotation"]) { transform.rotation = bindTransform.rotation; transform.Rotate(rotationOffset, Space.Self); } if (unit.Entity["ActionSpeed"]) { foreach (var particle in particles) { var particleMain = particle.main; particleMain.simulationSpeed = unit.Entity["ActionSpeed"]; } } } if (context["Duration"]) { context["Duration"] -= Time.deltaTime; if (context["Duration"] <= 0) { EndEffect(); } return; } if (context["End"]) { EndEffect(); return; } if (context["Loop"]) return; // 자연종료 안한다 foreach (var animator in anims) { if (animator.GetCurrentAnimatorStateInfo(0).loop || animator.GetCurrentAnimatorStateInfo(0).normalizedTime < 1.0f) { return; } } foreach (var particle in particles) { if (particle.isPlaying) { return; } } EndEffect(); } public static void NoUsePool() { Destroy(unusedRoot.gameObject); } }