using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering.Universal; using UnityEngine.UI; namespace Superlazy.UI { [RequireComponent(typeof(RawImage))] public class SLUIUnit : SLUIComponent { public bool useRawUnit = false; public string unitBind = "UnitBind"; public string renderCamPreset = "Default"; public string bindingAction; public string bindingEffects; private SLUIUnitObject unitObject; private string currentUnitBind; private RawImage rawImage; protected override void Validate() { rawImage = GetComponent(); rawImage.color = Color.clear; } protected override void Init() { } public void Update() { if (unitObject == null) return; unitObject.Update(); } protected override void Enable() { SLGame.AddNotify(bindParent.BindPath.CombinePath(unitBind), OnChange); if (string.IsNullOrEmpty(bindingAction) == false) { SLGame.AddNotify(bindParent.BindPath.CombinePath(bindingAction), OnChange); } if (string.IsNullOrEmpty(bindingEffects) == false) { SLGame.AddNotify(bindParent.BindPath.CombinePath(bindingEffects), OnChange); } } protected override void Disable() { RemoveUnit(); SLGame.RemoveNotify(bindParent.BindPath.CombinePath(unitBind), OnChange); if (string.IsNullOrEmpty(bindingAction) == false) { SLGame.RemoveNotify(bindParent.BindPath.CombinePath(bindingAction), OnChange); } if (string.IsNullOrEmpty(bindingEffects) == false) { SLGame.RemoveNotify(bindParent.BindPath.CombinePath(bindingEffects), OnChange); } } private void OnChange() { if (bindParent.Active == false) return; var sessionRoot = SLGame.SessionGet(bindParent.BindPath); if (sessionRoot == false) return; // TEMP: 세션루트가 삭제되었지만, 삭제되되기전 코루틴 처리가 있을 수 있음 if (currentUnitBind != SLGame.SessionGet(bindParent.BindPath.CombinePath(unitBind))) { RemoveUnit(); if (useRawUnit) { AddUnit(unitBind); } else if (SLGame.SessionGet(bindParent.BindPath.CombinePath(unitBind)) != false) { AddUnit(SLGame.SessionGet(bindParent.BindPath.CombinePath(unitBind))); } } if (unitObject != null && string.IsNullOrEmpty(bindingAction) == false && SLGame.SessionGet(bindParent.BindPath.CombinePath(bindingAction))) { var action = SLGame.SessionGet(bindParent.BindPath.CombinePath(bindingAction)); unitObject.SetAction(action); SLGame.SessionGet(bindParent.BindPath).Set(bindingAction, false); } if (unitObject != null && string.IsNullOrEmpty(bindingEffects) == false && SLGame.SessionGet(bindParent.BindPath.CombinePath(bindingEffects))) { var effects = SLGame.SessionGet(bindParent.BindPath.CombinePath(bindingEffects)); unitObject.MakeEffects(effects); SLGame.SessionGet(bindParent.BindPath).Set(bindingEffects, false); } } public void AddUnit(string bind) { if (SLSystem.Data["Units"].HasChild(bind) == false) return; currentUnitBind = bind; unitObject = SLUIUnitObject.MakeSLUIUnitObject(renderCamPreset, currentUnitBind); rawImage.texture = unitObject.texture; rawImage.color = Color.white; } public void RemoveUnit() { if (unitObject == null) return; rawImage.texture = null; rawImage.color = Color.clear; SLUIUnitObject.RemoveSLUIUnitObject(unitObject); unitObject = null; currentUnitBind = null; } } public class SLUIUnitObject : IZoneView { private static int unitHandle = 0; public Transform root; public Transform unitRoot; public Camera cam; public Texture texture; public int handle; public bool useLightMap; public float outline; private UnitView unit; private readonly string baseAction; private readonly string settingID; public Vector2 WorldOffset => Vector2.zero; public float WorldScale => 1; public bool UseSceneLight => useLightMap; public float Outline => outline; public int Layer => LayerMask.NameToLayer("SLUIUnit"); public Transform Root => unitRoot; public SLEntity Entity => SLGame.Session["UIUnit"][handle]; public float WorldHeight => 0; public SLEntity EntityView => SLGame.Session["UIUnit"][handle]; private static Dictionary> unused; private readonly List effects = new List(); public static SLUIUnitObject MakeSLUIUnitObject(string renderCamPreset, string currentUnitBind) { SLUIUnitObject inst; if (unused == null || unused.ContainsKey(renderCamPreset) == false || unused[renderCamPreset].Count == 0) { inst = new SLUIUnitObject(renderCamPreset, unitHandle); unitHandle += 1; } else { inst = unused[renderCamPreset].Dequeue(); } inst.Init(currentUnitBind); return inst; } public static void RemoveSLUIUnitObject(SLUIUnitObject unitObject) { unused ??= new Dictionary>(); if (unused.ContainsKey(unitObject.settingID) == false) { unused.Add(unitObject.settingID, new Queue()); } unitObject.Destroy(); unused[unitObject.settingID].Enqueue(unitObject); } public SLUIUnitObject(string renderCamPreset, int unitHandle) { settingID = renderCamPreset; var setting = SLSystem.Data["SLUIUnitCameraSettings"][settingID]; handle = unitHandle; root = new GameObject($"SLUI{unitHandle}").transform; root.SetParent(SLUIRoot.t, false); root.localPosition = 10 * unitHandle * Vector3.right; unitRoot = new GameObject("UnitRoot").transform; unitRoot.SetParent(root, false); unitRoot.localPosition = Vector3.zero; var resolution = 2048; if (setting["Resolution"]) { resolution = setting["Resolution"]; } var renderTexture = new RenderTexture(resolution, resolution, 16, RenderTextureFormat.ARGB32); renderTexture.Create(); texture = renderTexture; var go = new GameObject("UnitCamera"); go.transform.SetParent(root, false); cam = go.AddComponent(); cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = Vector4.zero; cam.useOcclusionCulling = true; var cameraData = go.AddComponent(); cameraData.antialiasing = AntialiasingMode.SubpixelMorphologicalAntiAliasing; cameraData.antialiasingQuality = AntialiasingQuality.High; cameraData.volumeLayerMask = LayerMask.GetMask("SLUIUnit"); cam.targetTexture = renderTexture; cam.cullingMask = LayerMask.GetMask("SLUIUnit"); baseAction = setting["BaseAction"]; cam.orthographic = setting["Projection"] == "Orthographic"; if (cam.orthographic) { cam.orthographicSize = setting["Size"]; } else { cam.fieldOfView = setting["fieldOfView"]; } cameraData.renderPostProcessing = setting["PostProcessing"]; useLightMap = setting["UseLightMap"]; outline = setting["Outline"]; } private void Init(string currentUnitBind) { root.gameObject.SetActive(true); var setting = SLSystem.Data["SLUIUnitCameraSettings"][settingID]; SLGame.Session["UIUnit"][handle] = SLSystem.Data["Units"][currentUnitBind].Override(); var unitEntity = SLGame.Session["UIUnit"][handle]; var action = baseAction; if (setting["TriggerAction"]) { action = setting["TriggerAction"]; } unitEntity["CurrentAction"] = unitEntity.GetAction(action); unitEntity["Handle"] = "Unit"; if (setting["AttachEffects"]) { unitEntity["AttachEffects"] = setting["AttachEffects"]; } unit = UnitView.MakeUnit(this, SLGame.Session["UIUnit"][handle]); unitRoot.localRotation = Quaternion.Euler(setting["UnitAngle"].ToVector3()); var uiUnitOffset = setting["UIUnitCustom"] ? new Vector3(0, -unit.Entity["UIUnitHeight"], 0) : Vector3.zero; if (setting["UIUnitCustom"]) { if (unit.Entity["UIUnitScale"]) { unit.transform.localScale = unit.Entity["UIUnitScale"].ToVector3(); } if (unit.Entity["UIUnitRotation"]) { unit.Entity["Forward"] = (Quaternion.Euler(unit.Entity["UIUnitRotation"].ToVector3()) * Vector3.forward).ToEntity(); } } cam.transform.localPosition = setting["CameraOffset"].ToVector3(); cam.transform.LookAt(unit.transform.position + setting["UnitOffset"].ToVector3() + uiUnitOffset, Vector3.up); foreach (var effect in effects) { EffectView.RemoveEffect(effect); } effects.Clear(); } private void Destroy() { UnitView.RemoveUnit(unit); unit = null; SLGame.Session["UIUnit"][handle] = false; root.gameObject.SetActive(false); } public void Update() { if (unit.Entity["CurrentAction"]["Frame"]) { var currentAction = unit.Entity["CurrentAction"]; int oldFrame = currentAction["OldFrame"]; currentAction["CurrentFrame"] += Time.deltaTime * 60; if (unit.Entity["CurrentAction"].HasChild("Messages")) { var frameDiff = currentAction["CurrentFrame"] - oldFrame; for (var i = 1; i <= frameDiff; i++) { var frame = i + oldFrame; if (currentAction.HasChild("Loop") && currentAction.HasChild("Frame") && frame > currentAction["Frame"]) { int actionFrame = currentAction["Frame"]; currentAction["MessageIndex"] = 1; currentAction["OldFrame"] -= actionFrame; currentAction["CurrentFrame"] -= actionFrame; frame -= actionFrame; oldFrame -= actionFrame; } while (currentAction["Messages"].HasChild(currentAction["MessageIndex"]) && currentAction["Messages"][currentAction["MessageIndex"]]["Frame"] == frame) { var messageContext = currentAction["Messages"][currentAction["MessageIndex"]]; unit.SendMessage(messageContext["Message"], messageContext, SendMessageOptions.DontRequireReceiver); currentAction["MessageIndex"] += 1; } } } currentAction["OldFrame"] = currentAction["CurrentFrame"]; if (currentAction.HasChild("Loop") == false && unit.Entity["CurrentAction"]["CurrentFrame"] >= unit.Entity["CurrentAction"]["Frame"]) { SetAction(baseAction); } } } public UnitView GetUnit(int handle) { return unit; } public void SetAction(string action) { unit.Entity["CurrentAction"] = false; string actionID; if (unit.Entity["Actions"][action]) { unit.Entity["CurrentAction"] = SLSystem.Data["Actions"][unit.Entity["Actions"][action]].Override(); actionID = unit.Entity["Actions"][action]; } else if (unit.Entity["CommonAction"] && SLSystem.Data["CommonActions"][action]) { unit.Entity["CurrentAction"] = SLSystem.Data["Actions"][SLSystem.Data["CommonActions"][action]].Override(); actionID = SLSystem.Data["CommonActions"][action]; } else { return; } unit.Entity["CurrentAction"]["MessageIndex"] = 1; unit.Entity["CurrentAction"]["CurrentFrame"] = 0; unit.Entity["CurrentAction"]["OldFrame"] = 0; unit.Entity["CurrentAction"]["ActionID"] = actionID; } public void MakeEffects(SLEntity effects) { foreach (var effect in this.effects) { EffectView.RemoveEffect(effect); } this.effects.Clear(); if (effects["Clear"]) return; foreach (var effect in effects) { this.effects.Add(effect["EffectID"]); unit.SendMessage("Effect", effect, SendMessageOptions.DontRequireReceiver); } } } }