[Unreal]#9-1_Shooting 설계 및 구현
Unreal 개발 중 "Gun Actor"에 대해 알아보겠습니다.
"Simple Shooter Game"의 Character Class 개발 과정 중 일부입니다.
Shoot 액션의 Input Binding 구현
1. Gun 클래스의 PullTrigger() 메서드 선언
// Gun.h 내부
class AGun : public AActor
{
//...
public:
void PullTrigger();
};
// Gun.cpp 내부
void AGun::PullTrigger()
{
// PullTrigger() 메서드가 호출되면 "Shoot!"을 출력합니다.
UE_LOG(LogTemp, Warning, TEXT("Shoot!"));
}
2. Character 클래스 내부 BindAction() 구현
// Character.h 내부...
class AShooterCharacter : public ACharacter
{
//...
public:
void Shoot();
};
// Character.cpp 내부...
void AShooterCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
//...
// Shoot() 메서드의 Input Binding을 구현합니다.
PlayerInputComponent->BindAction(TEXT("Shoot"), EInputEvent::IE_Pressed, this, &AShooterCharacter::Shoot);
}
3. 출력 Log 확인
Particle System 중 Muzzle Flash Effect 구현
1. Gun Mesh에 Socket 추가
2. Gun 클래스 내부 Muzzle Flash 데이터 멤버 선언
// Gun.h 내부...
class AGun : public AActor
{
//...
private:
UPROPERTY(EditAnywhere)
UParticleSystem* MuzzleFlash;
};
// Gun.cpp 내부...
# include "Kismet/GameplayStatics.h"
void AGun::PullTrigger()
{
UGameplayStatics::SpawnEmitterAttached(MuzzleFlash, Mesh, TEXT("MuzzleFlashSocket"));
}
UParticleSystemComponent* UGameplayStatics::SpawnEmitterAttached( UParticleSystem* EmitterTemplate, USceneComponent* AttachToComponent, FName AttachPointName, optional etc);
: 특정 Effet를 Attach 된 Component의 "Socket"위치에 Play 합니다.
"UParticleSystem" 타입은 이펙트들을 사용하기 위함이며, "UGamePlayStatics" 클래스가 제공하는 "SpawnEmitterAttached()" 메서드를 통해 특정 컴포넌트의 지정된 "Socket" 위치에 이펙트를 재생합니다.
3. Gun의 Blueprint 클래스 내부에서 Particle System 할당
- 앞서, C++에서 "UPROPERTY()"를 통해 특정 "ParticleSystem" 유형의 Asset을 할당받도록 했으므로, Editor에서 사용할 이펙트를 할당해줍니다!
결과 화면
Laytracing을 위한 Controller 시점 찾기
1. Owner의 Controller를 얻어오기
// Gun.cpp 내부
void AGun::PullTrigger()
{
//...
APawn* OwnerPawn = Cast<APawn>(GetOwner());
}
2. Controller 시점의 위치와 회전 구하기
// Gun.cpp 내부
void AGun::PullTrigger()
{
// 1. APawn* 타입의 Owner 구하기
APawn* OwnerPawn = Cast<APawn>(GetOwner());
if(OwnerPawn == nullptr) return;
// 2. 얻어온 Pawn의 Controller 구하기
AController* OwnerController = OwnerPawn->GetController();
if(OwnerController == nullptr) return;
FVector Location = FVector::ZeroVector;
FRotator Rotation = FRotator::ZeroRotator;
// 3. Controller 시점의 위치와 회전 값 구하기
OwnerController->GetPlayerViewPoint(Location, Rotation);
}
void AController::GetPlayerViewPoint( FVector& out_Location, FRotator& out_Rotation ) const
: Player 시점의 위치와 회전을 구합니다. 이때, Player의 시점이란 소유하고 있는 Pawn의 "눈"이 위치하는 시점을 의미하며, 최종적으로 Pawn이 갖고 있는 "Camera"가 바라보는 시점을 의미하겠죠!
*주의 : "out_Location"과 "out_Rotation"은 out parameter로 참조형으로 넘겨집니다.
3. DrawDebugCamera 호출
// Gun.cpp 내부
void AGun::PullTrigger()
{
DrawDebugCamera(GetWorld(), Location, Rotation, 2, FColor::Red, true);
}
void DrawDebugCamera( const UWorld* world, const FVector& location, FRotation& rotation,... etc)
: 인자로 넘겨진 "location"과 "rotation"에 디버깅을 위한 카메라 그림을 그려냅니다.
Line Trace By Channel
1. Trace Channel 세팅
1. "프로젝트 세팅"의 "콜리전" 카테고리를 선택합니다.
2. 새 트레이스 채널을 생성하고, Bullet으로 명명합니다.
3. Preset 중 "No Colliision"에서 Bullet을 "무시"로 설정합니다.
4. Preset 중 "Overlap All"에서 Bullet을 "겹칩"으로 설정합니다.
5. Preset 중 "Invisible Wall"과 "Invisible Wall Dynamic"에서 Bullet을 "무시"로 설정합니다.
2. Trace Channel 넘버 확인
1. Project File을 열어서 "Config" 폴더를 엽니다.
2. "DefaultEngine"을 Text Editor 파일로 열게 되면, 우리가 할당한 Trace Channel의 넘버 정보를 발견합니다.
* 총 18개의 Trace Channel이 있습니다.
3. 최대 목표 지점의 위치 구하기
// Gun.h 내부...
class AGun : public AActor
{
//...
private:
UPROPERTY(EditAnywhere)
float MaxRange = 1000;
};
// Gun.cpp 내부...
void AGun::PullTrigger()
{
// 1. ViewPort 위치로 부터 바라보는 방향의 Max Range 길이 만큼 떨어진 위치
FVector End = Location + Rotation.Vector() * MaxRange;
}
4. AGun::PullTrigger()
void AGun::PullTrigger()
{
//...
FHitResult hit;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
Params.AddIgnoredActor(OwnerPawn);
bool bSuccess = GetWorld()->LineTraceSingleByChannel(
hit,
Location,
End,
ECollisionChannel::ECC_EngineTraceChannel1
);
if(bSuccess == true)
{
FVector ShotDirection = -Rotation.Vector();
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactEffect, hit.Location, ShotDirection.Rotation() );
if(hit.GetActor() != nullptr)
{
FPointDamageEvent DamageEvent(Damage, hit, ShotDirection, nullptr);
hit.GetActor()->TakeDamage(Damage, DamageEvent, OwnerController, this);
}
}
}
FCollisionQueryParams Params;
// 1. 무시할 Actor 추가
Params.AddIgnoredActor(this);
// 2. 무시할 Actor 추가
Params.AddIgnoredActor(OwnerPawn);
- FCollisionQueryParams는 Collsion 함수에 전달되는 구조체입니다.
- Collision 함수의 세부 조건을 설정하는 구조체로 보면 될듯 합니다.
* bool UWorld::LineTraceByChannel(
struct FHitResult& outHit,
const FVector& Start,
const FVector& End,
ECollisionChannel TraceChannel,
const FCollisionQueryParams& Params,
... etc
)
- 특정 ECollision Channel을 통해 Wolrd내 Ray를 생성하고, 해당 Ray와 충돌한 첫 번째 결과를 저장합니다.
- 별도로 생성한 FHitResult 구조체의 참조를 인자로 받아, 충돌 결과 정보를 넘겨주겟죠?
'게임개발 > Unreal C++' 카테고리의 다른 글
[Unreal]#10_Damage + Health 구현 (0) | 2022.08.21 |
---|---|
[Unreal]#9-2_Shooting 설계 및 구현 (0) | 2022.08.20 |
[Unreal]#8_Attach Gun Actor to Shooter Character (0) | 2022.08.16 |
[Unreal]#7_Spawn Gun Actor in Runtime (0) | 2022.08.12 |
[Unreal]#6_Gun Actor Setting (0) | 2022.08.11 |