Skip to content

Instantly share code, notes, and snippets.

@josimard
Last active December 24, 2023 17:19
Show Gist options
  • Save josimard/c69b87dc88882e6f5286d9d69a5b6e85 to your computer and use it in GitHub Desktop.
Save josimard/c69b87dc88882e6f5286d9d69a5b6e85 to your computer and use it in GitHub Desktop.
UE4 - Rotate an actor around another in Unreal Engine using FVector::RotateAngleAxis() to create an orbital rotation (Not using transforms or attachments)
#include "RotateAroundActor.h"
#include "GameFramework/Actor.h"
// Sets default values for this component's properties
URotateAroundActor::URotateAroundActor()
{
// 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;
bAutoActivate = true;
}
// Called when the game starts
void URotateAroundActor::BeginPlay()
{
Super::BeginPlay();
Reset();
}
// Called every frame
void URotateAroundActor::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (IsActive() && RotateAroundActor != nullptr)
{
CurrentAngle += RotationSpeed * DeltaTime;
// Wrap angles around: Only good for this use case, otherwise use Math::UnwindDegrees()
if (CurrentAngle > 360.f) CurrentAngle -= 360.f;
// Get this actor location from rotating it's
const FVector NewLocation = RotateAroundActor->GetActorLocation() + RotationRadius.RotateAngleAxis(CurrentAngle, RotationAxis);
GetOwner()->SetActorLocationAndRotation(NewLocation, FRotator(0, CurrentAngle, 0));
}
}
void URotateAroundActor::Reset()
{
CurrentAngle = InitialRotationAngle;
if (RotateAroundActor == nullptr)
{
UE_LOG(LogTemp, Error, TEXT("RotateAroundActor is not set on %s"), *GetReadableName());
return;
}
if (RotationRadius.IsZero())
{
RotationRadius = GetOwner()->GetActorLocation() - RotateAroundActor->GetActorLocation();//FVector::Dist(GetOwner()->GetActorLocation(), RotateAroundActor->GetActorLocation());
}
}
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "RotateAroundActor.generated.h"
/**
* Rotate an actor around another.
*
* Notes:
* - ONLY USEFUL WHEN you cannot attach actors to each other for some reason.
* - Also demonstrates the use of FVector::RotateAngleAxis() in a reusable UActorComponent architecture
* The gist of it: WorldLocation = WorldLocation + RadiusVector.RotateAngleAxis(Angle, FVector::UpVector);
*
* If you can re-attach your actor's root component:
* - A simpler method consists of attaching a SceneComponent and using it's relative location and rotation to create an orbital movement
*
* @see USceneComponent::AttachToComponent
* https://api.unrealengine.com/INT/API/Runtime/Engine/Components/USceneComponent/AttachToComponent/index.html
**/
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class URotateAroundActor : public UActorComponent
{
GENERATED_BODY()
public:
// The target actor to rotate around
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RotateAround")
AActor* RotateAroundActor;
// The speed: Angles per second in degrees
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RotateAround")
float RotationSpeed = 50.f;
// The distance to rotate around the actor in CM
// ZERO: will take the initial distance of the two actors as the radius
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RotateAround")
FVector RotationRadius = FVector::ZeroVector;
// The 3D Axis around which the object will rotate, default horizontal (FVector::UpVector)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RotateAround")
FVector RotationAxis = FVector::UpVector;
// The current angle for FVector::RotateAngleAxis()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RotateAround", meta = (ClampMin = "0.0", ClampMax = "360.0", UIMin = "0.0", UIMax = "360.0"))
float InitialRotationAngle = 0.f;
protected:
float CurrentAngle = 0.f;
public:
// Sets default values for this component's properties
URotateAroundActor();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
// Will reset current angle axis and
UFUNCTION(BlueprintCallable)
void Reset();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment