[개요]

  • 플레이어 이동
  • 애니메이션
  • 사격
  • 체력과 죽음 처리
  • 비헤이비어 트리를 통한 AI구현
  • 승패 조건

 

[프로젝트 설정]

  • 빈 프로젝트를 생성한다. 프로젝트 이름 SimpleShooter
  • 다운받은 프로젝트를 버전에 맞춰서 포팅하고 안의 애셋들을 모두 이주시킨다

 

[비고]

  • 해당 섹션은 UE5가 아닌 여전히 UE4이지만 같은 강의에 있으므로 같은 카테고리에 둠
  • 상관없이 UE5로 진행하려 했으나 빠른 진도를 위해 4.26으로 진행

 

[158. Skeletal Animation 101]

 

언리얼에서의 스켈레톤은 스켈레탈 메시와 애니메이션이 결합된 형태이다.

 

[163. Inverse Transforming Vectors]

 

기본적으로 모든 오브젝트는 월드 공간에 상대적이다.

어떤 오브젝트가 회전했을 때 로컬 공간 입장에서 속도의 방향은 그대로겠지만 월드 공간에서 바라봤을 때는 방향이 바뀐다.

 

이때 속도의 방향을 해당 오브젝트의 트랜스폼으로 역변환을 하면 속도의 방향을 바라보는 관점이 월드 공간에서 로컬 공간으로 바뀐다.

 

역변환이 아닌 Transform Direction을 통해 반환받은 Yaw값과 속도를 회전값으로 분리해서 반환받은 Yaw값은 동일하기 때문에 기본적으로 월드좌표에 상대적임을 확인할 수 있다.

 

그래서 캐릭터(카메라) 회전과 관련없이 속도의 방향만을 통해서 회전값을 구하고 각을 구해서 애니메이션을 블렌딩 할 수 있다.

 

덧붙여서 위의 노드들을 C++ 코드로 작성하면 아래와 같다

 

/* KismetLibrary로 제공되는 함수들 */
KISMET_MATH_INLINE
FVector UKismetMathLibrary::InverseTransformDirection(const FTransform& T, FVector Direction)
{
    return T.InverseTransformVectorNoScale(Direction);
}

KISMET_MATH_FORCEINLINE
FRotator UKismetMathLibrary::MakeRotFromX(const FVector& X)
{
    return FRotationMatrix::MakeFromX(X).Rotator();
}

/* */

float AShooterCharacter::GetDirection()
{
    FVector InverseTransformDirection = GetActorTransform().InverseTransformVectorNoScale(GetVelocity());
    return FRotationMatrix::MakeFromX(InversTransformDirection).Rotator().Yaw;
}
// 위와 같이 구현하면 KismetMathLibrary에 접근할 필요가 없다

 

참고 링크 : https://community.gamedev.tv/t/inverse-transform-direction-in-c/147893

정리하자면 바라보는 역변환 시 바라보는 관점이 달라진다는 것이다. 본질이 바뀌는것이 아니다.

 

[164. Calculating Animation Speeds]

 

걷기/뛰기 애니메이션에는 각각 적절한 속력를 적용시켜야 움직임이 어색하지 않을것이다.

예를들어 100의 속력에 맞춰서 제작된 걷기모션에 300의 속력으로 움직인다면 마치 허공답보를 하는듯한 어색함이 느껴질 것이다.

이 속력을 구하는 방법을 우선 알아봐야한다.

 

공식은 다음과 같다

foot_speed = (y_finish - y_start) / (t_finish - t_start)

 

발 뒤꿈치가 땅에 닿았을 때 해당 본의 y좌표와 시간, 그리고 발 뒤꿈치가 떨어질 때 해당 본의 y좌표와 시간이 필요하다. 아주 정확한 값을 사용하는게 아니라 반올림을 한 근사값을 사용할것이기 때문에 약간의 오차는 괜찮을것이다.

y축을 사용하는 이유는 해당 애니메이션의 기즈모가 y축이 forward로 되어있기 때문이다.

 

 

 

 

위의 사진은 달리기 모션이고 계산결과는 아래와 같다.

foot_speed = (-28.533447 - 16.474468) / (0.3 - 0.16)
           = -321.485

 

반올림시 대략 350이 나온다.

같은 방법으로 걷기 모션을 계산하면 반올림시 대략 150이 나온다.

현재 예제에서만 해당하는 내용이고 다른 애니메이션을 사용한다면 따로 계산해봐야 할 것이다.

 

