초보 코린이의 성장 일지

UE4 Console, Command 본문

언리얼

UE4 Console, Command

코오린이 2023. 4. 5. 12:45

명령을 Console를 통해 내려 동작을 하도록 구현해 볼 것이다.

더보기
#pragma once

#include "CoreMinimal.h"

class EXAMPLE_API FExampleConsoleCommand
{
public:
	FExampleConsoleCommand();
	~FExampleConsoleCommand();
    
private:
	// I가 붙었지만 클래스가 아닌 구조체이다.
	// 연결해줄 변수
	struct IConsoleCommand* Command;
    
private:
	void ExecuteCommand();
#include "ExampleConsoleCommand.h"

#include "GameFramework/Character.h"
#include "HAL/IConsoleManager.h"
#include "Misc/MessageDialog.h"

FExampleConsoleCommand::FExampleConsoleCommand()
{
	// FConsoleCommandDelegate 매개변수가 없는 델리게이트
	FConsoleCommandDelegate command = FConsoleCommandDelegate::CreateRaw(this, &FExampleConsoleCommand::ExecuteCommand);
	// 커멘드명에는 띄어쓰기 사용 x, 
	Command = IConsoleManager::Get().RegisterConsoleCommand(L"ViexMesssageDialog", L"Move actor to position(FVector)", command);
}

FExampleConsoleCommand::~FExampleConsoleCommand()
{
	// 커멘드들이 등록되었는지 확인
	if (!!Command)
		IConsoleManager::Get().UnregisterConsoleObject(Command);
}

void FExampleConsoleCommand::ExecuteCommand()
{
	FText title = FText::FromString("Warning");
	FText context = FText::FromString("Don't moving, Need to position input");

	// 타이틀 제목에 들어갈 문자열 판단하기 위해 & 주소로 받으면된다.  
	FMessageDialog::Debugf(context, &title);

}

1. 입력시 입력한 로그 출력.

 

이번에는 원하는 값을 넣었을때 출력되도록 커멘드를 하나 더 추가해 보겠다.

더보기
#pragma once

#include "CoreMinimal.h"

class EXAMPLE_API FExampleConsoleCommand
{
public:
	FExampleConsoleCommand();
	~FExampleConsoleCommand();

private:
	// I가 붙었지만 클래스가 아닌 구조체이다.
	// 연결해줄 변수
	struct IConsoleCommand* Command;
	struct IConsoleCommand* Command2;


private:
	void ExecuteCommand();

};
#include "ExampleConsoleCommand.h"

#include "GameFramework/Character.h"
#include "HAL/IConsoleManager.h"
#include "Misc/MessageDialog.h"

FExampleConsoleCommand::FExampleConsoleCommand()
{
	// FConsoleCommandDelegate 매개변수가 없는 델리게이트
	FConsoleCommandDelegate command = FConsoleCommandDelegate::CreateRaw(this, &FExampleConsoleCommand::ExecuteCommand);
	// 커멘드명에는 띄어쓰기 사용 x, 
	Command = IConsoleManager::Get().RegisterConsoleCommand(L"ViexMesssageDialog", L"Move actor to position(FVector)", command);

	FConsoleCommandWithArgsDelegate command2;
	command2.BindLambda([&](const TArray<FString>& InArgs)// 람다식 사용, 외부변수 &로 모두 받는다.
	{
		FString content;
		for (FString str : InArgs)
			content += ", " + str;

		FMessageDialog::Debugf(FText::FromString(content));

	});

	Command2 = IConsoleManager::Get().RegisterConsoleCommand(L"ViexMesssageArgs", L"Move actor to position(FVector)", command2);
}

FExampleConsoleCommand::~FExampleConsoleCommand()
{
	// 커멘드들이 등록되었는지 확인
	if (!!Command)
		IConsoleManager::Get().UnregisterConsoleObject(Command);

	if (!!Command2)
		IConsoleManager::Get().UnregisterConsoleObject(Command2);
}

void FExampleConsoleCommand::ExecuteCommand()
{
	FText title = FText::FromString("Warning");
	FText context = FText::FromString("Don't moving, Need to position input");

	// 타이틀 제목에 들어갈 문자열 판단하기 위해 & 주소로 받으면된다.  
	FMessageDialog::Debugf(context, &title);

}

1. 입력한 값을 그대로 출력.

 

수치를 조정해서 캐릭터 위치를 옮겨보도록 해보겠다.

더보기
#pragma once

#include "CoreMinimal.h"

class EXAMPLE_API FExampleConsoleCommand
{
public:
	FExampleConsoleCommand();
	~FExampleConsoleCommand();

private:
	// I가 붙었지만 클래스가 아닌 구조체이다.
	// 연결해줄 변수
	struct IConsoleCommand* Command;
	struct IConsoleCommand* Command2;
	struct IConsoleCommand* Command3;


private:
	void ExecuteCommand();
	// 게임상에 World return 해준다.
	void ExecuteCommand3(const TArray<FString>& InArgs, UWorld* InWorld);

};
#include "ExampleConsoleCommand.h"

#include "GameFramework/Character.h"
#include "HAL/IConsoleManager.h"
#include "Misc/MessageDialog.h"

FExampleConsoleCommand::FExampleConsoleCommand()
{
	// FConsoleCommandDelegate 매개변수가 없는 델리게이트
	FConsoleCommandDelegate command = FConsoleCommandDelegate::CreateRaw(this, &FExampleConsoleCommand::ExecuteCommand);
	// 커멘드명에는 띄어쓰기 사용 x, 
	Command = IConsoleManager::Get().RegisterConsoleCommand(L"ViexMesssageDialog", L"Move actor to position(FVector)", command);

	FConsoleCommandWithArgsDelegate command2;
	command2.BindLambda([&](const TArray<FString>& InArgs)// 람다식 사용, 외부변수 &로 모두 받는다.
	{
		FString content;
		for (FString str : InArgs)
			content += ", " + str;

		FMessageDialog::Debugf(FText::FromString(content));

	});

	Command2 = IConsoleManager::Get().RegisterConsoleCommand(L"ViexMesssageArgs", L"Move actor to position(FVector)", command2);

	FConsoleCommandWithWorldAndArgsDelegate command3;
	command3.BindRaw(this, &FExampleConsoleCommand::ExecuteCommand3);

	Command3 = IConsoleManager::Get().RegisterConsoleCommand(L"MoveToPlayer", L"Move player to position(FVector)", command3);


}

FExampleConsoleCommand::~FExampleConsoleCommand()
{
	// 커멘드들이 등록되었는지 확인
	if (!!Command)
		IConsoleManager::Get().UnregisterConsoleObject(Command);

	if (!!Command2)
		IConsoleManager::Get().UnregisterConsoleObject(Command2);

	if (!!Command3)
		IConsoleManager::Get().UnregisterConsoleObject(Command3);
}

void FExampleConsoleCommand::ExecuteCommand()
{
	FText title = FText::FromString("Warning");
	FText context = FText::FromString("Don't moving, Need to position input");

	// 타이틀 제목에 들어갈 문자열 판단하기 위해 & 주소로 받으면된다.  
	FMessageDialog::Debugf(context, &title);

}

void FExampleConsoleCommand::ExecuteCommand3(const TArray<FString>& InArgs, UWorld* InWorld)
{
	// 포지션 100, 200, 300
	if (InArgs.Num() != 1)
	{
		GLog->Log("FVector format required input");

		return;
	}

	// 문자열 자르기.
	TArray<FString> strs;
	InArgs[0].ParseIntoArray(strs, L",");

	FVector position;
	position.X = FCString::Atof(*strs[0]);
	position.Y = FCString::Atof(*strs[1]);
	position.Z = FCString::Atof(*strs[2]);

	// 명령창을 통해서 콜을 해주는 것이므로, 게임모드인지 구분할수 있다.
	// 에디터나 버튼에서는 구분 못한다. 명령창에서만 가능
	if (FApp::IsGame())
	{
		// 게임모드일때는 플레이어가 빙의되어있을 것, 컨트롤러가 반드시 존재하므로,
		// 빙의된 캐릭터를 찾아준다. 그게 Player
		APlayerController* controller = InWorld->GetFirstPlayerController<APlayerController>();
		ACharacter* character = controller->GetPawn<ACharacter>();

		// 포지션으로 이동시켜준다.
		character->SetActorLocation(position);

		return;
	}

	// 에디터 월드, 현재 에디터 상에 열려있는 레벨 
	for (AActor* actor : InWorld->GetCurrentLevel()->Actors)
	{
		// Actor Name이 PlayerStart를 포함하고 있다면,
		if (!!actor && actor->GetName().Contains("PlayerStart"))
			actor->SetActorLocation(position); // 포지션으로 Player 이동시킴.

	}
}

 

1. 명령에 의하여 플레이어가  포지션에 따라 이동되는걸 볼 수 있다.

2. 게임모드일때나 아닐때 둘다 명령에 의해 이동이 가능하다.

 

자료형을 가지고 다양한 기능을 수행해 볼 것이다.

1. 클래스를 하나 생성해준다.

2. 월드에 배치되어있는 StaticMesh들을 편집해볼 것이다.

 

더보기
#pragma once

#include "CoreMinimal.h"
#include "IDetailCustomization.h"

// 싱글톤으로 만들거라 생성, 소멸자 제거
class EXAMPLE_API FStaticMeshActor_Detail
	: public IDetailCustomization
{

public:
	static TSharedRef<IDetailCustomization> MakeInstance();

public:
	void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
};
#include "ExampleModule.h"
#include "ExampleDubuggerCategory.h"
#include "ExampleConsoleCommand.h"
#include "StaticMeshActor_Detail.h"

#include "Engine/StaticMeshActor.h"

#include "GameplayDebugger.h" // 언리얼 헤더

#define LOCTEXT_NAMESPACE "FExampleModule"

IMPLEMENT_MODULE(FExampleModule, Example)

void FExampleModule::StartupModule()
{
	// Debugger
	{
		IGameplayDebugger::FOnGetCategory category;
		// 선언과 함께 초기화하려면 Create 사용, 그게 아니라면 Bind 사용
		category.BindStatic(&FExampleDubuggerCategory::MakeInstance); // static로 객체가 필요없으므로 주소만 넣어준다.

		// 싱글톤 Get(), Delegete 이므로 이벤트이다.
		// OnGet이 붙으면, static를 MakeInstance로 return 해준다고 생각하면 된다.
		// EGameplayDebuggerCategoryState 어떠한 상황에서 활성화 할껀지 체크
		// 마지막 매개변수는 슬롯으로 내가 원하는 번호에 할당한다.
		IGameplayDebugger::Get().RegisterCategory("Example", category, EGameplayDebuggerCategoryState::EnabledInGameAndSimulate, 5);
		IGameplayDebugger::Get().NotifyCategoriesChanged();
	}

	// Console Command
	{
		// 객체 동적할당 MakeShareable
		ConsoleCommand = MakeShareable(new FExampleConsoleCommand());
	}

	// Property Editor - StaticMeshActor
	{
		// 싱글톤으로 만들어질 객체
		FOnGetDetailCustomizationInstance instance;
		instance.BindStatic(&FStaticMeshActor_Detail::MakeInstance);

		// 모듈에 붙는다.
		FPropertyEditorModule& prop =  FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
		// 문자열 틀리면 위험하기 때문에 StaticClass() 사용하여 타입 return 해준다
		prop.RegisterCustomClassLayout(AStaticMeshActor::StaticClass()->GetFName(), instance);
	}
}

void FExampleModule::ShutdownModule()
{
	// 항상 할당을 했으면, 제거되어야 하는게 있는지 반드시 확인해줘야한다.
	// 그렇지 않으면 에디터상에 항상 남아있는다.
	if (IGameplayDebugger::IsAvailable())
		IGameplayDebugger::Get().UnregisterCategory("Example"); // 카테고리에서 해제

	// 스마트포인터는 자동제거가 되지만, 혹시 모르기 때문에 Reset로 제거해준다.
	if (ConsoleCommand.IsValid())
		ConsoleCommand.Reset();
}

#undef LOCTEXT_NAMESPACE
#pragma once

#include "CoreMinimal.h"
#include "IDetailCustomization.h"

// 싱글톤으로 만들거라 생성, 소멸자 제거
class EXAMPLE_API FStaticMeshActor_Detail
	: public IDetailCustomization
{

public:
	static TSharedRef<IDetailCustomization> MakeInstance();

public:
	void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
};
#include "StaticMeshActor_Detail.h"

TSharedRef<IDetailCustomization> FStaticMeshActor_Detail::MakeInstance()
{
	// 자신의 자료형 return 해준다.
	return MakeShareable(new FStaticMeshActor_Detail());
}

void FStaticMeshActor_Detail::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{

}

1. 싱글톤으로 Static 객체를 찾아서 모듈을 붙여주고, 원하는 기능이 적용되도록 만드는 사전 단계.

 

https://www.youtube.com/watch?v=KuDkXEVnzs4 

 

'언리얼' 카테고리의 다른 글

UE4 StaticMesh  (0) 2023.04.11
UE4 StaticMesh  (0) 2023.04.06
UE4 DrawDebugLine  (0) 2023.04.04
UE4 Slate UI, Plug In  (0) 2023.04.03
UE4 FPS IK, Pistol  (0) 2023.04.01
Comments