스파르타 클럽 챕터3 과제3 진행 (1/2)
회전발판과 움직이는 장애물을 만드는 과제입니다.
현재 발판의 회전과 움직이는 장애물을 구현했습니다.
https://youtu.be/lf_np--qstI?si=o1tioc16b0MkbILz
액터는 MovingActor와 RotateActor클래스를 따로 만들어서 BP로 상속받아서
현재는 맵에 직접 넣는 방식으로 구현했습니다.
MovingActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MovingActor.generated.h"
UCLASS()
class SPARTA3_4_API AMovingActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMovingActor();
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Mesh")
UStaticMeshComponent* MC;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
float Distance = 500.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
float Speed = 400.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
float TimerInterval = 0.01f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
bool bMoveVertical = false;
UFUNCTION(BlueprintCallable, Category = "Movement")
void StartMove();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
private:
FTimerHandle MoveTimer;
FTimerHandle ReturnTimer;
FVector StartLocation;
FVector EndLocation;
bool bIsReturning = false;
UFUNCTION()
void MoveForward();
UFUNCTION()
void ReturnStartLocation();
};
MovingActor.cpp
#include "MovingActor.h"
#include "Components/StaticMeshComponent.h"
#include "TimerManager.h"
// Sets default values
AMovingActor::AMovingActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
MC = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
RootComponent = MC;
}
void AMovingActor::StartMove()
{
StartLocation = GetActorLocation();
const FVector MoveDirection = bMoveVertical ? FVector::UpVector : GetActorForwardVector();
EndLocation = StartLocation + MoveDirection * Distance;
GetWorldTimerManager().ClearTimer(MoveTimer);
GetWorldTimerManager().ClearTimer(ReturnTimer);
GetWorldTimerManager().SetTimer(
MoveTimer,
this,
&AMovingActor::MoveForward,
TimerInterval,
true
);
}
void AMovingActor::BeginPlay()
{
Super::BeginPlay();
}
void AMovingActor::MoveForward()
{
const FVector CurrentLocation = GetActorLocation();
const FVector ToTarget = EndLocation - CurrentLocation;
const float DistanceToTarget = ToTarget.Size();
const float Step = Speed * TimerInterval;
if (DistanceToTarget <= Step)
{
SetActorLocation(EndLocation);
GetWorldTimerManager().ClearTimer(MoveTimer);
GetWorldTimerManager().SetTimer(
ReturnTimer,
this,
&AMovingActor::ReturnStartLocation,
TimerInterval,
true
);
return;
}
SetActorLocation(CurrentLocation + ToTarget.GetSafeNormal() * Step);
}
void AMovingActor::ReturnStartLocation()
{
const FVector CurrentLocation = GetActorLocation();
const FVector ToTarget = StartLocation - CurrentLocation;
const float DistanceToTarget = ToTarget.Size();
const float Step = Speed * TimerInterval;
if (DistanceToTarget <= Step)
{
SetActorLocation(StartLocation);
GetWorldTimerManager().ClearTimer(ReturnTimer);
GetWorldTimerManager().SetTimer(
MoveTimer,
this,
&AMovingActor::MoveForward,
TimerInterval,
true
);
return;
}
SetActorLocation(CurrentLocation + ToTarget.GetSafeNormal() * Step);
}
// Called every frame
void AMovingActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
RotateActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "RotateActor.generated.h"
UENUM(BlueprintType)
enum class ERotateMode : uint8
{
KeepRotating UMETA(DisplayName = "Keep Rotating"),
StopRotating UMETA(DisplayName = "Stop After Time"),
ReverseRotating UMETA(DisplayName = "Reverse Rotating"),
StopAndReverseRotating UMETA(DisplayName = "Stop After Time And Reverse"),
};
UENUM(BlueprintType)
enum class ERotateAxis : uint8
{
Pitch UMETA(DisplayName = "Pitch / Y Axis"),
Yaw UMETA(DisplayName = "Yaw / Z Axis"),
Roll UMETA(DisplayName = "Roll / X Axis"),
};
UCLASS()
class SPARTA3_4_API ARotateActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ARotateActor();
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Mesh")
UStaticMeshComponent* MC;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
float RotationSpeed = 90.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
float RotateDuration = 2.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
float TimerInterval = 0.01f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
float StopDuration = 1.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
ERotateMode RotateMode = ERotateMode::KeepRotating;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
ERotateAxis RotateAxis = ERotateAxis::Yaw;
UFUNCTION(BlueprintCallable, Category = "Rotation")
void StartRotating();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
private:
FTimerHandle RotateTimer;
FTimerHandle RestartTimer;
float ElapsedTime = 0.f;
float RotateDirection = 1.f;
UFUNCTION()
void RotateStep();
UFUNCTION()
void StopRotate();
UFUNCTION()
void ReverseRotate();
};
RotateActor.cpp
#include "RotateActor.h"
// Sets default values
ARotateActor::ARotateActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
MC = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
RootComponent = MC;
}
// Called when the game starts or when spawned
void ARotateActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ARotateActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ARotateActor::StartRotating()
{
ElapsedTime = 0.f;
GetWorldTimerManager().ClearTimer(RotateTimer);
GetWorldTimerManager().ClearTimer(RestartTimer);
GetWorldTimerManager().SetTimer(
RotateTimer,
this,
&ARotateActor::RotateStep,
TimerInterval,
true
);
}
void ARotateActor::RotateStep()
{
ElapsedTime += TimerInterval;
const float RotationAmount = RotationSpeed * RotateDirection * TimerInterval;
FRotator DeltaRotation = FRotator::ZeroRotator;
switch (RotateAxis)
{
case ERotateAxis::Pitch:
DeltaRotation.Pitch = RotationAmount;
break;
case ERotateAxis::Yaw:
DeltaRotation.Yaw = RotationAmount;
break;
case ERotateAxis::Roll:
DeltaRotation.Roll = RotationAmount;
break;
}
AddActorLocalRotation(DeltaRotation);
if (RotateMode == ERotateMode::StopRotating && ElapsedTime >= RotateDuration)
{
StopRotate();
}
else if (RotateMode == ERotateMode::ReverseRotating && ElapsedTime >= RotateDuration)
{
StopRotate();
ElapsedTime = 0.f;
GetWorldTimerManager().SetTimer(
RestartTimer,
this,
&ARotateActor::StartRotating,
StopDuration,
false
);
}
else if (RotateMode == ERotateMode::StopAndReverseRotating && ElapsedTime >= RotateDuration)
{
StopRotate();
ReverseRotate();
ElapsedTime = 0.f;
GetWorldTimerManager().SetTimer(
RestartTimer,
this,
&ARotateActor::StartRotating,
StopDuration,
false
);
}
}
void ARotateActor::StopRotate()
{
GetWorldTimerManager().ClearTimer(RotateTimer);
}
void ARotateActor::ReverseRotate()
{
RotateDirection *= -1.f;
}
Timer를 사용하여 0.01초가 지나면 조금씩 이동하도록 설정해서 부드럽게 움직이는 것 처럼 보이게 만들었습니다.
각 파라미터들은 에디터에서 변경 가능하도록 리플렉션 매크로를 EditAnywhere로 설정했습니다.
'언리얼 > 스파르타 클럽' 카테고리의 다른 글
| 언리얼 C++ 과제 4 (0) | 2026.06.09 |
|---|