Dev./UE 언리얼 엔진
[TIL_250128] 회전 발판, 움직이는 장애물, 비물질(?) 발판 구현하기
raindrovvv
2025. 1. 28. 16:09
💭회고
오늘은 과제를 해보며, 충돌 감지와 타이머 설정을 깊이 있게 공부했다.
SetCollisionEnabled와 ECollisionEnabled::QueryAndPhysics로 물체의 충돌 설정을 다루는 방법과 GetWorld()->GetTimerManager().SetTimer()를 사용해 타이머를 설정하는 방법을 배웠다.
✅ 충돌 감지 : 물체가 다른 물체와 상호작용하는 방식을 설정하는 방법을 익혔다. 이를 통해 게임 캐릭터가 벽에 부딪히거나, 총알이 적에게 맞는 등의 이벤트를 처리할 수 있을 것 같다.
✅ 타이머 설정 : FTimerHandle과 SetTimer 함수를 사용해서 특정 메서드를 주기적으로 호출하는 방법을 익혔다. 덕분에 게임 내에서 시간 제한과 카운트다운을 구현할 수 있게 됐다.
✅ 상태 전환 및 효과음 재생 : ToggleState와 SetSolidState 메서드를 통해 플랫폼의 상태를 전환하고, 효과음을 재생하는 방법을 배웠다. 여러 방면으로 응용하면 좋겠다!
🗺️마인드맵
📒학습 내용
구현 과제
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번: 시간 제한과 카운트다운 활용
- FTimerHandle과 GetWorld()->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.4 StaticMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
언리얼 엔진에서 특정 Static Mesh 컴포넌트의 충돌 설정을 활성화하는 코드.
Static Mesh 컴포넌트가 충돌을 감지하고 물리 시뮬레이션에 반응하도록 설정할 수 있다.
- SetCollisionEnabled : 충돌을 활성화하거나 비활성화하는 함수
- ECollisionEnabled::QueryAndPhysics : 충돌을 활성화하여 물리 시뮬레이션과 충돌 쿼리를 모두 처리한다
- Query (충돌 감지):
- 레이캐스트, 오버랩 테스트 등의 충돌 검사가 가능
- ✨ 플레이어가 벽을 조준할 때 히트 감지, 아이템 획득 범위 판정
- Physics (물리 시뮬레이션):
- 중력의 영향
- 다른 물체와 물리적으로 상호작용
- ✨ 벽에 부딪히거나, 물리 오브제와 상호작용
- Query (충돌 감지):
- 일반적인 물리 오브제(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)로 설정하는 역할.
각 상태에 따라 콜리전 설정과 머티리얼을 변경!
- 고체 상태 (bSolid가 true일 때):
- 모든 채널에 대해 콜리전 응답을 차단(ECR_Block)으로 설정
- ECR_Block??? ⬇️
-
더보기언리얼 엔진에서 물체의 충돌 반응을 설정할 때 사용하는 `ECollisionResponse` 열거형(enum)의 값 중 하나.
`ECR_Block`는 해당 물체가 다른 물체와 충돌할 때 그 충돌을 막아(Block)주는 것을 의미.
➡️ `ECR_Block`로 설정된 경우, 물체는 다른 물체와의 충돌을 감지하고 그 물체의 움직임을 멈추거나 반응!
✨예를 들어, 캐릭터가 `ECR_Block`으로 설정된 벽에 부딪히면 캐릭터의 이동이 막히게 된다.
아래는 `ECollisionResponse`의 몇 가지 다른 값들:
- `ECR_Block`: 물리적으로 충돌하여 막힘 (예: 벽)
- `ECR_Ignore`: 서로 통과함 (예: 유령처럼 통과)
- `ECR_Overlap`: 겹침을 감지하되 통과 가능 (예: 트리거 영역)
특정 채널에 대해서만 응답을 설정하고 싶다면 `SetCollisionResponseToChannel()`을 사용할 수 있다. - 플랫폼 머티리얼을 파란 머티리얼(SolidMaterial)로 설정
- 비물질 상태 (bSolid가 false일 때):
- 모든 채널에 대해 콜리전 응답을 무시(ECR_Ignore)로 설정
- 플랫폼의 머티리얼을 빨간 머티리얼(IntangibleMaterial)로 설정
1.8 #include "Kismet/GameplayStatics.h"
- `UGameplayStatics` 클래스를 사용하기 위해 포함하는 헤더 파일.
- 이 클래스는 게임플레이 관련한 다양한 유틸리티 함수들을 제공한다.
➡️ 사운드를 쓰기 위해 사용했음!
결과
🟣오늘의 옵시디언 현황