Adventure of 빠타박스
article thumbnail
728x90
728x90
SMALL

Unreal Engine 4 /4.26버전으로 수업을 진행합니다. 해당 내용은 수업에 대한 내용과 함께 

개인적인 생각 및 정보를 토대로 글을 씀을 알립니다. 

본 내용에 대하여 상업적으로 이용 및 배포를 금합니다.

이전내용) https://ppatabox.tistory.com/131

 

노션에서 보기

 

https://ppatabox.notion.site/UEC-_12_Light_Collision-ba3c544faa624887abc0612d3e2a47a2


UEC_12_Light_Collision.pdf
1.45MB

 

 

다른 델리게이트를 또하나 만들어 볼 것이다.

 

델리게이션 DECLARE_DELEGATE 부분 들어가서 보면

델리게이트 콤비네이션이라는 헤더가 열린다.

델리게이트 종류가 모여있다.

델리게이트 형식은 항상 디클레어 델리게이트로 시작한다.

DECLARE_DELEGATE

싱글델리게이트 : 델리게이트 하나가 함수 하나를 소유 하는 것

DECLARE_MULTICAST_DELEGATE

멀티캐스트 도 있다.

DECLARE_DYNAMIC_DELEGATE

다이나믹 델리게이트 :

DECLARE_EVENT

이벤트

이것들의 공통 적인 형식은 Parm이 안붙어있다. 파라미터를 갖지 않는 다는 것들

/** Declares a delegate that can only bind to one native function at a time */
#define DECLARE_DELEGATE( DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, void )

/** Declares a broadcast delegate that can bind to multiple native functions simultaneously */
#define DECLARE_MULTICAST_DELEGATE( DelegateName ) FUNC_DECLARE_MULTICAST_DELEGATE( DelegateName, void )

/**
 * Declares a multicast delegate that is meant to only be activated from OwningType
 * NOTE: This behavior is not enforced and this type should be considered deprecated for new delegates, use normal multicast instead
 */
#define DECLARE_EVENT( OwningType, EventName ) FUNC_DECLARE_EVENT( OwningType, EventName, void )

/** Declares a blueprint-accessible delegate that can only bind to one UFUNCTION at a time */
#define DECLARE_DYNAMIC_DELEGATE( DelegateName ) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_DELEGATE( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, , FUNC_CONCAT( *this ), void )

/** Declares a blueprint-accessible broadcast delegate that can bind to multiple native UFUNCTIONs simultaneously */
#define DECLARE_DYNAMIC_MULTICAST_DELEGATE( DelegateName ) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_MULTICAST_DELEGATE( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, , FUNC_CONCAT( *this ), void )

Return은 싱글 캐스트 에만 있다. 멀티 캐스트는 없다.

/** Declares a delegate with return value that can only bind to one native function at a time */
#define DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType )
// 파라미터 없이 리턴만 하는 함수 연결 하는 것 ReturnValueType -> 순서 대로 DelegateName  

/** Declares a blueprint-accessible delegate with return value that can only bind to one UNFUNCTION at a time */
#define DECLARE_DYNAMIC_DELEGATE_RetVal( ReturnValueType, DelegateName ) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_DELEGATE_RETVAL( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, ReturnValueType, , FUNC_CONCAT( *this ), ReturnValueType )

 

OneParam 있는 것들 어떤 타입이든 상관 없이

#define DECLARE_DELEGATE_RetVal_OneParam( ReturnValueType, ReturnValueType, Param1Type ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType, Param1Type )

함수 ReturnValueType ReturnValueType Param1Type 순서대로 돌아간다.

일반적인 델리게이트 파라미터의 변수명을 일치 시킬 필요 없다. 타입만 일치 시키면 된다.

다이나믹 델리게이트는 무조건 파라미터 명 까지 일치해야 한다.

#define DECLARE_DYNAMIC_DELEGATE_OneParam( DelegateName, Param1Type, Param1Name ) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_DELEGATE( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, FUNC_CONCAT( Param1Type InParam1 ), FUNC_CONCAT( *this, InParam1 ), void, Param1Type )
//Param1Name  파라미터 명

