필요한 개념
(싱글 캐스트에서 리턴값 및 파라미터 다루기)
// 이 매크로는 return Type이 있는 매크로
// 1 : 반환 타입 2 : 델리게이트 이름
// #define DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType )
// 파라미터가 한개 있는 매크로(1 ~ 8 파라미터 까지 있음)
// 1 : 델리게이트 이름 2 : 파라미터 타입
#define DECLARE_DELEGATE_OneParam( DelegateName, Param1Type ) FUNC_DECLARE_DELEGATE( DelegateName, void, Param1Type )
// ReturnValue도 하나있고, 파라미터도 하나 있는 것
// 우리는 이것을 쓸 것이다.
// 1 : 리턴타입 2 : 델리게이트 이름 3 : 파라미터 타입
//#define DECLARE_DELEGATE_RetVal_OneParam( ReturnValueType, DelegateName, Param1Type ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType, Param1Type )
-C03_Trigger.h
if (OnBoxLightRandomBeginOverlap.IsBound())
{
FLinearColor color;
// RandomFloatInRange()는 KismetMathLibrary에 있다.
color.R = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.G = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.B = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.A = 1.0f;
// 매크로에서 선언한 파라미터 타입으로 값을 파리미터에 넣어주고 리턴타입에 맞는 리턴값이 리턴됨
// 호출되는 애들에게 이 파라미터가 전달됨
FString str = OnBoxLightRandomBeginOverlap.Execute(color);
CLog::Log(str);
}
-C04_Light.h
UFUNCTION()
FString OnRandomLight(FLinearColor InColor);
함수의 리턴타입과 파라미터 타입을 맞춰줘야 한다.
Tip) 인텔리센스가 너무 느리다 싶으면
프로젝트 속성 -> 솔루션 다시 검사(수행시 조금은 나아짐)
VsualStudio2017을 쓰고 싶다면 에디터 개인설정 -> 일반 -> 소스코드에서 버젼 변경하면 된다.
그래도 도저히 인텔리센스가 안나오면
폴더 : Config, Content, source, 파일 : .unproject을 제외하고 나머지 폴더를 제거한다.(기본만 남기고)
리빌드 하면됨(파일 -> Visual Studio 프로젝트 새로고침)하면 해결이 됨
파일 복사 붙여넣기로 추가 생성시에 인텔리센스 엔진오류가 뜬다.
4.23부터는 파일 생성시 인텔리센스 정보를 만드는 과정이 추가되어 소스 파일을 복사해서 생성하기 보다는
언리얼 에디터를 통해 클래스 파일을 생성해야 인텔리센스가 잘 지원된다.
(멀티 캐스트 다루기)
*Tip) 멀티 캐스트는 RetValue만 없다.
함수에서 리턴은 하나만 일어날 수 있으므로 리턴은 하나만 정의가능
멀티캐스트 델리게이트는 여러개의 함수를 연결하게 되며
어느 함수에서 리턴이 일어날지 결정할 수 없으므로
멀티캐스트 델리게이트에서는 리턴 타입이 있는 델리게이트를 사용할 수 없다.
// 정의는 싱글과 유사
// DECLARE_MULTICAST_DELEGATE()로 멀티캐스트 델리게이트 정의가능
// 멀티캐스트인데 파라미터 2개(1개 ~ 8개)
// 1 : 델리게이트 이름 2 : 파라미터1 타입 3 : 파라미터2 타입
#define DECLARE_MULTICAST_DELEGATE_TwoParams( DelegateName, Param1Type, Param2Type ) FUNC_DECLARE_MULTICAST_DELEGATE( DelegateName, void, Param1Type, Param2Type )
// 멀티캐스트 델리게이트는 호출하는 방법이 다름
if (OnMultiLightBeginOverlap.IsBound())
{
// 0, 1, 2를 사용 위해
int32 index = UKismetMathLibrary::RandomIntegerInRange(0, 2);
FLinearColor color;
color.R = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.G = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.B = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.A = 1.0f;
// 멀티캐스트의 호출은 Execute가 아닌 Broadcast를 사용
// 파라미터가 있다면 파라미터를 넘겨줌
// Broadcast(범위, 터친다) : 연결되어있는 것들을 모두 실행해라. 파라미터를 던져주겠다. 의미
OnMultiLightBeginOverlap.Broadcast(index, color);
}
싱글 캐스트는 BindUFunction()으로 묶었지만, 멀티캐스트는 AddUFunction()으로 함수를 추가한다.
Tip)빌드 완료 소리 없애기
에디터 개인 설정 -> 레벨 에디터 -> 사운드 -> 에디터 사운드 활성화 체크 해제
GetComponentToWorld() : 해당 컴포넌트가 공간에 출력될 월드의 Transform을 리턴(액터의 위치에 상대위치를 더한 world위치를 리턴), SceneComponent에 있다.
Multicast 델리게이트에 함수추가
Multicast에 2개의 클래스 각 함수 1개 총 2개를 연결한 것이됨(Box, SpotLight)
현재 델리게이트에 두 개의 함수를 연결한 상황이므로 델리게이트 콜을 하게 되면 두개의 함수가 연쇄적으로 호출됨
결과를 보면 떨어지면서 라이트와 박스의 색이 같은것임을 확인가능하다.
싱글 캐스트 (파라미터 1개, returnValue)
C03_Trigger.h 추가된 내용
...
// 리턴 타입 1개 + 파라미터 1개
// 1 : 리턴타입 2 : 델리게이트 이름 3 : 파라미터 타입
DECLARE_DELEGATE_RetVal_OneParam(FString, FBoxLightRandomBeginOverlap, FLinearColor);
UCLASS()
class UONLINE_03_CPP_API AC03_Trigger : public AActor
{
...
public:
FBoxLightRandomBeginOverlap OnBoxLightRandomBeginOverlap;
}
C03_Trigger.cpp 추가된 내용
void AC03_Trigger::ActorBeginOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
...
if (OnBoxLightRandomBeginOverlap.IsBound())
{
FLinearColor color;
// RandomFloatInRange()는 KismetMathLibrary에 있다.
color.R = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.G = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.B = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.A = 1.0f;
// 매크로에서 선언한 파라미터 타입으로 값을 파리미터에 넣어주고 리턴타입에 맞는 리턴값이 리턴됨
// 호출되는 애들에게 이 파라미터가 전달됨
FString str = OnBoxLightRandomBeginOverlap.Execute(color);
CLog::Log(str);
}
}
04_Light.h 추가된 내용
...
UCLASS()
class UONLINE_03_CPP_API AC04_Light : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleDefaultsOnly)
class UPointLightComponent* Light2;
...
private:
...
UFUNCTION()
FString OnRandomLight(FLinearColor InColor);
};
04_Light.cpp 추가된 내용
...
AC04_Light::AC04_Light()
{
...
// Light에 상대적으로 200 이동하게
Light2->SetRelativeLocation(FVector(0, 200, 0));
Light2->Intensity = 1e+4f;
Light2->AttenuationRadius = 200;
Light2->LightColor = FColor(255, 128, 50);
}
void AC04_Light::BeginPlay()
{
...
trigger->OnBoxLightRandomBeginOverlap.BindUFunction(this, "OnRandomLight");
}
void AC04_Light::OffLight()
{
...
Light2->SetVisibility(false);
}
FString AC04_Light::OnRandomLight(FLinearColor InColor)
{
Light2->SetVisibility(true);
Light2->SetLightColor(InColor);
// 컬러의 값을 문자열로 리턴
return InColor.ToString();
}
멀티캐스트(Box, SpotLight 연결)
C05_MultiTrigger.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "C05_MulticastTrigger.generated.h"
//DECLARE_MULTICAST_DELEGATE()로 멀티캐스트 델리게이트 정의가능
DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiLightBeginOverlap, int32, FLinearColor);
UCLASS()
class UONLINE_03_CPP_API AC05_MulticastTrigger : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleDefaultsOnly)
class USceneComponent* Scene;
UPROPERTY(VisibleDefaultsOnly)
class UBoxComponent* Box;
UPROPERTY(VisibleDefaultsOnly)
class UTextRenderComponent* Text;
public:
AC05_MulticastTrigger();
protected:
virtual void BeginPlay() override;
private:
UFUNCTION()
void ActorBeginOverlap(AActor* OverlappedActor, AActor* OtherActor);
UFUNCTION()
void ActorEndOverlap(AActor* OverlappedActor, AActor* OtherActor);
public:
FMultiLightBeginOverlap OnMultiLightBeginOverlap;
};
C05_MultiTrigger.cpp
#include "C05_MulticastTrigger.h"
#include "Global.h"
#include "Components/BoxComponent.h"
#include "Components/TextRenderComponent.h"
AC05_MulticastTrigger::AC05_MulticastTrigger()
{
CHelpers::CreateComponent<USceneComponent>(this, &Scene, "Scene");
CHelpers::CreateComponent<UBoxComponent>(this, &Box, "Box", Scene);
CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Scene);
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());
}
void AC05_MulticastTrigger::BeginPlay()
{
Super::BeginPlay();
OnActorBeginOverlap.AddDynamic(this, &AC05_MulticastTrigger::ActorBeginOverlap);
OnActorEndOverlap.AddDynamic(this, &AC05_MulticastTrigger::ActorEndOverlap);
}
void AC05_MulticastTrigger::ActorBeginOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
// 멀티캐스트 델리게이트는 호출하는 방법이 다름
if (OnMultiLightBeginOverlap.IsBound())
{
// 0, 1, 2를 사용 위해
int32 index = UKismetMathLibrary::RandomIntegerInRange(0, 2);
FLinearColor color;
color.R = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.G = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.B = UKismetMathLibrary::RandomFloatInRange(0, 1);
color.A = 1.0f;
// 멀티캐스트의 호출은 Execute가 아닌 Broadcast를 사용
// 파라미터가 있다면 파라미터를 넘겨줌
OnMultiLightBeginOverlap.Broadcast(index, color);
}
}
void AC05_MulticastTrigger::ActorEndOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
}
C06_Box.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "C06_Box.generated.h"
UCLASS()
class UONLINE_03_CPP_API AC06_Box : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleDefaultsOnly)
class USceneComponent* Scene;
UPROPERTY(VisibleDefaultsOnly)
class UTextRenderComponent* Text;
UPROPERTY(VisibleDefaultsOnly)
class UStaticMeshComponent* Mesh[3];
public:
AC06_Box();
protected:
virtual void BeginPlay() override;
private:
UFUNCTION()
void OnPhysics(int32 InIndex, FLinearColor InColor);
private:
// 저장할 메터리얼
UMaterialInstanceDynamic* Materials[3];
// 최초 배치되어있던 Mesh의 월드 위치 저장
FVector WorldLocation[3];
};
C06_Box.cpp
#include "C06_Box.h"
#include "Global.h"
#include "C05_MulticastTrigger.h"
#include "Components/TextRenderComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialInstanceDynamic.h"
AC06_Box::AC06_Box()
{
CHelpers::CreateComponent<USceneComponent>(this, &Scene, "Scene");
CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Scene);
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());
UStaticMesh* mesh;
CHelpers::GetAsset<UStaticMesh>(&mesh, "StaticMesh'/Game/Meshes/M_Cube.M_Cube'");
for (int32 i = 0; i < 3; i++)
{
FString str;
str.Append("Mesh_");
str.Append(FString::FromInt(i + 1));
CHelpers::CreateComponent<UStaticMeshComponent>(this, &Mesh[i], FName(str), Scene);
Mesh[i]->SetRelativeLocation(FVector(0, i * 150, 0));
Mesh[i]->SetStaticMesh(mesh);
// 물리 꺼줌
Mesh[i]->SetSimulatePhysics(false);
}
}
void AC06_Box::BeginPlay()
{
Super::BeginPlay();
TArray<AC05_MulticastTrigger*> triggers;
CHelpers::FindActors<AC05_MulticastTrigger>(GetWorld(), triggers);
// 싱글은 BindUFunction으로 연결했으나 멀티에서는 함수 연결을 추가한다는
// 의미로 AddUFunction을 사용합니다.
triggers[0]->OnMultiLightBeginOverlap.AddUFunction(this, "OnPhysics");
// 메터리얼도 불러옴(Dynamic으로 쓸려고)
UMaterialInstanceConstant* material;
// BeginPlay여서 Dynamic으로 불러온다.
CHelpers::GetAssetDynamic<UMaterialInstanceConstant>(&material, "MaterialInstanceConstant'/Game/Materials/M_Mesh_Inst.M_Mesh_Inst'");
for (int32 i = 0; i < 3; i++)
{
// inouter에는 자기 this
Materials[i] = UMaterialInstanceDynamic::Create(material, this);
Mesh[i]->SetMaterial(0, Materials[i]);
// GetComponentToWorld() : 해당 컴포넌트가 공간에 출력될 월드의 Transform을 리턴(액터의 위치에 상대위치를 더한 world위치를 리턴)
FTransform transform = Mesh[i]->GetComponentToWorld();
// 값 저장
WorldLocation[i] = transform.GetLocation();
}
}
void AC06_Box::OnPhysics(int32 InIndex, FLinearColor InColor)
{
// 각각 총 3개의 물리를 켜서
// 랜덤으로 물리가 켜지고 떨어짐 + 색바뀜
for (int32 i = 0; i < 3; i++)
{
// 전부 초기화
Materials[i]->SetVectorParameterValue("Color", FLinearColor(1, 1, 1));
// 물리 꺼줌
Mesh[i]->SetSimulatePhysics(false);
// 월드로케이션 초기화(원래있던 위치로 이동)
Mesh[i]->SetWorldLocation(WorldLocation[i]);
}
// 입력받은 애만(InIndex)
// 색 변경
Materials[InIndex]->SetVectorParameterValue("Color", InColor);
// 다시 물리를 켜줌
Mesh[InIndex]->SetSimulatePhysics(true);
}
C07_SpotLight.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "C07_SpotLight.generated.h"
UCLASS()
class UONLINE_03_CPP_API AC07_SpotLight : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleDefaultsOnly)
class USceneComponent* Scene;
UPROPERTY(VisibleDefaultsOnly)
class UTextRenderComponent* Text;
UPROPERTY(VisibleDefaultsOnly)
class USpotLightComponent* SpotLights[3];
public:
AC07_SpotLight();
protected:
virtual void BeginPlay() override;
private:
UFUNCTION()
void OnLightColor(int32 InIndex, FLinearColor InColor);
};
C07_SpotLight.cpp
#include "C07_SpotLight.h"
#include "Global.h"
#include "C05_MulticastTrigger.h"
#include "Components/TextRenderComponent.h"
#include "Components/SpotLightComponent.h"
AC07_SpotLight::AC07_SpotLight()
{
CHelpers::CreateComponent<USceneComponent>(this, &Scene, "Scene");
CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Scene);
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());
for (int32 i = 0; i < 3; i++)
{
FString str;
str.Append("SpotLights_");
str.Append(FString::FromInt(i + 1));
CHelpers::CreateComponent<USpotLightComponent>(this, &SpotLights[i], FName(str), Scene);
SpotLights[i]->SetRelativeLocation(FVector(0, i * 150, 0));
// 위에서 아래로 비치도록 회전
SpotLights[i]->SetRelativeRotation(FRotator(-90, 0, 0));
SpotLights[i]->Intensity = 1e+5f;
// 라이트가 퍼질 각도를 정의(각도가 클수록 넓게 빛이 퍼짐)
SpotLights[i]->OuterConeAngle = 25.0f;
}
}
void AC07_SpotLight::BeginPlay()
{
Super::BeginPlay();
TArray<AC05_MulticastTrigger*> triggers;
CHelpers::FindActors<AC05_MulticastTrigger>(GetWorld(), triggers);
triggers[0]->OnMultiLightBeginOverlap.AddUFunction(this, "OnLightColor");
if (triggers[0]->OnMultiLightBeginOverlap.IsBound())
triggers[0]->OnMultiLightBeginOverlap.Broadcast(0, FLinearColor(0, 0, 1));
}
void AC07_SpotLight::OnLightColor(int32 InIndex, FLinearColor InColor)
{
for (int32 i = 0; i < 3; i++)
SpotLights[i]->SetLightColor(FLinearColor(1, 1, 1));
SpotLights[InIndex]->SetLightColor(InColor);
}
CHelpers.h 추가된 내용
...
class UONLINE_03_CPP_API CHelpers
{
public:
...
// 해당 월드에 있는 액터들을 찾아준다.
template<typename T>
static void FindActors(class UWorld* InWorld, TArray<T*>& OutActors)
{
// 기존거에 추가가 되지 않도록 한번 비우고
OutActors.Empty();
TArray<AActor*> actors;
UGameplayStatics::GetAllActorsOfClass(InWorld, T::StaticClass(), actors);
for (AActor* actor : actors)
OutActors.Add(Cast<T>(actor));
}
};
결과
결과를 보면 밑에 그대로 있고 색깔만 바뀌는 경우가 있다.
-> 물리에 의해서 이동되어 있으므로 이동된 위치를 World의 위치로 인식함
world위치가 물리가 먹어서 이동되어있는 것은 Relative가 적용이 되있는 상태로 간주, 상대 위치가 먹어서 set이 적용되도 그자리에 원래 있는 것으로(월드에서 바뀌지 않은 것으로)
'Unreal Engine 4 > C++' 카테고리의 다른 글
<Unreal C++> 19 - Function Override (0) | 2022.04.15 |
---|---|
<Unreal C++> 18 - Event (0) | 2022.04.15 |
<Unreal C++> 15 - Delegate (0) | 2022.04.07 |
<Unreal C++> 14 - Component Begin & End Overlap (0) | 2022.04.07 |
<Unreal C++> 13 - Actor Begin & End Overlap (0) | 2022.04.07 |