使用UE5的蓝图或者C++编写一个Actor功能是根据高度图生成地形模型具有碰撞和材质。请详细描述操作过程如果使用C++请提供完整cpp文件和头文件代码
操作过程:
-
创建一个新的Actor,命名为TerrainActor,打开它的蓝图或者C++代码。
-
定义一个高度图的变量,可以使用UE5自带的Landscape组件来生成高度图,也可以手动导入高度图文件。
-
在蓝图或者C++代码中,创建一个网格组件,用于生成地形模型。
-
在蓝图或者C++代码中,根据高度图生成地形模型,可以使用UE5自带的Landscape组件,也可以使用第三方库或自己编写的算法。
-
添加碰撞体,可以使用UE5自带的碰撞组件或者手动创建碰撞体。
-
添加材质,可以使用UE5自带的材质库或者创建自己的材质。
-
编译并运行Actor,查看生成的地形模型是否符合预期。
如果使用C++,以下是完整的cpp文件和头文件代码:
TerrainActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TerrainActor.generated.h"
UCLASS()
class MYPROJECT_API ATerrainActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATerrainActor();
// Called every frame
virtual void Tick(float DeltaTime) override;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
private:
UPROPERTY(EditAnywhere)
UTexture2D* HeightMap;
UPROPERTY(EditAnywhere)
UStaticMeshComponent* MeshComponent;
UPROPERTY(EditAnywhere)
UMaterialInterface* Material;
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
void GenerateTerrain();
float GetHeightFromTexture(FColor Color);
public:
UFUNCTION(BlueprintCallable)
void SetHeightMap(UTexture2D* InHeightMap);
UFUNCTION(BlueprintCallable)
void SetMaterial(UMaterialInterface* InMaterial);
};
TerrainActor.cpp
#include "TerrainActor.h"
#include "Components/StaticMeshComponent.h"
#include "Materials/MaterialInterface.h"
#include "Materials/MaterialInstanceDynamic.h"
// Sets default values
ATerrainActor::ATerrainActor()
{
PrimaryActorTick.bCanEverTick = true;
MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
RootComponent = MeshComponent;
}
// Called when the game starts or when spawned
void ATerrainActor::BeginPlay()
{
Super::BeginPlay();
GenerateTerrain();
}
// Called every frame
void ATerrainActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATerrainActor::SetHeightMap(UTexture2D* InHeightMap)
{
HeightMap = InHeightMap;
}
void ATerrainActor::SetMaterial(UMaterialInterface* InMaterial)
{
Material = InMaterial;
}
void ATerrainActor::GenerateTerrain()
{
if (!HeightMap)
{
return;
}
// Get the height map data
TArray<FColor> HeightMapData;
HeightMap->GetImageData(HeightMapData);
// Set up the mesh data
const int32 Width = HeightMap->GetSizeX();
const int32 Height = HeightMap->GetSizeY();
const float Scale = 10.0f;
const int32 NumVertices = Width * Height;
const int32 NumTriangles = (Width - 1) * (Height - 1) * 2 * 3;
Vertices.SetNum(NumVertices);
Triangles.SetNum(NumTriangles);
Normals.SetNum(NumVertices);
UVs.SetNum(NumVertices);
// Generate the vertices
for (int32 Y = 0; Y < Height; Y++)
{
for (int32 X = 0; X < Width; X++)
{
const int32 VertexIndex = Y * Width + X;
const float HeightValue = GetHeightFromTexture(HeightMapData[VertexIndex]);
const FVector VertexLocation = FVector(X * Scale, Y * Scale, HeightValue * Scale);
Vertices[VertexIndex] = VertexLocation;
Normals[VertexIndex] = FVector::UpVector;
UVs[VertexIndex] = FVector2D(X / (float)Width, Y / (float)Height);
}
}
// Generate the triangles
int32 TriangleIndex = 0;
for (int32 Y = 0; Y < Height - 1; Y++)
{
for (int32 X = 0; X < Width - 1; X++)
{
const int32 VertexIndex = Y * Width + X;
Triangles[TriangleIndex++] = VertexIndex;
Triangles[TriangleIndex++] = VertexIndex + 1;
Triangles[TriangleIndex++] = VertexIndex + Width;
Triangles[TriangleIndex++] = VertexIndex + 1;
Triangles[TriangleIndex++] = VertexIndex + Width + 1;
Triangles[TriangleIndex++] = VertexIndex + Width;
}
}
// Create the mesh
UStaticMesh* StaticMesh = NewObject<UStaticMesh>(this);
StaticMesh->InitResources();
FStaticMeshSourceModel& NewMesh = StaticMesh->AddSourceModel();
NewMesh.BuildSettings.bRecomputeNormals = false;
NewMesh.BuildSettings.bRecomputeTangents = false;
NewMesh.BuildSettings.bRemoveDegenerates = false;
NewMesh.BuildSettings.bUseMikkTSpace = false;
NewMesh.BuildSettings.bUseFullPrecisionUVs = false;
NewMesh.BuildSettings.bGenerateLightmapUVs = true;
NewMesh.BuildSettings.SrcLightmapIndex = 0;
NewMesh.BuildSettings.DstLightmapIndex = 1;
FStaticMeshVertexBuffer& VertexBuffer = NewMesh.VertexBuffers.StaticMeshVertexBuffer;
VertexBuffer.Init(NumVertices, 0);
FStaticMeshIndexBuffer32& IndexBuffer = NewMesh.IndexBuffer;
IndexBuffer.SetIndices(Triangles, Triangles.Num());
FStaticMeshVertexData& VertexData = NewMesh.VertexData;
VertexData.PositionComponent = FVertexStreamComponent(&VertexBuffer, STRUCT_OFFSET(FStaticMeshVertex, Position), sizeof(FStaticMeshVertex), VET_Float3);
VertexData.TextureCoordinateComponent[0] = FVertexStreamComponent(&VertexBuffer, STRUCT_OFFSET(FStaticMeshVertex, TextureCoordinate), sizeof(FStaticMeshVertex), VET_Float2);
VertexData.TangentBasisComponents[0] = FVertexStreamComponent(&VertexBuffer, STRUCT_OFFSET(FStaticMeshVertex, TangentX), sizeof(FStaticMeshVertex), VET_PackedNormal);
VertexData.ColorComponent = FVertexStreamComponent(&VertexBuffer, STRUCT_OFFSET(FStaticMeshVertex, Color), sizeof(FStaticMeshVertex), VET_Color);
for (int32 i = 0; i < NumVertices; i++)
{
FStaticMeshVertex& Vertex = VertexBuffer.Vertex(i);
Vertex.Position = Vertices[i];
Vertex.TextureCoordinate = UVs[i];
Vertex.TangentX = FVector::RightVector;
Vertex.TangentZ = Normals[i];
Vertex.Color = FColor::White;
}
StaticMesh->PostEditChange();
// Set the mesh and material
MeshComponent->SetStaticMesh(StaticMesh);
UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(Material, this);
MeshComponent->SetMaterial(0, DynamicMaterial);
}
float ATerrainActor::GetHeightFromTexture(FColor Color)
{
return (Color.R + Color.G + Color.B) / 3.0f / 255.0f;
}
原文地址: https://www.cveoy.top/t/topic/bMzq 著作权归作者所有。请勿转载和采集!