UE4 Runtime Landscape


參考了LandscapeEdModeComponentTool代碼,魔改以后可在運行時動態增加LandscapeComponent,更換貼圖,按需加載地圖

主要是為了landscape的優越性能,LOD等

為實現無限地圖提供了思路,只要把google的衛星地圖動態加載進來,就可以實現無限大的真實地景

.c文件

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"

#include "RuntimeGenerateTerrain.generated.h"

class ALandscapeProxy;
class UMaterialInstanceDynamic;
class UMaterialInstance;
class ULandscapeComponent;


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class FLIGHTSIM_API URuntimeGenerateTerrain : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	URuntimeGenerateTerrain();

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

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	bool LoadTexture(ULandscapeComponent* C);

	void DynamicAddLandscapeComponent();

	void SetXYtoComponentMap(ULandscapeComponent* C);

public:
	ALandscapeProxy* mLandscape = nullptr;

	UMaterialInstanceDynamic* GI;
	UMaterialInstance* SounceMaterial;
	TMap<FIntPoint, ULandscapeComponent*> mXYtoComponentMap;
private:
	bool bAddComponent:1;

};

  .cpp文件

URuntimeGenerateTerrain::URuntimeGenerateTerrain()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;
	bAddComponent = true;
	// ...
	static ConstructorHelpers::FObjectFinder<UMaterialInstance> _material(TEXT("MaterialInstanceConstant'/Game/GoogleMap/M_GoogleBASE_Inst.M_GoogleBASE_Inst'"));
	if (_material.Succeeded())
	{
		SounceMaterial = _material.Object;
	}


}

  

初始landscape更換貼圖

void URuntimeGenerateTerrain::BeginPlay()
{
	Super::BeginPlay();

	// ...
	UWorld* world = GetWorld();
	check(world);
	TArray<AActor*> _actor;
	UGameplayStatics::GetAllActorsOfClass(GetWorld(), ALandscapeProxy::StaticClass(), _actor);
	if (_actor.Num() > 0)
	{
		mLandscape = (ALandscapeProxy*)_actor[0];
	}
	if (mLandscape)
	{
		mLandscape->LandscapeMaterial = SounceMaterial;
		for (ULandscapeComponent* C : mLandscape->LandscapeComponents)
		{
			LoadTexture(C);
			SetXYtoComponentMap(C);
		}

	}
}

  

bool URuntimeGenerateTerrain::LoadTexture(ULandscapeComponent* C)
{
	if (C->IsRenderStateCreated())
	{
		C->MarkRenderStateDirty();
		FlushRenderingCommands();
	}

	for (int j = 0; j < C->MaterialInstances.Num(); j++)
	{
		if (!C->MaterialInstances[j]->IsA(UMaterialInstanceDynamic::StaticClass()))
		{
			C->MaterialInstances[j] = (UMaterialInstanceConstant*)UMaterialInstanceDynamic::Create(C->MaterialInstances[j], GetTransientPackage());// HACKY CAST!
		}
		UMaterialInstanceDynamic* MID = (UMaterialInstanceDynamic*)C->MaterialInstances[j];

		int XIndex = C->GetRelativeTransform().GetLocation().X / 7 + 1;
		int YIndex = C->GetRelativeTransform().GetLocation().Y / 7 + 1;

		FString _fileName = FString("Texture2D'/Game/GoogleMap/satellite_en/Terrain_1/18/");
		int rowOffset = FCString::Atoi(*UFS_Utils::GetTerrainConfigSection(FString("rowOffset")));  //UFS::Utils::GetTerrainConfigSection靜態方法,獲取config文件中定義的貼圖初始的offset
		int columnOffset = FCString::Atoi(*UFS_Utils::GetTerrainConfigSection(FString("columnOffset")));
		_fileName.Append(FString::FromInt(XIndex + rowOffset));
		_fileName.Append(FString("/"));
		_fileName.Append(FString::FromInt(YIndex + columnOffset));
		_fileName.Append(FString("."));
		_fileName.Append(FString::FromInt(YIndex + columnOffset));
		_fileName.Append(FString("'"));


		UTexture2D* texture = LoadObject<UTexture2D>(NULL, *_fileName);
		if (texture)
		{
			MID->SetTextureParameterValue(FName("Texture"), texture);
		}
		else
		{
			return false;
		}

	}
	C->RecreateRenderState_Concurrent();
	return true;
}

  只需要把Google衛星地圖編號,按照component的行號,列號對應相應的貼圖,加載進來。

  LandscapeComponent的行號,列號可以通過計算其相對位置獲得,7是Quard數目

	int XIndex = C->GetRelativeTransform().GetLocation().X / 7 + 1;
	int YIndex = C->GetRelativeTransform().GetLocation().Y / 7 + 1;

  在runtime狀態,無法獲取LandscapeInfo,所以我們要自己去存儲x,y索引號對應的landscapeComponent

  

