技术好文: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月前
|
开发工具 Android开发 开发者
移动应用开发之旅:从概念到市场的全景解析
在数字化浪潮的推动下,移动应用成为了我们日常生活的一部分。本文将带你穿越移动应用开发的迷宫,探索那些让应用从一个简单的想法变成数百万用户手中宝贝的秘密。我们将一探究竟,了解移动操作系统的基础、开发工具的选择、设计原则的应用,以及市场策略的实施。无论你是开发者还是对移动应用充满好奇的旁观者,这篇文章都将为你揭示移动应用背后的魔法。
59 0
|
4月前
|
开发框架 Android开发 Swift
移动应用开发之旅:从概念到上线
【9月更文挑战第35天】在这篇文章中,我们将一起踏上一段激动人心的旅程,探索移动应用开发的奥秘。我们将从最初的构想出发,逐步深入到移动操作系统的核心,了解如何将这些想法转化为现实中的应用。文章不仅会涵盖开发流程的各个阶段,还将通过实际代码示例,展示如何在Android和iOS这两大主流平台上实现功能。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在移动应用领域取得成功。
|
4月前
|
存储 前端开发 JavaScript
前端技术深度探索:从基础到现代框架的实践之旅
前端技术深度探索:从基础到现代框架的实践之旅
81 3
|
5月前
|
人工智能 前端开发 JavaScript
技术探索之旅:从基础到突破
在这篇技术性文章中,我将分享我的技术探索历程。从最初的编程入门,到如今的项目实践和团队合作,每一步都充满了挑战与收获。希望我的经历能为同样走在技术道路上的你带来一些启发。
|
5月前
|
前端开发 Android开发 开发者
探索移动应用开发之旅:从概念到上线
【9月更文挑战第13天】在数字化时代的浪潮中,移动应用已成为连接用户与服务的重要桥梁。本文将引导读者了解移动应用开发的核心要素,包括设计原则、开发流程、操作系统选择、以及如何将一个想法转化为现实中的应用。我们将通过实际案例,探讨如何在竞争激烈的应用市场中突围而出,并确保应用的可持续发展。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和启示。
47 4
|
6月前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
204 0
|
6月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
196 0
|
8月前
|
前端开发 关系型数据库 MySQL
技术好文:R基础学习(三)
技术好文:R基础学习(三)
36 0
|
8月前
|
算法 前端开发 程序员
一个土木工程专业背景的开发者,讲述开源带给他的力量
**谭雪峰在TDengine Open Day分享开源经历,揭示程序员如何通过开源项目成长。自学成才的他,从土木工程转行编程,借助开源社区学习、贡献代码,参与TDengine HiveMQ挑战赛获胜,最终加入涛思数据。开源不仅提升代码质量、提供实战经验,也拓宽技术视野,助力个人品牌建立,促进开发者、项目和社区的共赢。程序员通过参与开源,能从代码编写者转变为问题解决者和工具创造者。**
79 0
|
9月前
|
搜索推荐 开发者
开源打败商业 —— 敲敲云 apaas 平台成为了零代码领域的黑马,开启零代码新时代
随着数字化转型的浪潮席卷全球,企业对于高效、灵活的业务系统需求日益增长。在这个背景下,零代码开发平台应运而生,成为了市场上备受瞩目的产品。而在众多零代码产品中,敲敲云 以其开源的身份和高效的研发速度,彰显了其与众不同的竞争力。
621 0