Dev./UE 언리얼 엔진

[TIL_250128] 회전 발판, 움직이는 장애물, 비물질(?) 발판 구현하기

raindrovvv 2025. 1. 28. 16:09

💭회고

오늘은 과제를 해보며, 충돌 감지와 타이머 설정을 깊이 있게 공부했다.

SetCollisionEnabledECollisionEnabled::QueryAndPhysics로 물체의 충돌 설정을 다루는 방법과 GetWorld()->GetTimerManager().SetTimer()를 사용해 타이머를 설정하는 방법을 배웠다.

충돌 감지 : 물체가 다른 물체와 상호작용하는 방식을 설정하는 방법을 익혔다. 이를 통해 게임 캐릭터가 벽에 부딪히거나, 총알이 적에게 맞는 등의 이벤트를 처리할 수 있을 것 같다.
타이머 설정 : FTimerHandleSetTimer 함수를 사용해서 특정 메서드를 주기적으로 호출하는 방법을 익혔다. 덕분에 게임 내에서 시간 제한과 카운트다운을 구현할 수 있게 됐다.
상태 전환 및 효과음 재생 : ToggleStateSetSolidState 메서드를 통해 플랫폼의 상태를 전환하고, 효과음을 재생하는 방법을 배웠다. 여러 방면으로 응용하면 좋겠다!

🗺️마인드맵

📒학습 내용

구현 과제

1. Actor 클래스 2개 이상 구현
- StaticMeshComponent를 포함하고 맵에 배치 가능한 형태
- 서로 다른 동작 로직 수행

2. Tick 함수 기반 동적 Transform 변경
- 매 프레임 회전 (AddActorLocalRotation() 사용)
- 왕복 이동 (Tick(float DeltaTime) 사용)
- 프레임 독립성을 위해 DeltaTime 활용

3. 리플렉션 적용
- 주요 변수를 UPROPERTY로 선언하여 에디터에서 조정 가능하게 설정
- 카테고리와 접근 지정자를 지정하여 Details 패널에서 편집 가능하게 설정
- 플레이 중에 Details 패널에서 값 변경 시 즉시 반영되는지 확인

1. Moving Platform

1.1 상하/좌우 이동 플랫폼

 

<.h>

// Fill out your copyright notice in the Description page of Project Settings.  
  
#pragma once  
  
#include "CoreMinimal.h"  
#include "GameFramework/Actor.h"  
#include "CppPlatForm.generated.h"  
  
UCLASS()  
class ONLINELEARNINGKIT_API ACppPlatForm : public AActor  
{  
    GENERATED_BODY()  
    public:   
    // Sets default values for this actor's properties  
    ACppPlatForm();  
    // Called every frame  
    virtual void Tick(float DeltaTime) override;  
  
protected:  
    // Called when the game starts or when spawned  
    virtual void BeginPlay() override;  
    private:  
    USceneComponent* SceneRoot;  
  
    UPROPERTY(VisibleAnywhere)  
    UStaticMeshComponent* StaticMesh;  
  
    // 이동 거리 (진폭)  
    UPROPERTY(EditAnywhere, Category = "Movement")  
    float Amplitude = 100.0f;  
  
    // 이동 속도 (주기 조절)  
    UPROPERTY(EditAnywhere, Category = "Movement")  
    float Frequency = 1.0f;  
  
    FVector StartLocation;  
    float Time = 0.0f;  
    };
파라미터 설명
Amplitude: 플랫폼이 이동하는 최대 거리 (Sin 함수의 진폭)
Frequency: 이동 속도 조절. 값이 클수록 왕복 주기가 짧아짐.

GetActorForwardVector() : 액터의 전방 방향을 기준으로 이동
- GetActorRightVector()로 변경하면 오른쪽/왼쪽으로 이동
- GetActorUpVector() 사용해 상하 이동 가능
- 혹은 액터를 직접 돌려주면 됨

 

<.cpp>

#include "CppPlatForm.h"  
  
