728x90
반응형
1) 경계 상태를 의미하는 SkeletonStateEngage
public class SkeletonStateEngage : EnemyState
{
// 경계 상태를 의미하는 SkeletonStateEngage
SkeletonController _skeletonController;
Transform _player;
float _moveDir = 1;
public SkeletonStateEngage(EnemyController enemyBaseController, EnemyStateMachine enemyStateMachine, string animatorBoolParamName, SkeletonController enemyController)
: base(enemyBaseController, enemyStateMachine, animatorBoolParamName)
{
this._skeletonController = enemyController;
}
// 처음 EngageState에 들어오면 Player를 탐색함, 마찬가지로 비효율적인 동작이므로 수정할 예정
public override void Enter()
{
base.Enter();
_player = GameObject.Find("Player").transform;
}
public override void Exit()
{
base.Exit();
}
// EngageState의 매 프레임에서는 다음과 같이 처리함
public override void Update()
{
base.Update();
// 만약 플레이어를 탐지했다면
if (_skeletonController.DoDetectPlayer())
{
// _stateTimer를 _engageTimer으로 설정
_stateTimer = _skeletonController._engageTime;
// 만약, 탐지거리가 _attackDistance보다 작다면
if (_skeletonController.DoDetectPlayer().distance < _skeletonController._attackDistance)
{
// 공격할 수 있는지를 판단하고 State를 AttackState로 변경
if (DoDefineCanAttack())
_enemyStateMachine.ChangeState(_skeletonController._attackState);
}
}
// 탐지하지 못했다면
else
{
// _stateTimer가 0보다 작거나, 플레이어와 Skeleton의 거리가 7보다 크다면 EngageState를 풀고 IdleState로 전환
if (_stateTimer < 0 || Vector2.Distance(_player.transform.position, _skeletonController.transform.position) > 7)
_enemyStateMachine.ChangeState(_skeletonController._idleState);
}
// 만약 플레이어가 Skeleton의 오른쪽에 위치해있다면, 오른쪽으로
if (_player.position.x > _skeletonController.transform.position.x)
_moveDir = 1;
// 만약 플레이어가 Skeleton의 왼쪽에 위치해있다면, 왼쪽으로
else if (_player.position.x < _skeletonController.transform.position.x)
_moveDir = -1;
// Skeleton의 속력을 설정
_skeletonController.SetVelocity(_skeletonController._moveSpeed * _moveDir, _rigidbody2D.velocity.y);
}
// 공격가능한지를 판단하는 함수
private bool DoDefineCanAttack()
{
// 현재 시간이 마지막으로 공격한 시간인 _lastTimeAttacked에 공격 딜레이인 _attackCooldown을 합한 값보다 크다면
if (Time.time >= _skeletonController._lastTimeAttacked + _skeletonController._attackCooldown)
{
// 마지막으로 공격한 시간을 현재 시간으로 설정하고 true를 리턴
_skeletonController._lastTimeAttacked = Time.time;
return true;
}
// 아니라면 false를 리턴
return false;
}
}
2) 공격 상태를 나타내는 SkeletonStateAttack
public class SkeletonStateAttack : EnemyState
{
SkeletonController _skeletonController;
public SkeletonStateAttack(EnemyController enemyBaseController, EnemyStateMachine enemyStateMachine, string animatorBoolParamName, SkeletonController enemyController)
: base(enemyBaseController, enemyStateMachine, animatorBoolParamName)
{
this._skeletonController = enemyController;
}
public override void Enter()
{
base.Enter();
}
// AttakState는 탈출하면서 마지막으로 공격한 시간인 _lastTimeAttacked를 지금 시간으로 설정함
public override void Exit()
{
base.Exit();
_skeletonController._lastTimeAttacked = Time.time;
}
// 매 프레임마다 실행하는 AttackState의 Update 함수에서는 이동을 막고
// AnimationEvent가 발동되었다면, 다시 EngageState(=경계상태)로 돌아감
public override void Update()
{
base.Update();
_skeletonController.SetZeroVelocity();
if (_triggerCalled)
_enemyStateMachine.ChangeState(_skeletonController._engageState);
}
}
3) 공격 Animation의 AnimationTrigger를 발생시킬 SkeletonAnimationTrigger
public class SkeletonAnimationTrigger : MonoBehaviour
{
SkeletonController _skeletonController => GetComponentInParent<SkeletonController>();
// SkeletonController의 AnimationTrigger 함수를 호출
private void AnimationTrigger()
{
_skeletonController.AnimationTrigger();
}
}
만들어진 SkeletonAnimationTrigger의 경우,
Animator 컴포넌트가 붙여진 오브젝트에 붙여져야 합니다.
최종적으로 실행 결과를 보겠습니다.
728x90
반응형
'유니티 > 게임 프로젝트' 카테고리의 다른 글
2D RPG - (12 - 1) 카운터 어택 구현하기 (0) | 2024.12.31 |
---|---|
2D RPG - (11) 피격에 따른 효과 구현하기 (0) | 2024.12.30 |
2D RPG - (9) 스켈레톤 만들기 (1) (0) | 2024.12.18 |
2D RPG - (8) 적을 위한 세팅 (1) | 2024.12.18 |
2D RPG - (7) 무한반복되는 배경 만들기 (0) | 2024.12.11 |