Tower클래스에는 사용자 인풋 대신 타이머를 사용할 것이다. Tower 클래스의 타이머를 만들어서 타이머가 함수를 자주 호출하게 한다. 이를 위해 Fire 함수 호출 사이 간격을 초로 설정한다.
언리얼 엔진에서는 FTimerManager라고 하는 클래스로 타이머를 설정할 수 있다. 월드 타이머 매니저로 월드의 타이머를 관리하는데, 이 월드 타이머 매니저에 SetTimer라는 함수가 있고 이 함수는 다양한 인풋을 사용한다. 그 중 하나가 타이머 핸들이다. 이는 타이머 매니저가 관리하는 타이머의 핸들이다. 이 핸들을 사용하여 여러 타이머를 관리할 수 있으며 타이머 핸들을 타이머 이름이나 ID로 이용해 각각의 타이머 정보에 액세스 할 수 있다. 이건 FTimerHandle 타입의 오브젝트이다.
우리는 타이머 핸들을 생성하여 이걸 SetTimer함수에 입력할 예정이다. SetTimer에 필요한 다른 인풋으로 속도가 있는데, 타이머가 실행되기까지 기다리는 시간을 의미한다. 이건 콜백 함수 호출에 사용할 것이다. 함수를 만들어 타이머에 바인딩하고 충분한 시간이 지나면, 즉 타이머 속도에 맞춰 콜백 함수가 호출되는 것이다.
private:
FTimerHandle FireRateTimerHandle;
float FireRate = 2.f;
void CheckFireCondition();
기본적인 것들을 헤더파일의 private 영역에 추가해주고
GetWorldTimerManager().SetTimer(FireRateTimerHandle, this, &ATower::CheckFireCondition, FireRate, true);
헤더파일을 추가해서 WorldTimerManager에 액세스할 수 있게 되었다. GetWorldManager은 FTimerManager을 반환하고 포인터를 반환하지 않는다. 그래서 화살표가 아닌 .을 이용해 SetTiemr함수를 호출한다. 첫번째 매개변수는 FTimerHandle인데 헤더파일에 선언한 FireRateTimerHandle을 적어준다. 다음 매개변수는 UserClass인데 BindAxis나 BindAction에서처럼 지금 사용중인 바인딩 하는 함수의 클래스이므로 this라고 적어준다. 다음은 콜백 함수로 방금 작성한 CheckFireCondition을 적어준다. SetTimer은 함수 주소를 사용하기 때문에 연산자 주소를 꼭 적어준다. 다음 매개변수는 플로트로 콜백 함수를 호출하기 전에 기다릴시간이므로 FireRate라고 적어둔다. 마지막은 타이머가 반복할지 지정하는 불리언 값으로 true를 적어준다.
이렇게 함수를 타이머와 바인딩하고 2초마다 호출되도록 설정하였다.
void ATower::CheckFireCondition()
{
//함수를 만들고 이제 타이머를 설정해서 타이머가 실행되고 설정 시간이 지나면 함수가 호출되도록 한다.
if (Tank)
{
float Distance = FVector::Dist(GetActorLocation(), Tank->GetActorLocation());
if (Distance <= FireRange)
{
Fire();
}
}
}
사정거리 안에 있으면 타워도 구체를 그리도록 작성하였다
코드를 보면 CheckFireCondition부분과 Tick함수에 동일한 부분이 있기 때문에 bool 함수를 만들어 리팩토링 해준다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "Kismet/GameplayStatics.h"
#include "TimerManager.h"
#include "Tank.h"
#include "Tower.h"
void ATower::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 탱크까지의 거리 구한 후
if (InFireRange())
{
RotateTurret(Tank->GetActorLocation());
}
}
void ATower::BeginPlay()
{
Super::BeginPlay();
//탱크를 어떻게 불러오지? ->GetPlayerPawn 사용
Tank = Cast<ATank>(UGameplayStatics::GetPlayerPawn(this, 0));
//게임 시작 시 타이머가 실행되도록
GetWorldTimerManager().SetTimer(FireRateTimerHandle, this, &ATower::CheckFireCondition, FireRate, true); //GetWorldTimerManager은 FTimerManager을 반환하고 포인터를 반환하지 않음
}
void ATower::CheckFireCondition()
{
//함수를 만들고 이제 타이머를 설정해서 타이머가 실행되고 설정 시간이 지나면 함수가 호출되도록 한다.
if (InFireRange)
{
Fire();
}
}
bool ATower::InFireRange()
{
if (Tank)
{
float Distance = FVector::Dist(GetActorLocation(), Tank->GetActorLocation());
if (Distance <= FireRange)
{
return true;
}
}
return false;
}
훨씬 간결해진 코드이다.
🎮TIL: 헤더파일로 들고와서 함수를 매개변수를 확인하며 적절히 넣는게 굉장히 어려운 일이라는 걸 다시 느끼게 된다.... 나중에는 도큐먼트 보고 헤더파일 적절히 찾는 연습도 해야겠다는 생각을 하였다