728x90
반응형
이번 포스트에서는 첫번째 스킬 중 분신술을 구현해보겠습니다.
분신술을 위한 키를 따로 설정하지 않아 대쉬에 연동하여 발동하는 스킬로서,
분신은 적을 발견하면 적을 공격하게 됩니다.
분신술은 다음의절차에 의해 수행됩니다.
- 플레이어가 입력하면 SkillManager에 의해 분신술을 수행
- 수행된 분신술에서 이전에 만들고 설정해놓은 분신 Prefab을 생성
- 분신 Prefab에 설정된 컴포넌트에 의해 로직을 수행
위의 절차에 맞춰 코드를 알아보겠습니다.
1) SkillCloning
public class SkillCloning : SkillTemplate
{
[Header("Clone Info")]
[SerializeField]
private GameObject _clonePrefab;
[SerializeField]
private float _cloneDuration;
// SkillManager에 의해 분신술을 수행
public void DoCreateClone(Transform clonePosition, bool canAttack)
{
GameObject newClone = Instantiate(_clonePrefab);
newClone.GetComponent<SkillCloningController>().DoSetupClone(clonePosition, _cloneDuration, canAttack);
}
}
분신술 스킬은 위와 같이 구성되어 있습니다.
분신 prefab을 생성하고 이후의 로직은 분신 prefab이 수행합니다.
2) SkillManager
public class SkillManager : MonoBehaviour
{
public static SkillManager _skillManagerInstance;
// SkillManager는 스킬을 수행하는 주체이므로, 각 스킬에 대해 알고 있어야 함
.
.
.
public SkillCloning _skillCloning { get; private set; }
.
.
.
// 스킬을 가져와서 설정
private void Start()
{
.
.
.
_skillCloning = GetComponent<SkillCloning>();
}
}
SkillManager에서 분신술을 호출할 수 있도록 위와 같이 설정하였습니다.
3) PlayerController
public class PlayerController : BaseCharacterController
{
.
.
.
// Skill Info
public SkillManager _skillManager { get; private set; }
.
.
.
protected override void Start()
{
base.Start();
_skillManager = SkillManager._skillManagerInstance;
_stateMachine.Init(_idleState);
}
.
.
.
}
플레이어의 입력을 처리하는 PlayerController에서 스킬을 수행할 수 있도록
SkillManager를 캐싱해놓도록 하겠습니다.
4) PlayerStateDash
public class PlayerStateDash : PlayerState
{
.
.
.
// State에 돌입
public override void Enter()
{
base.Enter();
_controller._skillManager._skillCloning.DoCreateClone(_controller.transform, _controller.DoDetectIsGrounded());
_stateTimer = _controller._dashDuration;
}
.
.
.
}
일단은 대쉬 상태에서 분신술을 수행할 수 있도록 위와 같이 설정하였습니다.
5) SkillCloningController
public class SkillCloningController : MonoBehaviour
{
[SerializeField]
private float _colorLoosingSpeed;
private SpriteRenderer _spriteRenderer;
private Animator _animator;
private float _cloneTimer;
[SerializeField]
private Transform _attackCheck;
[SerializeField]
private float _attackCheckRadius = 0.8f;
private Transform _closestEnemy;
private void Awake()
{
_spriteRenderer = GetComponent<SpriteRenderer>();
_animator = GetComponent<Animator>();
}
private void Update()
{
_cloneTimer -= Time.deltaTime;
// 쿨타임이 충족되어 분신술 스킬을 사용가능한 경우
if (_cloneTimer < 0)
{
// 분신을 생성하고, 투명도를 조금씩 낮춤
_spriteRenderer.color = new Color(1, 1, 1, _spriteRenderer.color.a - (Time.deltaTime * _colorLoosingSpeed));
// 투명도가 0이 되면 GameObject 파괴
if (_spriteRenderer.color.a <= 0)
Destroy(gameObject);
}
}
// 분신을 생성
public void DoSetupClone(Transform newTransform, float cloneDuration, bool _canAttack)
{
// 공격할 수 있는 경우, 분신의 Animator 변수를 랜덤하게 설정하여 공격 애니메이션 재생
if (_canAttack)
_animator.SetInteger("AttackNumber", Random.Range(1, 3));
// 분신의 위치 설정 및 쿨타임을 설정
gameObject.transform.position = newTransform.position;
_cloneTimer = cloneDuration;
// 분신이 적을 마주하게끔 설정
DoFaceClosestTarget();
}
// 분신의 애니메이션에 사용될 이벤트 트리거 함수, 쿨타임을 -1로 설정하여 분신술 스킬을 사용가능하게 바꿈
private void AnimationTrigger()
{
_cloneTimer = -1.0f;
}
// 분신의 애니메이션에 사용될 이벤트 트리거 함수
private void AttackAnimationTrigger()
{
// 공격 범위를 설정하고 원 모양으로 오버랩을 수행
Collider2D[] colliders = Physics2D.OverlapCircleAll(_attackCheck.position, _attackCheckRadius);
// 수행 결과를 순회하면서
foreach (Collider2D hit in colliders)
{
// 적이라면 적의 DoGetDamage 함수 호출
if (hit.GetComponent<EnemyController>() != null)
hit.GetComponent<EnemyController>().DoGetDamage();
}
}
private void DoFaceClosestTarget()
{
// 분신의 위치에서 25 만큼의 반지름으로 원으로 오버랩한후,
Collider2D[] colliders = Physics2D.OverlapCircleAll(gameObject.transform.position, 25f);
float closestDistance = Mathf.Infinity;
// 오버랩 결과를 순회
foreach (var hit in colliders)
{
// 오버랩 결과가 적이라면
if (hit.GetComponent<EnemyController>() != null)
{
// 적과의 거리를 저장하고, 적을 기억
float distanceToEnemy = Vector2.Distance(gameObject.transform.position, hit.transform.position);
if (distanceToEnemy < closestDistance)
{
closestDistance = distanceToEnemy;
_closestEnemy = hit.transform;
}
}
}
// 기억된 적을 대상으로
if (_closestEnemy != null)
{
// 적이 나보다 x축 기준으로 뒤에 있다면 방향 회전
if (gameObject.transform.position.x > _closestEnemy.position.x)
transform.Rotate(0, 180, 0);
}
}
}
분신술로 생성되는 분신을 대상으로 동작하는 로직입니다.
이제 세부 설정 사항을 알아보겠습니다.
6) 유니티 에디터
플레이어의 분신 prefab은 위와 같이 설정되었습니다.
우리가 의도한 바라면, 땅에 서 있는 상태에서 생성한 분신은 적을 공격하고,
그렇지 않은 분신은 멀뚱히 서 있어야 합니다.
실제 실행 결과를 알아보겠습니다.
문제없이 수행되는 것을 볼 수 있습니다.
728x90
반응형
'유니티 > 게임 프로젝트' 카테고리의 다른 글
2D RPG - (15 - 2) 단검 투척 스킬 구현_02 (0) | 2025.01.28 |
---|---|
2D RPG - (15 - 1) 단검 투척 스킬 구현_01 (0) | 2025.01.27 |
2D RPG - (13) 스킬 시스템 구현을 위한 사전설정 (0) | 2025.01.06 |
2D RPG - (12 - 2) 카운터 어택 구현하기 (0) | 2024.12.31 |
2D RPG - (12 - 1) 카운터 어택 구현하기 (0) | 2024.12.31 |