ACppPlatForm::ACppPlatForm()  
{  
    PrimaryActorTick.bCanEverTick = true;  
  
    SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));  
    RootComponent = SceneRoot;  
  
    StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));  
    StaticMesh->SetupAttachment(SceneRoot);  
}  
  
void ACppPlatForm::BeginPlay()  
{  
    Super::BeginPlay();  
    StartLocation = GetActorLocation(); // 시작 위치 저장  
}  
  
void ACppPlatForm::Tick(float DeltaTime)  
{  
    Super::Tick(DeltaTime);  
  
    // 시간 누적  
    Time += DeltaTime * Frequency;  
  
    // Sin 함수로 -1 ~ 1 사이의 값 생성  
    float SinValue = FMath::Sin(Time);  
    // 진폭(Amplitude)을 곱해 이동 거리 계산  
    float Offset = Amplitude * SinValue;  
    // 시작 위치에서 이동 방향(액터 전방)으로 Offset만큼 이동  
    FVector NewLocation = StartLocation + GetActorForwardVector() * Offset;  
    // 위치 업데이트  
    SetActorLocation(NewLocation);  
}

 

1.2 원형 경로 이동 플랫폼

#include "CppPlatForm.h"

ACppPlatForm::ACppPlatForm()
{
    PrimaryActorTick.bCanEverTick = true;

    SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
    RootComponent = SceneRoot;

    StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
    StaticMesh->SetupAttachment(SceneRoot);
}

void ACppPlatForm::BeginPlay()
{
    Super::BeginPlay();
    StartLocation = GetActorLocation(); // 시작 위치 저장
}

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

    // 시간 누적 (프레임 독립적)
    Time += DeltaTime * Frequency;

    // Sin과 Cos 함수로 원형 경로 계산
    float X = Amplitude * FMath::Sin(Time); // X축 이동
    float Y = Amplitude * FMath::Cos(Time); // Y축 이동

    // 시작 위치에서 X, Y만큼 이동
    FVector NewLocation = StartLocation + FVector(X, Y, 0);
    
    // 위치 업데이트
    SetActorLocation(NewLocation);
}
파라미터 설명
Amplitude : 원의 반지름. 값이 클수록 원의 크기가 커진다.
Frequency : 이동 속도를 조절. 값이 클수록 더 빠르게 회전.

2. RotatePlatform

#pragma once

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

UCLASS()
class YOURPROJECT_API ARotatingPlatform : public AActor
{
    GENERATED_BODY()
    
public:    
    ARotatingPlatform();

protected:
    virtual void BeginPlay() override;

public:    
    virtual void Tick(float DeltaTime) override;

private:
    // 메시 컴포넌트
    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* Mesh;

    // 회전 속도 (초당 도 단위)
    UPROPERTY(EditAnywhere, Category = "Rotation")
    float RotationSpeed = 90.0f; // 기본값: 초당 90도
};
#include "RotatingPlatform.h"

ARotatingPlatform::ARotatingPlatform()
{
    PrimaryActorTick.bCanEverTick = true;

    // 메시 컴포넌트 생성 및 루트로 설정
    Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    RootComponent = Mesh;
}

void ARotatingPlatform::BeginPlay()
{
    Super::BeginPlay();
}

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

    // 로컬 좌표계 기준으로 회전 적용
    FRotator RotationDelta(0.0f, RotationSpeed * DeltaTime, 0.0f); // Y축 기준 회전
    AddActorLocalRotation(RotationDelta);
}

 

결과

 

구현 과제(기능 추가)

1번: 시간 제한과 카운트다운 활용
- FTimerHandleGetWorld()->GetTimerManager().SetTimer(...) 사용
- 매 프레임 호출이 아닌 특정 주기로 함수 호출 (퍼포먼스 이점)
- 시간 제한 로직 구현 (발판이 사라지거나 메시/위치 변경 등)

2번: 랜덤 퍼즐 생성
- SpawnActor를 통해 게임 시작 시 회전 발판/이동 플랫폼 등을 임의 좌표에 배치

