본문 바로가기

Unreal Engine 4/C++

<Unreal C++> 18 - Event

 

 

필요한 개념


델리게이트는 연결된것을 검사하고 콜가능하지만
Event는 해당 선언된 클래스 내에서만 가능

싱글, 멀티캐스트 델리게이트를 실제 많이 쓰지 않음(C내부적으로 콜할때 사용) 이 두개랑 1대1로 완전히 대응되는 Dynamic이 붙는 델리게이트가 있는데, 실제 Dynamic이 붙은
델리게이트를 많이 쓴다. 사용방법 선언방법 거의 유사, C에서 다룰수 있는 델리게이트를 그래서 다룸

지금할 거는 Event를 할 것이다. Event는 언급만 하고 넘어감
델리게이트는 외부 클래스에서도 실행 필요성이 있다면 IsBound()하고 Execute() or Broadcast()로 콜이 가능하다.(단, 어느부분에서 Broadcast하고 bound 했는지... 체크하기가 어렵다.)
Event는 그렇지 않음, 해당 선언된 객체 안에서만 실행이 가능(추가는 외부에서만 가능, 실행은 내부에서만 가능)

정리하면
델리게이트의 연결이나 호출은 어느 부분에서나 가능하므로 연결하거나 호출되는 부분은 관리하기가 어려운 면이 있다.
이벤트는 해당 클래스의 내부에서만 연결이나 호출이 가능하므로
관리가 용이한 면이 있으나 이벤트는 사용하는 것이 약간 까다로움(그래서 이벤트를 자주씀, 관리가 편함(호출이 자기 내부에서만 호출이 되니까))
이벤트는 BP와 통신이 가능하나, 까다로움(그래서 C내부에서만 많이 씀)
BP와 통신하는 것은 Dynamic 델리게이션을 사용

* 이벤트는 싱글이 없고 멀티만 있다.

델리게이트 선언은 클래스 외부에서 선언했었음(외부에서도 누구나 콜하고 사용할 수 있도록),
하지만 이벤트 선언은 다르다.(클래스 내부에 선언함(누구나 연결은 가능하지만, 콜은 내부에서만 처리)

델리게이트는 어느 부분에서나 사용이 가능하므로 전역으로 선언되지만, 이벤트는 해당 클래스 내부에서만 관리할 수 있으므로 클래스 내부에 타입을 정의함

*이벤트는 멀티캐스트가 기본이어서 retValue(return Value)를 지원하지 않음

*선언 형태(OwningType이 델리게이트와 다르게 있다.)
1 : OwningType이 존재(IsBound, Execute() 누가 실행하는지, 소유하는지), 2 : 이벤트 이름 3 : 파라미터 타입
#define DECLARE_EVENT_OneParam( OwningType, EventName, Param1Type ) FUNC_DECLARE_EVENT( OwningType, EventName, void, Param1Type )



class UONLINE_03_CPP_API AC08_EventTrigger : public AActor
{
GENERATED_BODY()
private:
DECLARE_EVENT_OneParam(AC08_EventTrigger, FEventTrigger, int32)
public:
// 변수 선언하는 것은 같다.
  FEventTrigger OnEventTrigger;
}

나머지는 다 동일

연결되어있는 함수를 끊는 것은 .clear()

void AC08_EventTrigger::ActorBeginOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
// 이 클래스 내부에서만 가능(이벤트니까)
if (OnEventTrigger.IsBound())
{
int32 index = UKismetMathLibrary::RandomIntegerInRange(0, 2);

OnEventTrigger.Broadcast(index);
}
}

정리
- Delegate : 어느 곳에서나 IsBound(), Execute(), Clear()가 가능
- Event : 해당 클래스 내부에서만 콜 가능

 

 

 

 

 

C08_EventTrigger.h


더보기
#pragma once

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

