충돌 이벤트로 획득되는 아이템 구현
1️⃣충돌 이벤트 기반 아이템 획득 방식
- 엔진에서 아이템을 플레이어가 다가가기만 해보 자동으로 회득한다면, 보통 콜리전 이벤트를 사용
- 충돌 영역을 설정해두면, 플레이어가 그 영역안에 들어오거나 나가면 자동으로 이벤트를 발생시켜 별도의 함수 없이 호출이 가능함
- 충돌 영역(Collision Volume)
- 아이템 액터 주변에 SaphereConponent나 BoxComponent 같은 충돌 컴포넌트를 붙여둠
- 플레이어가 이 영역 안으로 들어오면 Overlap 이벤트가 발생하고, 이를 감지해 아이템 획득 로직을 실행할 수 있다
- Overlap 이벤트 VS Hit 이벤트돌 이벤트로 획득되는 아이템 구현
- Overlap 이벤트 : 물리적으로 부딪히는 것 없이 액터들이 서로 겹치기 시작했을 때 발생, 아이템 획득, 트리거 존 감지 같은 경우 주로 사용
- Hit 이벤트 : 실제 물리 충돌이 일어날 때 발생
- 아이템 획득
- 충돌 영역 안에 플레이어가 들어오면(Overlap 되는 시점, 아이템이 즉시사라자거나, 점수를 올리거나, 체력을 회복하는 등 로직을 실행할 수 있음
2️⃣기본 아이템 클래스에 충돌 컴포넌트와 메시 컴포넌트 추가
- BaseItem 클래스는 모든 아이템에 공통으로 필요한 기능을 담는 부모 클래스 이므로 다음과 같은 컴포넌트와 함수가 포함된다
- SceneComponent* Scene : 액터의 루트 컴포넌트
- USpahereComponent : 플레이어가 범위 안에 들어오면 Overlap 이벤트를 발생시킬 출돌 컴포넌트
- UStaticMeshComponent : 아이템이 월드에 표시될 때, 시각적인 모습을 담당
UCLASS()
class SPARTAPROJECT_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;
virtual void OnItemOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult) override;
virtual void OnItemEndOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex) override;
virtual void ActivateItem(AActor* Activator) override;
virtual FName GetItemType() const override;
void DestroyItem();
};
.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->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
// 루트 컴포넌트로 설정
Collision->SetupAttachment(Scene);
// 스태틱 메시 컴포넌트 생성 및 설정
StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMesh->SetupAttachment(Collision);
// 메시가 불필요하게 충돌을 막지 않도록 하기 위해, 별도로 NoCollision 등으로 설정할 수 있음.
// Overlap 이벤트 바인딩
Collision->OnComponentBeginOverlap.AddDynamic(this, &ABaseItem::OnItemOverlap);
Collision->OnComponentEndOverlap.AddDynamic(this, &ABaseItem::OnItemEndOverlap);
}
void ABaseItem::OnItemOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult)
{
// OtherActor가 플레이어인지 확인 ("Player" 태그 활용)
if (OtherActor && OtherActor->ActorHasTag("Player"))
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Overlap!!!")));
// 아이템 사용 (획득) 로직 호출
ActivateItem(OtherActor);
}
}
void ABaseItem::OnItemEndOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex)
{
}
void ABaseItem::ActivateItem(AActor* Activator)
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Overlap!!"));
}
FName ABaseItem::GetItemType() const
{
return ItemType;
}
void ABaseItem::DestroyItem()
{
Destroy();
}
- CollisionComponent->InitSphereRadius(float data) : Sphere 의 크기를 지정
- CollisionComponent->SetCollisionProfileName(L"OverlapAllDynamic")) : SphereComponen가 다른 액터와 겹칠 때만 Overlap 이벤트를 발생시키도록 설정, 이 이름은 엔진의 Collision Preset에 등록된 애들의 이름과 일치해야함
- OnComponentBeginOverlap.AddDynamic(UObject*, &UObject::Function) : 이벤트 함수를 등록하는방법
- 이벤트 바인딩은 런타임중에 이벤트 바인딩이 일어나기 때문에 이벤트를 게임 실행중에 동적으로 바인딩을 하여 사용한다
3️⃣인터페이스 함수 시그니처 수정
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ItemInterface.generated.h"
UINTERFACE(MinimalAPI)
class UItemInterface : public UInterface
{
GENERATED_BODY()
};
class SPARTAPROJECT_API IItemInterface
{
GENERATED_BODY()
public:
UFUNCTION()
virtual void OnItemOverlap(
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;
virtual void ActivateItem(AActor* Activator) = 0;
virtual FName GetItemType() const = 0;
};
- 이벤트 바인딩을 하는 함수는 동적으로 사용하므로 리플렉션을 통해 엔진이 게임 실행중에 어떤 함수인지 찾을 수 있게 UFUNCTION()을 붙여줘야한다
아이템 Bleuprint 생성 미콜리전/매시 설정
1️⃣아이템 Blueprint 생성 및 StaticMesh 설정
- C++ 클래스 클래스 기반 블루프린트 생성하여 StaticMesh를 설정해준다
2️⃣콜리전 범위 조정
- 블루프린트에서 Sphere Radius를 변경하여 충돌 범위를 재설정한다
3️⃣아이템 액터의 콜리전 프리셋 설정
- 콜리전 컴포넌트 설정 확인
- 생성한 BP를 클릭하여 디테일 패널에 Collision 색션을 찾으면 Collision Presets가 어떠한 값으로 설정되어 있는지 확인할 수 있다
- 콜리전 프리셋(Collision Preste)
- 주요 프리셋 옵션
- No Collison
- 전혀충돌이 일어나지 않아 단순한 배경등에 이용한다
- BlockAll
- 모든 객체와 충돌하여 막게되는데, 물리적 충돌은 하지만, Overlap이벤트는 발생하지 않아 벽이나 바닥 같은 고정된 장치에 이용한다
- OveralpAll
- 모듣 객체와 Overlap 이벤트를 발생하며, 트리거존, 감지센서, 투명 오브젝트에 사용한다
- BlockAllDynamic
- 움직이는 객체만 충돌을 막으며, 고정된 객체와는 충돌하지 않아 움직이는 플레이어나 물리 오브젝트와 상호작용하는 문,벽, 고정된 환경 요소와는 무관하게 작동한다
- OverlapAllDynamic
- 움직이는 객체와 Overlap 이벤트를 발생하여, 플레이어 근처에 있는지 확인하는 아이템, 센서. Overlap이벤트가 필요하지만 물리 충돌이 필요 없는 경우 사용한다
- Pawn
- 플레이어나 AI 처럼 Pawn 타입 객체를 대상으로 충돌을 감지하거나 막으며, 플레이어 전용 문, AI전용 센서등에 사용한다
- Custom
- 각 채널에 대해 충돌 응답을 객체별로 세부적으로 설정할 수 있다
- No Collison
- 물리 반응 여부(Collision Enabled)
- 충돌이 켜져있어도 물리적 반응이 없는 "Query Only"인지, 물리적으로 반응까지 하는 "Query and Physics" 인지 구분해야 한다
- NoCollision : 충돌 비활성화
- Query Only : 충돌 이벤트는 감지하지만, 물리적으로 튕기거나 밀리는 반응은 없음
- Physics Only : 물리 반응만 일어나고 이벤트는 일어나지 않음
- Query and Physics : 충돌 이벤트 + 물리 반응 모두 일어남
- 충돌이 켜져있어도 물리적 반응이 없는 "Query Only"인지, 물리적으로 반응까지 하는 "Query and Physics" 인지 구분해야 한다
- 주요 프리셋 옵션
4️⃣캐릭터 태그 부여 및 콜리전 프리셋 설정
- 플레이어 클래스에서 "Player" 테그부여
- 캐릭터 BP의 디테일 패널에서 Tag를 찾아 Player를 추가해준다
- 이를 통해 C++코드에서 Other actor->ActorHasTag("Player")를 통해 플레이어인지 간단히 판별할 수 있다
- 플레이어 클래스 콜리전 채널 확인
- 플레이어 BP의 CapsuleCollision이 pawn으로 설정하고, 물리 반응 여부도 OverlapAllDynamic으로 되어있는지 확인한다
아이템 클래스 충돌 처리
1️⃣코인 아이템 공통 부모 클래스 충돌 처리
UCLASS()
class SPARTAPROJECT_API ACoinItem : public ABaseItem
{
GENERATED_BODY()
public:
ACoinItem();
protected:
// 코인 획득 시 플레이어에게 줄 점수
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
int32 PointValue;
// 부모 클래스에서 상속받은 ActivateItem 함수를 오버라이드
virtual void ActivateItem(AActor* Activator) override;
};
.cpp
#include "CoinItem.h"
ACoinItem::ACoinItem()
{
// 점수 기본값을 0으로 설정
PointValue = 0;
ItemType = "DefaultCoin";
}
void ACoinItem::ActivateItem(AActor* Activator)
{
// 플레이어 태그 확인
if (Activator && Activator->ActorHasTag("Player"))
{
// 점수 획득 디버그 메시지
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Player gained %d points!"), PointValue));
// 부모 클래스 (BaseItem)에 정의된 아이템 파괴 함수 호출
DestroyItem();
}
}
2️⃣빅 코인 아이템
#include "BigCoinItem.h"
ABigCoinItem::ABigCoinItem()
{
// 상위 CoinItem의 PointValue를 50으로 덮어쓰기
PointValue = 50;
ItemType = "BigCoin";
}
void ABigCoinItem::ActivateItem(AActor* Activator)
{
// 부모의 기본 점수 획득 로직 사용
Super::ActivateItem(Activator);
// 빅 코인만의 추가 동작(이펙트, 사운드 재생 등)을 여기서 추가할 수 있음
}
3️⃣스몰 코인 아이템
#include "SmallCoinItem.h"
ASmallCoinItem::ASmallCoinItem()
{
PointValue = 10;
ItemType = "SmallCoin";
}
void ASmallCoinItem::ActivateItem(AActor* Activator)
{
// 부모의 기본 점수 획득 로직 사용
Super::ActivateItem(Activator);
// 스몰 코인만의 별도 동작은 여기에 추가
}
4️⃣힐링 아이템 충돌 처리
UCLASS()
class SPARTAPROJECT_API AHealingItem : public ABaseItem
{
GENERATED_BODY()
public
AHealingItem();
protected:
// 회복량
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Healing")
int32 HealAmount;
virtual void ActivateItem(AActor* Activator) override;
};
.cpp
#include "HealingItem.h"
AHealingItem::AHealingItem()
{
HealAmount = 20.0f;
ItemType = "Healing";
}
void AHealingItem::ActivateItem(AActor* Activator)
{
if (Activator && Activator->ActorHasTag("Player"))
{
// 회복 디버그 메시지
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Player gained %d HP!"), HealAmount));
DestroyItem();
}
}
- 이후 Activator가 캐릭터라는 전제하에, 플레이어의 체력 변수 혹은 함수를 호출해 직접 체력 회복을 처리하는 구현을 해야(PlayerCharacter->Heal(HealAmount))
5️⃣지뢰 아이템 충돌
- 지뢰는 플레이어가 범위안에 들어오면 일전 시간이 흐른뒤 폭발하고, 그때 범위 내 액터에게 데미지를 주는 로직을 구현할 것이다
UCLASS()
class SPARTAPROJECT_API AMineItem : public ABaseItem
{
GENERATED_BODY()
public:
AMineItem();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
USphereComponent* ExplosionCollision;
// 폭발까지 걸리는 시간 (5초)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mine")
float ExplosionDelay;
// 폭발 범위
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mine")
float ExplosionRadius;
// 폭발 데미지
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mine")
float ExplosionDamage;
// 지뢰 발동 여부
FTimerHandle ExplosionTimerHandle;
virtual void ActivateItem(AActor* Activator) override;
void Explode();
};
.cpp
AMineItem::AMineItem()
{
ExplosionDelay = 5.0f;
ExplosionRadius = 300.0f;
ExplosionDamage = 30.0f;
ItemType = "Mine";
ExplosionCollision = CreateDefaultSubobject<USphereComponent>(TEXT("ExplosionCollision"));
ExplosionCollision->InitSphereRadius(ExplosionRadius);
ExplosionCollision->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
ExplosionCollision->SetupAttachment(Scene);
}
void AMineItem::ActivateItem(AActor* Activator)
{
// 5초 후 폭발 실행
GetWorld()->GetTimerManager().SetTimer(ExplosionTimerHandle, this, &AMineItem::Explode, ExplosionDelay);
}
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 by MineItem"), ExplosionDamage));
}
}
// 지뢰 제거
DestroyItem();
}
- ActivateItem
- GetWorld()->GetTimerManager().SetTimer()를 사용해 5초 후 Explode()가 자동 실행되도록 지연 호출한다
- Explode()
- 폭발이 일어날 주변 액터를 검사해, 지뢰 범위안에 있고 지뢰 콜리전 컴포넌트와 여전히 Overlap 상태인 플레이얼에세 데미지를 주는 예시이다
- 폭발 처리 후 DestroyItem()을 호출하여 지뢰 아이템을 제거한다
- ExplosionCollision-> GetOverlappingActors(TArray<AActor*>))
- GetOverlappingActors를 사용하면 현재 collision에 충돌한 모든 액터를 가져와 저장할 수 있다
'Unreal Bootcamp > Unreal C++' 카테고리의 다른 글
15.캐릭터 체력 및 점수 관리 시스템 (0) | 2025.02.10 |
---|---|
14.아이템 스폰 및 레벨 데이터 관리 (0) | 2025.02.07 |
12.인터페이스 기반 아이템 클래스 (0) | 2025.02.04 |
11.State Machine 설계를 통한 캐릭터 동작 애니메이션 적용 (0) | 2025.01.31 |
10.캐릭터 동작 구현과 입력 처리 (0) | 2025.01.31 |