技术好文:UEFoliage工具拓展

简介: 技术好文:UEFoliage工具拓展

项目从4.24升级到4.26后 foliage的editor ui布局发生了很大的变化 顺带原项目中加的功能也要简单的移植下。


1.Fix:


UE的植被有个Invalid的标志,即Fix左边的这个小图标,这个是ue的自带功能选出所有的不合法的植被。


合法条件为: FoliageInstance的BaseComponent是否能在规定范围(射线检测)内找到,且误差在5cm内,且附着的BaseComponent与FoliageInstance是否在同一个level中(项目中新加)。


通俗的理解 所谓的不合法是指1.该植被没有附着的地面 2.地面与植被处于不同的level 3.离开地面过高 4.陷入地面过低。


1/2会影响关卡拆分移动重组导致植被的位置出错。3/4在表现上不美观。


不合法的植被是怎么来的。1美术同学的操作,比如为了美观移动某个植被到“空中”。2.拆关卡,静态合批拆合批的操作。


新加的Fix功能的目的即整体处理上面的4个问题。 具体代码与FoliageEdMode.cpp的SelectInvalidInstances相对应。分成2部分。


part1


step1 找到离需要修复的FoliageInstance最近的地面


step2 给FoliageInstance一个新的BaseId


step3 设置一个能够满足5cm误差的ZOffset来完成”对冲“


1 bool FEdModeFoliage::FixInstanceToGround(AInstancedFoliageActor InIFA, float AlignMaxAngle, FFoliageInfo& Mesh, int32 InstanceIdx)


2 {


3 UWorld InWorld = GetWorld();


4


5 FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(FoliageGroundCheck), true);


6 QueryParams.bReturnFaceIndex = false;


7 FCollisionShape SphereShape;


8 SphereShape.SetSphere(0.f);


9 TArray Hits; Hits.Reserve(16);


10 FHitResult HitResult;


11


12 FFoliageInstance& Instance = Mesh.Instances【InstanceIdx】;


13


14 FVector InstanceTraceRange = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, FOLIAGE_INVALID_TEST_TRACE));


15 FVector Start = Instance.Location + InstanceTraceRange;


16 FVector End = Instance.Location - InstanceTraceRange;


17


18 InWorld->SweepMultiByObjectType(Hits, Start, End, FQuat::Identity, FCollisionObjectQueryParams(ECC_WorldStatic), SphereShape, QueryParams);


19


20 // Find nearest hit


21 float TempDistance = InstanceTraceRange.Size();


22 for (const FHitResult& Hit : Hits)


23 {


24 float HitDistance = (Hit.Location - Instance.Location).Size();


25 if (HitDistance [span style="color: rgba(0, 0, 0, 1)"> TempDistance)


26 {


27 TempDistance = HitDistance;


28 HitResult = Hit;


29 }


30 }


31


32 if (HitResult.bBlockingHit)


33 {


34 UPrimitiveComponent HitComponent = HitResult.Component.Get();


35


36 // Find BSP brush


37 UModelComponent ModelComponent = Cast(HitComponent);


38 if (ModelComponent)


39 {


40 ABrush BrushActor = ModelComponent->GetModel()->FindBrush(HitResult.Location);


41 if (BrushActor)


42 {


43 HitComponent = BrushActor->GetBrushComponent();


44 }


45 }


46


47 // Set new base


48 auto NewBaseId = InIFA->InstanceBaseCache.AddInstanceBaseId(Mesh.ShouldAttachToBaseComponent() ? HitComponent : nullptr);


49 Mesh.RemoveFromBaseHash(InstanceIdx);


50 Instance.BaseId = NewBaseId;


51 if (Instance.BaseId == FFoliageInstanceBaseCache::InvalidBaseId)


52 {


53 Instance.BaseComponent = nullptr;


54 }


55 Mesh.AddToBaseHash(InstanceIdx);


56


57 // Set new ZOffset


58 float InstanceZUpOffsetFixLimitThreshold = CVarOffGroundZUpOffsetThreshold.GetValueOnGameThread();


59 float InstanceZDownOffsetFixLimitThreshold = CVarOffGroundZDownOffsetThreshold.GetValueOnGameThread();


60 float InstanceOffGroundLocalThreshold = CVarOffGroundTreshold.GetValueOnGameThread();


61 float InstanceWorldTreshold = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, InstanceOffGroundLocalThreshold)).Size();


62 float InstanceWorldZUpOffsetFixLimitThreshold = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, InstanceZUpOffsetFixLimitThreshold)).Size();


63 float InstanceWorldZDownOffsetFixLimitThreshold = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, InstanceZDownOffsetFixLimitThreshold)).Size();


64


65 FVector InstanceWorldZOffset = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, Instance.ZOffset));


