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

splinecomponent

Unreal Engine C++ Fundamentals – Using Spline Components

By Development, Tutorial, Unreal No Comments

Hey guys,

Today we are going to take a look how to use Spline Components and modify their various properties to allow us to specify custom materials as well as determine how meshes are attached to the spline.

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

Splines splines splines !

So why do we care about splines ? Well splines allow us to construct various objects within our world by providing us with easily extensible and modifiable joined sets of meshes.

Splines can also provide support for navigation by being used as a path for something or someone to move along. In our case though we are going to use it for prop building.

Let’s dive into how to initialize a Spline Component.

#include "Components/SplineComponent.h"

UPROPERTY(VisibleAnywhere, Category = "Spline")
USplineComponent* SplineComponent;

Inside our constructor we simply instantiate it.

// Sets default values
ASplineActor::ASplineActor()
{
 	// 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;

  SplineComponent = CreateDefaultSubobject<USplineComponent>("Spline");
  if(SplineComponent)
  {
    SetRootComponent(SplineComponent);	
  }	
}

With our spline component setup we can proceed with attaching Spline Mesh Components by iterating over the available spline points.

To do this we want to override the OnConstruction method in order to allow us to specify the Spline Mesh Components during the process of modifying our spline within the editor.

void ASplineActor::OnConstruction(const FTransform& Transform)
{	
  Super::OnConstruction(Transform);

  if(SplineComponent && SplineMeshMap.Num() > 0)
  {
    // lookup all pertinent values
    FSplineMeshDetails* StartMeshDetails = nullptr;
    if(SplineMeshMap.Contains(ESplineMeshType::START))
    {
      StartMeshDetails = SplineMeshMap.Find(ESplineMeshType::START);	
    }

    FSplineMeshDetails* EndMeshDetails = nullptr;
    if(SplineMeshMap.Contains(ESplineMeshType::END))
    {
      EndMeshDetails = SplineMeshMap.Find(ESplineMeshType::END);
    }

    FSplineMeshDetails* DefaultMeshDetails = nullptr;
    if(SplineMeshMap.Contains(ESplineMeshType::DEFAULT))
    {
      DefaultMeshDetails = SplineMeshMap.Find(ESplineMeshType::DEFAULT);	
    }
    else
    {
      // exit if we don't have a default mesh to work with
      return;
    }
    
    const int32 SplinePoints = SplineComponent->GetNumberOfSplinePoints();

    for(int SplineCount = 0; SplineCount < (SplinePoints - 1); SplineCount++)
    {
      USplineMeshComponent *SplineMesh = NewObject<USplineMeshComponent>(this, USplineMeshComponent::StaticClass());

      UStaticMesh* StaticMesh = DefaultMeshDetails->Mesh;
      UMaterialInterface* Material = nullptr;
      ESplineMeshAxis::Type ForwardAxis = DefaultMeshDetails->ForwardAxis;
  
      // start mesh
      if(StartMeshDetails && StartMeshDetails->Mesh && SplineCount == 0)
      {
        StaticMesh = StartMeshDetails->Mesh;
        ForwardAxis = StartMeshDetails->ForwardAxis;
        
        if(StartMeshDetails->DefaultMaterial)
        {
          Material = StartMeshDetails->DefaultMaterial;
        }					
      }
      else if(EndMeshDetails && EndMeshDetails->Mesh && SplinePoints > 2 && SplineCount == (SplinePoints - 2))
      {
        // end mesh
        StaticMesh = EndMeshDetails->Mesh;
        ForwardAxis = EndMeshDetails->ForwardAxis;
        
        if(EndMeshDetails->DefaultMaterial)
        {
          Material = EndMeshDetails->DefaultMaterial;
        }	
      }
      else
      {
        // default assignment - middle mesh
        if(DefaultMeshDetails->AlternativeMaterial && SplineCount > 0 && SplineCount % 2 == 0)
        {
          Material = DefaultMeshDetails->AlternativeMaterial;
        }
        else if(DefaultMeshDetails->DefaultMaterial)
        {
          Material = DefaultMeshDetails->DefaultMaterial;
        }			
      }

      // update mesh details
      SplineMesh->SetStaticMesh(StaticMesh);
      SplineMesh->SetForwardAxis(ForwardAxis, true);
      SplineMesh->SetMaterial(0, Material);
      
  
      // initialize the object
      SplineMesh->RegisterComponentWithWorld(GetWorld());
  
      SplineMesh->CreationMethod = EComponentCreationMethod::UserConstructionScript;
      SplineMesh->SetMobility(EComponentMobility::Movable);
  
      SplineMesh->AttachToComponent(SplineComponent, FAttachmentTransformRules::KeepRelativeTransform);
  
      // define the positions of the points and tangents
      const FVector StartPoint = SplineComponent->GetLocationAtSplinePoint(SplineCount, ESplineCoordinateSpace::Type::Local);
      const FVector StartTangent = SplineComponent->GetTangentAtSplinePoint(SplineCount, ESplineCoordinateSpace::Type::Local);
      const FVector EndPoint = SplineComponent->GetLocationAtSplinePoint(SplineCount + 1, ESplineCoordinateSpace::Type::Local);
      const FVector EndTangent = SplineComponent->GetTangentAtSplinePoint(SplineCount + 1, ESplineCoordinateSpace::Type::Local);
      SplineMesh->SetStartAndEnd(StartPoint, StartTangent, EndPoint, EndTangent, true);
  
      // query physics
      SplineMesh->SetCollisionEnabled(ECollisionEnabled::Type::QueryAndPhysics);
    }
  }
}

WHOA that’s a lot of stuff , slow down !