50단위가 되기때문에 최대속력인 350에서 50을 나누면 7개의 그리드로 설정이 가능하다는 것을 알 수 있다.

 

 

 

블렌드 스페이스를 조금 더 세부적으로 만들게 되었다.

 

[169. Spawning Particle Effects]

 

SpawnEmitterAtLocation 말고도 SpawnEmitterAttached로 파티클을 재생시킬 수 있다.

 

[170. Player View Point]

 

void GetPlayerViewPoint(FVector out_Location, FRotator out_Rotation);

 

플레이어의 시점 정보를 레퍼런스에 담아서 준다.

AI라면 눈에 해당하는 시점, 사람이라면 카메라의 시점을 의미한다.

 

[171. Line Tracing By Channel]

 

플레이어 시점의 시작점(카메라 위치), 시작점+(방향벡터 * 길이)로 라인 트레이싱을 한다면 카메라의 정 중앙에 라인 트레이스롤 쏜다.

 

[181. Checking AI Line Of Sight]

 

bool LineOfSightTo(const AActor* Other, FVector ViewPoint, bool bAlternateChecks);

 

AIController를 소유한 액터의 ViewPoint에서 Other의 위치에 라인 트레이싱을 해서 충돌 검출시 true를 반환해준다.

 

 

[182. BehaviorTrees And Blackboards]

 

블랙보드는 AI의 기억과 비슷한 개념이다.

행동 트리는 AIController가 사용하거나 실행할 수 있는 것이다.

 

void RunBehaviorTree(UBehaviorTree* BTAsset);
// 행동 트리를 실행한다.

 

행동 트리는 런타임 도중에 실행 흐름을 확인할 수 있다.

작명 규칙은 보통 행동 트리는 BT_[이름], 블랙보드는 BB_[이름]으로 짓는다.

 

[183. C++에서 칠판 키 설정하기]

 

블랙보드는 <key, value> 형식으로 관리된다. key는 Entry Name, value는 실제 값이다.

C++에서 블랙보드에 접근하려면 GetBlackboardComponent() 함수를 사용해주면 된다.

 

GetBlackboardComponent()->SetValueAsVector(
    TEXT("PlayerLocation"), // BB에서 만든 key 이름
    PlayerPawn->GetActorLocation() // value
);

 

[185. BT Decorators And Selectors]

 

노드에 데코레이터를 붙여서 조건에 따라 실행 여부를 결정할 수 있다. 일종의 if이다.

 

위와 같은 경우는 Blackboard의 key인 PlayerLocation에 value가 설정되어있는지를 체크하는 부분이다.

설정 여부는 value의 존재 유무로 판단한다 (SetValue, ClearValue)

 

기본적으로 관찰자 중단이 None으로 설정되어 있다면 노드의 실행 도중에 조건이 바뀌어도 노드를 재시작하지 않는다.

하지만 그 외의 값으로 설정되어 있다면 노드의 실행을 중단하고 재시작한다.

 

Notify Observer

  • On Result Change: 조건이 변경될 때마다 재시작한다.
  • On Value Change: 관찰중인 블랙보드의 값이 변경될 때마다 재시작한다.

 

Observer Aborts

  • None: 아무것도 중단하지 않는다.
  • Self: 자신과 이 노드 아래 실행중인 서브트리도 중단한다.
  • Lower Priority: 오른쪽에 있는 모든 노드를 중단한다.
  • Both: Self + Lower Priority

 

[187. Executing BTTasks]

 

FName UBTTask_BlackboardBase::GetSelectedBlackboardKey();
// UBTTask_BlackboardBase에서 선택된 블랙보드 키를 가져온다.

 

 

ExecuteTask 함수를 오버라이딩해서 구현하면 된다.

 

키의 값을 추적해야 할 필요가 있는 경우에는 BTTask_BlackboardBase를 상속받아서 만들고 추적할 필요가 없는 경우에는 BTTaskNode를 상속받아서 만들면 된다.

 

[188. BTTasks That Use The Pawn]

 

Move To 노드의 디테일 패널에서 Observe Blackboard Value가 false라면 이동 목표값(Blackboard Key)이 변하더라도 해당 작업을 끝까지 수행한다. true라면 이동 목표값이 변하면 노드를 재실행한다.

데코레이터 노드의 Notify Observer와 비슷한 개념으로 보인다.

 

 

AAIController* UBehaviorTreeComponent::GetAIOwner();

 