C02_Trigger.h

#pragma once

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

DECLARE_DELEGATE(FBoxLightOverlap); //void ___()
DECLARE_DELEGATE_RetVal_OneParam(FString, FBoxLightColorOverlap, FLinearColor);

UCLASS()
class U2110_03_API AC04_Trigger : public AActor
{
	GENERATED_BODY()
	
private:
	UPROPERTY(VisibleDefaultsOnly)
		class USceneComponent* Root;

	UPROPERTY(VisibleDefaultsOnly)
		class UBoxComponent* Box;

	UPROPERTY(VisibleDefaultsOnly)
		class UTextRenderComponent* Text;

public:	
	AC04_Trigger();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	UFUNCTION()
		void OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

public:
	FBoxLightOverlap OnBoxLightBeginOverlap;
	FBoxLightOverlap OnBoxLightEndOverlap;
	FBoxLightColorOverlap OnBoxLightColorOverlap;
};

C02_Trigger.cpp

#include "C04_Trigger.h"
#include "Global.h"
#include "Components/BoxComponent.h"
#include "Components/TextRenderComponent.h"

AC04_Trigger::AC04_Trigger()
{
	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
	CHelpers::CreateComponent<UBoxComponent>(this, &Box, "Box", Root);
	CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Root);


	Box->SetRelativeScale3D(FVector(3));
	Box->bHiddenInGame = false;

	Text->SetRelativeLocation(FVector(0, 0, 100));
	Text->SetRelativeRotation(FRotator(0, 180, 0));
	Text->SetRelativeScale3D(FVector(2));
	Text->TextRenderColor = FColor::Red;
	Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;
	Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));
}

void AC04_Trigger::BeginPlay()
{
	Super::BeginPlay();
	
	Box->OnComponentBeginOverlap.AddDynamic(this, &AC04_Trigger::OnComponentBeginOverlap);
	Box->OnComponentEndOverlap.AddDynamic(this, &AC04_Trigger::OnComponentEndOverlap);
}

void AC04_Trigger::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OnBoxLightBeginOverlap.IsBound())
		OnBoxLightBeginOverlap.Execute();

	if (OnBoxLightColorOverlap.IsBound()) //파라미터도 있고 리턴 밸류도 있다. 
	{
		FLinearColor color = FLinearColor::MakeRandomColor();
		FString str = OnBoxLightColorOverlap.Execute(color); 

		CLog::Print(str);
	} //
}

void AC04_Trigger::OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if (OnBoxLightEndOverlap.IsBound())
		OnBoxLightEndOverlap.Execute();
}

FString str = OnBoxLightColorOverlap.Execute(color);

실행은 똑같은데 현재 연결되어있는 델리게이트를 함수로 호출 해주는 것이다. 파라미터를 이것이 넘겨 주는 것이다.

FString str 델리게이트 연결된 함수에 연결되어 있음

 

C04_Light.h

#pragma once

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

UCLASS()
class U2110_03_API AC04_Light : public AActor
{
	GENERATED_BODY()
	
private:
	UPROPERTY(VisibleDefaultsOnly)
		class USceneComponent* Root;

	UPROPERTY(VisibleDefaultsOnly)
		class UPointLightComponent* PointLight;

	UPROPERTY(VisibleDefaultsOnly)
		class UPointLightComponent* PointLight2;

	UPROPERTY(VisibleDefaultsOnly)
		class UTextRenderComponent* Text;

public:	
	AC04_Light();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void OnLight();

	UFUNCTION()
		void OffLight();

	UFUNCTION()
		FString OnRandomLight(FLinearColor InColor);
};

C04_Light.cpp

#include "C04_Light.h"
#include "Global.h"
#include "Components/PointLightComponent.h"
#include "Components/TextRenderComponent.h"
#include "C04_Trigger.h"

