<style>.lazy{display:none}</style> Skip to main content

Unreal Engine C++ Fundamentals – Using Inheritance by moving the player & particles along a spline

By October 17, 2021Tutorial, Unreal

Hey guys,

Today we are going to continue exploring our last tutorial by looking at inheritance and what it takes to refactor our class.

The project files for this video & article can be found on our GitHub page.

The majority of the inheritance work is by creating a base class that our children can derive from which contains the primary logic that all children are always expected to perform.

To do this we are going to take our previous MovingSplineActor class make some of it’s methods virtual and migrate all of it’s Mesh specific logic to a new MeshMovingSplineActor class.

My making our MovingSplineActor functions virtual we provide the child class to override said functions and implement it’s own logic.

Since we need to have our MeshMovingSplineActor do it’s own work on a static mesh that the parent MovingSplineActor those methods need to be accessible otherwise it’s going to cause conflicts in our logic.

UCLASS()
class UE4FUNDAMENTALS14LIVE_API AMovingSplineActor : public AActor
{
  GENERATED_BODY()
  
public:	
  // Sets default values for this actor's properties
  AMovingSplineActor();

protected:
  // Called when the game starts or when spawned
  virtual void BeginPlay() override;

public:	
  // Called every frame
  virtual void Tick(float DeltaTime) override;

  UFUNCTION()
  virtual void ProcessMovementTimeline(float Value);

  UFUNCTION()
  virtual void OnEndMovementTimeline();

  UFUNCTION()
  virtual void TriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
  UFUNCTION()
  virtual void TriggerEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
}

Additionally we need to ensure that the logic we had in our MovingSplineActor is available to our child class. In this case we need to move the SplineLocation and SplineRotation variables out of the initial processing logic and into their own protected variables.

protected:
  FVector StartingSplineLocation;
  FVector CurrentSplineLocation;
  FRotator CurrentSplineRotation;

We can then modify our CPP file to ensure those variables are populated.

void AMovingSplineActor::ProcessMovementTimeline(float Value)
{
  const float SplineLength = SplineComponent->GetSplineLength();

  CurrentSplineLocation = SplineComponent->GetLocationAtDistanceAlongSpline(Value * SplineLength, ESplineCoordinateSpace::World);
  CurrentSplineRotation = SplineComponent->GetRotationAtDistanceAlongSpline(Value * SplineLength, ESplineCoordinateSpace::World);
}

By migrating all of our logic to the MeshMovingSplineActor we need to ensure it inherits from our new parent class. To do this we need to modify the header definitions to change it from AActor.

UCLASS()
class UE4FUNDAMENTALS14LIVE_API AMeshMovingSplineActor : public AMovingSplineActor
{
  GENERATED_BODY()
  
public:	
  // Sets default values for this actor's properties
  AMeshMovingSplineActor();

protected:
  // Called when the game starts or when spawned
  virtual void BeginPlay() override;

public:	
  // Called every frame
  virtual void Tick(float DeltaTime) override;

  virtual void ProcessMovementTimeline(float Value) override;

  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Spline", meta=(AllowPrivateAccess = "true"))
  UStaticMeshComponent* MeshComponent;
};

With our headers defined we can now migrate the logic of actually moving the spline component into our new child actor.

AMeshMovingSplineActor::AMeshMovingSplineActor()
{
 	// 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;

  MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh Component"));
  MeshComponent->SetupAttachment(SplineComponent);

  TriggerComponent->SetupAttachment(MeshComponent);
}

// Called when the game starts or when spawned
void AMeshMovingSplineActor::BeginPlay()
{
  Super::BeginPlay();

  MeshComponent->SetWorldLocation(StartingSplineLocation);
}

// Called every frame
void AMeshMovingSplineActor::Tick(float DeltaTime)
{
  Super::Tick(DeltaTime);

}

void AMeshMovingSplineActor::ProcessMovementTimeline(float Value)
{
  Super::ProcessMovementTimeline(Value);

  FRotator SplineRotation = CurrentSplineRotation;
  SplineRotation.Pitch = 0.f;
  MeshComponent->SetWorldLocationAndRotation(CurrentSplineLocation, SplineRotation);
}

If you notice, each override method ends up calling “Super::MethodSignature()” in order to execute the logic in the parent. Since our parent is simply moving the timeline around we don’t have to worry about anything other than the mesh in our timeline.

So that’s it, you just did some inhertiance work by shuffling around a few class definitions.

If you would like to see more examples check out the video as well as the GitHub project for examples of child classes that move the player and particles around.

Leave a Reply