ExecuteTask의 매개변수인 OwnerComp를 통해 블랙보드 뿐만 아니라 AIController에 접근할 수 있다.

이 컨트롤러로 빙의중인 Pawn을 받아오는것도 당연히 가능하다.

 

[189. BTServices In C++]

 

행동 트리의 노드가 실행되는동안 매 틱마다 실행된다.

 

[190. Ignoring Actors In Line Traces]

 

FCollisionParams으로 라인 트레이싱 등의 충돌 검사시 충돌 규칙을 정할 수 있다.

특정 액터나 컴포넌트와의 충돌을 무시할 수 있다.

 

FCollisionQueryParams Params;
Params.AddignoredActor(this);

 

 

DetachFromControllerPendingDestory();
// 컨트롤러에서 폰을 안전하게 분리한다.

 

AI는 컨트롤러가 없어지면 행동 트리도 실행이 중지된다.

 

[191. Ending The Game]

 

게임모드는 보통 서버에만 존재하기 때문에 UI나 재시작 등의 로직은 플레이어 컨트롤러에서 실행되어야 한다.

 

현재 프로젝트의 로직은 ShooterPawn이 게임모드의 함수를 호출하는 방식을 택하고 있다.

이게 맞는 방법인지는 아직 잘 모르겠다.

 

현재 호출은 이런식으로 이루어진다.

 

추가로 만약 UE5로 진행중이었다면 Standalone으로 실행해야 에러가 발생하지 않는다.

 

[193. Displaying A Lose Screen]

 

일반적으로 UI는 블루프린트로 작성하는게 훨씬 쉽다.

UI가 매우 복잡해지지 않는 이상 C++로 작성하는것이 크게 이점이 있지는 않다.

 

[194. Iterating Other Actors]

 

TActorRange<AController>(GetWorld());

 

월드 내에 존재하는 AController를 모두 가져온다.

GetAllActorsOfClass와 유사한 것으로 보인다.

 

[195. Calculating The Win Condition]

 

죽은 캐릭터가 플레이어 컨트롤러를 소유중인지, 맵에 AI 컨트롤러가 존재하지 않는지 등의 여부를 검사해서 게임 종료 조건을 설정할 수 있다.

 

[197. Weapon Sound Effects]

 

총구 화염 파티클을 총구에 붙였던것처럼 사운드도 특정 컴포넌트에 붙여서 재생시킬 수 있다.

 

[198. Randomized Sound Cues]

 

사운드 큐는 일종의 머티리얼과 비슷한 개념으로 보인다.

랜덤한 소리를 재생하거나 음향효과를 만드는 등 편집된 애셋이다.

 

 

[199. Sound Spatialization]

 

소리에 입체감을 주기 위해서는 감쇠(Attenuation)와 공간화(Spatialization)를 지속시키는 것이 매우 중요하다.

사운드 큐에 사운드 감쇠 애셋을 적용시켜서 입체감을 준다.

 

 

[202. Aim Offsets]

 

에임 오프셋은 블렌드 스페이스와 비슷하게 생겼다.

Yaw, Pitch 두 값을 이용해서 두 개의 애니메이션을 블렌드한다.

 

단순히 컨트롤러의 회전값으로만 pitch값을 설정하면 위를 바라보는것은 되지만 아래를 보는것은 문제가 생긴다.

 

언리얼에서 회전자는 기본적으로 음수로 내려가지 않고 0˚~360˚에서만 계속 반복된다. (최단경로 고려X)

그래서 0˚ 아래로 내리는 순간 360˚로 값이 바뀌기 때문에 총구가 갑자기 하늘을 향하게 된다.

수학적으로도 -90˚ = 270˚ 이기 때문에 틀린것은 아니다. 다만 최단경로를 고려할때는 -90˚를 꼭 사용해야 한다.

회전자는 정규화를 시키면 최단경로를 고려하여 값을 반환해주기 때문에 음수도 정상적으로 나오게 된다.

 

 

GetControlRotation().GetNormalize().Pitch;

 

원하는 값을 구하려면 컨트롤러의 회전자를 정규화 시켜서 pitch값을 사용하면 되지만 기본적으로 제공되는 블루프린트 함수는 단일 회전자를 정규화 시켜주는게 없다.

 

그래서 Delta를 사용하는데, 컨트롤러의 회전자(A)에 항상 pitch가 0인 액터의 회전자(B)를 빼서 정규화를 시키고 pitch값을 가져온다면 언제나 컨트롤러의 pitch값이 나오게된다.

 

 