AC04_Light::AC04_Light()
{ //Trigger에서 만듬 으로 PointLight2 생성 
	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
	CHelpers::CreateComponent<UPointLightComponent>(this, &PointLight, "PointLight", Root);
	CHelpers::CreateComponent<UPointLightComponent>(this, &PointLight2, "PointLight2", Root);
	CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Root);


	PointLight->SetRelativeLocation(FVector(0, -50, 0));
	PointLight->LightColor = FColor::Red;
	PointLight->Intensity = 1e+4f; //1 * 10 ^ 4
	PointLight->AttenuationRadius = 200;

	PointLight2->SetRelativeLocation(FVector(0, 50, 0));
	PointLight2->LightColor = FColor::Red;
	PointLight2->Intensity = 1e+4f; //1 * 10 ^ 4
	PointLight2->AttenuationRadius = 200;


	Text->SetRelativeLocation(FVector(0, 0, 100));
	Text->SetRelativeRotation(FRotator(0, 180, 0));
	Text->SetRelativeScale3D(FVector(2));
	Text->TextRenderColor = FColor::Red;
	Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;
	Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));
}

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

	PointLight->SetVisibility(false);
	PointLight2->SetVisibility(false);


	//for (AActor* actor : GetWorld()->GetCurrentLevel()->Actors)
	//{
	//	if (!!actor && actor->IsA<AC04_Trigger>())
	//		CLog::Log(actor->GetName());
	//}

	AC04_Trigger* trigger = CHelpers::FindActor<AC04_Trigger>(GetWorld());
	//CLog::Log(trigger);
	if (!!trigger)
	{
		trigger->OnBoxLightBeginOverlap.BindUFunction(this, "OnLight");
		trigger->OnBoxLightEndOverlap.BindUFunction(this, "OffLight");

		trigger->OnBoxLightColorOverlap.BindUFunction(this, "OnRandomLight");
	}
}

void AC04_Light::OnLight()
{
	PointLight->SetVisibility(true);
}

void AC04_Light::OffLight()
{
	PointLight->SetVisibility(false);
	PointLight2->SetVisibility(false);
}

FString AC04_Light::OnRandomLight(FLinearColor InColor)
{
	PointLight2->SetVisibility(true);
	PointLight2->SetLightColor(InColor);

	return InColor.ToString();
}

들어가서 촤라락 켜질때는이런 델리게이트는 멀티캐스트로 만든다. 그리고 싱글 캐스트는 정확히 1:1 로 맵핑 되어야 하는 UI같은 것들은 사용 하되 일반적으로 대부분 멀티캐스트를 사용한다

C05_MultiTrigger.h

#pragma once

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

DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiLightOverlap, int32, FLinearColor);

UCLASS()
class U2110_03_API AC05_MultiTrigger : public AActor
{
	GENERATED_BODY()
	
private:
	UPROPERTY(VisibleDefaultsOnly)
		class USceneComponent* Root;

	UPROPERTY(VisibleDefaultsOnly)
		class UBoxComponent* Box;

	UPROPERTY(VisibleDefaultsOnly)
		class UTextRenderComponent* Text;

public:	
	AC05_MultiTrigger();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

public:
	FMultiLightOverlap OnMultiLightOverlap;
};

멀티캐스트를 체크하면 이벤트와 리턴밸류가 사라진다. 멀티캐스트는 리턴밸류를 사용할 수 없다. 이곳에서는 여러 함수를 사용할 것인데. 어떠한 함수를 리턴 할지 모른다. 결정할 수 없으니 2개 이상 함수를 연결하는 것은 리턴이 존재 하지 않는다. 이벤트는 기본 리턴이여서 없음

 

DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiLightOverlap, int32, FLinearColor);

C05_MultiTrigger.cpp

#include "C05_MultiTrigger.h"
#include "Global.h"
#include "Components/BoxComponent.h"
#include "Components/TextRenderComponent.h"

