1188 lines
38 KiB (Stored with Git LFS)
C#
1188 lines
38 KiB (Stored with Git LFS)
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Superlazy;
|
|
using UnityEngine;
|
|
|
|
public abstract class UnitViewComponent : MonoBehaviour
|
|
{
|
|
private UnitView unitView;
|
|
|
|
protected SLEntity Entity => unitView.Entity;
|
|
protected IZoneView Zone => unitView.Zone;
|
|
protected Vector3 Position => unitView.Position;
|
|
protected Quaternion Rotation => unitView.Rotation;
|
|
protected Vector3 Scale => unitView.Scale;
|
|
protected Vector3 EffectScale => unitView.EffectScale;
|
|
|
|
protected void HitEffect(SLEntity context, SLEntity playEffect)
|
|
{
|
|
unitView.HitEffect(context, playEffect);
|
|
}
|
|
|
|
protected void BuffEffect(SLEntity context, SLEntity playEffect)
|
|
{
|
|
unitView.BuffEffect(context, playEffect);
|
|
}
|
|
|
|
public void Init(UnitView unitView)
|
|
{
|
|
this.unitView = unitView;
|
|
}
|
|
|
|
public abstract void Init();
|
|
|
|
public abstract void Bind();
|
|
|
|
public abstract void Unbind();
|
|
}
|
|
|
|
public class UnitView : MonoBehaviour, IUnitAnimatorBinder, IUnitSpriteBinder
|
|
{
|
|
private static Queue<UnitView> unused;
|
|
private static Transform unusedRoot;
|
|
|
|
private static List<System.Type> unitViewComponents;
|
|
|
|
private static int useSceneLightId;
|
|
private static int outlineId;
|
|
private static int enemyId;
|
|
private static int[] shaderKeywords;
|
|
|
|
private class ShaderInfo
|
|
{
|
|
public int shader;
|
|
public float onSpeed = -1;
|
|
public float offSpeed = -1;
|
|
public float max = -1;
|
|
public float activeValue = -1;
|
|
public string valueContext = null;
|
|
}
|
|
|
|
private static ShaderInfo hitGlowShaderInfo;
|
|
private static Dictionary<string, ShaderInfo> shaderInfos;
|
|
|
|
private class ShaderCheck
|
|
{
|
|
public ShaderInfo shaderInfo;
|
|
public string checkKey;
|
|
public string checkValue;
|
|
}
|
|
|
|
private static List<List<ShaderCheck>> shaderChecks;
|
|
|
|
public static void InitGlobal()
|
|
{
|
|
unused = new Queue<UnitView>();
|
|
unusedRoot = new GameObject("UnusedUnitView").transform;
|
|
unusedRoot.gameObject.SetActive(false);
|
|
|
|
unitViewComponents = new List<System.Type>();
|
|
unitViewComponents.CreateTypeList<UnitViewComponent>();
|
|
|
|
useSceneLightId = Shader.PropertyToID("UseSceneLight");
|
|
outlineId = Shader.PropertyToID("Outline");
|
|
enemyId = Shader.PropertyToID("Enemy");
|
|
|
|
var propertyIDs = new Dictionary<string, int>();
|
|
foreach (var shader in SLSystem.Data["Shaders"])
|
|
{
|
|
propertyIDs[shader["Keyword"]] = Shader.PropertyToID(shader["Keyword"]);
|
|
}
|
|
|
|
shaderKeywords = propertyIDs.Values.ToArray();
|
|
|
|
shaderInfos = new Dictionary<string, ShaderInfo>();
|
|
foreach (var shader in SLSystem.Data["Shaders"])
|
|
{
|
|
var shaderInfo = new ShaderInfo();
|
|
shaderInfo.shader = Shader.PropertyToID(shader["Keyword"]);
|
|
|
|
if (shader["Keyword"] == "HitGlow")
|
|
{
|
|
hitGlowShaderInfo = shaderInfo;
|
|
}
|
|
|
|
if (shader["OnSpeed"])
|
|
{
|
|
shaderInfo.onSpeed = shader["OnSpeed"];
|
|
}
|
|
if (shader["OffSpeed"])
|
|
{
|
|
shaderInfo.offSpeed = shader["OffSpeed"];
|
|
}
|
|
if (shader["Max"])
|
|
{
|
|
shaderInfo.max = shader["Max"];
|
|
}
|
|
if (shader["ActiveValue"])
|
|
{
|
|
shaderInfo.activeValue = shader["ActiveValue"];
|
|
}
|
|
if (shader["ValueContext"])
|
|
{
|
|
shaderInfo.valueContext = shader["ValueContext"];
|
|
}
|
|
shaderInfos.Add(shader.ID, shaderInfo);
|
|
}
|
|
|
|
shaderChecks = new List<List<ShaderCheck>>();
|
|
foreach (var ss in SLSystem.Data["Shaders"].Where(e => e["CheckKey"]).GroupBy(e => e["Keyword"]))
|
|
{
|
|
var group = new List<ShaderCheck>();
|
|
foreach (var s in ss)
|
|
{
|
|
var check = new ShaderCheck();
|
|
check.shaderInfo = shaderInfos[s.ID];
|
|
check.checkKey = s["CheckKey"];
|
|
if (s["CheckValue"])
|
|
{
|
|
check.checkValue = s["CheckValue"];
|
|
}
|
|
|
|
group.Add(check);
|
|
}
|
|
|
|
group.Reverse();
|
|
shaderChecks.Add(group);
|
|
}
|
|
}
|
|
|
|
public static UnitView MakeUnit(IZoneView zoneView, SLEntity unit)
|
|
{
|
|
UnitView newUnit;
|
|
if (unused != null && unused.Count != 0)
|
|
{
|
|
newUnit = unused.Dequeue();
|
|
}
|
|
else
|
|
{
|
|
var newObject = new GameObject();
|
|
newObject.SetActive(false);
|
|
newUnit = newObject.AddComponent<UnitView>();
|
|
|
|
newUnit.Init();
|
|
}
|
|
|
|
newUnit.transform.SetParent(zoneView.Root, false);
|
|
newUnit.gameObject.SetActive(true);
|
|
newUnit.BindUnit(zoneView, unit);
|
|
|
|
// TODO: 확장 컴포넌트 사용 가능한 구조로 개선
|
|
if (newUnit.Entity["UseSpawnEffect"])
|
|
{
|
|
newUnit.gameObject.AddComponent<SpawnEffect>();
|
|
}
|
|
return newUnit;
|
|
}
|
|
|
|
public static void RemoveUnit(UnitView unitView)
|
|
{
|
|
unitView.UnbindUnit();
|
|
unused.Enqueue(unitView);
|
|
unitView.gameObject.SetActive(false);
|
|
unitView.transform.SetParent(unusedRoot);
|
|
}
|
|
|
|
private GameObject offsetRoot;
|
|
private SLResourceObject baseRoot;
|
|
|
|
private SLResourceObject shadow;
|
|
|
|
public SLEntity Entity { get; private set; }
|
|
public IZoneView Zone { get; private set; }
|
|
public Vector3 Position => transform.position; // offset만 미적용
|
|
public Quaternion Rotation => transform.rotation;
|
|
public Vector3 Scale => offsetRoot.transform.localScale;
|
|
public Vector3 EffectScale => Entity.HasChild("EffectScale") ? Vector3.one * Entity["EffectScale"] : Vector3.one;
|
|
|
|
// animation
|
|
public bool UseAnimation => Entity.HasChild("CurrentAction");
|
|
|
|
public string Animation => Entity["CurrentAction"]["Animation"];
|
|
public bool RequireReset => Entity["CurrentAction"]["RequireReset"];
|
|
public int CurrentFrame => Entity["CurrentAction"]["CurrentFrame"];
|
|
public bool UseActionAnimationBegin => Entity["CurrentAction"].HasChild("AnimationBegin");
|
|
public float ActionAnimationBegin => Entity["CurrentAction"]["AnimationBegin"];
|
|
public bool UseCrossFade => SLGame.Session.HasChild("Camp") == false && Entity.HasChild("ActionStop") == false && Entity["CurrentAction"].HasChild("NoBeginBlending") == false;
|
|
public float CrossFade => Entity["CurrentAction"].HasChild("CrossFade") ? (float)Entity["CurrentAction"]["CrossFade"] : 0.1f;
|
|
public bool ActionStop => Entity["ActionStop"];
|
|
public bool Loop => Entity["CurrentAction"].HasChild("Loop");
|
|
|
|
// sprite
|
|
public string SpritePath => Entity["Sprite"];
|
|
|
|
private Dictionary<int, float> currentShaderValues;
|
|
private Dictionary<int, ShaderInfo> currentShaderInfos;
|
|
private Dictionary<int, bool> currentShaderOnOffs;
|
|
private Dictionary<string, EffectView> buffEffects;
|
|
private Dictionary<string, EffectView> actionEffects;
|
|
private Dictionary<string, EffectView> customEffects;
|
|
private Dictionary<string, EffectView> attachParts;
|
|
private Dictionary<string, string> buffs;
|
|
private List<UnitViewComponent> components;
|
|
|
|
public void Init()
|
|
{
|
|
offsetRoot = new GameObject("OffsetRoot");
|
|
offsetRoot.transform.SetParent(transform, false);
|
|
|
|
currentShaderValues = new Dictionary<int, float>();
|
|
currentShaderInfos = new Dictionary<int, ShaderInfo>();
|
|
currentShaderOnOffs = new Dictionary<int, bool>();
|
|
|
|
buffEffects = new Dictionary<string, EffectView>();
|
|
actionEffects = new Dictionary<string, EffectView>();
|
|
customEffects = new Dictionary<string, EffectView>();
|
|
attachParts = new Dictionary<string, EffectView>();
|
|
buffs = new Dictionary<string, string>();
|
|
updators = new List<IUnitViewUpdator>();
|
|
components = new List<UnitViewComponent>();
|
|
|
|
foreach (var unitViewComponent in unitViewComponents)
|
|
{
|
|
var component = gameObject.AddComponent(unitViewComponent) as UnitViewComponent;
|
|
components.Add(component);
|
|
component.Init(this);
|
|
}
|
|
}
|
|
|
|
public void OnDisable()
|
|
{
|
|
offsetRoot.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
|
}
|
|
|
|
private void BindUnit(IZoneView zoneView, SLEntity unit)
|
|
{
|
|
Zone = zoneView;
|
|
Entity = unit;
|
|
|
|
gameObject.name = Entity["UnitType"] + Entity["Handle"];
|
|
|
|
// 특수 처리들 추가
|
|
if (Entity["Components"]["Projectile"]) // 프로젝타일 전용 특수 처리 추가
|
|
{
|
|
foreach (var buff in Entity["Context"]["TargetBuffs"])
|
|
{
|
|
var effect = SLSystem.Data["Effects"][buff["BuffEffect"]];
|
|
if (effect["Projectile"] && customEffects.ContainsKey(buff["BuffEffect"]) == false) // 투사체용 이펙트
|
|
{
|
|
customEffects.Add(buff["BuffEffect"], EffectView.MakeUnitEffect(this, effect["Projectile"]["Effect"], effect["Projectile"]));
|
|
}
|
|
}
|
|
}
|
|
|
|
Update();
|
|
}
|
|
|
|
private void UnbindUnit()
|
|
{
|
|
if (Entity["EndEffect"])
|
|
{
|
|
var position = Entity["EndEffectPosition"] ? Entity["EndEffectPosition"].ToVector3() : Entity["Position"].ToVector3();
|
|
var effect = EffectView.MakeEffect(Zone, Entity["EndEffect"]["Effect"], Entity["EndEffect"], position, Quaternion.Euler(Entity["Forward"].ToVector3()), Vector3.one);
|
|
effect.gameObject.SetActive(true);
|
|
}
|
|
|
|
if (Entity["EndAreaEffect"])
|
|
{
|
|
var context = Entity["EndAreaEffect"];
|
|
var position = context["Position"] ? context["Position"].ToVector3() : Entity["Position"].ToVector3();
|
|
var area = SLSystem.Data["Ranges"][Entity["TargetContext"]["Area"]];
|
|
var minArea = SLEntity.Empty;
|
|
if (Entity["TargetContext"]["MinArea"])
|
|
{
|
|
minArea = SLSystem.Data["Ranges"][Entity["TargetContext"]["MinArea"]];
|
|
}
|
|
foreach (var range in area)
|
|
{
|
|
if (minArea && minArea.Any(ma => ma["X"] == range["X"] && ma["Y"] == range["Y"])) continue;
|
|
|
|
//var cellForward = Entity["Forward"].ForwardToCellForward();
|
|
//var ax = CellUtil.CellRotation(cellForward, range["X"], range["Y"], "X");
|
|
//var ay = CellUtil.CellRotation(cellForward, range["X"], range["Y"], "Y");
|
|
//var newPosition = position + new Vector3(ax, 0, ay);
|
|
//var effect = EffectView.MakeEffect(Zone, context["Effect"], context, newPosition, Quaternion.Euler(Entity["Forward"].ToVector3()), Vector3.one);
|
|
//effect.gameObject.SetActive(true);
|
|
}
|
|
}
|
|
|
|
Update();
|
|
|
|
foreach (var customEffect in customEffects.Values)
|
|
{
|
|
customEffect.EndEffect();
|
|
}
|
|
customEffects.Clear();
|
|
|
|
UnbindBaseRoot();
|
|
foreach (var component in components)
|
|
{
|
|
component.Unbind();
|
|
}
|
|
gameObject.SetActive(false);
|
|
}
|
|
|
|
private void UnbindBaseRoot()
|
|
{
|
|
if (baseRoot == null) return;
|
|
|
|
currentShaderValues.Clear();
|
|
currentShaderInfos.Clear();
|
|
currentShaderOnOffs.Clear();
|
|
|
|
//TODO: ObjectOffs도 별도 리스트로 관리하기(엔티티는 오염될가능성이 있어서)
|
|
foreach (var offs in Entity["ObjectOffs"])
|
|
{
|
|
var t = baseRoot.GetTransform(offs.ID);
|
|
t.gameObject.SetActive(true);
|
|
}
|
|
|
|
baseRoot.Destroy();
|
|
baseRoot = null;
|
|
|
|
if (shadow)
|
|
{
|
|
shadow.Destroy();
|
|
shadow = null;
|
|
}
|
|
|
|
foreach (var attachPart in attachParts)
|
|
{
|
|
attachPart.Value.EndEffect();
|
|
}
|
|
attachParts.Clear();
|
|
}
|
|
|
|
public void UpdateAppearance()
|
|
{
|
|
if (baseRoot && Entity["Base"] != baseRoot.name)
|
|
{
|
|
UnbindBaseRoot();
|
|
}
|
|
|
|
if (baseRoot == null && Entity["Base"])
|
|
{
|
|
baseRoot = SLResources.CreateInstance(Entity["Base"], offsetRoot.transform);
|
|
|
|
// BaseSwap이 일어날때만 업데이트 해주는게 맞을까?
|
|
foreach (var attachEffect in Entity["AttachEffects"])
|
|
{
|
|
attachParts.Add(attachEffect.ID, EffectView.MakeUnitEffect(this, attachEffect["Effect"], attachEffect));
|
|
}
|
|
|
|
var isEnemy = false;
|
|
if (Entity["UnitType"] == "Monster" && Entity.HasChild("FieldData") && Entity["FieldData"]["Character"] &&
|
|
SLSystem.Data["Heroes"][Entity["FieldData"]["Character"]] && SLSystem.Data["Heroes"][Entity["FieldData"]["Character"]]["Ready"] == false) // TEMP: 영웅몹 체크용 임시 처리, 추후 확장 및 데이터기반으로 조정
|
|
{
|
|
isEnemy = true;
|
|
}
|
|
|
|
float useSceneLight = Entity.HasChild("UseSceneLight") ? (float)Entity["UseSceneLight"] : Zone.UseSceneLight ? 1 : 0;
|
|
var outlineDepth = Zone.Outline;
|
|
|
|
foreach (var r in baseRoot.renderers)
|
|
{
|
|
r.gameObject.layer = Zone.Layer;
|
|
r.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off;
|
|
|
|
foreach (var mat in r.materials)
|
|
{
|
|
mat.SetFloat(useSceneLightId, useSceneLight);
|
|
mat.SetFloat(outlineId, outlineDepth);
|
|
mat.SetFloat(enemyId, isEnemy ? 1 : 0);
|
|
foreach (var shaderKeyword in shaderKeywords)
|
|
{
|
|
mat.SetFloat(shaderKeyword, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Entity.HasChild("ShadowRadius"))
|
|
{
|
|
if (shadow == null)
|
|
{
|
|
shadow = SLResources.CreateInstance("Shadow", transform);
|
|
}
|
|
// TODO: 성능상 이슈가 있다면 별도 객체로 분리
|
|
|
|
if (baseRoot && Entity.HasChild("ShadowBind"))
|
|
{
|
|
var shadowBind = baseRoot.GetTransform(Entity["ShadowBind"]);
|
|
|
|
shadow.transform.position = new Vector3(shadowBind.position.x, Zone.WorldHeight, shadowBind.position.z);
|
|
}
|
|
else
|
|
{
|
|
shadow.transform.position = new Vector3(transform.position.x, Zone.WorldHeight, transform.position.z);
|
|
}
|
|
|
|
var shadowRenderer = shadow.renderers.First();
|
|
|
|
if (transform.position.y - Zone.WorldHeight > 0) // 지상
|
|
{
|
|
var diff = transform.position.y - Zone.WorldHeight;
|
|
var ips = Entity["ShadowRadius"] <= 0.001 ? 0.001f : (float)Entity["ShadowRadius"];
|
|
shadow.transform.localScale = Entity["ShadowRadius"] * Vector3.one;
|
|
shadowRenderer.material.color = new Color(1, 1, 1, 1 - Mathf.Clamp01(diff / ips * 0.5f)); // 그림자 진하기가 점점 감소
|
|
}
|
|
else // 지하 // 사망 등
|
|
{
|
|
// 크기가 점점감소, HitHeight 만큼 파이면 없어짐
|
|
var diff = Zone.WorldHeight - transform.position.y;
|
|
var ips = Entity["ShadowRadius"] <= 0.001 ? 0.001f : (float)Entity["ShadowRadius"];
|
|
shadow.transform.localScale = Entity["ShadowRadius"] * Mathf.Clamp01(1 - 2 * (diff / ips)) * Vector3.one;
|
|
shadowRenderer.material.color = new Color(1, 1, 1, 1);
|
|
}
|
|
|
|
if (Entity.HasChild("ShadowAlpha"))
|
|
{
|
|
shadowRenderer.material.color = new Color(1, 1, 1, shadowRenderer.material.color.a * Entity["ShadowAlpha"]);
|
|
}
|
|
|
|
shadowRenderer.gameObject.layer = Zone.Layer;
|
|
}
|
|
else
|
|
{
|
|
if (shadow)
|
|
{
|
|
shadow.Destroy();
|
|
shadow = null;
|
|
}
|
|
}
|
|
|
|
if (baseRoot && Entity.HasChild("Base"))
|
|
{
|
|
UpdateShader();
|
|
UpdateAction();
|
|
UpdateUpdators();
|
|
}
|
|
}
|
|
|
|
private void UpdateAction()
|
|
{
|
|
var action = Entity["CurrentAction"];
|
|
|
|
if (action["ActionID"] != Entity["CurrentActionID"])
|
|
{
|
|
foreach (var eff in actionEffects)
|
|
{
|
|
eff.Value.EndEffect();
|
|
}
|
|
actionEffects.Clear();
|
|
|
|
foreach (var actionEffect in action["Effects"])
|
|
{
|
|
actionEffects.Add(actionEffect.ID, EffectView.MakeUnitEffect(this, actionEffect["Effect"], actionEffect));
|
|
}
|
|
|
|
foreach (var offs in Entity["ObjectOffs"])
|
|
{
|
|
var t = baseRoot.GetTransform(offs.ID);
|
|
t.gameObject.SetActive(true);
|
|
}
|
|
|
|
Entity["ObjectOffs"] = false;
|
|
|
|
Entity["CurrentActionID"] = action["ActionID"];
|
|
|
|
if (action["ObjectOnOff"])
|
|
{
|
|
foreach (var obj in action["ObjectOnOff"])
|
|
{
|
|
if (Entity[obj.ID] && Entity[obj.ID].IsValue == false) // TODO: IsValue 안쓰고 바인딩 여부 체크 필요
|
|
{
|
|
foreach (var bind in Entity[obj.ID])
|
|
{
|
|
var t = baseRoot.GetTransform(bind);
|
|
if (t != baseRoot.transform)
|
|
{
|
|
Entity["ObjectOffs"][bind] = obj != "On";
|
|
t.gameObject.SetActive(obj == "On");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var t = baseRoot.GetTransform(obj.ID);
|
|
if (t != baseRoot.transform)
|
|
{
|
|
Entity["ObjectOffs"][obj.ID] = obj != "On";
|
|
t.gameObject.SetActive(obj == "On");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SetShader(ShaderInfo info, SLEntity context, bool on = true)
|
|
{
|
|
currentShaderOnOffs[info.shader] = info.activeValue == -1 && on; // 액티브 밸류 있으면 힛처럼 바로 꺼지는 사례일듯
|
|
currentShaderInfos[info.shader] = info;
|
|
var value = 0f;
|
|
currentShaderValues.TryGetValue(info.shader, out value);
|
|
|
|
var newValue = on ? 1.0f : 0.0f;
|
|
if (info.valueContext != null && context.HasChild(info.valueContext))
|
|
{
|
|
newValue = context[info.valueContext];
|
|
}
|
|
else if (info.activeValue != -1)
|
|
{
|
|
newValue = info.activeValue;
|
|
}
|
|
|
|
if (newValue != value)
|
|
{
|
|
ApplyShader(info.shader, newValue);
|
|
}
|
|
currentShaderValues[info.shader] = newValue;
|
|
}
|
|
|
|
private void UpdateShader()
|
|
{
|
|
foreach (var ss in shaderChecks)
|
|
{
|
|
for (var i = 0; i < ss.Count; ++i)
|
|
{
|
|
var shaderCheck = ss[i];
|
|
if (shaderCheck.checkValue == null)
|
|
{
|
|
if (Entity[shaderCheck.checkKey])
|
|
{
|
|
SetShader(shaderCheck.shaderInfo, Entity, true);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Entity[shaderCheck.checkKey] == shaderCheck.checkValue)
|
|
{
|
|
SetShader(shaderCheck.shaderInfo, Entity, true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == ss.Count - 1)
|
|
{
|
|
SetShader(shaderCheck.shaderInfo, Entity, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var shaderKeyword in shaderKeywords)
|
|
{
|
|
if (currentShaderValues.ContainsKey(shaderKeyword) == false) continue;
|
|
|
|
var info = currentShaderInfos[shaderKeyword];
|
|
var value = currentShaderValues[shaderKeyword];
|
|
if (currentShaderOnOffs[shaderKeyword])
|
|
{
|
|
if (info.onSpeed > 0)
|
|
{
|
|
var newValue = value + Time.deltaTime * info.onSpeed;
|
|
if (newValue > info.max)
|
|
{
|
|
newValue = info.max;
|
|
}
|
|
if (value != newValue)
|
|
{
|
|
currentShaderValues[shaderKeyword] = newValue;
|
|
ApplyShader(shaderKeyword, newValue);
|
|
}
|
|
}
|
|
else if (info.max > 0 && info.max != value)
|
|
{
|
|
currentShaderValues[shaderKeyword] = info.max;
|
|
ApplyShader(shaderKeyword, info.max);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (info.offSpeed > 0)
|
|
{
|
|
var newValue = value - Time.deltaTime * info.offSpeed;
|
|
if (newValue < 0)
|
|
{
|
|
newValue = 0;
|
|
}
|
|
if (value != newValue)
|
|
{
|
|
currentShaderValues[shaderKeyword] = newValue;
|
|
ApplyShader(shaderKeyword, newValue);
|
|
}
|
|
}
|
|
else if (0 != value)
|
|
{
|
|
currentShaderValues[shaderKeyword] = 0;
|
|
ApplyShader(shaderKeyword, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ApplyShader(int key, float value)
|
|
{
|
|
foreach (var renderer in baseRoot.renderers)
|
|
{
|
|
foreach (var mat in renderer.materials)
|
|
{
|
|
mat.SetFloat(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateBuffs()
|
|
{
|
|
var removeBuffs = new List<string>();
|
|
foreach (var buff in buffs)
|
|
{
|
|
if (Entity["Buffs"].HasChild(buff.Key) == false)
|
|
{
|
|
if (buffEffects.ContainsKey(buff.Key))
|
|
{
|
|
buffEffects[buff.Key].EndEffect();
|
|
}
|
|
removeBuffs.Add(buff.Key);
|
|
}
|
|
else
|
|
{
|
|
var buffEntity = Entity["Buffs"][buff.Key];
|
|
var countType = SLSystem.Data["Buffs"][buffEntity["BuffKey"]]["CountType"];
|
|
if (countType)
|
|
{
|
|
buffEntity["ViewCount"] = buffEntity.Get(countType);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var removeBuff in removeBuffs)
|
|
{
|
|
EndBuff(removeBuff);
|
|
buffs.Remove(removeBuff);
|
|
if (buffEffects.ContainsKey(removeBuff))
|
|
{
|
|
buffEffects.Remove(removeBuff);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
if (Entity.HasChild("KeywordBase") && Entity["KeywordBase"] && baseRoot)
|
|
{
|
|
baseRoot.SetKeyword(Entity["CurrentAction"]["Tag"], true);
|
|
}
|
|
|
|
if (Entity.HasChild("DragPosition"))
|
|
{
|
|
var position = Entity["DragPosition"].ToVector3();
|
|
transform.localPosition = position;
|
|
}
|
|
else
|
|
{
|
|
transform.localPosition = Entity["Position"].ToVector3();
|
|
|
|
if (Entity.HasChild("Offset"))
|
|
{
|
|
var offset = Entity["Offset"].ToVector3();
|
|
offsetRoot.transform.localPosition = offset;
|
|
}
|
|
else
|
|
{
|
|
offsetRoot.transform.localPosition = Vector3.zero;
|
|
}
|
|
}
|
|
|
|
// TODO: 뷰에서는 엔티티 할당 안하도록 하고 공통 컴포넌트를 만드는게 좋을듯
|
|
if (Entity.HasChild("Position"))
|
|
{
|
|
Entity["HitPosition"] = Entity["Position"];
|
|
Entity["HitPosition"]["Y"] += Entity["HitHeight"];
|
|
Entity["TopPosition"] = Entity["Position"];
|
|
Entity["TopPosition"]["Y"] += Entity["TopHeight"];
|
|
Entity["PositionDepth"] = Entity["Position"]["X"] - Entity["Position"]["Z"];
|
|
}
|
|
|
|
if (Entity.HasChild("DragForward"))
|
|
{
|
|
var forward = Entity["DragForward"].ToVector3();
|
|
transform.localRotation = Quaternion.LookRotation(forward, Vector3.up);
|
|
}
|
|
else if (Entity.HasChild("Forward"))
|
|
{
|
|
var forward = Entity["Forward"].ToVector3();
|
|
transform.localRotation = Quaternion.LookRotation(forward, Vector3.up);
|
|
}
|
|
else
|
|
{
|
|
transform.localRotation = Quaternion.identity;
|
|
}
|
|
|
|
UpdateScale();
|
|
UpdateAppearance();
|
|
UpdateBuffs();
|
|
UpdateChat();
|
|
}
|
|
|
|
private void UpdateScale()
|
|
{
|
|
if (Entity.HasChild("DragPosition"))
|
|
{
|
|
if (Entity.HasChild("Scale"))
|
|
{
|
|
offsetRoot.transform.localScale = Entity["Scale"] * SLSystem.Data["Default"]["DragScale"] * Vector3.one;
|
|
}
|
|
else
|
|
{
|
|
offsetRoot.transform.localScale = Vector3.one * SLSystem.Data["Default"]["DragScale"];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Entity.HasChild("Scale"))
|
|
{
|
|
offsetRoot.transform.localScale = Vector3.one * Entity["Scale"];
|
|
}
|
|
else
|
|
{
|
|
offsetRoot.transform.localScale = Vector3.one;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Effect(SLEntity context)
|
|
{
|
|
EffectView.MakeUnitEffect(this, context["Effect"], context);
|
|
}
|
|
|
|
private void Sound(SLEntity context)
|
|
{
|
|
if (context["Volume"] == false)
|
|
{
|
|
context["Volume"] = 1;
|
|
}
|
|
|
|
if (context["Tag"] == false)
|
|
{
|
|
context["Tag"] = "Common";
|
|
}
|
|
|
|
SLSound.PlaySound(context["Sound"], context["Tag"], context["Loop"], context["Unique"], context["Reset"], context["Volume"]);
|
|
}
|
|
|
|
private void ObjectOnOff(SLEntity context)
|
|
{
|
|
foreach (var obj in context["Binds"])
|
|
{
|
|
if (Entity[obj.ID] && Entity[obj.ID].IsValue == false) // TODO: IsValue 안쓰고 바인딩 여부 체크 필요
|
|
{
|
|
foreach (var bind in Entity[obj.ID])
|
|
{
|
|
var t = baseRoot.GetTransform(bind);
|
|
t.gameObject.SetActive(obj == "On");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var t = baseRoot.GetTransform(obj.ID);
|
|
t.gameObject.SetActive(obj == "On");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Hit(SLEntity context)
|
|
{
|
|
var playEffect = SLEntity.Empty;
|
|
if (context["HitResult"])
|
|
{
|
|
playEffect = SLSystem.Data["Effects"][context["HitResult"]];
|
|
}
|
|
else if (context["SenderContext"])
|
|
{
|
|
playEffect = SLSystem.Data["Effects"][context["SenderContext"]];
|
|
}
|
|
else
|
|
{
|
|
if (context["Heal"]) // 대미지와 힐과 실드를 같이 주면 이펙트를 다 써야하나..
|
|
{
|
|
playEffect = SLSystem.Data["Effects"]["Heal"];
|
|
}
|
|
else if (context["Shield"])
|
|
{
|
|
playEffect = SLSystem.Data["Effects"]["Shield"];
|
|
}
|
|
// else // Buff는 AddBuff에서 독립으로 처리
|
|
}
|
|
|
|
if (playEffect)
|
|
{
|
|
HitEffect(context, playEffect);
|
|
}
|
|
}
|
|
|
|
public void HitEffect(SLEntity context, SLEntity playEffect)
|
|
{
|
|
var position = Entity["Position"].ToVector3();
|
|
var forward = (context["Sender"]["Position"].ToVector3() - position).normalized; // TODO: 센더와의 거리
|
|
forward.y = 0;
|
|
|
|
var rotation = Quaternion.identity;
|
|
if (forward != Vector3.zero) // 불기둥같이 같은 자리에서 맞는 경우가 있어 context안에 Forward가 있을 때만 처리
|
|
{
|
|
rotation = Quaternion.LookRotation(forward, Vector3.up);
|
|
}
|
|
|
|
if (Entity["To"] && Entity["ActionStop"] == false) // 이동 예측 ActionStop 있을 때는 안하도록
|
|
{
|
|
var dir = (Entity["To"].ToVector3() - Entity["Position"].ToVector3()).normalized;
|
|
|
|
position += 5.0f * Entity["MoveSpeed"] * Time.deltaTime * dir;
|
|
}
|
|
|
|
if (playEffect["HitSound"])
|
|
{
|
|
SLSound.PlaySound(playEffect["HitSound"], "Effect", false, false, false);
|
|
}
|
|
|
|
if (playEffect["Hit"])
|
|
{
|
|
var hitEffect = context["HitEffect"];
|
|
if (hitEffect["Effect"]) // 스킬전용 히트이펙트
|
|
{
|
|
playEffect = playEffect.Override();
|
|
playEffect["Hit"]["Effect"] = hitEffect["Effect"];
|
|
}
|
|
|
|
if (playEffect["Hit"]["Bind"])
|
|
{
|
|
EffectView.MakeUnitEffect(this, playEffect["Hit"]["Effect"], playEffect["Hit"]);
|
|
}
|
|
else
|
|
{
|
|
EffectView.MakeEffect(Zone, playEffect["Hit"]["Effect"], playEffect["Hit"], position, rotation, Vector3.one);
|
|
}
|
|
}
|
|
|
|
// 힛글로우
|
|
if (context["HitGlow"])
|
|
{
|
|
if (Entity["Dead"])
|
|
{
|
|
context["HitGlow"] = SLSystem.Data["Default"]["DeadHitGlow"];
|
|
}
|
|
SetShader(hitGlowShaderInfo, context);
|
|
}
|
|
else if (playEffect["HitGlow"])
|
|
{
|
|
// 힛글로우
|
|
context["HitGlow"] = playEffect["HitGlow"];
|
|
SetShader(hitGlowShaderInfo, context);
|
|
}
|
|
|
|
if (context["Shake"])
|
|
{
|
|
context["Shake"]["Direction"] = forward.normalized.ToEntity();
|
|
ZoneController.PlayCameraOperation(context);
|
|
}
|
|
else if (playEffect["HitCamera"])
|
|
{
|
|
if (playEffect["HitCamera"]["Shake"])
|
|
{
|
|
playEffect["HitCamera"]["Shake"]["Direction"] = forward.normalized.ToEntity();
|
|
}
|
|
ZoneController.PlayCameraOperation(playEffect["HitCamera"]);
|
|
}
|
|
|
|
if (playEffect["HitText"])
|
|
{
|
|
var eventContext = SLEntity.Empty;
|
|
|
|
if (context["Damage"])
|
|
{
|
|
eventContext["Value"] = context["Damage"];
|
|
}
|
|
else if (context["Heal"]) // 대미지와 힐과 실드를 같이 주면 이펙트를 다 써야하나..
|
|
{
|
|
eventContext["Value"] = context["Heal"];
|
|
}
|
|
else if (context["Shield"])
|
|
{
|
|
eventContext["Value"] = context["Shield"];
|
|
}
|
|
// else // Buff는 AddBuff에서 독립으로 처리
|
|
|
|
eventContext["Text"] = playEffect["HitText"];
|
|
eventContext["Position"] = Entity["HitPosition"];
|
|
|
|
eventContext["PositionDepth"] = eventContext["Position"]["X"] - eventContext["Position"]["Z"];
|
|
eventContext["Type"] = playEffect["HitTextType"];
|
|
|
|
MakeDamageText(eventContext);
|
|
}
|
|
}
|
|
|
|
private void MakeDamageText(SLEntity context)
|
|
{
|
|
if (Entity["LastDamageTextTime"])
|
|
{
|
|
if (Time.unscaledTime > Entity["LastDamageTextTime"] + SLSystem.Data["Default"]["DamageTextStackDuration"])
|
|
{
|
|
Entity["DamageTextCount"] = 0;
|
|
}
|
|
}
|
|
|
|
Entity["LastDamageTextTime"] = Time.unscaledTime;
|
|
context["Position"]["Y"] += Entity["DamageTextCount"] * SLSystem.Data["Default"]["DamageTextStackHeight"];
|
|
Entity["DamageTextCount"] += 1;
|
|
|
|
SLGame.Event($"BattleText.{Entity["Handle"]}", context);
|
|
}
|
|
|
|
private void AddBuff(SLEntity context)
|
|
{
|
|
if (context["Immune"])
|
|
{
|
|
BuffEffect(context, SLSystem.Data["Effects"]["Immune"]);
|
|
return;
|
|
}
|
|
|
|
var playEffect = SLSystem.Data["Effects"][context["BuffKey"]];
|
|
if (playEffect == false)
|
|
{
|
|
SLLog.Error($"[{context["BuffKey"]}]버프 데이터 없음");
|
|
return;
|
|
}
|
|
|
|
if (playEffect["Icon"] == false)
|
|
{
|
|
context["NoIcon"] = true;
|
|
}
|
|
|
|
BuffEffect(context, playEffect);
|
|
|
|
if (buffs.ContainsKey(context["BuffKey"]) == false)
|
|
{
|
|
buffs.Add(context["BuffKey"], context["BuffName"]);
|
|
}
|
|
// shader 처리
|
|
}
|
|
|
|
public void BuffEffect(SLEntity context, SLEntity playEffect)
|
|
{
|
|
var position = Entity["Position"].ToVector3();
|
|
var forward = Entity["Forward"].ToVector3().normalized;
|
|
var rotation = Quaternion.LookRotation(forward, Vector3.up);
|
|
|
|
if (playEffect["StartSound"])
|
|
{
|
|
SLSound.PlaySound(playEffect["StartSound"], "Effect", false, false, true);
|
|
}
|
|
|
|
if (playEffect["Start"])
|
|
{
|
|
if (playEffect["Start"]["Bind"])
|
|
{
|
|
EffectView.MakeUnitEffect(this, playEffect["Start"]["Effect"], playEffect["Start"]);
|
|
}
|
|
else
|
|
{
|
|
EffectView.MakeEffect(Zone, playEffect["Start"]["Effect"], playEffect["Start"], position, rotation, Vector3.one);
|
|
}
|
|
}
|
|
|
|
if (context["HitGlow"])
|
|
{
|
|
if (Entity["Dead"])
|
|
{
|
|
context["HitGlow"] = SLSystem.Data["Default"]["DeadHitGlow"];
|
|
}
|
|
SetShader(hitGlowShaderInfo, context);
|
|
}
|
|
else if (playEffect["HitGlow"])
|
|
{
|
|
// 힛글로우
|
|
context["HitGlow"] = playEffect["HitGlow"];
|
|
SetShader(hitGlowShaderInfo, context);
|
|
}
|
|
|
|
if (playEffect["Play"])
|
|
{
|
|
if (buffEffects.ContainsKey(context["BuffKey"]) == false) // 갱신
|
|
{
|
|
buffEffects.Add(context["BuffKey"], EffectView.MakeUnitEffect(this, playEffect["Play"]["Effect"], playEffect["Play"]));
|
|
}
|
|
}
|
|
|
|
if (context["Shake"])
|
|
{
|
|
context["Shake"]["Direction"] = forward.normalized.ToEntity();
|
|
ZoneController.PlayCameraOperation(context);
|
|
}
|
|
else if (playEffect["Camera"])
|
|
{
|
|
if (playEffect["Camera"]["Shake"])
|
|
{
|
|
playEffect["Camera"]["Shake"]["Direction"] = forward.normalized.ToEntity();
|
|
}
|
|
ZoneController.PlayCameraOperation(playEffect["Camera"]);
|
|
}
|
|
|
|
if (playEffect["Text"])
|
|
{
|
|
var eventContext = SLEntity.Empty;
|
|
eventContext["Text"] = playEffect["Text"];
|
|
|
|
eventContext["Position"] = Entity["TopPosition"];
|
|
eventContext["Type"] = playEffect["TextType"];
|
|
|
|
var textForward = 0.25f * Random.Range(0.5f, 1.5f) * forward;
|
|
eventContext["Position"]["X"] -= textForward.x;
|
|
eventContext["Position"]["Z"] -= textForward.z;
|
|
eventContext["PositionDepth"] = eventContext["Position"]["X"] - eventContext["Position"]["Z"];
|
|
|
|
SLGame.Event($"BattleText.{Entity["Handle"]}", eventContext);
|
|
}
|
|
}
|
|
|
|
private void EndBuff(string buffName)
|
|
{
|
|
var playEffect = SLSystem.Data["Effects"][buffName];
|
|
|
|
var position = Entity["Position"].ToVector3();
|
|
var forward = Entity["Forward"].ToVector3().normalized;
|
|
var rotation = Quaternion.LookRotation(forward, Vector3.up);
|
|
|
|
if (playEffect["End"])
|
|
{
|
|
if (playEffect["End"]["Bind"])
|
|
{
|
|
EffectView.MakeUnitEffect(this, playEffect["End"]["Effect"], playEffect["End"]);
|
|
}
|
|
else
|
|
{
|
|
EffectView.MakeEffect(Zone, playEffect["End"]["Effect"], playEffect["End"], position, rotation, Vector3.one);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ControlUnit(SLEntity context)
|
|
{
|
|
// Shader
|
|
if (context["Shaders"])
|
|
{
|
|
foreach (var shader in context["Shaders"])
|
|
{
|
|
SetShader(shaderInfos[shader.ID], shader, shader["On"]);
|
|
}
|
|
}
|
|
|
|
// Effect
|
|
if (context["Effects"])
|
|
{
|
|
foreach (var effect in context["Effects"])
|
|
{
|
|
Effect(effect);
|
|
}
|
|
}
|
|
|
|
// Chat
|
|
if (context["Text"] || context["Emotion"])
|
|
{
|
|
Entity["Chat"] = false;
|
|
if (context["Emotion"])
|
|
{
|
|
var emotion = SLSystem.Data["Emotions"][context["Emotion"]].Clone();
|
|
if (emotion["Animation"])
|
|
{
|
|
Entity["Chat"]["Animation"] = context["Emotion"];
|
|
}
|
|
else
|
|
{
|
|
Entity["Chat"]["Icon"] = emotion["EmotionIcon"];
|
|
}
|
|
|
|
if (emotion["Sound"])
|
|
{
|
|
var sound = emotion["Sound"];
|
|
if (sound["Volume"] == false)
|
|
{
|
|
sound["Volume"] = 1;
|
|
}
|
|
|
|
if (sound["Tag"] == false)
|
|
{
|
|
sound["Tag"] = "Common";
|
|
}
|
|
|
|
SLSound.PlaySound(sound["Sound"], sound["Tag"], sound["Loop"], sound["Unique"], sound["Reset"], sound["Volume"]);
|
|
}
|
|
|
|
Entity["Chat"]["Type"] = emotion["Type"];
|
|
Entity["Chat"]["Frame"] = emotion["Frame"];
|
|
Entity["Chat"]["Scale"] = emotion["Scale"];
|
|
}
|
|
else
|
|
{
|
|
Entity["Chat"]["Type"] = "Round";
|
|
Entity["Chat"]["Frame"] = 180;
|
|
Entity["Chat"]["Scale"] = 1;
|
|
}
|
|
|
|
if (context["Text"]) Entity["Chat"]["Text"] = context["Text"];
|
|
if (context["Type"]) Entity["Chat"]["Type"] = context["Type"];
|
|
if (context["Frame"]) Entity["Chat"]["Frame"] = context["Frame"];
|
|
if (context["Scale"]) Entity["Chat"]["Scale"] = context["Scale"];
|
|
}
|
|
}
|
|
|
|
private void UpdateChat()
|
|
{
|
|
if (Entity["Chat"])
|
|
{
|
|
if (Entity["Chat"]["Loop"]) return;
|
|
|
|
if (Entity["Chat"]["Frame"])
|
|
{
|
|
if (Entity["Chat"]["Frame"] > 0)
|
|
{
|
|
Entity["Chat"]["Frame"] -= Time.deltaTime * 60;
|
|
}
|
|
|
|
if (Entity["Chat"]["Frame"] <= 0)
|
|
{
|
|
Entity["Chat"] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Chapter2 Special
|
|
private void MakeShip(SLEntity context)
|
|
{
|
|
gameObject.AddComponent<SpawnEffect>();
|
|
}
|
|
|
|
// Udpators
|
|
|
|
private List<IUnitViewUpdator> updators;
|
|
|
|
public void AddUpdate(IUnitViewUpdator updator)
|
|
{
|
|
updators.Add(updator);
|
|
}
|
|
|
|
public void RemoveUpdate(IUnitViewUpdator updator)
|
|
{
|
|
updators.Remove(updator);
|
|
}
|
|
|
|
private void UpdateUpdators()
|
|
{
|
|
foreach (var u in updators)
|
|
{
|
|
u.OnUpdate();
|
|
}
|
|
}
|
|
} |