AI의 행동 트리는 플레이어의 위치 벡터만 가지고 있기 때문에 고저차가 존재하는 곳에서는 회전값이 없어서 직선으로만 발사하므로 정상적인 전투를 하지 못한다.

Vector 대신 Object(Actor)로 변경하면 MoveTo에 의해 고저차에 의한 회전자값까지 적용되어서 플레이어를 정확히 조준하게 된다.

'언리얼 엔진 > UE5 C++' 카테고리의 다른 글

[UE5 C++] Toon Tanks (v2)  (0) 2022.10.04
[UE5 C++] Crypt Raider  (0) 2022.09.29
[UE5 C++] Obstacle Assalult  (0) 2022.09.28
[UE5 C++] Warehouse Wreckage  (0) 2022.09.28

[개요]

  • 이동 가능한 탱크 생성
  • WASD 이동과 마우스 클릭 액션
  • 적 포탑 생성
  • 발사체 생성
  • 체력, 대미지, 파괴
  • 이펙트 추가
  • 승/패 UI 출력

 

[프로젝트 설정]

  • 프로젝트를 다운로드 받아서 맞는 버전으로 포팅

 

[126. Using the Mouse Cursor]

 

if (PlayerControllerRef)
{
    FHitResult HitResult;
    // Line tracing과 충돌이 검출될 채널, 추적 복잡성 여부, 충돌체
    PlayerControllerRef->GetHitResultUnderCursor(ECollisionChannel::ECC_Visibility, false, HitResult);
    DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, 25.f, 12, FColor::Red, false);
}

 

마우스 커서가 위치한곳에 Line tracing을 해서 최초로 충돌하는 오브젝트의 정보를 레퍼런스로 반환받는다.

2번째 인자인 bTraceComplex는 메시의 단순 콜리전, 복합 콜리전과 관계가 있는 것으로 보인다.

아주 정교한 위치가 필요한 경우가 아니라면 false를 사용해 주는 것이 연산량이 적을 것이다.

 

[127. Rotating the Turret]

 

FVector를 FRotator로 변환시킬 수 있다.

 

FVector ToTarget = LookAtTarget - TurretMesh->GetComponentLocation();
FRotator LookAtRotation = FRotator(0.f, ToTarget.Rotation().Yaw, 0.f);
TurretMesh->SetWorldRotation(LookAtRotation);

 

회전보간을 통해서 한번에 스냅되는게 아닌 조금 더 자연스러운 포탑 회전을 만들 수 있다.

void ABasePawn::RotateTurret(FVector LookAtTarget)
{
    FVector ToTarget = LookAtTarget - TurretMesh->GetComponentLocation();
    FRotator LookAtRotation = FRotator(0.f, ToTarget.Rotation().Yaw, 0.f);
        TurretMesh->SetWorldRotation(
            FMath::RInterpTo(TurretMesh->GetComponentRotation(),
            LookAtRotation, 
            UGameplayStatics::GetWorldDeltaSeconds(this),
        	25.f)
    );
}

 

[130. Timer]

 

타이머 핸들에 함수를 바인딩 시켜서 일정 시간 뒤에 실행시키거나 일정 시간마다 반복 실행 시킬 수 있다.

 

[132. Spawning The Projectile]

 

SpawnActor는 새로운 액터를 월드에 생성시켜준다. 템플릿에는 어떤 클래스를 만들어 줄 것인지를 지정하고 첫 번째 인자인 UClass에는 블루프린트도 들어갈 수 있기때문에 의미가 다르다.

 

예를 들어 C++ 클래스를 블루프린트로 파생시켜서 만들고 코드상에서는 스태틱 메시를 따로 지정하지 않는다면 C++ 클래스를 SpawnActor 하게될 시 보이지 않는 액터가 생성이 될 것이다. 하지만 스태틱 메시를 포함한 다른 값들이 지정된 블루프린트를 SpawnActor 한다면 정상적으로 잘 보일것이다.

 

[133. Projectile Movement Component]

 

발사체를 이동시키는 방법은 크게 3가지가 있다.

  • 매 틱마다 Transform을 변화시키는 것 (Set Location & Rotation)
  • 물리를 적용시켜서 운동량을 변화시키는 것 (Add Impulse)
  • 발사체 전용 Movement Component 사용

 

언리얼에는 발사체를 위한 Movement 컴포넌트가 따로 존재한다.

