目录:第九艺术-目录
Version:UnrealEngine 4.22.3
做开放大世界的话,初始可以用ProceduralFoliageSpawner 生成,由于都是随机生成的,之后一般都还会需要很多的细节调整,那就得用到默认的笔刷工具来生成植被了。因此就有了也在笔刷中实现上一篇文章功能的需求,也要在笔刷中允许配置各种Foliage的远近亲疏关系,借同样的需求,熟悉一下默认笔刷的流程。
在这里选择Foliage之后,点击要刷的表面。
- 调用到 FEdModeFoliage::StartFoliageBrushTrace,开始spawn 满足 UISettings和FoliageType里配置的Instances,这是和 Spawner 一个比较大的区别,用笔刷是可以获取到ActiveTool等Editor信息的,但spawner由于是在之外生成的,就得不到这些信息,之后做一些拓展的时候,会需要考虑到这些区别
- 之后调用到 FEdModeFoliage::ApplyBrush,这里会计算你操作工具的压力大小,作为最终生成Instance数量的系数,当然鼠标就是 1 了,然后判断如果是 Paint 工具,就开始生成
- 调用到 FEdModeFoliage::AddInstancesForBrush,根据Brush大小,构建这次生成的Buckets,主要用于LandscapeLayer的权重检查,会先查看一下已有的bucket里有多少
- 拿到之后,就走到 笔刷和Spawner工具类似的主流程了,调用FEdModeFoliage::AddInstancesImp,逐个生成Instance
- 相比Spawner会调用线程安全的 CalculatePotentialInstances_ThreadSafe,笔刷不会多线程,所以直接调用CalculatePotentialInstances
- FEdModeFoliage::CalculatePotentialInstances,和spawner类似
- FoliageTrace 找到地块
- VertexMaskCheck
- LandscapeLayerCheck
- 满足条件的Instance加到要Spawn的数组
- 逐个生成 Instance
如上流程可以看到,之前也说过,在笔刷工具做这个功能的时候,最大的优势就是刷的每种FoliageType都已经可以知道当前地块上有哪些已经放好的 Instance,所以可以直接做距离检查,那就很简单了,只要再最后做各种检查的地方加一个远近亲疏关系的检查就可以了
实现代码如下:
FoliageType里
添加配置结构体,模型和距离,这里可以新加一个,也可以用spawner的
USTRUCT()
struct FRelatedFoliageTypeEntry
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere,Category = RelatedFoliage)
UStaticMesh* FoliageMesh;
UPROPERTY(EditAnywhere,Category = RelatedFoliage)
float Radius;
};
添加属性配置
UPROPERTY(EditAnywhere, Category = Custom)
TArray<FRelatedFoliageTypeEntry> CloseToFoliages;
UPROPERTY(EditAnywhere, Category = Custom)
TArray<FRelatedFoliageTypeEntry> FarFromFoliages;
添加 距离检查的方法 RelatedFoliageTypesCheck,逻辑很简单
static bool CheckForOverlappingSphere(AInstancedFoliageActor* IFA, const UStaticMesh* Mesh, const FSphere& Sphere)
{
if (IFA)
{
TArray<const UFoliageType*> MeshFoliageTypes;
IFA->GetAllFoliageTypesForSource(Mesh, MeshFoliageTypes);
for (int i = 0; i < MeshFoliageTypes.Num(); ++i)
{
if (CheckForOverlappingSphere(IFA, MeshFoliageTypes[i], Sphere))
return true;
}
}
return false;
}
bool RelatedFoliageTypesCheck(const FHitResult& Hit, const UFoliageType* Settings)
{
if (!Settings)
return false;
if (Settings->CloseToFoliages.Num() == 0 && Settings->FarFromFoliages.Num() == 0)
return true;
AInstancedFoliageActor* IFA = AInstancedFoliageActor::GetInstancedFoliageActorForLevel(Hit.Component->GetComponentLevel(),false);
if (!IFA)
{
if (Settings->CloseToFoliages.Num())
return false;
return true;
}
for (int i = 0; i < Settings->CloseToFoliages.Num(); ++i)
{
if (Settings->CloseToFoliages[i].FoliageMesh && Settings->GetSource() != Settings->CloseToFoliages[i].FoliageMesh && Settings->CloseToFoliages[i].Radius > 0)
{
if (!CheckForOverlappingSphere(IFA, Settings->CloseToFoliages[i].FoliageMesh, FSphere(Hit.Location, Settings->CloseToFoliages[i].Radius)))
return false;
}
}
for (int i = 0; i < Settings->FarFromFoliages.Num(); ++i)
{
if (Settings->FarFromFoliages[i].FoliageMesh && Settings->GetSource() != Settings->FarFromFoliages[i].FoliageMesh && Settings->FarFromFoliages[i].Radius > 0)
{
if (CheckForOverlappingSphere(IFA, Settings->FarFromFoliages[i].FoliageMesh, FSphere(Hit.Location, Settings->FarFromFoliages[i].Radius)))
return false;
}
}
return true;
}
最后就是调用了,在最后 CalculatePotentialInstances 和 CalculatePotentialInstances_ThreadSafe 里面都加上这个判断
类似
const bool bValidInstance = CheckLocationForPotentialInstance_ThreadSafe(Settings, Hit.ImpactPoint, Hit.ImpactNormal)
&& VertexMaskCheck(Hit, Settings)
&& LandscapeLayerCheck(Hit, Settings, LocalCache, HitWeight)
&& RelatedFoliageTypesCheck(Hit, Settings)//多加了这一句