먼저 핵심 내용을 살펴보자!
Entities 밑에 TopDownShooting.cs를 생성하자.
public class TopDownShooting : MonoBehaviour
{
private TopDownCharacterController _controller;
[SerializeField] private Transform projectileSpawnPosition;
// Vector2.right : 벡터를 계속 새로 생성하지 말고 기본값 Vector2.right를 재사용하기 위한 코드
private Vector2 _aimDirection = Vector2.right;
private void Awake()
{
_controller = GetComponent<TopDownCharacterController>();
}
}
일단 여기까지 작성하고 TopDownCharacterController.cs로 와서 OnAttackEvent를 추가하자.
public class TopDownCharacterController : MonoBehaviour
{
public event Action<Vector2> OnMoveEvent;
public event Action<Vector2> OnLookEvent;
public event Action OnAttackEvent;
private float _timeSinceLastAttack = float.MaxValue; // 마지막으로 공격했던 시간
protected bool IsAttacking { get; set; }
// 하위 클래스에서 상속받아서 사용할 수 있도록
protected virtual void Update()
{
HandleAttackDelay();
}
private void HandleAttackDelay()
{
// 공격 딜레이
if(_timeSinceLastAttack <= 0.2f) // 이후 수정
{
_timeSinceLastAttack += Time.deltaTime;
}
// 공격 딜레이가 초과되었고 공격을 했다면
if (IsAttacking && _timeSinceLastAttack > 0.2f)
{
// 공격 딜레이 초기화.
_timeSinceLastAttack = 0;
CallAttackEvent();
}
}
public void CallMoveEvent(Vector2 direction)
{
OnMoveEvent?.Invoke(direction);
}
public void CallLookEvent(Vector2 direction)
{
OnLookEvent?.Invoke(direction);
}
public void CallAttackEvent()
{
OnAttackEvent?.Invoke();
}
}
이후 OnAttackEvent를 받아오기 위해 PlayerInputcontroller.cs의 OnFire를 수정해주자.
public void OnFire(InputValue value)
{
IsAttacking = value.isPressed; // 공격 키가 입력이 되면
}
이후 TopDownShooting.cs에서 이벤트를 구독하자.
public class TopDownShooting : MonoBehaviour
{
private TopDownCharacterController _controller;
[SerializeField] private Transform projectileSpawnPosition;
private Vector2 _aimDirection = Vector2.right; // Vector2.right; 벡터를 계속 새로 생성하지 말고 기본값 Vector2.right를 재사용하기 위한 코드
private void Awake()
{
_controller = GetComponent<TopDownCharacterController>();
}
// Start is called before the first frame update
void Start()
{
_controller.OnAttackEvent += OnShoot;
_controller.OnLookEvent += OnAim;
}
private void OnAim(Vector2 newAimDireciton)
{
_aimDirection = newAimDireciton;
}
private void OnShoot()
{
CreateProjectile();
}
private void CreateProjectile()
{
Debug.Log("Fire"); // 구독 확인용.
}
}
Input에서 Event를 콜 하면, 구독되어 있는 곳에 전부 뿌려준다.
이후 플레이어에게 TopDownShooting.cs를 연결해주고 확인해보자!
정상적으로 작동하는 모습!
화살 스프라이트를 생성한 후, Order in Layer를 6으로 맞추자.
이후, Create Empty를 해서 이름을 Arrow로 수정한다. 생성한 화살 스프라이트를 Arrow 밑으로 넣어준다.
그리고 화살이 현재 위쪽을 바라보고 있으므로 Rotation의 Z축을 -90으로 변경해준다.
먼저 Prefabs 폴더를 생성하자. 그 밑에 Projectiles 폴더를 생성하자.
이후 만든 Arrow를 Projectiles 폴더에 옮긴다.
옮기면 Arrow가 파랑색으로 변하는 것을 확인할 수 있는데, 이것이 프리팹이 된 것이다. 이제 프리팹에 있는 Arrow가 원본이 되었기 때문에, 하이라키에 있는 Arrow는 삭제해도 된다.
이제 테스트로 화살이 생성되는지 확인해보자.
// TopDownShooting.cs
private void CreateProjectile()
{
// 임시로 화살이 생성되는지만 확인하는 코드
// Instantiate의 기본 오버라이딩은 Instantiate(복제할 게임 오브젝트, 복제할 부모 오브젝트) 로 정의되어 있기 때문에
// Instantiate(복제할 게임 오브젝트, 생성할 위치, Rotation); -> Instantiate에서 position 값으로 생성하기 위해서는 해당 오버라이딩을 사용해야 한다.
Instantiate(testPrefab, projectileSpawnPosition.position, Quaternion.identity); // Quaternion.identity : Vector3 (0, 0, 0)과 같다.
}
화살이 생성되는 모습! ▼