발사체에 컴포넌트를 붙여주고 속도 설정만 해주면 된다.

 

[134. Hit Events]

 

어떤 Primitive Component와 충돌 이벤트(Hit, OnOverlap)가 발생했을 때 바인딩 시킨 메소드들을 호출할 수 있다.

 

ProjectileMesh->OnComponentHit.AddDynamic(this, &AProjectile::OnHit);


void AProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
    // do something
}

 

매개변수 형식이 미리 지정되어 있으므로 반드시 위의 매개변수 형식을 지켜야 한다.

이벤트 발생시 자동으로 호출해준다.

 

만약 코드 작성 직후 작동하지 않는다면 에디터를 끄고 IDE에서 빌드를 다시 해주고 실행하면 된다.

 

[135. Health Component]

 

체력이나 그외 공통적인 부분은 액터 컴포넌트로 따로 만들어서 관리하는 것이 재사용성에도 좋고 코드가 길어지지 않아서 가독성에도 좋다.

 

[136. Applying Damage]

 

투사체를 생성할 때 단순히 생성만으로 끝나면 누구의 투사체가 데미지를 주었는지를 알 수가 없다.

SpawnActor의 반환값은 AActor이므로 소유자를 설정해서 처리해야한다.

 

auto Projectile = GetWorld()->SpawnActor<AProjectile>(ProjectileClass, Transform);
Projectile->SetOwner(this);

 

ApplyDamage의 매개변수중에 데미지를 입힌 주체의 Controller를 요구하는데 이때 Owner가 필요하다.

 

ApplyDamage & TakeDamage 추가 정보 : https://erikanes.tistory.com/352

 

[137. The Game Mode Class]

 

게임 모드는 게임의 규칙이나 승리 조건을 처리할 수 있다.

GameMode는 GameModeBase를 상속하고 멀티플레이에 관한 기능 등 좀더 다양한 기능이 있다.

 

[138. Handling Pawn Death]

 

죽음에 대한 처리는 게임모드에 의해 처리되도록 한다.

 

Tank->DisableInput(TankPlayerController); // 입력 비활성화
TankPlayerController->bShowMouseCursor = false; // 마우스 커서 표시 비활성화

 

[139. Custom Player Controller]

 

임의의 플레이어 컨트롤러를 만들어서 게임모드에 오버라이딩 시킬 수 있다.

 

[140. Starting The Game]

 

TimerManager의 SetTimer 매개변수중에 FTimerDelegate 라는 것이 있다.

https://erikanes.tistory.com/353

위의 글을 참고하면 된다.

 

[143. Displaying Countdown Time]

 

UI 구성요소를 변수화 시켜서 그래프에 노출시킬 수 있다. 

 

 

타이머가 0초 미만으로 내려가면 더 이상 필요없는 위젯이므로 제거해준다.

 

[144. Winning And Losing]

 

TArray<AActor*> Towers;
UGameplayStatics::GetAllActorsOfClass(this, ATower::StaticClass(), Towers);

 

월드에 존재하는 특정 액터들을 TArray에 반환받을 수 있다.

태그, 클래스+태그, 인터페이스까지 총 4가지의 함수가 있다.

 

[145. Game Over HUD]

 

위젯은 AddToViewport를 통해서 화면에 출력될 수 있다.

 

 

위젯의 구성요소들은 변수로 변환이 되고 외부에서 접근이 가능하다.

 

[147. Smoke Trail]

 

UParticleSystem은 파티클 애셋, UParticleSystemComponent는 UParticleSystem을 관리하는 컴포넌트이다. 둘의 차이가 있다.

UParticleSystem은 UGamePlaystatics::SpawnEmitterAtLocation을 통해 월드에 스폰되거나 UParticleSystemComponent를 통해 제어될 수 있다.

 

[150. Camera Shake]

 

언리얼은 카메라 흔들림에 대한 클래스가 이미 만들어져있다.

여기서는 마티네 카메라 셰이크에서 파생된 블루프린트 클래스를 사용한다.

 

 

블루프린트에서 속성을 지정해주고 C++ 코드에서 해당 카메라 셰이크를 재생시 설정된 값으로 흔들린다.

카메라 셰이크를 C++ 코드로 만들어줄수도 있지만 단순히 값만 지정하는거라서 블루프린트로 만들고 관리하는것이 편해보인다.

 

if (HitCameraShakeClass)
{
    APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
    PlayerController->ClientPlayCameraShake(HitCameraShakeClass);
}

 