AC05_MultiTrigger::AC05_MultiTrigger()
{
	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
	CHelpers::CreateComponent<UBoxComponent>(this, &Box, "Box", Root);
	CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Root);


	Box->SetRelativeScale3D(FVector(3));
	Box->bHiddenInGame = false;

	Text->SetRelativeLocation(FVector(0, 0, 100));
	Text->SetRelativeRotation(FRotator(0, 180, 0));
	Text->SetRelativeScale3D(FVector(2));
	Text->TextRenderColor = FColor::Red;
	Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;
	Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));
}

void AC05_MultiTrigger::BeginPlay()
{
	Super::BeginPlay();
	
	Box->OnComponentBeginOverlap.AddDynamic(this, &AC05_MultiTrigger::OnComponentBeginOverlap);
}

void AC05_MultiTrigger::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	//if (OnMultiLightOverlap.IsBound() == false) return;
	CheckFalse(OnMultiLightOverlap.IsBound()); //매크로 false면 Return


	int32 index = UKismetMathLibrary::RandomIntegerInRange(0, 2);
	FLinearColor color = FLinearColor::MakeRandomColor();

	OnMultiLightOverlap.Broadcast(index, color); // 하나 이상 연결 되었을때 하나일때는 Execute , 모든것에 전파 한다는 개념 Broadcast 
}

//if (OnMultiLightOverlap.IsBound() == false) return; 하나 이상 연결 되어있다면 true가 나온다.

C05_Box.h

#pragma once

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

UCLASS()
class U2110_03_API AC05_Box : public AActor
{
	GENERATED_BODY()
	
private:
	UPROPERTY(VisibleDefaultsOnly)
		class USceneComponent* Root;

	UPROPERTY(VisibleDefaultsOnly)
		class UStaticMeshComponent* Meshes[3];

	UPROPERTY(VisibleDefaultsOnly)
		class UTextRenderComponent* Text;

public:	
	AC05_Box();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void OnPhysics(int32 InIndex, FLinearColor InColor);

private:
	class UMaterialInstanceDynamic* Materials[3];
	FVector WorldLocation[3];
};

C05_Box.cpp

#include "C05_Box.h"
#include "Global.h"
#include "C05_MultiTrigger.h" 
#include "Components/StaticMeshComponent.h"
#include "Components/TextRenderComponent.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialInstanceDynamic.h"

AC05_Box::AC05_Box()
{
	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
	CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Root);

	
	UStaticMesh* mesh;
//언리얼에서 cube에 대한 경로 
	CHelpers::GetAsset<UStaticMesh>(&mesh, "StaticMesh'/Game/Meshes/Cube.Cube'");
	
	for (int32 i = 0; i < 3; i++)
	{
		FString str;
		str.Append("Meshes");
		str.Append(FString::FromInt(i + 1)); // 이름 번호

		CHelpers::CreateComponent<UStaticMeshComponent>(this, &Meshes[i], FName(str), Root);

		Meshes[i]->SetRelativeLocation(FVector(0, i * 150, 0));
		Meshes[i]->SetStaticMesh(mesh);
		Meshes[i]->SetSimulatePhysics(true);
	}

	Text->SetRelativeLocation(FVector(0, 0, 100));
	Text->SetRelativeRotation(FRotator(0, 180, 0));
	Text->SetRelativeScale3D(FVector(2));
	Text->TextRenderColor = FColor::Red;
	Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;
	Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));
}

void AC05_Box::BeginPlay()
{
	Super::BeginPlay();
	
	UMaterialInstanceConstant* material;
//실행때는 컨스트럭션? 
	CHelpers::GetAssetDynamic<UMaterialInstanceConstant>(&material, "MaterialInstanceConstant'/Game/Materials/M_Color_White.M_Color_White'");
	
	for (int32 i = 0; i < 3; i++)
	{
		Materials[i] = UMaterialInstanceDynamic::Create(material, this);
		Materials[i]->SetVectorParameterValue("Color", FLinearColor::White);

		Meshes[i]->SetMaterial(0, Materials[i]); 
		Meshes[i]->SetSimulatePhysics(false);

		FTransform transform = Meshes[i]->GetComponentToWorld(); // 
		WorldLocation[i] = transform.GetLocation();
	}

	AC05_MultiTrigger* trigger = CHelpers::FindActor<AC05_MultiTrigger>(GetWorld());
	CheckNull(trigger);

	trigger->OnMultiLightOverlap.AddUFunction(this, "OnPhysics");
}