66 FVector OffsetToGround = Instance.Location - (HitResult.Location + InstanceWorldZOffset);


67 bool bIsUpOffset = OffsetToGround.Z > 0 ? true : false;


68 float DistanceToGround = OffsetToGround.Size();


69


70 if (OffsetToGround.Z >= 0)


71 {


72 if (DistanceToGround > InstanceWorldZUpOffsetFixLimitThreshold)


73 {


74 return false;


75 }


76 if ((DistanceToGround - InstanceWorldTreshold) > KINDA_SMALL_NUMBER)


77 {


78 check(InstanceWorldZUpOffsetFixLimitThreshold > KINDA_SMALL_NUMBER);


79 float ScaleRateLocalToWorld = InstanceZUpOffsetFixLimitThreshold / InstanceWorldZUpOffsetFixLimitThreshold;


80 Instance.ZOffset += DistanceToGround ScaleRateLocalToWorld;


81 }


82 }


83 else


84 {


85 if (DistanceToGround > InstanceWorldZDownOffsetFixLimitThreshold)


86 {


87 return false;


88 }


89 if ((DistanceToGround - InstanceWorldTreshold) > KINDA_SMALL_NUMBER)


90 {


91 check(InstanceWorldZDownOffsetFixLimitThreshold > KINDA_SMALL_NUMBER);


92 float ScaleRateLocalToWorld = InstanceZDownOffsetFixLimitThreshold / InstanceWorldZDownOffsetFixLimitThreshold;


93 Instance.ZOffset -= DistanceToGround ScaleRateLocalToWorld;


94 }


95 }


96 return true;


97 }


98 return false;


99 }


View Code


part2


step1 将植被移到合理的关卡中


1 void FEdModeFoliage::MoveSelectInstancesToCorrectLevel(UWorld InWorld)