UCLASS()
class UONLINE_03_CPP_API AC08_EventTrigger : public AActor
{
	GENERATED_BODY()

private:
	DECLARE_EVENT_OneParam(AC08_EventTrigger, FEventTrigger, int32)

private:
	UPROPERTY(VisibleDefaultsOnly)
		class USceneComponent* Scene;

	UPROPERTY(VisibleDefaultsOnly)
		class UBoxComponent* Box;

	UPROPERTY(VisibleDefaultsOnly)
		class UTextRenderComponent* Text;
	
public:	
	AC08_EventTrigger();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void ActorBeginOverlap(AActor* OverlappedActor, AActor* OtherActor);

public:
	FEventTrigger OnEventTrigger;

};

 

 

 

 

 

 

 

C08_EventTrigger.cpp


더보기
#include "C08_EventTrigger.h"
#include "Global.h"
#include "Components/BoxComponent.h" 
#include "Components/TextRenderComponent.h"

AC08_EventTrigger::AC08_EventTrigger()
{
	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 AC08_EventTrigger::BeginPlay()
{
	Super::BeginPlay();

	OnActorBeginOverlap.AddDynamic(this, &AC08_EventTrigger::ActorBeginOverlap);
}

void AC08_EventTrigger::ActorBeginOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
	// 이 클래스 내부에서만 가능(이벤트니까)
	if (OnEventTrigger.IsBound())
	{
		int32 index = UKismetMathLibrary::RandomIntegerInRange(0, 2);

		OnEventTrigger.Broadcast(index);
	}
}

 

 

C09_Particles.h


더보기
#pragma once

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

UCLASS()
class UONLINE_03_CPP_API AC09_Particles : public AActor
{
	GENERATED_BODY()

private:
	UPROPERTY(VisibleDefaultsOnly)
		class USceneComponent* Scene;

	UPROPERTY(VisibleDefaultsOnly)
		class UTextRenderComponent* Text;

	// ParticleSystemComponent로 해도되는 데 일단은
	// ParticleSystem으로 함
	UPROPERTY(VisibleDefaultsOnly)
		class UParticleSystem* Particles[3];

public:	
	AC09_Particles();

protected:
	virtual void BeginPlay() override;

private:
	UFUNCTION()
		void PlayParticle(int32 InIndex);
};

 

 

 

 

 

 

 

C09_Particles.cpp


더보기
#include "C09_Particles.h"
#include "Global.h"
#include "C08_EventTrigger.h"
#include "Components/TextRenderComponent.h"
#include "Particles/ParticleSystem.h"

AC09_Particles::AC09_Particles()
{
	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());

	CHelpers::GetAsset<UParticleSystem>(&Particles[0], "ParticleSystem'/Game/Effects/P_Genno_Weapon_Lightning_01.P_Genno_Weapon_Lightning_01'");
	CHelpers::GetAsset<UParticleSystem>(&Particles[1], "ParticleSystem'/Game/Effects/P_Cube_Mesh_Test.P_Cube_Mesh_Test'");
	CHelpers::GetAsset<UParticleSystem>(&Particles[2], "ParticleSystem'/Game/Effects/P_HeldCharge_Ice_00.P_HeldCharge_Ice_00'");
}

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

	// 트리거 찾아서 연결
	TArray<AC08_EventTrigger*> triggers;
	CHelpers::FindActors<AC08_EventTrigger>(GetWorld(), triggers);
	triggers[0]->OnEventTrigger.AddUFunction(this, "PlayParticle");

	// 이벤트는 여기서 콜하면 안된다.(이벤트는 이벤트 클래스 내부에서만 콜)
}

void AC09_Particles::PlayParticle(int32 InIndex)
{
	FTransform transform;
	// 현재 위치 잡아줌
	transform.SetLocation(GetActorLocation());

	// SpawnEmitterAtLocation() : 파티클 효과를 생성해서 정해진 위치에서 나타나게 함
	// 1 : 월드 2 : 파티클 3 : 트랜스폼
	UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), Particles[InIndex], transform);
}

 

 

 

 

 

결과