마티네 카메라 셰이크에는 카메라를 어떻게 흔들지에 대한 정보가 담길뿐이고 실제로 카메라를 흔드는것은 플레이어 컨트롤러이다. 

 

// 특정 플레이어의 카메라 흔들기
APlayerController::ClientPlayCameraShake

// 범위 내 거리에 따른 카메라 흔들기
UGameplayStatics::PlayWorldCameraShake

 

[151. Polish And Wrap-Up]

 

몇 가지 문제를 수정하고 마무리 한다.

 

1. 카메라 무빙

SpringArm의 CameraLag, CameraRotationLag을 활성화 시켜주면 포탑의 회전이 보간되어서 자연스러워진 것처럼 약간의 딜레이가 생기기 때문에 카메라의 움직임이 부드러워진다. 값을 낮출수록 딜레이가 더 길어진다.

 

 

2. 플레이어가 죽은뒤에도 계속 공격하는 타워

 

void ATank::HandleDestruction()
{
    Super::HandleDestruction();
    SetActorHiddenInGame(true);
    SetActorTickEnabled(false);
}

 

단순히 액터를 숨긴다고 실제로 월드에서 비활성화 되는것은 아니다. 눈에 안보일뿐이지 타워는 계속 플레이어를 인지한다.

추가로 콜리전까지 꺼주면 콜리전 이벤트에서 검출되지 않을것이다.

 

다만 이 실습에서는 콜리전 이벤트를 통한 공격이 이루어지는것이 아닌 타겟(플레이어)를 처음부터 설정해놓고 매 틱마다 추적하는 방식이기때문에 플레이어에게 생존여부를 알수있는 bool값을 추가해서 죽은 상태라면 타워가 플레이어를 추적하거나 발사하지 않는 방식을 사용해야한다.

 

OnComponentBeginOverlap같은 이벤트라면 콜리전을 없애는것 만으로도 해결이 될 것이라고 생각된다. 

'언리얼 엔진 > UE5 C++' 카테고리의 다른 글

[UE5 C++] Simple Shooter -完-  (0) 2022.10.12
[UE5 C++] Crypt Raider  (0) 2022.09.29
[UE5 C++] Obstacle Assalult  (0) 2022.09.28
[UE5 C++] Warehouse Wreckage  (0) 2022.09.28

[개요]

  • 루멘 조명을 포함한 레벨 디자인
  • 문에 적용할 Mover 컴포넌트 제작
  • 플레이어에 적용할 Grabber 컴포넌트 제작
  • 블루프린트에서 Grabber 호출 (C++ 함수를 블루프린트에서 호출)
  • Pressure Plate 컴포넌트 제작

 

[프로젝트 설정]

  • 1인칭 슈팅 프로젝트 이름 CryptRaider
  • 마켓플레이스에서 [Medieval Dungeon] 다운로드 및 프로젝트에 추가
  • 빈 레벨을 만들고 Dungeon으로 저장 및 기본 레벨로 설정

 

[72. Light Types]

 

  • Directional Light: 배치되는 위치는 상관이 없다. 거리가 무한한 빛. 회전으로 방향 조정가능 (=태양광)
  • Point Light: 회전이 별로 의미가 없다 (=전구)
  • Spot Light: 회전에 따라 빛의 방향이 바뀐다 (=손전등)
  • Rect Light: Point Light + Spot Light를 합친듯한 효과이다
  • Sky Light: 주위 환경을 캡처하여 라이트로 사용한다

 

Sky Sphere와 Directional Light를 연동시켜서 하늘에 태양을 띄울 수 있다. Directional Light를 회전시킨 후 Refresh Material을 누르면 태양의 위치가 바뀐다.

 

Sky Light가 적용되지 않는것 같다면 리캡처를 눌러주면 된다.

 

완전 폐쇄된 공간이 아닌이상 Sky Sphere + Directional Light + Sky Light 세가지를 같이 써줘야 할 것 같다.

 

[73. Lumen & Light Bleed]

 

Lumen은 UE5에서 새로 생긴 기능이다.

픽셀 뎁스 오프셋은 루멘이 지원하지 않기때문에 그림자가 이상하게 생기므로 머티리얼에서 연결 핀을 제거해주어야 한다.

 

픽셀 뎁스 오프셋 적용(좌) / 미적용(우)

 

[74. Level Lighting]

 