void URuntimeGenerateTerrain::SetXYtoComponentMap(ULandscapeComponent* C)
{
	int XIndex = C->GetRelativeTransform().GetLocation().X / 7 + 1;
	int YIndex = C->GetRelativeTransform().GetLocation().Y / 7 + 1;
	mXYtoComponentMap.Add(FIntPoint(XIndex, YIndex), C);
}

  聲明:

  TMap<FIntPoint, ULandscapeComponent*> mXYtoComponentMap;

  在動態增刪LandscapeComponent要根據x,y索引來獲取相應的Component

  

  動態增加:

void URuntimeGenerateTerrain::DynamicAddLandscapeComponent()
{
	if (!mLandscape)return;
      //目前為硬編碼做測試,后續這里是變量,動態改變
	int ComponentIndexX1 = 0;
	int ComponentIndexY1 = -1;
	int ComponentIndexX2 = 8;
	int ComponentIndexY2 = -1;

	TArray<ULandscapeComponent*> NewComponents;
	mLandscape->Modify();


	for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
	{
		for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
		{
			ULandscapeComponent* LandscapeComponent = mXYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY));
			if (!LandscapeComponent)
			{

				// Add New component...
				FIntPoint ComponentBase = FIntPoint(ComponentIndexX, ComponentIndexY)*mLandscape->ComponentSizeQuads;
				LandscapeComponent = NewObject<ULandscapeComponent>(mLandscape, NAME_None, RF_Transactional);

				mLandscape->LandscapeComponents.Add(LandscapeComponent);
				NewComponents.Add(LandscapeComponent);
				LandscapeComponent->Init(
					ComponentBase.X, ComponentBase.Y,
					mLandscape->ComponentSizeQuads,
					mLandscape->NumSubsections,
					mLandscape->SubsectionSizeQuads
				);


				LandscapeComponent->AttachToComponent(mLandscape->GetRootComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale);
                   //按理來說,landscapeComponent的相對位置z應該為0,但不知道什么原因,設置為0時,新增的component和landscape有高度差,只能通過一個一個試,最后確定256為正確值(目前不知道原因)
				LandscapeComponent->SetRelativeLocation(FVector(ComponentBase.X, ComponentBase.Y, 256.f));
				
				// Assign shared properties
				LandscapeComponent->UpdatedSharedPropertiesFromActor();

				int32 ComponentVerts = (mLandscape->SubsectionSizeQuads + 1) * mLandscape->NumSubsections;
				// Update Weightmap Scale Bias
				LandscapeComponent->WeightmapScaleBias = FVector4(1.0f / (float)ComponentVerts, 1.0f / (float)ComponentVerts, 0.5f / (float)ComponentVerts, 0.5f / (float)ComponentVerts);
				LandscapeComponent->WeightmapSubsectionOffset = (float)(LandscapeComponent->SubsectionSizeQuads + 1) / (float)ComponentVerts;

				TArray<FColor> HeightData;
				HeightData.Empty(FMath::Square(ComponentVerts));
				HeightData.AddZeroed(FMath::Square(ComponentVerts));
				LandscapeComponent->InitHeightmapData(HeightData, true);

				LandscapeComponent->UpdateMaterialInstances();
				LandscapeComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);

				SetXYtoComponentMap(LandscapeComponent);
				LoadTexture(LandscapeComponent);
			}
		}
	}

	// Need to register to use general height/xyoffset data update
	for (int32 Idx = 0; Idx < NewComponents.Num(); Idx++)
	{
		NewComponents[Idx]->RegisterComponent();
	}
      //必須的 否則新增的component閃爍
	for (ULandscapeComponent* NewComponent : NewComponents)
	{
		// Update Collision
		NewComponent->UpdateCachedBounds();
		NewComponent->UpdateBounds();
		NewComponent->MarkRenderStateDirty();
	}

}

  因為ue4是不支持runtime landscape的,所以很多方法都是有check(GIsEditor)判定的,要修改源碼,把這些Assert注釋掉,否則會崩潰

  具體有:

  LandscapeEdit.cpp 208行 282行

  Landscape.cpp 985

  MaterialInstanceConstant.cpp 54行 78 行90行

  MaterialInstance 3508行

 

  

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM