UE4 C++ 프로파일러


Tags :


스포너 Spawner

앞서 생성해 둔 C02_Mesh들을 스폰해 줄 스포너 액터를 생성해보자
신규 C++클래스 > Actor > C03_Spawner 생성

헤더

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "C03_Spawner.generated.h"

UCLASS()
class U03_BASIC_API AC03_Spawner : public AActor
{
    GENERATED_BODY()
    
private:
    // 블프에디터에서만 수정표시가능
    UPROPERTY(EditDefaultsOnly, Category = "Settings")
        TSubclassOf<class AC02_Mesh> SpawnClasses[3];
public:	
    AC03_Spawner();

protected:
	virtual void BeginPlay() override;

};

TSubClassOf : UClass 유형의 안전성을 보장해 주는 템플릿 클래스 블루프린트 에디터에서 SpawnClasses배열에 AC02_Mesh클래스 유형의 원소 3개를 넣어 줄 수 있게 선언

소스

#include "01_Spawn/C03_Spawner.h"
#include "Global.h"
#include "C02_Mesh.h"

AC03_Spawner::AC03_Spawner()
{

}

void AC03_Spawner::BeginPlay()
{
	Super::BeginPlay();

	for (int32 i = 0; i < 3; i++)
	{
		if (SpawnClasses[i] == nullptr)
			continue;

		FVector location = GetActorLocation();
		FTransform transform;

		transform.SetLocation(FVector(location.X, location.Y + i * 350, location.Z));
		GetWorld()->SpawnActor<AC02_Mesh>(SpawnClasses[i], transform);
	}
}

C03_Spawner의 Location에서 Y축 기준 350씩 떨어진 위치에 SpawnActor함수를 통해 액터들 스폰!
기존에 블프에서 해봤던 SpawnActorFromClass가 C++에선 SpawnActor로 활용된다
11 11_1


프로파일러 Profiler

C03_Spawner를 스폰관련 기본적인 작업을 마무리하고 두번째 C++클래스 디렉토리 02_Profiler를 생성해주자
프로젝트 작업 중 디버깅 오류확인용으로 PrintString이나 Log메세지 출력 함수를 편하게 사용할 수 있게 해줄 빈 C++ 클래스 CLog와 적용해 Test해 볼 액터 클래스C01_Log 생성

빈 C++ 클래스(직렬되지 않는 클래스) : 언리얼엔진에서 생성됐지만 에디터의 콘텐츠브라우저에 보이지 않는다
신규 C++클래스 생성 시 선택하는 부모클래스가 없는 상태이기 때문에 다른 액터나 컴포넌트클래스,등과 달리 최상위 클래스로부터 상속(직렬)되지 않는 클래스라 직렬되지 않는 클래스라고 한다
이게 시리얼라이즈 Serialize와 연관된 건지는 좀 더 알아봐야겠다

출력로그 Log

출력로그에 메세지를 출력해보자
Global.hCLog.h 포함

#include "Utilities/CLog.h"
  • 기본 구현방법
    • 헤더
        #pragma once
        #include "CoreMinimal.h"
        class U03_BASIC_API CLog
        {
        public:
            static void Log(int32 InValue);
        }
      
    • 소스
        #include "Utilities/CLog.h"
        void CLog::Log(int32 InValue)
        { 
            GLog->Log("GamePlay", ELogVerbosity::Display, FString::FromInt(InValue));
        }
      

      ELogVerbosity : 출력될 로그메세지의 특징 Enum - Fatal, Error, Warning, Display, Log, Verbose, VeryVerbose
      Verbose는 ‘장황한’으로 해석되는데 로그메세지를 얼마나 장황하게 출력할지 선택하는것 같다ㅋㅋ

    • C01_Log 소스
        void AC01_Log::BeginPlay()
        {
            Super::BeginPlay();
      
            CLog::Log(10);
        }
      

      12

  • Macro 활용
    출력할 InValue값을 여러 자료형으로 받을 수 있도록 각 자료형별로 함수 오버로딩을 해서 매크로 및 static함수로 활용해보자
    • 헤더
        #pragma once
        #include "CoreMinimal.h"
      
        #define LogLine() { CLog::Log(__FILE__, __FUNCTION__,__LINE__);} // __FILE__ : '현재'의 파일, 함수, 라인넘버 조회 가능
      
        class U03_BASIC_API CLog
        {
        public:
            static void Log(int32 InValue);
            static void Log(float InValue);
            static void Log(const FString&	InValue);		// 인자를 const로 하면 전달하는 과정에서 수정편집이 불가하다
            static void Log(const FVector&	InValue);
            static void Log(const FRotator&	InValue);
            static void Log(const UObject*	InValue);       // Object의 Name 출력
            static void Log(const FString&	InFileName, const FString& InFuncName, int32 InLineNo);	// 디버깅 시 현재 파일, 함수, 라인 출력
        };
      
    • 소스
        #include "Utilities/CLog.h"
        #include "Engine.h"
      
        DEFINE_LOG_CATEGORY_STATIC(GP,Display,All)
      
        void CLog::Log(int32 InValue)
        {    
            //GLog->Log("GamePlay", ELogVerbosity::Display, FString::FromInt(InValue));
            UE_LOG(GP, Display, L"%d", InValue);
        }
        void CLog::Log(float InValue)
        {
            UE_LOG(GP, Display, L"%f", InValue);
        }
        void CLog::Log(const FString& InValue)
        {
            UE_LOG(GP, Display, L"%s", *InValue);	// string._cstr() = *str
        }
        void CLog::Log(const FVector& InValue)
        {
            UE_LOG(GP, Display, L"%s", *InValue.ToString());
        }
        void CLog::Log(const FRotator& InValue)
        {
            UE_LOG(GP, Display, L"%s", *InValue.ToString());
        }
        void CLog::Log(const UObject* InValue)
        {
            FString str;
      
            if (!!InValue)	// !!: Is Not Nullptr) if(InValue != nullptr) 이거 대신 Unreal에선 !!를 활용한다
                str.Append(InValue->GetName());
      
            str.Append(!!InValue ? "Not Null" : "Null");
            UE_LOG(GP, Display, L"%s", *str);
        }
        // 파일명, Function명 LineNo. 표기
        void CLog::Log(const FString& InFileName, const FString & InFuncName, int32 InLineNo)
        {
            // " C:\Unreal\CPP\~CLog.cpp " 형식처럼 디렉토리 주소값 출력되기 때문에 파일명 이전 디렉토리는 삭제해줘야 함
            int32 index = 0, length = 0;
            InFileName.FindLastChar(L'\\', index);
            length = InFileName.Len() - 1;
            FString fileName = InFileName.Right(length - index); // 파일명의 오른쪽을 index만큼 자르기
                  
            UE_LOG(GP, Display, L"%s, %s, %d", *fileName, *InFuncName, InLineNo);
        }
      
    • C01_Log.cpp
        void AC01_Log::BeginPlay()
        {
            Super::BeginPlay();
      
            CLog::Log(10);
            CLog::Log(PI);
            CLog::Log("C01_Log");
            CLog::Log(GetActorLocation());
            CLog::Log(GetActorRotation());
      
            LogLine();
        }
      

      13

스크린메세지 Print

뷰포트에 출력될 스크린메세지도 매크로 및 static함수를 활용해 출력하기

  • 헤더
      #define PrintLine() { CLog::Print(__FILE__, __FUNCTION__,__LINE__);}
    
      {
          ... class U03_BASIC_API CLog 이어서 ...
          static void Print(int32 InValue, int32 InKey = -1, float InDuration = 10.0f, FColor InColor = FColor::Blue);
    
          static void Print(float InValue, int32 InKey = -1, float InDuration = 10.0f, FColor InColor = FColor::Blue);
          static void Print(const FString& InValue, int32 InKey = -1, float InDuration = 10.0f, FColor InColor = FColor::Blue);
          static void Print(const FVector& InValue, int32 InKey = -1, float InDuration = 10.0f, FColor InColor = FColor::Blue);
          static void Print(const FRotator& InValue, int32 InKey = -1, float InDuration = 10.0f, FColor InColor = FColor::Blue);
          static void Print(const UObject* InValue, int32 InKey = -1, float InDuration = 10.0f, FColor InColor = FColor::Blue);
          static void Print(const FString& InFileName, const FString& InFuncName, int32 InLineNo);
      }
    
  • 소스
      #include "Engine.h"
    
      void CLog::Print(int32 InValue, int32 InKey, float InDuration, FColor InColor)
      {
      	// InKey : 디버그 시 스크린에 출력될 메세지 고정 라인넘버 같은 것 ( -1 = 최근 출력된 메세지 다음)
      	// Duration : 표출시간
          GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, FString::FromInt(InValue));
      }
    
      void CLog::Print(float InValue, int32 InKey, float InDuration, FColor InColor)
      {
          GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, FString::SanitizeFloat(InValue));
      }
    
      void CLog::Print(const FString& InValue, int32 InKey, float InDuration, FColor InColor)
      {
          GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, InValue);
      }
    
      void CLog::Print(const FVector& InValue, int32 InKey, float InDuration, FColor InColor)
      {
          GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, InValue.ToString());
      }
    
      void CLog::Print(const FRotator& InValue, int32 InKey , float InDuration, FColor InColor)
      {
          GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, InValue.ToString());
      }
    
      void CLog::Print(const UObject* InValue, int32 InKey, float InDuration, FColor InColor)
      {
      	FString str;
    
          if (!!InValue)	// !!: Is Not Nullptr) if(InValue != nullptr) 이거 대신 Unreal에선 !!를 활용한다
              str.Append(InValue->GetName());
    
          str.Append(!!InValue ? "Not Null" : "Null");
    
          GEngine->AddOnScreenDebugMessage(InKey, InDuration, InColor, str);
      }
    
      void CLog::Print(const FString& InFileName, const FString& InFuncName, int32 InLineNo)
      {
          int32 index = 0, length = 0;
          InFileName.FindLastChar(L'\\', index);
          length = InFileName.Len() - 1;
    
          FString fileName = InFileName.Right(length - index); // 파일명의 오른쪽을 index만큼 자르기
          FString str = FString::Printf(L"%s, %s, %d", *fileName, *InFuncName, InLineNo);
    
          GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Blue, str);
      }
    
  • C01_Log.h
    private:
      float TotalTime;
    
  • C01_Log.cpp
      {
          ... AC01_Log::BeginPlay() 이어서 ...
          CLog::Print(10);
          CLog::Print(PI);
          CLog::Print("C01_Log");
          CLog::Print(GetActorLocation());
          CLog::Print(GetActorRotation());
          CLog::Print(this);
    
          PrintLine();
      }
    
      void AC01_Log::Tick(float DeltaTime)
      {
          Super::Tick(DeltaTime);
    
          TotalTime += DeltaTime;
    
          CLog::Print(TotalTime, 10);
      }
    

    14

DrawDebug

DrawDebug : 모양을 가지고 있는 Debug그리기 - Point, Line, Box, Circle, Sphere, Capsule 등등의 모양을 위치, 크기, 색깔 등 조건에 맞게 그려준다

