So, I think there are better ways to do that (in Unity you have animators), but at lesson 98 (“Open the door” challenge) I thought: well, if you stick there a 90° Yaw rotation the door will not “open”, it will “start open”. So I thought of animating it through code.
This is the resulting code. It supports (or should support) opening and closing, and the duration of the animation is set via “blueprint” (Component parameter).
cpp file
// Source code freely available for any kind of use
#include "OpenDoor.h"
#include "GameFramework/Actor.h"
const float UOpenDoor::ANGLE_TO_OPEN = 90.f;
// Sets default values for this component's properties
UOpenDoor::UOpenDoor()
: bIsOpening(false), bIsClosing(false), RotatedAngle(0)
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
void UOpenDoor::Open()
{
UE_LOG(LogTemp, Warning, TEXT("parameters: %d, %d, %f"), bIsClosing, bIsOpening, RotatedAngle);
if (bIsClosing || bIsOpening || IsOpen())
return;
UE_LOG(LogTemp, Warning, TEXT("Opening door"));
bIsOpening = true;
}
void UOpenDoor::Close()
{
if (bIsClosing || bIsOpening || IsClosed())
return;
UE_LOG(LogTemp, Warning, TEXT("Closing door"));
bIsClosing = true;
}
// Called when the game starts
void UOpenDoor::BeginPlay()
{
Super::BeginPlay();
Open();
}
bool UOpenDoor::IsClosed() const
{
return FMath::Abs(RotatedAngle) <= 0.01f;
}
bool UOpenDoor::IsOpen() const
{
return FMath::Abs(RotatedAngle - 90) <= 0.01f;
}
// Called every frame
void UOpenDoor::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (bIsOpening || bIsClosing)
{
auto CurrentRotation = GetOwner()->GetActorRotation();
float deltaAngle = GetAngleByDelta(DeltaTime);
CurrentRotation.Yaw += deltaAngle;
GetOwner()->SetActorRotation(CurrentRotation);
UE_LOG(LogTemp, Warning, TEXT("Moving the door by %f"), deltaAngle);
RotatedAngle += deltaAngle;
if ((bIsOpening && IsOpen()) || (bIsClosing && IsClosed()))
StopMoving();
}
// ...
}
void UOpenDoor::StopMoving()
{
bIsOpening = bIsClosing = false;
}
float UOpenDoor::GetAngleByDelta(float DeltaTime) const
{
float deltaAngle = (ANGLE_TO_OPEN / TimeToOpen) * DeltaTime * (bIsOpening ? 1 : -1);
float min = bIsOpening ? 0 : -RotatedAngle;
float max = bIsOpening ? ANGLE_TO_OPEN - RotatedAngle : ANGLE_TO_OPEN;
UE_LOG(LogTemp, Warning, TEXT("GetAngleByDelta: for %f seconds, clamping %f between %f and %f"),
DeltaTime, deltaAngle, min, max);
return FMath::Clamp(deltaAngle, min, max);
}
header file
// Source code freely available for any kind of use
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "OpenDoor.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ROOMESCAPE_API UOpenDoor : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UOpenDoor();
void Open();
void Close();
protected:
// Called when the game starts
virtual void BeginPlay() override;
bool bIsOpening;
bool bIsClosing;
float RotatedAngle;
bool IsOpen() const;
bool IsClosed() const;
void StopMoving();
float GetAngleByDelta(float DeltaTime) const;
static const float ANGLE_TO_OPEN;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float TimeToOpen = 2.f;
};
Here’s a GIF with the result (sorry for the quality):

What would be the correct way to animate an opening door in a given amount of time?
