
게임
개발기간2026. 05. 10. ㅡ 2026. 05. 14.
역할1인 개발
사용 기술
UnityC#2D

2D 핵앤슬래시 게임입니다. 5라운드를 생존하며 모든 적을 물리치면 승리합니다.
PlayerSkillController에서 현재 Animator state와 normalized time을 확인해 콤보 연결 가능 여부를 판단합니다.
데미지 판정은 AnimationHit 이벤트에서 발생시켜, 애니메이션 이벤트로 처리합니다.
공격별 입력 조건, 체인 조건, 애니메이션 state, 쿨다운, 이펙트 등은 SkillSO ScriptableObject에서 관리합니다.
private bool TrySelectSkill(int requestedSkillIndex, float now)
{
SkillSO requestedSkill = skills[requestedSkillIndex];
if (now < skillReadyTimes[requestedSkillIndex])
return false;
if (TryGetActiveSkillIndex(out int activeSkillIndex))
{
if (!IsInChainWindow(activeSkillIndex))
return false;
return requestedSkill.CanChainFrom(skills[activeSkillIndex], player);
}
return requestedSkill.CanStartFromIdle(player);
}
public void AnimationHit()
{
if (currentSkillIndex < 0 || !entry.IsAlive)
return;
skills[currentSkillIndex].PlayHit(player, effects);
}
공격 이펙트는 PlayerEffectSpec에 프리팹, 앵커, 방향 적용 여부, 사운드, 카메라 쉐이크 값을 담아 재생합니다.EffectManager는 이펙트를 생성하고, 프리팹에 Hitbox가 있으면 팀, 데미지, 방향만 설정합니다.
실제 피격 처리는 Entry.TakeHit(CombatHit)로 들어가게 해서 플레이어 공격, 적 공격, 패링 반격이 같은 데미지 처리 흐름을 사용합니다.
public void PlayOneShot(
PlayerEffectSpec effect,
Transform spawnPoint,
SpriteRenderer sortingSource,
int facingDirection,
EntryTeam ownerTeam,
int damage)
{
GameObject effectInstance = SpawnEffect(effect, sortingSource);
int visualDirection = effect.UseFacingDirection ? facingDirection : 1;
ApplySpriteSpawnTransform(effectInstance, spawnPoint, visualDirection);
if (effectInstance.TryGetComponent<Hitbox>(out var hitbox))
hitbox.Configure(ownerTeam, damage, facingDirection);
RunOneShotEffect(effectInstance);
}
패링은 EntryHitInterceptor에서 CombatHit을 가로채는 방식으로 처리합니다.
근접 공격과 투사체가 같은 Entry.TakeHit 경로로 들어오기 때문에, 패링 쪽에서 입력 버퍼와 회복 시간만 확인합니다.
성공 시에는 들어온 hit point를 기준으로 반격 이펙트를 재생합니다.
public override bool TryInterceptHit(CombatHit hit)
{
if (hit.SourceTeam != EntryTeam.Enemy || !entry.IsAlive)
return false;
if (Time.time < nextParryTime || Time.time > parryInputExpiresAt)
return false;
int sourceDirection = hit.Direction >= 0 ? -1 : 1;
effects.PlayAt(parryEffect, hit.Point, sourceDirection, EntryTeam.Player, damage);
parryInputExpiresAt = 0f;
nextParryTime = Time.time + parryRecoverySeconds;
Parried?.Invoke();
return true;
}