아웃라이너에서 변경한 값들을 블루프린트에 일괄적용 시킬 수 있다.

 

[77. C++ Actor Component]

 

씬 컴포넌트 : 계층 구조를 가지는 컴포넌트. 트랜스폼을 가지고 있다.

액터 컴포넌트 : 기능만 가지는 컴포넌트. 트랜스폼이 없다.

 

씬 컴포넌트는 액터 컴포넌트를 상속받고 트랜스폼을 추가한 형태이다.

 

같은 기능이라도 상황에 따라 상속 또는 액터 컴포넌트를 적절히 골라야 할 것 같다.

 

[78. Pointer Types & GetOwner()]

 

액터 컴포넌트에서 GetOwner를 통해 컴포넌트를 소유중인 액터를 받아올 수 있다.

 

[80. Linkers, Headers and Includes]

 

Code -> Unreal Header Tools(UHT) -> Preprocessor(전처리기) -> Compiler(컴파일러) -> Linker(링커) -> Executable

Preprocessor ~ Linker는 Standard C++ Compilation이다.

단계가 여러개로 나뉜 덕분에 컴파일 시간이 단축이 된다.

 

[87. Const References & Out Parameters]

 

함수 파라미터로 레퍼런스가 전달되었다면 함수가 그 값에 출력할 것으로 기대되는 경우 이름 앞에 Out 접두사를 붙여야한다.

 

[92. DrawDebugSphere()]

 

HitResult.Location(초록색) / HitResult.ImpactPoint(빨간색)

 

HitResult에 Location과 ImpactPoint가 있고 약간의 차이가 있다.

Location은 충돌 대상과 trace shape가 접촉했을 때 trace shape의 원점 월드 좌표이다.

ImpactPoint는 충돌 대상과 trace shape가 접촉하는 월드 좌표이다.

 

[93. Grabbing With Physics Handle]

 

어떤 오브젝트를 잡기 위해서 PhysicsHandle이라는 액터 컴포넌트를 사용할 수 있다.

PhysicsHandle::GrabComponent()를 호출하여 물리가 적용된 오브젝트를 잡을 수 있다. (피직스 시뮬레이터가 켜져있어야함)

해당 오브젝트를 잡았다고 해서 계층구조가 변하지는 않는다.

 

[94. Waking Physics Objects]

 

물리학 시뮬레이션이 켜져있는 오브젝트는 일정 시간 가만히 있으면 절전 모드로 전환된다.

UPrimitiveComponent::WakeAllRigidBodies()를 호출하여 깨울 수 있다.

 

PhysicsHandle::GetGrabbedComponent() : 잡고있는 컴포넌트를 반환한다.

PhysicsHandle::ReleaseComponent() : 컴포넌트를 놓는다.

 

[97. Constructor]

 

생성자는 월드가 시작되기전에 호출된다.

컴포넌트 생성자에 로그출력을 해놓고 에디터에서 컴포넌트를 붙이면 로그가 즉시 출력되는것을 확인할 수 있다.

 

[101. Actor Tags]

 

액터에 태그를 부여해서 특정 액터만을 구분할 수 있다.

 

if (Actor->ActorHasTag("Unlock1"))
{
    // do something
}

 

[107. Level Polish]

 

포스트 프로세스 볼륨의 시야 적응(Exposure) 오버라이딩을 통해서 특정 구역에서 과도하게 어두워지는 현상을 수정할 수 있다.

'언리얼 엔진 > UE5 C++' 카테고리의 다른 글

[UE5 C++] Simple Shooter -完-  (0) 2022.10.12
[UE5 C++] Toon Tanks (v2)  (0) 2022.10.04
[UE5 C++] Obstacle Assalult  (0) 2022.09.28
[UE5 C++] Warehouse Wreckage  (0) 2022.09.28

[개요]

  • 외부 애셋 사용
  • C++에 필요한 툴 설치 (UE5는 VS2022 사용. 닷넷 코어 3.1도 필요함)
  • C++ 기초
  • 움직이고 회전하는 플랫폼 제작

 

[프로젝트 설정]

  • 마켓플레이스에서 [Unreal Learning Kit] 다운로드 및 프로젝트 생성
  • 프로젝트 이름 ObstacleAssault
  • [파일 - 다음으로 현재 레벨 저장] 으로 Main을 만들고 기본 레벨로 설정

 

[42. Compiling a C++ Project]

 

라이브코딩이 활성화 되어있는 도중에는 IDE에서 빌드가 안된다.

