Tick()에서 AddActorLocalRotation(RotationSpeed * DeltaTime)으로 회전
FRotator 타입의 RotationSpeed를 UPROPERTY(EditAnywhere)로 지정해 블루프린트에서 설정 가능
회전축 조정 시 피벗 위치가 중요하며, 회전이 꼭짓점 기준으로 되면 피벗을 수정해야 함
피벗 수정은 StaticMeshAsset 에디터에서 "Enable Pivot Editing" 버튼으로 조정 가능
📎 [Unreal UE5] 피벗 옮기기
📎 언리얼 5 - 액터의 피벗 위치 변경하기 (How to Change Actor Pivots)
Yaw(Y축) 기준 회전이 어긋나면 Roll(X축)으로 변경 시 해결될 수 있음
// 생성자
PlatformMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PlatformMesh"));
RootComponent = PlatformMesh;
RotationSpeed = FRotator(0.0f, 90.0f, 0.0f); // Y축 기준 90도 회전
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "RotatingPlatform.generated.h"
UCLASS()
class MYPROJECT_API ARotatingPlatform : public AActor
{
GENERATED_BODY()
public:
ARotatingPlatform(); // 생성자
virtual void Tick(float DeltaTime) override;
protected:
virtual void BeginPlay() override;
private:
// 피벗 보정
UPROPERTY()
USceneComponent* RootScene;
UPROPERTY()
UStaticMeshComponent* PlatformMesh;
public:
// 회전 속도 (에디터에서 조절 가능)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
FRotator RotationSpeed;
};
#include "RotatingPlatform.h"
ARotatingPlatform::ARotatingPlatform()
{
PrimaryActorTick.bCanEverTick = true;
// 중심 잡기 위한 Scene 루트 생성
RootScene = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));
RootComponent = RootScene;
// 메쉬 생성 및 루트에 붙이기
PlatformMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PlatformMesh"));
PlatformMesh->SetupAttachment(RootScene);
// ✔ 메쉬 피벗 보정 위치 설정
// 아래 값은 발판 높이의 절반만큼 내림
// 예: 메쉬 높이가 100이라면 -50 하면 중심 맞음
PlatformMesh->SetRelativeLocation(FVector(0.f, 0.f, 200.f)); // 값은 직접 조절해봐야 정확해
// ✔ 회전 방향: Roll (Y축 기준 회전)
RotationSpeed = FRotator(0.f, 0.f, 90.f);
}
void ARotatingPlatform::BeginPlay()
{
Super::BeginPlay();
}
// Tick 함수에서 회전 처리
void ARotatingPlatform::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 프레임 독립적 움직임 -> * DeltaTime
AddActorLocalRotation(RotationSpeed * DeltaTime);
}
피벗 보정을 두 가지 방식으로 시도했지만 안된다...
Tick()에서 DeltaTime을 곱한 거리만큼 Z축 이동
→ 프레임 독립성을 보장
StartLocation, MaxRange, MoveSpeed, bMovingUp 사용
일정 거리 도달 시 방향 반전
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MovingDoor.generated.h"
UCLASS()
class MYPROJECT_API AMovingDoor : public AActor
{
GENERATED_BODY()
public:
AMovingDoor();
virtual void Tick(float DeltaTime) override;
protected:
virtual void BeginPlay() override;
private:
UPROPERTY()
USceneComponent* RootScene;
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* DoorMesh;
// 시작 위치 저장
FVector StartLocation;
// 방향 전환 (왕복 이동 범위 계산 때 필요)
// true -> 위로, false -> 아래로
bool bMovingUp;
public:
// 에디터에서 조절 가능하게
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Door Settings")
float MoveSpeed = 100.f;
// 위로 올라갈 수 있는 최대 높이
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Door Settings")
float MaxRange = 200.f;
};
#include "MovingDoor.h"
AMovingDoor::AMovingDoor()
{
PrimaryActorTick.bCanEverTick = true;
RootScene = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));
RootComponent = RootScene;
DoorMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
DoorMesh->SetupAttachment(RootScene);
}
void AMovingDoor::BeginPlay()
{
Super::BeginPlay();
StartLocation = GetActorLocation(); // 처음 위치 저장
bMovingUp = true; // true -> 처음엔 위로 올라감
}
void AMovingDoor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 현재 문 위치 받아옴
FVector CurrentLocation = GetActorLocation();
float MoveDelta = MoveSpeed * DeltaTime;
// 위로 이동 중
if (bMovingUp)
{
CurrentLocation.Z += MoveDelta; // Z위치 값 더하기 -> 위로 움직임
if (CurrentLocation.Z >= StartLocation.Z + MaxRange) // 최대 높이 넘거나 도달하면
{
CurrentLocation.Z = StartLocation.Z + MaxRange; // 그 위치에 멈추고
bMovingUp = false; // 다음 프레임부터는 내려감
}
}
else
{
CurrentLocation.Z -= MoveDelta; // Z위치 값 줄이기 -> 아래로 움직임
if (CurrentLocation.Z <= StartLocation.Z) // 시작 위치보다 낮아지면
{
CurrentLocation.Z = StartLocation.Z; // 원래 위치로 고정
bMovingUp = true; // 다음 프레임부터는 위로 올라감
}
}
SetActorLocation(CurrentLocation);
}
일정 시간 뒤 Explode() 함수 호출 → 메시 숨기고 충돌 제거
GEngine->AddOnScreenDebugMessage() 로 디버그 메시지 띄움
UE_LOG()는 콘솔에서만 보임!GEngine 사용디버그 메시지가 안 보이면 Play In Editor (PIE) 모드나 Output Log 설정 확인
GEngine->AddOnScreenDebugMessage(
-1, // 키값: -1이면 새로운 메시지로 계속 추가
2.0f, // 2초간 표시
FColor::Green,
TEXT("BOOM! ExplodingTank exploded!")
);
파티클 시스템(P_Explosion) 사용
UPROPERTY(EditAnywhere)
UParticleSystem* ExplosionEffect;
UGameplayStatics::SpawnEmitterAtLocation(
GetWorld(),
ExplosionEffect,
GetActorLocation(),
FRotator::ZeroRotator,
FVector(3.0f) // 불이 너무 작아서 크기 3배 확대
);
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ExplodingTank.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogExplodingTank, Log, All);
UCLASS()
class MYPROJECT_API AExplodingTank : public AActor
{
GENERATED_BODY()
public:
AExplodingTank();
protected:
virtual void BeginPlay() override;
private:
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* TankMesh;
// 폭발 타이머 핸들
FTimerHandle ExplosionTimer;
UPROPERTY(EditAnywhere, Category = "Effect")
UParticleSystem* ExplosionEffect;
// 폭발 처리 함수
UFUNCTION()
void Explode();
public:
// 대기 시간 (에디터에서 조절 가능)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Explosion Settings")
float ExplosionDelay = 3.0f;
};
#include "ExplodingTank.h"
#include "TimerManager.h"
#include "Engine/World.h"
#include "Kismet/GameplayStatics.h"
DEFINE_LOG_CATEGORY(LogExplodingTank);
AExplodingTank::AExplodingTank()
{
// 메시 설정, 루트 컴포넌트 설정
TankMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("TankMesh"));
RootComponent = TankMesh;
}
void AExplodingTank::BeginPlay()
{
Super::BeginPlay();
// 일정 시간 뒤에 Explode 실행
GetWorld()->GetTimerManager().SetTimer(
ExplosionTimer,
this,
&AExplodingTank::Explode,
3.0f,
false // 반복 안 함
);
}
void AExplodingTank::Explode()
{
// 폭발하고 메시 숨기기, 충돌 끄기
TankMesh->SetVisibility(false);
TankMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
// 폭발 파티클 재생
if (ExplosionEffect)
{
UGameplayStatics::SpawnEmitterAtLocation(
GetWorld(),
ExplosionEffect,
GetActorLocation()
);
}
// 폭발 로그 출력
UE_LOG(LogTemp, Warning, TEXT("BOOM! ExplodingTank exploded!"))
// 월드에 출력
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(
-1,
5.0f,
FColor::Red,
TEXT("BOOM! ExplodingTank exploded!"));
}
// 폭발 이펙트 생성
UGameplayStatics::SpawnEmitterAtLocation(
GetWorld(),
ExplosionEffect,
GetActorLocation(),
FRotator::ZeroRotator,
FVector(3.0f) // 크기 확대
);
}
UPROPERTY()는 클래스 스코프 안에 있어야 함
함수 밖, 클래스 블록 안에 선언해야 빌드 오류 없음
피벗 편집 기능은 일부 StaticMesh에는 비활성화되어 있을 수 있음
계속 안 고쳐져서 축이 이상하다...
디버그 메시지가 화면에 안 보이면 GEngine->AddOnScreenDebugMessage() 사용
Yaw 값으로 회전축이 원하는 대로 안 될 경우, Roll 또는 Pitch로 바꿔 시도
SceneComponent는 위치와 회전(트랜스폼)의 기준이 된다 그래서 Root Component로 사용한다
모든 DeltaTime 기반 로직은 프레임 독립성을 보장해준다
디버그용 메시지는 GEngine으로 시각화, 로그용은 UE_LOG