先看效果:
UE:运行时(伪)无限细分网格
实际代码如下:
InfiniteGrid.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "InfiniteGrid.generated.h"
USTRUCT()
struct FGridLayer
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = "Grid")
float CellSize = 100.0f;
UPROPERTY(EditAnywhere, Category = "Grid")
float MaxVisibleDistance = 1000.0f;
UPROPERTY(EditAnywhere, Category = "Grid")
float FadeStartRatio = 0.8f; // 开始淡出的距离比例(例如0.8表示在80%最大距离时开始淡出)
};
UCLASS()
class FALCONV_API AInfiniteGrid : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AInfiniteGrid();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
void GenerateGridLayer(const FGridLayer& Layer, const FVector& WorldPos, float Thickness, int32 ActiveLayerIndex);
UPROPERTY(VisibleAnywhere)
ULineBatchComponent* LineBatch;
float gGridSize = 1000.0f;
float gGridMinPixelsBetweenCells = 2.0f;
float gGridCellSize = 0.025f;
FLinearColor gGridColorThick = FLinearColor(0.0f, 0.0f, 0.0f, 1.0f);
UPROPERTY(EditAnywhere, Category = "Grid")
TArray<FGridLayer> GridLayers;
UPROPERTY(EditAnywhere, Category = "Grid")
FLinearColor gGridColorThin = FLinearColor(0.5f, 0.5f, 0.5f, 1.0f); // 基础颜色
};
InfiniteGrid.cpp
#include "Util/InfiniteGrid.h"
#include "Camera/CameraComponent.h"
#include "Engine/World.h"
#include "Camera/CameraTypes.h"
#include "Components/LineBatchComponent.h"
#include "Camera/PlayerCameraManager.h"
#include "Kismet/GameplayStatics.h"
// Sets default values
AInfiniteGrid::AInfiniteGrid()
{
PrimaryActorTick.bCanEverTick = true;
LineBatch = CreateDefaultSubobject<ULineBatchComponent>(TEXT("LineBatch"));
RootComponent = LineBatch;
float BaseCellSize = 10.0f;
GridLayers.Add({BaseCellSize, 400.0f, 0.8f});
// 每级CellSize和MaxVisibleDistance放大10倍
for (int32 i = 1; i < 10; i++)
{
FGridLayer Layer;
Layer.CellSize = GridLayers[i - 1].CellSize * 5.0f;
Layer.MaxVisibleDistance = GridLayers[i - 1].MaxVisibleDistance * 5.0f;
Layer.FadeStartRatio = 0.8f;
GridLayers.Add(Layer);
}
// for (int32 i = 0; i < 10; i++)
// {
// GridLayers[i].MaxVisibleDistance +=50;
// }
}
// Called when the game starts or when spawned
void AInfiniteGrid::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AInfiniteGrid::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
auto WorldRef = GetWorld();
if (!WorldRef) return;
// 获取相机信息
APlayerCameraManager* PCM = UGameplayStatics::GetPlayerCameraManager(WorldRef, 0);
APlayerController* PC = UGameplayStatics::GetPlayerController(this, 0);
if (!PCM || !PC) return;
// 获取视口大小
FVector2D ViewportSize;
GEngine->GameViewport->GetViewportSize(ViewportSize);
// 获取视口中心射线
FVector RayStart;
FVector RayDirection;
PC->DeprojectScreenPositionToWorld(
ViewportSize.X * 0.5f,
ViewportSize.Y * 0.5f,
RayStart,
RayDirection
);
// 计算射线与网格平面的交点
FVector CurrentWorldPos = GetActorLocation();
float PlaneZ = CurrentWorldPos.Z;
float t = (PlaneZ - RayStart.Z) / RayDirection.Z;
FVector IntersectionPoint;
if (!FMath::IsNearlyZero(RayDirection.Z) && t >= 0)
{
IntersectionPoint = RayStart + t * RayDirection;
}
else
{
// 备用方案:使用相机水平投影
IntersectionPoint = FVector(RayStart.X, RayStart.Y, PlaneZ);
}
// 更新Actor位置(保持Z不变)
SetActorLocation(FVector(IntersectionPoint.X, IntersectionPoint.Y, PlaneZ));
// 更新Actor位置到射线交点(省略射线计算部分,同之前代码)
FVector WorldPos = GetActorLocation();
// 计算相机水平距离
// FVector Camera2DPos(PCM->GetCameraLocation().X, PCM->GetCameraLocation().Y);
// FVector Grid2DPos(WorldPos.X, WorldPos.Y);
float HorizontalDistance = FVector::Distance(PCM->GetCameraLocation(), WorldPos);
// 筛选当前主层级和次层级
int32 ActiveLayerIndex = 0;
for (int32 i = 0; i < GridLayers.Num(); i++)
{
if (HorizontalDistance < GridLayers[i].MaxVisibleDistance)
{
ActiveLayerIndex = i;
break;
}
}
ActiveLayerIndex = FMath::Clamp(ActiveLayerIndex, 0, GridLayers.Num() - 2);
FGridLayer& PrimaryLayer = GridLayers[ActiveLayerIndex];
FGridLayer& SecondaryLayer = GridLayers[ActiveLayerIndex + 1];
// 清除旧线条
LineBatch->Flush();
float BaseCellSize = GridLayers[0].CellSize;
float PrimaryLineThickness = 0.1f * PrimaryLayer.CellSize / BaseCellSize;
float SecondaryLineThickness = 0.05f * SecondaryLayer.CellSize / BaseCellSize;
// 生成主层级(小网格)
GenerateGridLayer(PrimaryLayer, WorldPos, PrimaryLineThickness,ActiveLayerIndex+1); // 线宽5.0f
// 生成次层级(大网格)
GenerateGridLayer(SecondaryLayer, WorldPos, SecondaryLineThickness,ActiveLayerIndex+1); // 线宽2.5f
}
// 辅助函数:生成单个层级的网格
void AInfiniteGrid::GenerateGridLayer(const FGridLayer& Layer, const FVector& WorldPos, float Thickness,int32 ActiveLayerIndex)
{
// 对齐到当前层级的网格单元
float GridCenterX = FMath::GridSnap(WorldPos.X, Layer.CellSize);
float GridCenterY = FMath::GridSnap(WorldPos.Y, Layer.CellSize);
// 动态计算覆盖范围(根据层级单元大小扩展)
int32 CellsToExtend = FMath::CeilToInt(5000.0f*ActiveLayerIndex*ActiveLayerIndex*ActiveLayerIndex*ActiveLayerIndex*ActiveLayerIndex / Layer.CellSize);
float MinX = GridCenterX - Layer.CellSize * CellsToExtend;
float MaxX = GridCenterX + Layer.CellSize * CellsToExtend;
float MinY = GridCenterY - Layer.CellSize * CellsToExtend;
float MaxY = GridCenterY + Layer.CellSize * CellsToExtend;
// 生成水平线
for (float y = MinY; y <= MaxY; y += Layer.CellSize)
{
FVector Start(MinX, y, WorldPos.Z);
FVector End(MaxX, y, WorldPos.Z);
LineBatch->DrawLine(Start, End, gGridColorThin, 0.0f, Thickness, 0);
}
// 生成垂直线
for (float x = MinX; x <= MaxX; x += Layer.CellSize)
{
FVector Start(x, MinY, WorldPos.Z);
FVector End(x, MaxY, WorldPos.Z);
LineBatch->DrawLine(Start, End, gGridColorThin, 0.0f, Thickness, 0);
}
}
快速部署
1.将代码编译至UE5工程
2.在场景中创建AInfiniteGrid实例