- 회전/이동 속도, 이동 범위, 각도 등을 난수로 생성해 매번 다른 퍼즐 코스 구성

1. 1번: 시간 제한과 카운트다운 활용

#pragma once  
  
#include "CoreMinimal.h"  
#include "GameFramework/Actor.h"  
#include "TimePlatform.generated.h"  
  
UCLASS()  
class ONLINELEARNINGKIT_API ATimePlatform : public AActor  
{  
    GENERATED_BODY()  
  
public:  
    ATimePlatform();  
  
protected:  
    virtual void BeginPlay() override;  
  
private:  
    USceneComponent* SceneRoot;  
       UPROPERTY(VisibleAnywhere, Category = "Mesh")  
    UStaticMeshComponent* StaticMesh;  
  
    UPROPERTY(EditAnywhere, Category = "Materials")  
    UMaterialInterface* SolidMaterial;  
  
    UPROPERTY(EditAnywhere, Category = "Materials")  
    UMaterialInterface* IntangibleMaterial;  
  
    UPROPERTY(EditAnywhere, Category = "Sound")  
    USoundBase* ChangeStateSound;  
  
    // 타이머 핸들  
    FTimerHandle StateTimerHandle;  
  
    // 상태 전환 주기 (초 단위)  
    UPROPERTY(EditAnywhere, Category = "Timer")  
    float StateChangeInterval = 5.0f;  
  
    // 현재 상태  
    bool bIsSolid = true;  
  
    // 상태 전환 함수  
    void ToggleState();  
  
    // 머티리얼 및 콜리전 설정  
    void SetSolidState(bool bSolid);  
};
#include "TimePlatform.h"  
#include "Kismet/GameplayStatics.h"  
  
ATimePlatform::ATimePlatform()  
{  
    PrimaryActorTick.bCanEverTick = false;
     
    SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
    RootComponent = SceneRoot;
  
    StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));  
    StaticMesh->SetupAttachment(SceneRoot);  
    StaticMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); // 초기 콜리전 설정 (BlockAll)}  
  
void ATimePlatform::BeginPlay()  
{  
    Super::BeginPlay();
    // 최초 상태 설정
    SetSolidState(true);
    
    // 타이머 시작
    GetWorld()->GetTimerManager().SetTimer(  
       StateTimerHandle,  
       this,  
       &ATimePlatform::ToggleState,  
       StateChangeInterval,  
       true // 무한 반복  
    );  
}  
  
void ATimePlatform::ToggleState()  
{  
    bIsSolid = !bIsSolid;  
    SetSolidState(bIsSolid);  
    UGameplayStatics::PlaySoundAtLocation(this, ChangeStateSound, GetActorLocation());// 효과음 재생  
}  
  
void ATimePlatform::SetSolidState(bool bSolid)
{  
    if (bSolid)  
    {       // Solid 상태: 콜리전 활성화 + 파란 머티리얼  
       StaticMesh->SetCollisionResponseToAllChannels(ECR_Block);  
       StaticMesh->SetMaterial(0, SolidMaterial);  
    }    else  
    {  
       // Intangible 상태: 콜리전 비활성화 + 빨간 머티리얼  
       StaticMesh->SetCollisionResponseToAllChannels(ECR_Ignore);  
       StaticMesh->SetMaterial(0, IntangibleMaterial);  
    }
}

1.1 UMaterialInterface

UMaterialInterface* SolidMaterial; : 엔진에서 사용할 머티리얼을 가리키는 포인터

  • UMaterialInterface :
    • 머티리얼의 기본 인터페이스를 제공하며, 머티리얼을 조작하거나 설정하는 데 사용
    • 다양한 머티리얼 클래스(UMaterial, UMaterialInstance 등)의 부모 클래스

1.2 USoundBase

USoundBase* ChangeStateSound; :

언리얼 엔진에서 사운드를 재생하기 위한 포인터

➡️ 특정 이벤트가 발생할 때 효과음을 재생

  • USoundBase :
    • 사운드 자산의 기본 클래스
    • USoundWave, USoundCue 등의 사운드 클래스가 이 클래스를 상속함