void AC05_Box::OnPhysics(int32 InIndex, FLinearColor InColor)
{
//기본값으로 돌려놓기 위함 
	for (int32 i = 0; i < 3; i++)
	{
		Materials[i]->SetVectorParameterValue("Color", FLinearColor::White);
		Meshes[i]->SetSimulatePhysics(false);
		Meshes[i]->SetWorldLocation(WorldLocation[i]);//원래 위치로 돌리기
	}

	Materials[InIndex]->SetVectorParameterValue("Color", InColor);
	Meshes[InIndex]->SetSimulatePhysics(true);
}

찾아서 사용할 헤더들을 체크 해준다.

GetComponentToWorld : Component공간을 World공간으로 바꿔줌. 해당 Component가 있는 World 공간 위치로 리턴해준다. → 위치를 기억 FVector WorldLocation[3];

 

 

멀티캐스트라 하나더 출력 - 스폿라이트도 비슷한 개념

C05_SpotLight.h

#pragma once

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

UCLASS()
class U2110_03_API AC05_SpotLight : public AActor
{
	GENERATED_BODY()
	
private:
	UPROPERTY(VisibleDefaultsOnly)
		class USceneComponent* Root;

	UPROPERTY(VisibleDefaultsOnly)
		class USpotLightComponent* SpotLights[3];

	UPROPERTY(VisibleDefaultsOnly)
		class UTextRenderComponent* Text;

public:	
	AC05_SpotLight();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void OnLight(int32 InIndex, FLinearColor InColor);
};

C05_SpotLight.cpp

#include "C05_SpotLight.h"
#include "Global.h"
#include "Global.h"
#include "C05_MultiTrigger.h"
#include "Components/SpotLightComponent.h"
#include "Components/TextRenderComponent.h"

AC05_SpotLight::AC05_SpotLight()
{
	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
	CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Root);


	for (int32 i = 0; i < 3; i++)
	{
		FString str;
		str.Append("SpotLight");
		str.Append(FString::FromInt(i + 1));

		CHelpers::CreateComponent<USpotLightComponent>(this, &SpotLights[i], FName(str), Root);


		SpotLights[i]->SetRelativeLocation(FVector(0, i * 150, 0));
		SpotLights[i]->SetRelativeRotation(FRotator(-90, 0, 0)); //회전값을 밑으로 
		SpotLights[i]->Intensity = 1e+5f;
		SpotLights[i]->OuterConeAngle = 25; // 각도 조정
	}


	Text->SetRelativeLocation(FVector(0, 0, 100));
	Text->SetRelativeRotation(FRotator(0, 180, 0));
	Text->SetRelativeScale3D(FVector(2));
	Text->TextRenderColor = FColor::Red;
	Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;
	Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));
}

void AC05_SpotLight::BeginPlay()
{
	Super::BeginPlay();
	
	AC05_MultiTrigger* trigger = CHelpers::FindActor<AC05_MultiTrigger>(GetWorld());
	CheckNull(trigger);

	trigger->OnMultiLightOverlap.AddUFunction(this, "OnLight");
}

void AC05_SpotLight::OnLight(int32 InIndex, FLinearColor InColor)
{
	for (int32 i = 0; i < 3; i++)
		SpotLights[i]->SetLightColor(FLinearColor::White);

	SpotLights[InIndex]->SetLightColor(InColor);
}

AddRaw는 UObject가 아니라 C순수한 함수일 때만 다룬다. (UI때 사용할 수 있다) - AddUFunction

 

728x90
728x90
LIST
profile

Adventure of 빠타박스

@PPATABOX

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!