새 C++ 액터 C02_DrawDebug클래스 생성해서 각각의 Debug들을 그려보자

  • 헤더
      #pragma once
    
      #include "CoreMinimal.h"
      #include "GameFramework/Actor.h"
      #include "C02_DrawDebug.generated.h"
    
      UCLASS()
      class U03_BASIC_API AC02_DrawDebug : public AActor
      {
          GENERATED_BODY()
        
      public:	
          AC02_DrawDebug();
    
      private:
          UPROPERTY(EditAnywhere, Category = "Settings")
              FVector InitLocation[5];
    
          UPROPERTY(EditAnywhere, Category = "Settings")
              FBox Box;
    
      private:
          // 컴포넌트들은 대부분 보통 VisibleAnywhere를 사용
          UPROPERTY(VisibleAnywhere)
              class UPointLightComponent* PointLight;
    
      protected:
          virtual void BeginPlay() override;
    
      public:	
          virtual void Tick(float DeltaTime) override;
    
      private:
          float EaseOutBounce(float x);
      private:
          FVector Location[5];
      };
    
  • 소스
      #include "02_Profiler/C02_DrawDebug.h"
      #include "Global.h"
      #include "Components/PointLightComponent.h"
    
      AC02_DrawDebug::AC02_DrawDebug()
      {
          PrimaryActorTick.bCanEverTick = true;
    
          CHelpers::CreateComponent<UPointLightComponent>(this, &PointLight, "Light");
    
          InitLocation[0] = FVector(0, 0, 0);
          InitLocation[1] = FVector(0, 1000, 0);
          InitLocation[2] = FVector(0, 500, 0);
          InitLocation[3] = FVector(0, 1500, 0);
          InitLocation[4] = FVector(500, 0, 0);
    
          Box = FBox(FVector(-50, -100, -50), FVector(50, 100, 50));
      }
    
      void AC02_DrawDebug::BeginPlay()
      {
          Super::BeginPlay();
        
          PointLight->SetVisibility(false);
      }
    
      void AC02_DrawDebug::Tick(float DeltaTime)
      {
          Super::Tick(DeltaTime);
    
          // 현재 액터의 위치를 기준으로 좌표점 생성
          for (int32 i = 0; i < 5; i++)
              Location[i] = InitLocation[i] + GetActorLocation();
    
          DrawDebugSolidBox(GetWorld(), Location[0] + Box.GetCenter(), Box.GetExtent(), FColor::Red);
          DrawDebugPoint(GetWorld(), Location[1], 100, FColor::Green);	// Point는 Controller가 있는 방향을 향한다
          DrawDebugSphere(GetWorld(), Location[2], 100, 30, FColor::Blue);
          DrawDebugCircle(GetWorld(), Location[3], 100, 50, FColor::Magenta);
          DrawDebugLine(GetWorld(), Location[2], Location[3], FColor::Yellow,false,-1.0f,0,10);
    
          FVector location = Location[2];
    
          location.X += 10;
          location.Y += 10;
          // location.Z += FMath::Sin(GetWorld()->GetTimeSeconds() * 5.0f) * 400.0f;  // 위아래 같은 높낮이로 움직임
    
          location.Z += EaseOutBounce(FMath::Sin(GetWorld()->GetTimeSeconds() * 3.0f)) * 300.0f;  // 바운스되는 것처럼 점점 위아래움직임이 짧아짐
    
          DrawDebugCapsule(GetWorld(), location, 200, 50, FQuat::Identity, FColor::White);
          DrawDebugDirectionalArrow(GetWorld(), Location[3], location, 400, FColor::Black, false, -1, 0, 20);
    
          bool b = FMath::Sign(FMath::Cos(GetWorld()->GetTimeSeconds() * 5.0f)) >= 0;
    
          PointLight->SetVisibility(b);
      }
    
      float AC02_DrawDebug::EaseOutBounce(float x)
      {
          // https://github.com/ai/easings.net 참조
    
          const float n1 = 7.5625f;
          const float d1 = 2.75f;
    
          if (x < 1.0f / d1)
          {
              return n1 * x * x;
          }
          else if (x < 2.0f / d1)
          {
              return n1 * (x -= 1.5f / d1) * x + 0.75f;
          }
          else if (x < 2.5f / d1)
          {
              return n1 * (x -= 2.25f / d1) * x + 0.9375f;
          }
          else
          {
              return n1 * (x -= 2.625f / d1) * x + 0.984375f;
          }
      }
    

EaseOutBounce : 바운스되는 높이값을 비선형함수로 계산하는방식, 캐릭터 추락 시 카메라 흔들림효과에 활용하면 좋을 것 같다

선형움직임(EaseOutBounce() 사용안한 경우) 14_1
비선형움직임(EaseOutBounce() 사용) UE4_CPP_002