本人最近幾天一直想寫一個賽道建構的例子,一下使用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;
}