RPG :: 캐릭터를 가리는 벽 투명하게 만들기


작성 기준 버전 :: 4.21.1


탑/다운에 가까운 쿼터뷰에서 내려보는 RPG에서는 건물이나 벽, 기둥 같은 오브젝트에 캐릭터가 가려지는 경우가 많다.



이런 경우 플레이어는 자신의 캐릭터의 위치를 찾기가 어려워지고 플레이에 매우 큰 지장을 준다. 그렇기 때문에 개발자들은 이런 문제를 해소하기 위해서 여러 가지 테크닉을 사용하는데 그 중 대표적인 것이 캐릭터를 가리고 있는 벽을 투명하게 만드는 것이다.


이 섹션에서는 캐릭터를 가리고 있는 벽을 투명하게 만드는 방법에 대해서 배워볼 것이다.


본격적인 내용에 들어가기에 앞서, 이 섹션은 진행하기 위해서는 다음과 같은 선행 지식이 필요하다.


머티리얼 인스턴싱 :: 머티리얼 파라미터와 머티리얼 인스턴스

C++ 코드에서 머티리얼 인스턴스 다이내믹 생성하고 다루기

콜리전과 콜리전 이벤트


이번 섹션은 지난 섹션 중 RPG :: 마우스 입력 이동 구현하기 섹션에 이어서 진행되는 섹션이다.



설계


이 기능을 구현하기 위한 설계는 다음과 같다.


1. 벽이나 기둥 같은 오브젝트가 가리는 위치에 캐릭터가 존재하고 있는지 확인하기 위한 콜리전을 깔아줄 SeeingThroughCollision 클래스


2. SeeingThroughCollision에게 신호를 받아서 투명하게 만들어질 SeeingThroughActor



이렇게 바닥에 콜리전을 까는 방법 이외에도 캐릭터와 카메라 사이에 콜리전을 두고 여기에 벽이나 기둥같은 오브젝트가 닿으면 투명하게 만드는 방법도 생각해볼 수 있다.



기능 구현


SeeingThroughActor


SeeingThroughActor 구현


새 C++ 클래스를 하나 추가하자. Actor 클래스를 상속방아서 SeeingThroughActor라는 이름으로 클래스를 생성한다.


 

이 클래스는 콜리전으로부터 통지를 받아서 자신의 머티리얼을 투명하게 하는 역할을 한다.


SeeingThroughActor.h에 다음 멤버 변수들을 추가한다.


private:
    UPROPERTY(EditAnywhere, meta = (AllowPrivateAccess = "true"))
    class UStaticMeshComponent* SeeingTroughMesh;

    UPROPERTY(EditAnywhere, meta = (AllowPrivateAccess = "true"))
    float SeeingThroughTime;

    bool bSeeingThrough;

    float RunningTimer;


먼저 SeeingThroughMesh는 메시가 가진 머티리얼을 다이내믹 인스턴스로 만들어서 투명화 작업을 하기 위한 변수이다.


SeeingThroughTime은 메시가 투명해지는데까지 걸리는 시간에 대한 변수이다. 이 변수는 외부에 공개해서 다른 개발자가 수정할 수 있게 하였다.


bSeeingThrough는 지금 메시가 투명해지는 중인지 아니면 불투명해지는 중인지에 대한 변수이다.


RunningTimer는 진행도를 위한 변수이다.


그 다음에는 SeeingThroughActor.cpp로 가서 스태틱 메시 컴포넌트의 기능을 사용하기 위해서 다음 전처리기를 추가한다.


#include "Engine/Classes/Components/StaticMeshComponent.h"


그리고 ASeeingThroughActor::ASeeingThroughActor() 생성자 함수로 가서 멤버 변수들을 초기화하는 코드를 추가한다.


SeeingTroughMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("SeeingThroughMesh"));
RootComponent = SeeingTroughMesh;
SeeingTroughMesh->CreateDynamicMaterialInstance(0);

SeeingTroughMesh->SetCollisionProfileName(TEXT("InvisibleWall"));

SeeingThroughTime = 0.3f;
RunningTimer = 0.0f;
bSeeingThrough = false;