1.3 FTimerHandle

FTimerHandle StateTimerHandle;

  • FTimerHandle: 타이머를 식별하는 구조체. 타이머의 상태를 저장하고, 타이머를 시작, 정지, 초기화 등의 작업을 수행할 때 사용됨.
    1. 타이머 설정: 특정 간격으로 함수를 호출하도록 설정
    2. 타이머 정지: 설정된 타이머를 정지하거나 취소할 때 사용
    3. 타이머 상태 확인: 타이머의 활성 여부 확인

1.4 StaticMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);

언리얼 엔진에서 특정 Static Mesh 컴포넌트의 충돌 설정을 활성화하는 코드.

Static Mesh 컴포넌트가 충돌을 감지하고 물리 시뮬레이션에 반응하도록 설정할 수 있다.

  • SetCollisionEnabled : 충돌을 활성화하거나 비활성화하는 함수
  • ECollisionEnabled::QueryAndPhysics : 충돌을 활성화하여 물리 시뮬레이션과 충돌 쿼리를 모두 처리한다
    • Query (충돌 감지):
      • 레이캐스트, 오버랩 테스트 등의 충돌 검사가 가능
      • ✨ 플레이어가 벽을 조준할 때 히트 감지, 아이템 획득 범위 판정
    • Physics (물리 시뮬레이션):
      • 중력의 영향
      • 다른 물체와 물리적으로 상호작용
      • ✨ 벽에 부딪히거나, 물리 오브제와 상호작용
  • 일반적인 물리 오브제(ex. 던질 수 있는 물체, 밟을 수 있는 플랫폼 등)에 자주 사용
  • 이 함수는 충돌 감지 자체 기능을 켜거나 끌 뿐, 구체적인 동작(Block, Overlap, Ignore)은 콜리전 채널에서 지정해야 한다.
    • ex) ECR_Block로 설정된 오브젝트만 서로 막을 수 있음

Query(충돌 감지)

더보기

물체 충돌 여부 확인

게임에서 캐릭터가 벽에 부딪혔는지, 총알이 적에게 맞았는지 등을 확인하는 작업.

  • 충돌 감지: 물체 간의 충돌 여부를 감지하고 그에 따라 게임 로직 처리. 예를 들어, 캐릭터가 적과 충돌했을 때 체력을 감소시키거나, 물체가 바닥에 떨어졌을 때 게임을 종료하는 등의 로직을 구현.
  • 레이캐스트 (Raycasting): 특정 방향으로 가상의 레이저를 쏘아 해당 경로에 물체가 있는지 확인. 주로 조준 시스템, 시야 판정, 길찾기 등에 사용.
  • 오버랩 검사 (Overlap Checking): 두 물체가 일정 범위 내에 겹쳐 있는지 확인. 주로 범위 공격, 트리거 영역 등을 구현할 때 사용.

다른 충돌 옵션들...
- `NoCollision`: 모든 충돌 비활성화, 시각적인 요소나 배경 오브제
- `QueryOnly`: 충돌 감지만 가능하고 물리는 비활성화, 트리거 영역이나 감지 영역으로 사용할 때
- `PhysicsOnly`: 물리만 활성화하고 충돌 감지는 비활성화, 보이지 않은 벽이나 경계를 만들 때

// 모든 충돌 비활성화
StaticMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);

// 충돌 감지만 활성화 (물리 효과 없음)
StaticMesh->SetCollisionEnabled(ECollisionEnabled::QueryOnly);

// 물리 시뮬레이션만 활성화 (충돌 감지 없음)
StaticMesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);

// 물리와 충돌 감지 모두 활성화 (앞서 본 예시)
StaticMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);

1.5 GetTimerManager()

 

