You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1085 lines
31 KiB

using DG.Tweening;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using IVDataFormat;
using Spine;
using Spine.Unity;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine;
using AnimationState = Spine.AnimationState;
using Vector2 = UnityEngine.Vector2;
using Vector3 = UnityEngine.Vector3;
// 전투용 캐릭터/몬스터에서 상속받는 부모 클래스.
public class CreatureBase : Entity, IBattleEntity
{
protected static readonly Vector3 V3_MonSizeNormal = new Vector3(0.75f, 0.75f, 0.75f);
protected static readonly Vector3 V3_MonSizeElite = new Vector3(0.8f, 0.8f, 0.8f);
protected static readonly Vector3 V3_MonSizeBoss = new Vector3(1f, 1f, 1f);
protected TweenerCore<Vector3, Vector3, VectorOptions> twcSummon = null;
protected TweenerCore<Vector3, Vector3, VectorOptions> twcUnsummon = null;
protected BattleMgr battleMgr => BattleMgr.Instance;
protected int iIndex = -1;
protected bool bInitNeed = true;
protected bool bLoading = false;
protected bool bSummonNeed = false;
bool _bFriendly = true;
protected bool bFriendly
{
get => _bFriendly;
set
{
if(_bFriendly != value)
{
_bFriendly = value;
ClearTags();
if(_bFriendly)
{
AddTags(EntityTag.Character, EntityTag.Player);
TargetFilter = Bitset32.GetMask(EntityTag.Enemy);
}
else
{
AddTags(EntityTag.Character, EntityTag.Enemy);
TargetFilter = Bitset32.GetMask(EntityTag.Player);
}
}
}
}
public Bitset32.Mask TargetFilter;
protected bool bCoward = false;
protected int isDamaged = 0;
#region Battle State
public enum eState
{
idle,
move,
attack,
damage,
die,
skill,
ceremony,
warp_in,
warp_out,
attack2,
attack3,
rush,
}
public enum eCreatureClass
{
charEnemy = -2,
character = -1,
normal = 0,
elite = 1,
boss = 2,
bigboss = 3,
summon = 10
}
/// <summary>
/// 몬스터 위치별 번호
/// </summary>
public enum eDgCreatureClass
{
DgN1,
DgN2,
DgN3,
DgN4,
DgN5,
DgE1,
DgBs,
DgAwBs
}
public bool IsInit { get; private set; }
protected ParticleSystem ptcHit;
[SerializeField] protected SkeletonAnimation skAnim;
Dictionary<EventData, Action> animEventHandlers = new();
AnimationState _anState;
protected AnimationState anState
{
get => _anState;
set
{
if(_anState != null)
_anState.Event -= HandleAnimEvent;
KeyValuePair<string, Action>[] handlerPairs =
{
new KeyValuePair<string, Action>(nameof(AttackDamage), AttackDamage),
new KeyValuePair<string, Action>(nameof(CheckAttackState), CheckAttackState),
new KeyValuePair<string, Action>(nameof(CheckDamageState), CheckDamageState),
new KeyValuePair<string, Action>(nameof(CheckDieState), CheckDieState),
new KeyValuePair<string, Action>(nameof(SkillEffect), SkillEffect),
new KeyValuePair<string, Action>(nameof(CheckSkillState), CheckSkillState),
new KeyValuePair<string, Action>(nameof(CheckCeremonyState), CheckCeremonyState),
new KeyValuePair<string, Action>(nameof(CheckWarp_outState), CheckWarp_outState),
};
_anState = value;
animEventHandlers.Clear();
for(int i = 0; i < handlerPairs.Length; ++i)
{
var eventData = skAnim.Skeleton.Data.FindEvent(handlerPairs[i].Key);
if (eventData != null)
animEventHandlers.Add(eventData, handlerPairs[i].Value);
}
_anState.Event += HandleAnimEvent;
}
}
protected HpBar hpBar;
[SerializeField]
protected eState animState = eState.idle;
[SerializeField]
protected eCreatureClass creatureClass = eCreatureClass.normal;
protected IBattleEntity target;
protected Transform targetTransform => target != null ? (target as Entity).transform : null;
protected Vector2 targetPos => target != null ? (target as Entity).transform.position : Vector2.zero;
protected float sqrtDistToTarget => target != null ? ((Vector2)transform.position - targetPos).sqrMagnitude : 9999f;
protected float useSkillRange = 0f;
protected bool bCanMove = true;
protected bool bCanAttack = true;
protected bool bDead = false;
public bool IsDead => bDead;
protected bool bSummon = false;
protected bool bAttackable = false;
public bool IsLookRight => LookDirection.x > 0;
[SerializeField]
protected float fAttackTick = 0f;
protected float fAttackTime = 1.2f;
protected float fSearchTick = 0f;
protected const float F_SearchTime = 0.1f;
protected BoxCollider areaSize;
protected Vector3 minArea;
protected Vector3 maxArea;
[Range(0.1f, 1.0f)] protected float atkSpeed = 1.0f;
[Range(1.0f, 4.0f)] protected float motionSpeed = 1.0f;
protected Stack<int> stackSkillAvail;
#endregion Battle State
#region Status
protected float attackRange = 7f;
protected float recogRange = 10f; // 플레이어 인식범위
protected float enemySlowSpeed = 0.2f; // 적군 농땡이 속도
protected eMonsterType eType;
protected bool alertPlayer = false;
protected BigInteger maxAtk = 0L;
protected float fCrtDam = dConst.RateMaxFloat;
protected int iCrtRate = 0;
protected float fMov = dConst.RateMaxFloat;
protected float fSpd = dConst.RateMaxFloat;
protected BigInteger currentAtk = 1;
protected BigInteger maxHp = 10;
protected BigInteger currentHp = 10;
protected float fCrtDamCalc = dConst.RateMaxFloat;
protected int iCrtRateCalc = 0;
protected float moveSpeed = 3f;
protected float fSpdCalc = dConst.RateMaxFloat;
#endregion Status
#region Debuff
public class cDamage
{
public int sec;
public float time;
public BigInteger atk;
public float crtdam;
public int crtrate;
public int idnum;
}
// 둔화.
protected float fDecMov = 0f;
protected float fDecMovTime = 0f;
// 출혈.
protected int iDamageSecCnt = 0;
protected cDamage[] damageSecs = new cDamage[2] { new cDamage(), new cDamage() };
// 기절.
protected bool bStun = false;
protected float fStunTime = 0f;
// 기절/넉백 쿨타임.
protected float fStunPushCool = 0f;
#endregion Debuff
protected int backAllowed = 1;
[SerializeField] float backtime = 0f;
protected bool isdash = false;
protected virtual void Start()
{
Init();
if (bInitNeed && !bLoading)
{
bInitNeed = false;
anState = skAnim.AnimationState;
}
_bFriendly = true;
ClearTags();
AddTags(EntityTag.Character, EntityTag.Player);
TargetFilter = Bitset32.GetMask((int)EntityTag.Enemy);
}
protected void Update()
{
SetAnimTimeScale();
if (battleMgr.BattlePause || bSummon || bDead) return;
float ftimedec = Time.deltaTime;
#region Debuff
// 둔화.
if (fDecMovTime > 0f)
{
fDecMovTime -= ftimedec;
if (fDecMovTime <= 0f)
{
fDecMov = 0f;
moveSpeed = fMov * 12f / dConst.RateMaxFloat;
}
}
// 출혈.
if (iDamageSecCnt > 0)
{
for (int i = 0; i < damageSecs.Length; i++)
{
if (damageSecs[i].time <= 0f)
continue;
damageSecs[i].time -= ftimedec;
if (damageSecs[i].time <= 0f)
{
GetDamage(damageSecs[i].atk, damageSecs[i].crtdam, damageSecs[i].crtrate, damageSecs[i].idnum);
iDamageSecCnt--;
continue;
}
int isec = (int)damageSecs[i].time;
if (isec < damageSecs[i].sec)
{
damageSecs[i].sec = isec;
GetDamage(damageSecs[i].atk, damageSecs[i].crtdam, damageSecs[i].crtrate, damageSecs[i].idnum);
}
}
}
// 기절.
if (bStun)
{
fStunTime -= ftimedec;
if (fStunTime <= 0f)
{
bStun = false;
}
}
else if (fStunPushCool > 0f)
{
fStunPushCool -= ftimedec;
}
#endregion Debuff
if (bStun) return;
// 유저가 터치하면 이동
if (bFriendly && VirtualPad.isTouchScreen())
{
if (animState != eState.move)
{
ChangeStateForce(eState.move);
}
}
// 아니라면 AI로 동작
else
{
switch (animState)
{
case eState.idle:
// 공격 대기 시간 체크.
if (fAttackTick < fAttackTime)
{
fAttackTick += Time.deltaTime;
if (fAttackTick >= fAttackTime)
bAttackable = true;
}
// 목표 재설정 대기 시간 체크.
if (fSearchTick < F_SearchTime)
fSearchTick += Time.deltaTime;
else
SearchTarget();
// 현재 상태가 idle이고 공격 범위 안에 있으며 공격력이 0이 아니면 공격 체크.
if (animState == eState.idle && maxAtk > 0 && sqrtDistToTarget <= attackRange * attackRange)
{
CheckAttack();
}
break;
case eState.move:
// 공격 대기 시간 체크.
if (fAttackTick < fAttackTime)
{
fAttackTick += Time.deltaTime;
if (fAttackTick >= fAttackTime)
bAttackable = true;
}
// 목표 재설정 대기 시간 체크.
if (fSearchTick < F_SearchTime)
fSearchTick += Time.deltaTime;
else
SearchTarget();
// 현재 상태가 move이고 이동 이후 공격 범위 안에 있으며 공격력이 0이 아니면 공격 체크.
if (animState == eState.move)
{
MoveToTarget();
if(maxAtk > 0 && sqrtDistToTarget <= attackRange * attackRange)
{
CheckAttack();
}
}
break;
// 스킬 중에 구르는 모션이 필요한게 있음. 아직 파악 불가.
case eState.rush:
backAllowed--;
// 공격 대기 시간 체크.
if (fAttackTick < fAttackTime)
{
fAttackTick += Time.deltaTime;
if (fAttackTick >= fAttackTime)
bAttackable = true;
}
//1f 가 시간
backtime += Time.deltaTime;
if (backtime > 0.4f)
{
CheckAttack();
backtime = 0;
isdash = false;
}
break;
}
}
}
#region Base & Init
// 컴포넌트 세팅.
public void SetHpbar(HpBar hpbar)
{
hpBar = hpbar;
}
// 피아 지정.
public void SetFriendly(bool bfriendly)
{
bFriendly = bfriendly;
}
// 크리쳐 종류, 인덱스 지정.
public void SetClassIndex(eCreatureClass cls, int index)
{
creatureClass = cls;
iIndex = index;
switch (creatureClass)
{
case eCreatureClass.normal:
skAnim.transform.localScale = V3_MonSizeNormal;
break;
case eCreatureClass.elite:
skAnim.transform.localScale = V3_MonSizeElite;
break;
case eCreatureClass.boss:
skAnim.transform.localScale = V3_MonSizeBoss;
break;
}
}
// 초기화.
public void Init()
{
if(IsInit) return;
IsInit = true;
ptcHit = transform.Find("hit").GetComponent<ParticleSystem>();
InitTween();
stackSkillAvail = new Stack<int>();
}
// 트윈 초기화.
protected virtual void InitTween()
{
twcSummon = transform.DOScale(Global.V3_1, 0.8f).ChangeStartValue(Global.V3_001).SetEase(Ease.OutQuad).OnComplete(SummonEnd).SetAutoKill(false).Pause();
twcUnsummon = transform.DOScale(Global.V3_1, 2.0f).SetEase(Ease.InQuad).OnComplete(UnsummonEnd).SetAutoKill(false).Pause();
}
#endregion Base & Init
#region Stat
// 스탯 세팅.
public void SetStatus(BigInteger atk, BigInteger hp, float fcrtdam, int icrtrate, float fmov, float fspd, float frange, bool bhpcurkeep = false, bool isrunaway = false, int igroup = 1)
{
maxAtk = atk;
currentAtk = maxAtk;
maxHp = hp;
if (!bhpcurkeep)
currentHp = maxHp;
fCrtDam = fcrtdam;
iCrtRate = icrtrate;
fMov = fmov;
fSpd = fspd;
eType = (eMonsterType)igroup;
fCrtDamCalc = fCrtDam;
iCrtRateCalc = iCrtRate;
fSpdCalc = fSpd;
moveSpeed = fMov * 12f / dConst.RateMaxFloat;
if (fDecMov > 0f)
moveSpeed *= (1f - fDecMov);
attackRange = frange / dConst.RateMaxFloat;
float foffset = dConst.RateMaxFloat / fSpdCalc;
atkSpeed = Mathf.Lerp(0.1f, 0.75f, foffset); // 공속이 높을 수록 0.1로
motionSpeed = Mathf.Lerp(4.0f, 1.5f, foffset); // 공속이 높을 수록 4로
fAttackTime = 0.5f * atkSpeed;
bCoward = isrunaway;
areaSize = BattleMgr.Instance.GetBgSize();
minArea = areaSize.bounds.min;
maxArea = areaSize.bounds.max;
}
public eCreatureClass GetCreatureClass()
{
return creatureClass;
}
public BigInteger GetAtk()
{
return currentAtk;
}
public float GetCrtDam()
{
return fCrtDamCalc;
}
public int GetCrtRate()
{
return iCrtRateCalc;
}
public eMonsterType GetMonType()
{
return eType;
}
#endregion Stat
#region Transform Control
// 타겟쪽으로 이동(1프레임).
protected virtual void MoveToTarget() { }
// 타겟쪽에게 대시 이동(1프레임).
public void FrontToTarget(float fdt)
{
isdash = true;
ChangeState(eState.rush);
ChangeLookDirection(targetTransform);
Vector3 moveVec = (targetPos - (Vector2)transform.position).normalized * fdt;//5 가 거리
transform.DOMove(transform.position + moveVec, 0.8f).SetEase(Ease.OutQuart).OnComplete(InitPos);//1f 가 시간
}
public void InitPos()
{
MoveToTarget();
}
public virtual void MoveToRush(int dir, float fdt, bool bright)
{
Vector3 dirVec;
switch (dir)
{
case 0:
dirVec = Vector3.up;
break;
case 1:
dirVec = Vector3.Lerp(Vector3.up, Vector3.right, 0.33f).normalized;
break;
case 2:
dirVec = Vector3.Lerp(Vector3.up, Vector3.right, 0.66f).normalized;
break;
case 3:
dirVec = Vector3.right;
break;
case 4:
dirVec = Vector3.Lerp(Vector3.right, Vector3.down, 0.33f).normalized;
break;
case 5:
dirVec = Vector3.Lerp(Vector3.right, Vector3.down, 0.66f).normalized;
break;
case 6:
dirVec = Vector3.down;
break;
case 7:
dirVec = Vector3.Lerp(Vector3.down, Vector3.left, 0.33f).normalized;
break;
case 8:
dirVec = Vector3.Lerp(Vector3.down, Vector3.left, 0.66f).normalized;
break;
case 9:
dirVec = Vector3.left;
break;
case 10:
dirVec = Vector3.Lerp(Vector3.left, Vector3.up, 0.33f).normalized;
break;
case 11:
dirVec = Vector3.Lerp(Vector3.left, Vector3.up, 0.66f).normalized;
break;
default:
dirVec = (targetPos - (Vector2)transform.position).normalized;
break;
}
dirVec = bright ? dirVec : new Vector3(dirVec.x * -1, dirVec.y, dirVec.z);
Vector3 moveVec = dirVec * fdt;//5 가 거리
transform.DOMove(transform.position + moveVec, 0.2f).SetEase(Ease.OutQuart);//1f 가 시간
}
#endregion Transform Control
#region Object Control
public bool IsBattleAvail() => gameObject.activeSelf && !bDead && !bSummon;
// 애니메이션 로드 대기중.
public void SetLoading()
{
bLoading = true;
}
public virtual void Summon(Vector3 v3pos, bool bfrontright)
{
transform.position = v3pos;
ChangeLookDirection(new Vector2(bfrontright ? 1f : -1f, 0f));
skAnim.skeleton.A = 1.0f;
Summon();
}
public virtual void Summon()
{
// 애니메이션 로드 대기중이면 소환 안함. 로드 완료되는 타이밍에 소환.
if (bLoading)
{
bSummonNeed = true;
return;
}
bDead = false;
bSummon = true;
bAttackable = false;
fAttackTick = 0f;
target = null;
gameObject.SetActive(true);
twcSummon.Restart();
// 애니메이션이 다른 애니메이션으로 변경되면 새로 로드해야 함.
if (bInitNeed)
{
bInitNeed = false;
anState = skAnim.AnimationState;
}
ChangeStateForce(eState.idle);
}
public void SummonEnd()
{
bSummon = false;
ChangeStateForce(eState.idle);
fSearchTick = F_SearchTime;
}
public virtual void Unsummon()
{
bSummon = true;
bAttackable = false;
twcUnsummon.Restart();
}
public void UnsummonEnd()
{
ChangeState(eState.idle);
gameObject.SetActive(false);
}
#endregion Object Control
#region Skill & Attack
// 사용 가능한 스킬 추가.
public void AddSkillAvail(int idx)
{
stackSkillAvail.Push(idx);
}
// 사용 가능한 스킬 모두 제거.
public void ClearSkillAvail()
{
stackSkillAvail.Clear();
}
public virtual void CheckSkill()
{
if (bStun)
return;
// 스킬이 사용 가능할 경우.
if (stackSkillAvail.Count > 0 && target != null)
{
//스킬 사용부분
int skillid = stackSkillAvail.Peek();
int skillused = battleMgr.UseSkill(bFriendly, skillid, this, target, fCrtDamCalc, iCrtRateCalc, currentAtk);
if (skillused != 0) // 스킬 사용함 or 사용할 수 없는 스킬.
stackSkillAvail.Pop();
}
}
public virtual void CheckAttack()
{
if (bStun) return;
// 공격 가능(공격 속도 참조한 공격 시간 경과됨).
if (bAttackable)
{
ChangeLookDirection(targetTransform);
ChangeState(eState.attack);
}
else
{
ChangeState(eState.idle);
}
}
#endregion Skill & Attack
#region Damage & Heal & Die
// 피격.
public void GetDamage(BigInteger atk, float crtdamamage, int crtrate, int dnum = -1)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
bool isCritical = CheckCritical(crtrate);
if(isCritical)
atk = GetCriticalDamage(atk, crtdamamage, crtrate);
if (currentHp >= maxHp)
currentHp = maxHp;
if (dnum != -1)
BattleMgr.SSetTotalDamage(atk, dnum);
currentHp -= atk;
if (currentHp <= 0)
{
hpBar.ShowHpBar(0f);
battleMgr.AddPvpDamage(bFriendly, atk, 0f);
ChangeState(eState.die);
}
else
{
float fractionalPart = (float)(currentHp * 10000 / maxHp) / 10000f;
hpBar.ShowHpBar(fractionalPart);
battleMgr.AddPvpDamage(bFriendly, atk, fractionalPart);
if (eType == eMonsterType.SmallObject)
{
ChangeState(eState.damage);
}
else
{
StartCoroutine(DamagedRed());
}
}
Damage dmg = new Damage
{
value = atk,
isCritical = isCritical,
};
OnDamaged(dmg);
}
protected virtual void OnDamaged(Damage damage) { }
public static bool CheckCritical(int crtRate)
{
if (crtRate <= 0) return false;
int actualRate = crtRate % dConst.RateMax;
return UnityEngine.Random.Range(0, dConst.RateMax) < actualRate;
}
public static BigInteger GetCriticalDamage(BigInteger atk, float crtDamage, int crtRate)
{
// 치명타 확률 오버한만큼 데미지 추가 ???
int crtLevel = crtRate / dConst.RateMax;
crtLevel = crtLevel < 1 ? 1 : crtLevel;
//@ fcrtdam 현재는 치명타 데미지 증가 스탯을 사용하지 않음. 추후 기획으로 추가되면 적용.
return atk * (1 + crtLevel);
}
IEnumerator DamagedRed()
{
Skeleton sk = skAnim.skeleton;
sk.SetColor(Color.red);
yield return new WaitForSeconds(0.2f);
sk.SetColor(Color.white);
}
// 회복.
public void GetHeal(float frate)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
BigInteger birate = new BigInteger(frate);
BigInteger biadd = maxHp * birate / dConst.RateMaxBi;
currentHp += biadd;
if (currentHp > maxHp)
currentHp = maxHp;
}
// 사망시 처리.
protected virtual void OnDie()
{
bDead = true;
bAttackable = false;
SetIsDamaged(0);
twcUnsummon.Restart();
}
public int GetIsDamaged()
{
return isDamaged;
}
public void SetIsDamaged(int idamcnt)
{
isDamaged = idamcnt;
}
#endregion Damage & Heal & Die
#region Debuff
// 초기화.
public void ResetDebuff()
{
fDecMov = 0f;
fDecMovTime = 0f;
damageSecs[0].time = 0;
damageSecs[1].time = 0;
bStun = false;
fStunTime = 0f;
fStunPushCool = 0f;
}
// 둔화(이동속도 감소).
public void SetDecMove(float ftime, float fvalue)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
if (fvalue < fDecMov) return;
fDecMov = fvalue;
fDecMovTime = ftime;
moveSpeed = fMov * 12f / dConst.RateMaxFloat;
if (fDecMov > 0f)
moveSpeed *= (1f - fDecMov);
}
// 출혈(초당 데미지).
public virtual void SetDamageSec(float ftime, BigInteger fatk, float fcrtdam, int icrtrate, int idnum = -1)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
if (ftime < 1f)
{
return;
}
int index = -1;
for (int i = 0; i < damageSecs.Length; i++)
{
if (damageSecs[i].time <= 0f)
{
index = i;
break;
}
}
if (index < 0)
return;
damageSecs[index].time = ftime - 0.05f;
damageSecs[index].sec = (int)ftime;
damageSecs[index].atk = fatk;
damageSecs[index].crtdam = fcrtdam;
damageSecs[index].crtrate = icrtrate;
damageSecs[index].idnum = idnum;
iDamageSecCnt++;
}
// 기절.
public void SetStun(float ftime)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
if (fStunPushCool > 0f) return;
if (!bStun)
{
bStun = true;
ChangeStateForce(eState.idle);
if (creatureClass == eCreatureClass.boss)
fStunPushCool = 10f;
}
fStunTime = ftime;
}
// 넉백.
public void PushFrame(float fpushforce)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead || fStunPushCool > 0f) return;
if (creatureClass == eCreatureClass.boss)
fStunPushCool = 10f;
}
// 넉백(강제).
protected void PushFrameForce(float fpushforce)
{
Vector3 v3moveto = transform.position;
fpushforce *= 0.5f;
if (IsLookRight)
{
v3moveto.x -= 10;
}
else
{
v3moveto.x += 10;
}
Vector3 v3framepos = Vector3.MoveTowards(transform.position, v3moveto, fpushforce);
Vector3 v3movedir = v3framepos - transform.position;
v3movedir.y = 0f;
v3movedir.z = 0f;
if (gameObject.activeSelf)
{
transform.DOLocalMoveX(transform.position.x + v3movedir.x, 0.5f).SetEase(Ease.OutExpo);
}
}
// 끌어당기기
public void SetPull(BigInteger bivalue, Transform trf)
{
float pullValue = (int)bivalue / dConst.RateMaxFloat;
Vector3 v3framepos = Vector3.Lerp(transform.position, trf.position, pullValue);
if (gameObject.activeSelf)
{
transform.DOLocalMove(v3framepos, 0.5f).SetEase(Ease.Linear);
}
}
#endregion Debuff
#region Target & Animation
// 타겟 위치 찾기.
protected virtual void SearchTarget() { }
// 애니메이션 이벤트. 스파인 애니메이션에서 호출됨.
void HandleAnimEvent(TrackEntry trackEntry, Spine.Event e)
{
if(animEventHandlers.TryGetValue(e.Data, out Action action))
action();
}
// 캐릭터 상태(애니메이션) 변경.
public void ChangeState(eState changestate)
{
if (changestate == animState && changestate != eState.attack && changestate != eState.skill) return;
animState = changestate;
switch (animState)
{
case eState.idle:
anState.SetAnimation(0, "idle", true);
break;
case eState.move:
anState.SetAnimation(0, "move", true);
break;
case eState.attack:
case eState.attack2:
case eState.attack3:
bAttackable = false;
fAttackTick = 0f;
anState.SetAnimation(0, animState.ToString(), false);
break;
case eState.die:
anState.SetAnimation(0, "die", false);
OnDie();
break;
case eState.skill:
bAttackable = false;
fAttackTick = 0f;
anState.SetAnimation(0, "attack", false);
break;
case eState.ceremony:
anState.SetAnimation(0, "ceremony", false);
break;
case eState.warp_in:
anState.SetAnimation(0, "warp_in", false);
break;
case eState.warp_out:
anState.SetAnimation(0, "warp_out", false);
break;
case eState.rush:
anState.SetAnimation(0, "rush", false);
break;
case eState.damage:
anState.SetAnimation(0, "damage", false);
break;
}
}
// 캐릭터 상태(애니메이션) 변경.
public void ChangeStateForce(eState changestate)
{
if (anState == null) return;
animState = changestate;
switch (animState)
{
case eState.idle:
anState.SetAnimation(0, "idle", true);
break;
case eState.move:
anState.SetAnimation(0, "move", true);
break;
case eState.attack:
bAttackable = false;
fAttackTick = 0f;
anState.SetAnimation(0, animState.ToString(), false);
break;
case eState.die:
anState.SetAnimation(0, "die", false);
OnDie();
break;
case eState.skill:
bAttackable = false;
fAttackTick = 0f;
anState.SetAnimation(0, "attack", false);
break;
case eState.ceremony:
anState.SetAnimation(0, "ceremony", false);
break;
case eState.warp_in:
anState.SetAnimation(0, "warp_in", false);
break;
case eState.warp_out:
anState.SetAnimation(0, "warp_out", false);
break;
case eState.rush:
anState.SetAnimation(0, "rush", false);
break;
case eState.damage:
anState.SetAnimation(0, "damage", false);
break;
}
}
//애니메이션 속도 초가화
public void SetAnimTimeScale()
{
if (animState == eState.attack || animState == eState.attack2)
skAnim.timeScale = 1.0f * motionSpeed;
else
skAnim.timeScale = 1.0f;
}
#endregion Target & Animation
#region Ani Event
// 공격 애니메이션 마지막 프레임.
public void CheckAttackState()
{
SearchTarget();
}
// 스킬 애니메이션 마지막 프레임.
public void CheckSkillState()
{
SearchTarget();
}
// 피해받음 애니메이션 마지막 프레임.
public void CheckDamageState()
{
ChangeState(eState.idle);
}
// 사망 애니메이션 마지막 프레임.
public void CheckDieState()
{
}
// 세레머니 애니메이션 마지막 프레임.
public void CheckCeremonyState()
{
ChangeState(eState.idle);
}
public void CheckWarp_outState()
{
ChangeState(eState.idle);
}
// 공격 애니메이션 중간 키(데미지 타이밍).
public virtual void AttackDamage() { }
// 스킬 애니메이션 중간 키.
public virtual void SkillEffect()
{
}
#endregion Ani Event
}