언리얼/스파르타 클럽

언리얼 C++과제3

코드망령 2026. 6. 4. 21:25

스파르타 클럽 챕터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