아이템 객체에 컴포넌트 생성
Collision Preset (Custom)
타이머
범위 감지
overlap이나 hit 이벤트를 발동시키기 위해 collision sphere/box를 추가해줌
static mesh에서 할 수도 있지만, 그러면 static mesh에서 하는 계산이 무거워지기 때문에, 따로 collision component를 추가해주는 것이 좋음
// BaseItem.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemInterface.h"
#include "BaseItem.generated.h"
class USphereComponent;
UCLASS()
class SPARTPROJECT_API ABaseItem : public AActor, public IItemInterface
{
GENERATED_BODY()
public:
ABaseItem();
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
FName ItemType;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
USceneComponent* Scene;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
USphereComponent* Collision;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
UStaticMeshComponent* StaticMesh;
// ... //
};
// BaseItem.cpp
#include "BaseItem.h"
#include "Components/SphereComponent.h"
ABaseItem::ABaseItem()
{
PrimaryActorTick.bCanEverTick = false;
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
SetRootComponent(Scene);
Collision = CreateDefaultSubobject<USphereComponent>(TEXT("Collision"));
// Collision Preset을 OverlapAllDynamic으로 설정
Collision->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
Collision->SetupAttachment(Scene);
StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMesh->SetupAttachment(Collision);
}
에디터에서 변수 값을 보기만 가능하게 하며, 객체 수정을 막음
예를 들어, UStaticMeshComponent* 같은 포인터 변수를 VisibleOnly로 설정 시, 변수(포인터)가 다른 객체를 가리키도록 바꾸는 행위(재할당)를 막음
ptr = &b; // 여기서 ptr의 가리키는 대상이 바뀌지 못하게 함
그러나 객체 내부의 여러 속성은 수정 가능 (Mesh 에셋 등)
VisibleAnywhere여도 블루프린트 에디터(디테일 패널)에서 내부 속성을 수정하여 StaticMesh를 설정해줄 수 있다


언리얼 블루프린트의 Event Graph에서 Collision 컴포넌트의 BeginOverlap 노드의 경우 사진과 같이 6개의 값을 반환함
따라서 우리가 만들었던 InterfaceItem의 OnItemBeginOverlap의 매개인자를 위와같이 바꾸어줘야함
// ItemInterface.h
UFUNCTION()
virtual void OnItemBeginOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult) = 0;
UFUNCTION()
virtual void OnItemEndOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex) = 0;
OverlappedComp : overlap 발생한 자기 자신
OtherActor : overlap 시킨 상대방
OtherComp ': 충돌일으킨 상대방의 컴포넌트
OtherBodyIndex, bFrmoSweep, SweepResult : 물리효과 관련으로 나중에 학습
EndOverlap같은 경우는 뒤의 2개인자는 필요 없음

생성자에서 동적으로 Binding해주어, overlap시 만들어준 함수가 연결되게 해줌
런타임중에 binding해주는 것이므로, OnItemBeginOverlap과 OnItemEndOverlap은 InterfaceItem헤더에서 UFUNCTION으로 리플렉션 시스템에 등록해줌
OnItemBeginOverlap함수를 구현해줌void ABaseItem::OnItemBeginOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult)
{
// OtherActor가 null아닌지 확인 && Actor의 Tag가 player인지 확인
if (OtherActor && OtherActor->ActorHasTag("Player"))
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green,
FString::Printf(TEXT("Overlap!!!")));
ActivateItem(OtherActor);
}
}
void ABaseItem::ActivateItem(AActor* Activator)
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green,
FString::Printf(TEXT("Activate!!!")));
Destroy();
}
GEngine->AddOnScreenDebugMessage() : 스크린에 출력해주는 함수-1은 매번 새로운 아이디를 생성, 2초동안 출력, 초록색으로, Overlap!!!을 출력OtherActor->ActorHasTag("Player") : OtherActor가 "Player"라는 Tag가지는지 확인
지뢰의 경우, Overlap을 감지할 컴포넌트(Collision)와 추가로 폭발하는 범위에 플레이어가 있는지 확인할 또다른 Collision 컴포넌트가 필요
BaseItem에서 SphereComponent를 이용해 collision을 만든것과 동일하게 하면 된다
AMineItem::AMineItem()
{
ExplosionDelay = 5.0f;
ExplosionRadius = 300.0f;
ExplosionDamage = 30;
ItemType = "Mine";
ExplosionCollision = CreateDefaultSubobject<USphereComponent>(TEXT("ExplosionCollision"));
ExplosionCollision->InitSphereRadius(ExplosionRadius);
ExplosionCollision->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
ExplosionCollision->SetupAttachment(Scene);
}
ExplosionCollision 내에 있을 시 피해를 입도록 구현해야 한다타이머 핸들러 : 시간을 계산해주는 시계
월드에 존재하는 여러 개의 타이머 핸들러를 타이머 매니저가 관리해줌
지뢰가 Activate되었을 때부터 시간을 재도록 구현
// MineItem.h
UCLASS()
class SPARTPROJECT_API AMineItem : public ABaseItem
{
// ... //
FTimerHandle ExplosionTimerHandle; // 타이머 핸들러
virtual void ActivateItem(AActor* Activator) override;
virtual void Explode();
};
// MineItem.cpp
void AMineItem::ActivateItem(AActor* Activator)
{
GetWorld()->GetTimerManager().SetTimer(
ExplosionTimerHandle,
this,
&AMineItem::Explode,
ExplosionDelay,
false
);
}
GetWorld()->GetTimerManager().SetTimer() : 타이머 생성 및 실행ExplosionTimerHandle : 헤더에서 선언한 타이머핸들러 객체this : 시간 되었을 시 호출할 함수의 객체&AMineItem::Explode : 시간 되었을 시 호출할 함수ExplosionDelay : 타이머 시간false : 타이머 반복 여부타이머가 종료 후 호출되어 폭발 로직을 실행
ExplosionCollision에 플레이어가 있을 시 피해를 입음
void AMineItem::Explode()
{
TArray<AActor*> OverlappingActors;
ExplosionCollision->GetOverlappingActors(OverlappingActors);
for (AActor* Actor : OverlappingActors) {
if (Actor && Actor->ActorHasTag("Player")) {
GEngine->AddOnScreenDebugMessage(
-1,
2.0f,
FColor::Red,
FString::Printf(TEXT("Player damaged : %d"), ExplosionDamage)
);
}
}
DestroyItem();
}
ExplosionCollision->GetOverlappingActors(arr) : 현재 Overlap되어 있는 모든 액터들을 arr에 추가해줌
오버래핑 된 액터중에 플레이어 캐릭터가 있을 경우 피해를 입힘