본문 바로가기

Unreal

UE - 총알 발사하기

목표

  • 사용자가 발사 버튼을 누르면 총알을 발사하고 싶다.

 

구현 순서

  1. 발사 입력 추가하기
  2. 총 메시 에셋 추가하기
  3. 플레이어에 총 추가하기
  4. 발사 기능 구현하기
  5. 총알 인스턴스 제거하기

 

1. 발사 입력 추가하기

 

IA_Fire 추가

 

 

IMC_TPS에 IA_Fire Mapping

 

 

2. 총 메시 에셋 추가하기

언리얼 FPS 템플릿 프로젝트에서 제공하는 총이 있습니다. FPS 템플릿을 프로젝트에 추가합니다.

 

Content Browser에서 Content Pack 추가

 

 

First Person Project 추가

 

 

3. 플레이어에 총 추가하기

총 스켈레탈 메시 컴포넌트 멤버 변수 추가(TPSPlayer.h)

UCLASS()
class TPSPROJECT_API ATPSPlayer : public ACharacter
{
	GENERATED_BODY()
	
	UPROPERTY(VisibleAnywhere, Category=GunMesh)
	class USkeletalMeshComponent* gunMeshComp;
}

 

총 스켈레탈메시 컴포넌트 등록하기(TPSPlayer.cpp)

ATPSPlayer::ATPSPlayer()
{
	// ... 생략
	
	// 4. 총 스켈레탈메시 컴포넌트 등록
	gunMeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("GunMeshComp"));
	
	// 4-1. 부모 컴포넌트를 Mesh 컴포넌트로 설정
	gunMeshComp->SetupAttachment(GetMesh());
	
	// 4-2. 스켈레탈메시 데이터 로드
	ConstructorHelpers::FObjectFinder<USkeletalMesh> TempGunMesh(TEXT(""));
}

 

 

SK_FPGun 경로 복사

 

총 스켈레탈메시 컴포넌트 데이터 설정

// 4-2. 스켈레탈메시 데이터 로드
ConstructorHelpers::FObjectFinder<USkeletalMesh> TempGunMesh(TEXT("/Script/Engine.SkeletalMesh'/Game/FPWeapon/Mesh/SK_FPGun.SK_FPGun'"));

// 4-3. 데이터 로드가 성공했다면
if (TempGunMesh.Succeeded())
{
	// 4-4. 스켈레탈메시 데이터 할당
	gunMeshComp->SetSkeletalMesh(TempGunMesh.Object);

	// 4-5. 위치 조정하기
	gunMeshComp->SetRelativeLocation(FVector(-14, 52, 120));

}

 

 

빌드 후 총 컴포넌트가 추가된 모습

 

4. 발사 기능 구현하기

  • 목표 : 사용자가 발사 버튼을 누르면 총알을 발사하고 싶다.
  • 필요 속성 : 총알 공장

 

총알 공장 속성 추가(TPSPlayer.h)

// 총알 공장
UPROPERTY(EditDefaultsOnly, Category=BulletFactory)
TSubclassOf<class ABullet> bulletFactory;

 

빌드 후 bullet Factory 속성에 BP_Bullet 추가

 

총알 발사 처리 함수 InputFire 선언하기(TPSPlayer.h)

UPROPERTY(EditDefaultsOnly, Category = "Input")
class UInputAction* ia_Fire;

// 총알 발사 처리함수
void InputFire(const struct FInputActonValue& inputValue);

 

InputFire 함수 구현 및 바인딩.

// Called to bind functionality to input
void ATPSPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	auto PlayerInput = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);
	if (PlayerInput)
	{
		//... 생략

		// 총알 발사 이벤트 처리 함수 바인딩
		PlayerInput->BindAction(ia_Fire, ETriggerEvent::Started, this, &ATPSPlayer::InputFire);
	}

}

void ATPSPlayer::InputFire(const FInputActionValue& inputValue)
{
	// 총알 발사 처리
	FTransform firePosition = gunMeshComp->GetSocketTransform(TEXT("FirePosition"));
	GetWorld()->SpawnActor<ABullet>(bulletFactory, firePosition);
}

 

총구 위치 소켓 생성

 

5. 총알 인스턴스 제거하기

 

 

계속해서 생성되기만 하는 총알 인스턴스

 

 

액터를 월드 상에서 제거하는 방법

  • InitialLifeSpan 속성을 이용하여 생명력을 주는 방법
  • Destroy() 함수를 이용하여 직접 제거하는 방법.

 

총알의 생명 시간 주기 Bullet 생성자 - InitialLifeSpan

// 생명 시간 주기
InitialLifeSpan = 2.0f;

 

 

Destroy()함수를 이용하여 직접 제거

  • 총알 제거 함수 Die 선언
// 총알 제거 함수.
void Die();

 

 

SetTimer 함수 원형

void SetTimer
{
	FTimerHandle & InOutHandle,
	UserClass * InObj,
	typename FTimeDelegate::TUObjectMethodDelegate< UserClass >::FMethodPtr InTimerMehthod,
	float InrRate,
	bool InbLoop,
	float InFirstDelay
}

 

 

