天天看點

Unreal4 使用spline , splinemesh元件建構賽道小例子

本人最近幾天一直想寫一個賽道建構的例子,一下使用UnrealReal4中spline,splinemesh元件。具體怎麼用大家去看官方的wiki就行了,這裡直接貼代碼`。

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "RoadSpline.generated.h"

class USplineComponent;
class USplineMeshComponent;
class UTextRenderComponent;


USTRUCT()
struct FSplineRoadData
{
    GENERATED_USTRUCT_BODY()


    UPROPERTY( )
    bool    LeftGuardRail;

    UPROPERTY()
    bool    RightGuardRail;

    UPROPERTY( )
    float   TrackBank;

    UPROPERTY( )
    float   TrackWidth;

    UPROPERTY( )
    float   TrackThickness;

    FSplineRoadData()
    : LeftGuardRail(true)
    , RightGuardRail(true)
    , TrackBank(f)
    , TrackWidth(f)
    , TrackThickness(f)
    {

    }
};


UCLASS()
class RACER_API ARoadSpline : public AActor
{
    GENERATED_BODY()

public: 
    // Sets default values for this actor's properties
    ARoadSpline();

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 OnConstruction(const FTransform& Transform);
    USplineMeshComponent* AddTrackElement(int index,UStaticMesh* mesh);
    virtual void UpdateSplineMesh(TArray<USplineMeshComponent*>& splineArray);

    virtual UTextRenderComponent* DrawTrackNumber( int index );
public:

    UPROPERTY(EditDefaultsOnly, Category = "Road Components")
    USplineComponent* mSplineComponent;

    UPROPERTY( )
    int NumberOfSplinePoints;

    UPROPERTY( )
    TArray<FSplineRoadData> RoadDataArray;

    UPROPERTY( )
    TArray<USplineMeshComponent*> SplineMeshArray;
    UPROPERTY( )
    TArray<USplineMeshComponent*> LeftSplineMeshArray;
    UPROPERTY( )
    TArray<USplineMeshComponent*> RightSplineMeshArray;

    UPROPERTY( )
    TArray<UTextRenderComponent*> textArray;

    UPROPERTY(EditDefaultsOnly, Category = "LoopRoad")
    bool    LoopingTrack; // 是否循環,貌似現在不可用

    UPROPERTY()
    int    mCurrentIndex; // 目前編輯第幾個

    UPROPERTY()
    int    mPointIndex; // 目前編輯第幾個

    UPROPERTY(transient)
    class UStaticMesh* CameraMesh;
    UPROPERTY(transient)
    class UStaticMesh* rightMesh;
    UPROPERTY(transient)
    class UStaticMesh* leftMesh;

    UPROPERTY( EditDefaultsOnly, Category = "Collision" )
    bool Collisions; // 是否支援碰撞


};
           

一下是實作:

// Fill out your copyright notice in the Description page of Project Settings.

#include "RoadSpline.h"
#include "Components/SplineComponent.h"
#include "Components/SplineMeshComponent.h"
#include "Components/TextRenderComponent.h"
#include "Engine/CollisionProfile.h"
#include "UObject/ConstructorHelpers.h"


// Sets default values
ARoadSpline::ARoadSpline()
: Collisions(false)
, mPointIndex()
{
    // 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;
    mSplineComponent = CreateDefaultSubobject<USplineComponent>(TEXT("RoadSpline"));
    mSplineComponent->SetupAttachment(RootComponent);
//    SetRootComponent(mSplineComponent);

    {
        static ConstructorHelpers::FObjectFinder<UStaticMesh> ObjMesh(TEXT("StaticMesh'/Game/TrackGenerator/Props/RoadMesh.RoadMesh'"));
        CameraMesh = ObjMesh.Object;
    }

    {
        static ConstructorHelpers::FObjectFinder<UStaticMesh> ObjMesh(TEXT("StaticMesh'/Game/TrackGenerator/Props/L_GuardRail.L_GuardRail'"));
        leftMesh = ObjMesh.Object;
    }

    {
        static ConstructorHelpers::FObjectFinder<UStaticMesh> ObjMesh(TEXT("StaticMesh'/Game/TrackGenerator/Props/R_GuardRail.R_GuardRail'"));
        rightMesh = ObjMesh.Object;
    }


    NumberOfSplinePoints = ;
    mCurrentIndex = ;

}

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

}

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

}

void ARoadSpline::OnConstruction(const FTransform &Transform)
{
    if (mSplineComponent == NULL) {
        return ;
    }
    NumberOfSplinePoints = mSplineComponent->GetNumberOfSplinePoints();

    int RoadDataNum = RoadDataArray.Num() ;

    if (RoadDataNum < NumberOfSplinePoints) {
        int addNum = NumberOfSplinePoints - RoadDataNum;
        for (int i = ; i<addNum; i++) {
            FSplineRoadData item;
            RoadDataArray.Add( item );
        }
    }
    else if( RoadDataNum > NumberOfSplinePoints )
    {
        int subNum = NumberOfSplinePoints - RoadDataNum;
        for (int i = ; i<subNum; i++) {
            RoadDataArray.Pop();
        }
    }

    int lastIndex = LoopingTrack ? NumberOfSplinePoints -  : NumberOfSplinePoints - ;
    for ( ; mCurrentIndex <= lastIndex; mCurrentIndex++)
    {
        FSplineRoadData road_item = RoadDataArray[mCurrentIndex];
        if (road_item.RightGuardRail)
        {
            USplineMeshComponent* splineMeshCom = AddTrackElement(mCurrentIndex, rightMesh);
            RightSplineMeshArray.Add(splineMeshCom);
        }
        if (road_item.LeftGuardRail)
        {
            USplineMeshComponent* splineMeshCom = AddTrackElement(mCurrentIndex, leftMesh);
            LeftSplineMeshArray.Add(splineMeshCom);

        }
        {
//            GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("list"));
            USplineMeshComponent* splineMeshCom = AddTrackElement(mCurrentIndex, CameraMesh);
            SplineMeshArray.Add(splineMeshCom);
        }
    }
    for (; mPointIndex < NumberOfSplinePoints ; mPointIndex ++)
    {
//        GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("point"));
        UTextRenderComponent* renderCom = DrawTrackNumber( mPointIndex );
        textArray.Add( renderCom );
    }

    for (int i =  ; i< textArray.Num(); i++)
    {

        UTextRenderComponent* renderCom = textArray[i];
        if (renderCom) {
            FVector LocalPosition,LocalTangent;
            mSplineComponent->GetLocalLocationAndTangentAtSplinePoint(i,LocalPosition,LocalTangent);

            LocalPosition.Z += ;
            FVector Scale3D(,,);
            LocalTangent *= -;
            FRotator rotator = LocalTangent.Rotation();
            FTransform tranform( rotator,LocalPosition,Scale3D );
            renderCom->SetRelativeTransform(tranform);
        }


    }

    UpdateSplineMesh(SplineMeshArray);
    UpdateSplineMesh(LeftSplineMeshArray);
    UpdateSplineMesh(RightSplineMeshArray);

}

void ARoadSpline::UpdateSplineMesh(TArray<USplineMeshComponent*>& splineArray)
{
    for (int i = ; i<splineArray.Num(); i++) {
        USplineMeshComponent* segment = splineArray[i];

        if (segment) {
            FVector LocalStartPosition,LoccalStartTangent,LocalEndPosition,LocalEndTangent;
            mSplineComponent->GetLocalLocationAndTangentAtSplinePoint(i,LocalStartPosition,LoccalStartTangent);
            mSplineComponent->GetLocalLocationAndTangentAtSplinePoint(i+, LocalEndPosition,
                                                                      LocalEndTangent);
            segment->SetStartAndEnd(LocalStartPosition, LoccalStartTangent,
                                    LocalEndPosition, LocalEndTangent);
        }
    }
}

USplineMeshComponent* ARoadSpline::AddTrackElement(int index, UStaticMesh *mesh)
{

    UStaticMesh* LocalTrackElementMesh = mesh;
    int LocalCurrentTrackPoint = index;
    int LocalNextTrackPoint = ( index +  ) % NumberOfSplinePoints;

    FString text = FString::Printf(TEXT("%d,%d"),LocalCurrentTrackPoint,LocalNextTrackPoint);

    GEngine->AddOnScreenDebugMessage(-, f, FColor::Red, text);
    FSplineRoadData roadItem = RoadDataArray[LocalCurrentTrackPoint];
    FSplineRoadData nextRoadItem = RoadDataArray[LocalNextTrackPoint];


    int LocalStartRoll = roadItem.TrackBank;
    int LocalEndRoll = nextRoadItem.TrackBank;
    FVector2D LocalEndScale(nextRoadItem.TrackWidth,nextRoadItem.TrackThickness);
    FVector2D LocalStartScale(roadItem.TrackWidth,roadItem.TrackThickness);



    USplineMeshComponent* const Segment = NewObject<USplineMeshComponent>(this);

    if (Segment)
    {
//      GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("add"));
        Segment->SetStaticMesh(LocalTrackElementMesh);

        Segment->SetStartRoll(LocalStartRoll,true);
        Segment->SetEndRoll(LocalEndRoll);
        Segment->SetStartScale(LocalStartScale);
        Segment->SetEndScale(LocalEndScale);
        Segment->SetMobility(EComponentMobility::Stationary);
        if ( Collisions)
            Segment->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
        else
            Segment->SetCollisionEnabled(ECollisionEnabled::NoCollision);

        Segment->SetForwardAxis(ESplineMeshAxis::X);

        Segment->SetupAttachment( RootComponent );
        Segment->RegisterComponent();

        return Segment;
    }
    return NULL;
}

UTextRenderComponent* ARoadSpline::DrawTrackNumber(int index)
{
    FString indexStr = FString::Printf( TEXT( "%d" ),index);
    FText TestHUDText = FText::FromString(indexStr);
    UTextRenderComponent* renderCom = NewObject<UTextRenderComponent>(this);

    if ( renderCom ) {
        renderCom->SetupAttachment( RootComponent );
        renderCom->RegisterComponent( );
        renderCom->SetText( TestHUDText );
        return renderCom;
    }

    return NULL;

}