에디터에서의 라이브 코딩 단축키 [Ctrl+Alt+F11]

 

[43. UPROPERTY Variables]

 

블루프린트나 에디터에서 값을 변경한 경우 C++의 기본값이 반영되지 않는다.

C++에서 수행한 모든 작업을 재정의한다.

 

[44. Live Coding Issues]

 

라이브 코딩은 에디터의 변경에만 영향을 미치고 디스크에 저장되지 않는 문제가 있다.

라이브 코딩 이후 에디터의 값을 변경하고 저장 후 프로젝트를 재시작 했을 때 해당 값이 사라져있다면 우선 프로젝트를 종료하고 IDE에서 빌드를 하면 된다.

 

라이브 코딩을 끄고 핫 리로드 방식으로 변경할수도 있는데 설정을 바꿔주고나면 에디터를 재시작 해주어야 정상적으로 적용이 된다.

 

[55. Using Member Functions]

 

DeltaTime으로 특정 구간 사이의 반복 이동을 구현할 경우 오차가 발생하기 때문에 단순히 이동거리만을 비교하면 안되고 값을 보정해주어야 한다. 속도가 급격하게 커질경우 오차도 급격하게 커진다.

속도의 법선벡터에 정해진 거리를 곱한 값을 사용하여 보정하면 된다.

 

[58. GameMode]

 

레벨에 들어가서 게임의 규칙을 제어하는 액터이다.

 

 

'언리얼 엔진 > UE5 C++' 카테고리의 다른 글

[UE5 C++] Simple Shooter -完-  (0) 2022.10.12
[UE5 C++] Toon Tanks (v2)  (0) 2022.10.04
[UE5 C++] Crypt Raider  (0) 2022.09.29
[UE5 C++] Warehouse Wreckage  (0) 2022.09.28

[개요]

 

  • 물리학 체험
  • 발사체 생성
  • 레벨, 씬 생성 (BSP사용)
  • 탄약 제한 및 계산
  • 레벨 리로드

 

[프로젝트 설정]

 

  • 빈 프로젝트 [시작용 컨텐츠 포함]
  • 프로젝트 이름 WarehouseWreckage

 

[13. Objects and References]

 

에디터에서 특정 액터를 선택하면 해당 액터에 대한 레퍼런스를 레벨 블루프린트에서 사용할 수 있다. (큐브의 주소 참조)

 

[14. Adding an Impulse]

 

힘(Force) = 질량(Mass) * 가속도(Acceleration)

충격량(Impulse) = 질량 * 속도 변화(Velocity change)

 

AddImpulse에서 Vel Change를 체크하면 질량을 무시하고 속도 변화만 받는다.

 

[19. Rotation Control]

 

폰은 기본적으로 폰의 회전과 카메라 회전이 동기화되어있지 않다.

 

[23. Geometry Brushes (BSP)]

 

지오메트리로 간단한 맵 등을 만들때는 스케일을 조정하기 보다는 브러시 세팅의 값을 조정하는것이 낫다.

스케일을 조정하면 실제 사이즈와 맞지 않기 때문이다.

지오메트리 두개를 겹쳐서 브러시 타입을 Addtive 또는 Subtractive로 설정하면 간단한 성형이 가능하다.

 

 

[25. Actor Components]

 

액터는 여러개의 컴포넌트를 가질 수 있다.

여러개의 정적 메시를 가져서 단일 액터가 될 수 있다.

 

[29. Functions]

 

당연한 얘기겠지만 블루프린트에서도 노드들을 함수로 묶을 수 있다.

그래프의 가독성이 좋아질지는 몰라도 코드는 여전히 확인하기 어려운 것 같다.

 

[31. Pure Functions]

 

함수를 실행해도 영향을 미치지 않는 것. 즉, side effects가 발생하지 않는 것. (ex: getter)

C++에서는 상수화로 구분할 수 있을것이고 블루프린트에서는 핀이 없는것으로 구분한다.

 

 

함수 실행도중 프로젝트에 영향을 주는 요소가 없다면 퓨어를 체크해주면 된다.

'언리얼 엔진 > UE5 C++' 카테고리의 다른 글

[UE5 C++] Simple Shooter -完-  (0) 2022.10.12
[UE5 C++] Toon Tanks (v2)  (0) 2022.10.04
[UE5 C++] Crypt Raider  (0) 2022.09.29
[UE5 C++] Obstacle Assalult  (0) 2022.09.28

+ Recent posts