SetTimer 함수의 매개변수들

  • InOutHandle : 등록할 알람 시계
  • InObj : 알림 처리를 갖고 있는 객체
  • InTimerMethod : 알림 처리 함수
  • InRate : 알람 시간
  • InbLoop : 반복 여부
  • InFirstDelay : 처음 호출되기 전 지연 시간

 

 

Timer를 이용한 Die 함수 호출하기.

// Called when the game starts or when spawned
void ABullet::BeginPlay()
{
	Super::BeginPlay();

	FTimerHandle deathTimer;
	GetWorld()->GetTimerManager().SetTimer(deathTimer, this, &ABullet::Die, 2.0f, false);
	
}

void ABullet::Die()
{
	Destroy();
}

 

 

람다를 이용한 타이머 설정을 추가로 알아보기.

람다의 구문 형태

[캡처](매개변수) -> Return Type { 구현 몸체 }

 

 

람다 문법 설명

  • 캡처
    • 외부에 정의된 변수나 상수 구현 몸체에서 사용
    • 참조 또는 복사 방식 사용
  • 매개변수
    • 함수에서 사용할 인수 목록
  • Return Type
    • 함수의 반환 유형
  • 구현 몸체
    • 함수의 구현부

 

 

람다 문법

캡처는 람다 구문에서 외부에 있는 데이터를 가져다 사용하기 위한 요소입니다. 변수 상수 같은 것들이 될 수 있습니다. 보통 참조 유형을 많이 사용하지만 복사 방식도 사용 됩니다. 참조는 캡처하고 있는 변수의 주소가 넘어가기 때문에 람다 몸체에서 수정 되면 원본에서 적용됩니다. 복사 방식은 값이 복사되어 사용되기에 원본에 적용되지 않습니다.

매개변수는 함수의 매개변수와 같습니다.
Return Type 구문은 람다 식에서 반환하고자 하는 반환 자료형이 됩니다.
마지막 구현 몸체는 함수의 몸체입니다.

 

 

예시

void LambdaTestFunc()
{
	// 함수 객체 생성
	auto lamdaFunc = []()->void {
		UE_LOG(LogTemp, Warning, TEXT("Lambda test"));
	};
	
	// 함수 객체 실행.
	lambdaFunc();
}

함수와 비슷하지만 대단한 유연함을 제공합니다. 따로 함수를 선언할 필요가 없습니다.

 

 

예시 2

void LambdaTestFunc()
{
	int32 sum = 10;
	// 함수 객체 생성
	auto lambdaFunc = [&sum](int number)->void {
		sum += number;
	};
	
	// 함수 객체 실행
	lambdaFunc(20);
	UE_LOG(LogTemp, Warning, TEXT("sum : %d"), sum);
}

 

 

람다를 이용한 타이머 처리

// Called when the game starts or when spawned
void ABullet::BeginPlay()
{
	Super::BeginPlay();

	FTimerHandle deathTimer;
	// GetWorld()->GetTimerManager().SetTimer(deathTimer, this, &ABullet::Die, 2.0f, false);

	GetWorld()->GetTimerManager().SetTimer(deathTimer, FTimerDelegate::CreateLambda([this]()->void
		{
			Destroy();
		}), 2.0f, false);
	
}

 

 

블루프린트에서 특정 변수 값을 수정했을 때 특정 처리

특정 변수 값을 수정하면 이를 감지하여 특정 처리를 하고 싶은 경우 예시로 총알 속도를 제어할 speed라는 변수를 만들어 사용한다고 가정할 때 이럴 경우 spped 값이 변경되면 총알의 프로젝타일 무브먼트 컴포넌트 속성인 InitalSpeed, MaxSpeed 값이 함께 업데이트 되어야 합니다.

그래야 총알 속도가 변경된 값으로 적용됩니다. 혹은 지정된 변수의 값이 최대최소 범위를 넘어가지 않도록 하고 싶을 수도 있습니다. 이를 위해서 언리얼에서는 속성이 변경되면 감지하여 알려주는 PostEditChangeProperty() 이벤트 함수를 제공합니다. 총알 속도를 변경하는 속성으로 speeed를 Bullet.h 헤더 파일에 추가.

 

 

총알 속도 speed 변수 추가

// 총알 속도
UPROPERTY(VisibleAnywhere, Category = Collision)
float speed = 5000;
// 액터의 특정 속성을 수정하면 호출되는 이벤트 함수
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;

 

 

속성이 변경될 경우 발생하는 이벤트 함수 구현하기.

void ABullet::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	// speed 값이 수정되었는지 체크
	if (PropertyChangedEvent.GetPropertyName() == TEXT("speed"))
	{
		// 프로젝타일 무브먼트 컴포넌트에 speed 값 적용
		movementComp->InitialSpeed = speed;
		movementComp->MaxSpeed = speed;
	}
}

 

 

speed 변경시 Initial Speed, Max Speed가 같이 변경됨.

'Unreal' 카테고리의 다른 글

UE - 저격 총 조준 모드 구현하기  (2) 2024.11.16
UE - 총알 제작하기  (2) 2024.11.02
UE - 플레이어 이동 처리  (1) 2024.10.26
UE - 3인칭 플레이어 생성  (0) 2024.10.19
UE - 3인칭 TPS 프로젝트 생성하기  (0) 2024.10.12