SeeingThroughMesh를 생성한 뒤, RootComponent로 설정해주고 다이내믹 머티리얼 인스턴스를 생성한다. 그리고 콜리전 프로필을 InvisibleWall로 설정해주는데, 이것은 벽이 물체는 가로막고 마우스를 클릭했을때 발생하는 트레이스는 통과시키기 위함이다.


그리고 투명해지는데 걸리는 시간은 기본으로 0.3초로 설정한다.


BeginPlay() 함수에 다음 코드를 추가한다.


PrimaryActorTick.SetTickFunctionEnable(false);


게임 시작했을때 Tick() 함수를 작동하지 않도록 만들어서 성능 낭비가 없도록 한다.


그 다음 Tick() 함수를 다음과 같이 구현한다.


void ASeeingThroughActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    RunningTimer += DeltaTime;

    float Opacity = 0.0f;
    if (bSeeingThrough)
    {
        Opacity = FMath::Lerp(1.0f, 0.0f, RunningTimer * (1.0f / SeeingThroughTime));
    }
    else
    {
        Opacity = FMath::Lerp(0.0f, 1.0f, RunningTimer * (1.0f / SeeingThroughTime));
    }

    SeeingTroughMesh->SetScalarParameterValueOnMaterials(TEXT("Opacity"), Opacity);

    if (RunningTimer > SeeingThroughTime)
    {
        RunningTimer = 0.0f;
        PrimaryActorTick.SetTickFunctionEnable(false);
    }
}


위의 코드는 틱이 작동하는 동안 bSeeingThrough의 상태에 따라서 다이내믹 머티리얼 인스턴스의 머티리얼 파라미터 "Opacity"를 0에서 1로 만들거나 1에서 0으로 만들고 작동이 끝나면 Tick() 함수의 작동을 멈추게 한다. 참고로 머티리얼 파라미터 "Opacity"는 이후 작업에서 추가한다.


다음 함수의 선언을 헤더에 추가한다.


public:

    void SetShowSeeingThrough(bool bThroughShow);


그리고 SeeingThroughActor.cpp로 가서 함수 구현을 완료한다.


void ASeeingThroughActor::SetShowSeeingThrough(bool bThroughShow)
{
    bSeeingThrough = bThroughShow;
    if (RunningTimer != 0.0f)
    {
        RunningTimer = SeeingThroughTime - RunningTimer;
    }
    PrimaryActorTick.SetTickFunctionEnable(true);
}


SetShowSeeingThrough() 함수는 bShow 변수를 받아서 액터가 투명해지기 시작하는지 불투명해지기 시작하는지 결정한 뒤 Tick() 함수를 작동시킨다.


코드 작업이 끝났다면 솔루션 탐색기에서 프로젝트를 빌드한 뒤, 에디터로 돌아간다.


투명해지는 만들기


이번에는 SeeingThroughActor가 사용할 머티리얼을 만들 차례이다. Props 폴더 안에 Materials 폴더를 만든 다음, 콘텐츠 브라우저 패널의 파일 창에 우클릭해서 머티리얼을 선택한다. 그리고 생성된 머티리얼의 이름을 M_SeeingThrough로 한다.



머티리얼을 더블클릭해서 머티리얼 에디터를 열고 디테일 패널에서 Material 카테고리의 Blend Mode를 Translucent로 설정한다.



그리고 TextureSamleParameter2D와 ScalarParameter를 추가하고 각각 이름을 Texture와 Opacity로 한다.



그리고 Opacity 파라미터 노드를 선택한 뒤, 디테일 패널에서 Default Value를 1로 설정한다.


그 다음, 적용과 저장을 하고 머티리얼 에디터를 닫는다.


언리얼 에디터로 돌아와서, 콘텐츠 브라우저 패널에서 방금 만든 머티리얼을 우클릭하고 머티리얼 인스턴스 생성을 선택한다.



머티리얼 인스턴스가 만들어지면 더블클릭해서 머티리얼 인스턴스 에디터를 열고 디테일 패널에서 Opacity를 체크해주고 머티리얼 인스턴스를 저장한 뒤, 머티리얼 인스턴스 에디터를 닫는다.



SeeingThroughActor 배치


이제 SeeingThroughActor를 배치할 차례이다. 콘텐츠 브라우저 패널에서 SeeingTroughActor를 찾아서 벽 토대 위에 배치한다.