2 {


3 GEditor->BeginTransaction(NSLOCTEXT("UnrealEd", "FoliageModeTransaction:MoveSelectInstancesToCorrectLevel", "Move Foliage To Correct Level"));


4 {


5


6 const int32 NumLevels = InWorld->GetNumLevels();


7 for (int32 LevelIdx = 0; LevelIdx < NumLevels; ++LevelIdx)


8 {


9 ULevel Level = InWorld->GetLevel(LevelIdx);


10 AInstancedFoliageActor IFA = AInstancedFoliageActor::GetInstancedFoliageActorForLevel(Level);


11 if (IFA)


12 {


13 bool bFoundSelection = false;


14


15 for (auto& MeshPair : IFA->FoliageInfos)


16 {


17 FFoliageInfo& FoliageInfo = MeshPair.Value;


18


19 if (FoliageInfo.SelectedIndices.Array().Num() > 0)


//代码参考:https://weibo.com/u/7930362378

20 {


21 // Mark actor once we found selection


22 if (!bFoundSelection)


23 {


24 IFA->Modify();


25 bFoundSelection = true;


26 }


27 int InstanceIndex = 0;


28 while (InstanceIndex [span style="color: rgba(0, 0, 0, 1)"> FoliageInfo.Instances.Num())


29 {


30 if (!FixInstanceToCorrectLevel(IFA, MeshPair.Key->AlignMaxAngle, FoliageInfo, InstanceIndex))


31 {


32 InstanceIndex++;


33 }


34 }


35 }


36 }


37 }


38 }


39 }


40 GEditor->EndTransaction();


41 }


View Code


1 bool FEdModeFoliage::FixInstanceToCorrectLevel(AInstancedFoliageActor InIFA, float AlignMaxAngle, FFoliageInfo& Mesh, int32 InstanceIdx)


2 {


3 UWorld InWorld = GetWorld();


4


5 FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(FoliageGroundCheck), true);


6 QueryParams.bReturnFaceIndex = false;


7 FCollisionShape SphereShape;


8 SphereShape.SetSphere(0.f);


9 TArray Hits; Hits.Reserve(16);


10 FHitResult HitResult;


11


12 FFoliageInstance& Instance = Mesh.Instances【InstanceIdx】;


13


14 FVector InstanceTraceRange = Instance.GetInstanceWorldTransform().TransformVector(FVector(0.f, 0.f, FOLIAGE_INVALID_TEST_TRACE));


15 FVector Start = Instance.Location + InstanceTraceRange;


16 FVector End = Instance.Location - InstanceTraceRange;


17


18 InWorld->SweepMultiByObjectType(Hits, Start, End, FQuat::Identity, FCollisionObjectQueryParams(ECC_WorldStatic), SphereShape, QueryParams);


19


20 // Find nearest hit


21 float TempDistance = InstanceTraceRange.Size();


22 for (const FHitResult& Hit : Hits)


23 {


24 float HitDistance = (Hit.Location - Instance.Location).Size();


25 if (HitDistance [span style="color: rgba(0, 0, 0, 1)"> TempDistance)


26 {


27 TempDistance = HitDistance;


28 HitResult = Hit;


29 }


30 }


31


32 if (HitResult.bBlockingHit)


33 {


34 UPrimitiveComponent HitComponent = HitResult.Component.Get();


35


36 if (HitComponent->GetComponentLevel() != InIFA->GetLevel())


37 {


38 TSet InInstanceSet;


39 InInstanceSet.Emplace(InstanceIdx);


40 for (auto& Pair : InIFA->FoliageInfos)


41 {


42 if (&Pair.Value.Get() == &Mesh)


43 {


44 UFoliageType* FoliageType = Pair.Key;


45 InIFA->MoveInstancesToLevel(HitComponent->GetComponentLevel(), InInstanceSet, &Mesh, FoliageType);


46 return true;


47 }


48 }


49 }


50 <span style="color: rg

相关文章
|
6月前
|
供应链 物联网 区块链
未来触手可及:探索新兴技术的趋势与应用安卓开发中的自定义视图:从基础到进阶
【8月更文挑战第30天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实正在重塑我们的世界。本文将深入探讨这些技术的发展趋势和应用场景,带你领略未来的可能性。
|
6月前
|
开发工具 Android开发 开发者
移动应用开发之旅:从概念到市场的全景解析
在数字化浪潮的推动下,移动应用成为了我们日常生活的一部分。本文将带你穿越移动应用开发的迷宫,探索那些让应用从一个简单的想法变成数百万用户手中宝贝的秘密。我们将一探究竟,了解移动操作系统的基础、开发工具的选择、设计原则的应用,以及市场策略的实施。无论你是开发者还是对移动应用充满好奇的旁观者,这篇文章都将为你揭示移动应用背后的魔法。
63 0
|
3月前
|
前端开发 测试技术 Android开发
移动应用开发之旅:从概念到市场
在数字化时代的浪潮中,移动应用已成为日常生活和商业活动不可或缺的一部分。本文将引导读者穿越移动应用开发的迷宫,从最初的灵感迸发到最终的产品发布。我们将探索移动操作系统的多样性,理解不同平台的独特需求,并深入剖析开发过程中的关键步骤。你将学习如何将一个简单想法转化为现实,包括市场调研、设计原则、编码实践、测试策略以及上线后的推广和维护。加入我们,启航你的移动应用开发之旅,让你的梦想在用户的手掌中闪耀。
|
3月前
|
数据可视化
如何使用“黑科技”软件提升生产力
日程安排软件是现代职场和个人生活的必备工具,能帮助用户高效管理时间,清晰规划任务,提供及时提醒,提高工作效率。其可视化功能和跨设备同步特性,使任务管理更加便捷,无论何时何地都能保持井井有条。适合各行业人士使用,助力实现生活与工作的完美平衡。
|
4月前
|
存储 前端开发 JavaScript
前端技术深度探索:从基础到现代框架的实践之旅
前端技术深度探索:从基础到现代框架的实践之旅
83 3
|
4月前
|
vr&ar Android开发 UED
移动应用与系统:探索现代科技的核心动力
本文旨在深入探讨移动应用开发和移动操作系统的关键技术,揭示它们如何共同推动现代科技的发展。通过分析移动应用开发的趋势、工具和技术,以及移动操作系统的特点和优势,我们将看到这些技术如何影响我们的生活、工作和娱乐方式。此外,我们还将讨论未来移动技术和其在不同领域的应用前景,为读者提供全面而深入的理解。
65 6
|
5月前
|
人工智能 前端开发 JavaScript
技术探索之旅:从基础到突破
在这篇技术性文章中,我将分享我的技术探索历程。从最初的编程入门,到如今的项目实践和团队合作,每一步都充满了挑战与收获。希望我的经历能为同样走在技术道路上的你带来一些启发。
|
4月前
|
机器学习/深度学习 人工智能 Kubernetes
技术探索之旅:从基础到进阶的心得体会
本文记录了作者在技术领域的学习和实践过程中积累的经验与感悟。文章涵盖了从基础知识的学习、项目实践,到新技术的探索与应用,最终总结出几点对未来技术发展的思考。希望这些分享能够启发更多的技术爱好者,共同进步。
|
5月前
|
开发框架 前端开发 JavaScript
探索移动应用开发之旅:从概念到市场
【9月更文挑战第17天】在这篇文章中,我们将一起揭开移动应用开发的神秘面纱,从构思的火花到市场上的应用。我们将通过一个实际的代码示例,展示如何将一个简单的想法转化为现实。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和知识,帮助你在移动应用开发的道路上更进一步。
43 0
|
6月前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
222 0