2025-07-08 10:46:31 +00:00
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 ( ) ;
}
}
}