SeeingThroughActor의 SeeingThroughMesh를 선택하고 Static Mesh에 Box를 할당한다.



그 다음, Material에 방금 전에 만든 M_SeeingThrough_Inst를 할당한다.


 

SeeingThroughActor의 스케일을 {1.0, 12.0, 5.0}으로 설정한다.



액터를 복사해서 다음과 같이 만든다.






SeeingThroughCollision


SeeingThroughCollision 구현


Actor 클래스를 상속받아서 SeeingThroughCollision 이라는 이름으로 클래스를 생성한다.


 

SeeingThroughCollision.h에 다음 멤버 변수 선언을 추가한다.


private:
    UPROPERTY()
    class UBoxComponent* SeeingThroughCollision;

    UPROPERTY(EditAnywhere, meta = (AllowPrivateAccess = "true"))
    TArray<class ASeeingThroughActor*> SeeingThroughActors;


SeeingThroughActor를 배열로 선언한 이유는 SeeingThroughCollision과 SeeingThroughActor를 1:1 매치를 시킬 수도 있지만, 1:N의 매치도 가능하게 유연성을 주기 위한 것이다.


SeeingThroughCollision.cpp로 가서 박스 컴포넌트와 SeeingThroughActor의 기능을 쓰기 위해서 다음 전처리기들을 추가한다.


#include "Engine/Classes/Components/BoxComponent.h"

#include "SeeingThroughActor.h"


ASeeingThroughCollision::ASeeingThroughCollision() 생성자 함수에 초기화 코드를 추가한다.


SeeingThroughCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("SeeingThroughCollision"));
RootComponent = SeeingThroughCollision;
SeeingThroughCollision->SetCollisionProfileName(TEXT("OverlapOnlyPawn"));


콜리전의 프로필 네임을 OverlapOnlyPawn으로 설정해서 폰이 오버랩되었을 때만 반응하도록 만든다.


다시 SeeingThroughCollision.h로 가서 겹침 이벤트를 받아서 처리할 함수를 덮어씌우는 코드를 추가한다.


protected:
    virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;

    virtual void NotifyActorEndOverlap(AActor* OtherActor) override;


그리고 SeeingThroughCollision.cpp로 가서 두 함수를 구현한다.


void ASeeingThroughCollision::NotifyActorBeginOverlap(AActor * OtherActor)
{
    ARpgCharacter* Character = Cast<ARpgCharacter>(OtherActor);
    if (Character)
    {
        for (auto SeeingThroughActor : SeeingThroughActors)
        {
            SeeingThroughActor->SetShowSeeingThrough(true);
        }
    }
}

void ASeeingThroughCollision::NotifyActorEndOverlap(AActor * OtherActor)
{
    ARpgCharacter* Character = Cast<ARpgCharacter>(OtherActor);
    if (Character)
    {
        for (auto SeeingThroughActor : SeeingThroughActors)
        {
            SeeingThroughActor->SetShowSeeingThrough(false);
        }
    }
}


코드 작업이 끝나면 솔루션 탐색기에서 프로젝트를 빌드하고 언리얼 에디터로 넘어간다.


SeeingThroughCollision 배치


콘텐츠 브라우저 패널에서 SeeingThroughCollision을 드래그해서 레벨에 배치한다.



배치한 SeeingThroughCollision의 위치를 {-420.0 -790.0 0.0}으로, 스케일을 {4.0, 22.0, 1.0}으로 수정한다. 그렇게 하면 벽 너머에 캐릭터가 들어갔을 때 가려지는 영역에 콜리전이 위치하게 된다.



그 다음, SeeingThroughActors 배열에 + 버튼을 눌러서 엘리먼트를 추가해준 다음, 투명해져야 하는 벽을 할당한다.



 

테스트


플레이 버튼을 누르고 캐릭터를 벽 뒤로 이동시켜보면 벽이 투명해지고 그 지역을 벗어나면 다시 벽이 불투명해지는 것을 확인할 수 있다.



나머지 기둥과 벽으로 가려지는 부분에도 적당하게 SeeingThroughCollision을 배치하고 SeeingThroughActor와 매칭시켜서 벽이 투명해지도록 만들어보자.

반응형

+ Recent posts