타이머를 설정하여 특정 함수를 주기적으로 호출하는 역할

  • GetWorld()->GetTimerManager(): 월드 타이머 매니저를 가져와 타이머 설정
  • SetTimer: 타이머를 설정하는 함수
    • 파라미터 설명:
      • StateTimerHandle: 타이머를 식별하는 핸들. 나중에 타이머를 멈추거나 조작할 때 사용됨. 여러 타이머를 관리할 때 필요함.
      • this: 타이머가 호출할 함수가 속한 객체. 여기서는 현재 객체 ATimePlatform 클래스의 인스턴스.
      • &ATimePlatform::ToggleState: 타이머가 호출할 함수. ATimePlatform 클래스의 ToggleState 함수를 가리킴.
      • StateChangeInterval: 타이머가 호출되는 시간 간격(초 단위)
      • true: 타이머를 반복적으로 호출할지 여부 결정. true면 반복, false면 한 번만 호출.

1.6 void ATimePlatform::ToggleState() {...}

 

함수를 호출하면 플랫폼의 상태를 변경하고, 효과음을 재생하는 역할을 한다.

  • bIsSolid = !bIsSolid; : 현재 플랫폼의 상태를 반전시킴
    • true면 false로, false면 true로 변경
  • SetSolidState(bIsSolid); : 상태 변경된 값을 기반으로 플랫폼 상태를 설정. bIsSolid 값에 따라 플랫폼이 고체가 되거나 아닌 상태가 됨.
  • UGameplayStatics::PlaySoundAtLocation(this, ChangeStateSound, GetActorLocation()); : 플랫폼 위치에서 소리 재생
    • this: 현재 객체를 나타냄
    • ChangeStateSound: 재생할 사운드 에셋
    • GetActorLocation(): 플랫폼의 위치에서 사운드 재생

1.7 void ATimePlatform::SetSolidState(bool bSolid) {...}

 

ATimePlatform 클래스의 SetSolidState 메서드는 플랫폼의 상태를 고체(Solid) 또는 비물질(Intangible)로 설정하는 역할.

각 상태에 따라 콜리전 설정과 머티리얼을 변경!

  • 고체 상태 (bSolidtrue일 때):
    • 모든 채널에 대해 콜리전 응답을 차단(ECR_Block)으로 설정
    • ECR_Block??? ⬇️
    • 더보기
      언리얼 엔진에서 물체의 충돌 반응을 설정할 때 사용하는 `ECollisionResponse` 열거형(enum)의 값 중 하나. 

      `ECR_Block`는 해당 물체가 다른 물체와 충돌할 때 그 충돌을 막아(Block)주는 것을 의미.
      ➡️ `ECR_Block`로 설정된 경우, 물체는 다른 물체와의 충돌을 감지하고 그 물체의 움직임을 멈추거나 반응! 
      ✨예를 들어, 캐릭터가 `ECR_Block`으로 설정된 벽에 부딪히면 캐릭터의 이동이 막히게 된다.

      아래는 `ECollisionResponse`의 몇 가지 다른 값들:
      - `ECR_Block`: 물리적으로 충돌하여 막힘 (예: 벽)
      - `ECR_Ignore`: 서로 통과함 (예: 유령처럼 통과)
      - `ECR_Overlap`: 겹침을 감지하되 통과 가능 (예: 트리거 영역)

      특정 채널에 대해서만 응답을 설정하고 싶다면 `SetCollisionResponseToChannel()`을 사용할 수 있다.
    • 플랫폼 머티리얼을 파란 머티리얼(SolidMaterial)로 설정
  • 비물질 상태 (bSolidfalse일 때):
    • 모든 채널에 대해 콜리전 응답을 무시(ECR_Ignore)로 설정
    • 플랫폼의 머티리얼을 빨간 머티리얼(IntangibleMaterial)로 설정

1.8 #include "Kismet/GameplayStatics.h"

  • `UGameplayStatics` 클래스를 사용하기 위해 포함하는 헤더 파일.
  • 이 클래스는 게임플레이 관련한 다양한 유틸리티 함수들을 제공한다.

➡️ 사운드를 쓰기 위해 사용했음!

 

결과

🟣오늘의 옵시디언 현황