C++ 코드를 사용해서 아이템을 구현해보았다.
스코어를 쌓을 수 있는 코인, 체력을 회복하는 회복포션, 범위 안에 들어가면 터지는 지뢰가 있다.
아이템들은 플레이어와 닿았을 때의 충돌처리가 가장 중요한 것 같다.
언리얼 충돌 옵션들
Collsion 카테고리의 여러 옵션들을 보면 충돌과 물리에 대한 옵션들이 많이 있는데, 상황에 맞게 어떻게 쓸 수 있는지 정리해보았다.
Collsion Preset
- NoCollision : 충돌 자체를 처리하지 않을 때
- BlockAll : 모든 것들을 막을 때(벽이나, 바닥같은)
- OverlapAll : 모든 객체 통과하는지 감지(주로 트리거 장치에 주로 사용)
- BlockAllDynamic : 움직이는 객체만 막음(플레이어만 상호작용하고 싶을 때)
- OverlapAllDynamic : 움직이는 객체만 처리함(센서용으로 많이 사용)
- Pawn : Pawn 타입 객체들 전용으로 충돌처리
Collsion Enabled
- Collision Enable(Query and Physics) : 충돌, 물리적 감지 모두 활성화 ← 보통의 상황에 주로 사용
- Query Only(No Physics Collsion) : 충돌만 감지(overlap, hit) 물리적 감지 반응 x
- Physics Only(Noe Query Collsion) : 물리적 감지 x
이렇게 다양한 옵션들이 있으니 상호작용하는 물체마다 적절한 옵션을 넣어주면 될 것 같다.
아이템 클래스 설계
아이템들을 구현하기 위해 설계를 어떻게 하면 좋을지 고민해보았다.
이렇게 공통 특성을 묶어서 설계하면 코드의 유지보수가 엄청 편해진다는 것을 이해했다.
Interface 정의
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ItemInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UItemInterface : public UInterface
{
GENERATED_BODY()
};
class PRACTICE_API IItemInterface
{
GENERATED_BODY()
public:
UFUNCTION()
virtual void OnItemOverlap(
UPrimitiveComponent* OverlappedComp, // SphereComponent
AActor* OtherActor, // SphereComponent에 부딪힌 다른 Actor
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
) = 0;
UFUNCTION()
virtual void OnItemEndOverlap(
UPrimitiveComponent* OverlappedComp, // SphereComponent
AActor* OtherActor, // SphereComponent에 부딪힌 다른 Actor
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex
) = 0;
virtual void ActivateItem(AActor* Activator) = 0;
virtual FName GetItemType() const = 0;
};
인터페이스는 순수가상함수로만 이뤄진 클래스를 말한다.
모두 순수가상함수로 정의해서 상속받은 클래스에서 무조건 해당 함수를 정의하게 만들어줬고, 오버랩 이벤트를 사용해주었다.
참고로 인터페이스 클래스라고해도 cpp 파일은 삭제하지 않는 편이 좋다고 한다. 엔진에서 컴파일할 때 존재를 알려주는 역할? 로 작용한다고 한다.
BaseItem 정의
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemInterface.h"
#include "BaseItem.generated.h"
class USphereComponent;
UCLASS()
class PRACTICE_API ABaseItem : public AActor, public IItemInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
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;
virtual void DestroyItem();
};
BaseItem에서는 아이템들이 가지게 될 루트 컴포넌트와 충돌 컴포넌트, 메쉬를 정의해주었다.
블루프린트에서도 접근할 수 있도록 매크로도 추가해주었다.
+ 스크린상에서 로그 띄우는 방법
GEngine->AddOnScreenDebugMessage(id, 몇초동안 띄울지, 폰트색상, 출력하고싶은 문구);
이렇게 해주면 된다.
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Blue, FString::Printf(TEXT("Overlap!!")));
나는 이렇게 사용해줬다. id 의 -1은 id가 로그출력될 때마다 바로바로 생성된다는 의미로 이해하면 될 것 같다.
결과
이렇게 구현한대로, 플레이어와 아이템이 닿으면 아이템이 사라지고 로그가 스크린에 표출되는 것을 볼 수 있다.
하지만 지뢰에서 SphereCollision의 반지름을 300.0f 나 크게 설정해줬는데 로그를 보면 ExplosionCollision이 아니라 Mine 자체의 Collision에서 로그가 나오는 것을 볼 수 있다.
왜그런지는 내일 다시 코드를 살펴보고 수정해야 할 사항이다.
마지막 말
Overlap과 Physics의 Collision은 어떤 차이점이 있을까? 물리를 추가하면 중력이나 여러 부가적인 기능 때문에 무거워지는 것 같지만 어떤 상황에서 Overlap과 물리의 Collision을 사용하는 건지 궁금해졌다.
어떤 상황이 두 기능의 특징들을 잘 살릴 수 있는지 테스트를 해보려고 한다.