说明
在游戏中动态改变网格数量和形状等,该功能是寻路功能的前期准备,即在基础移动地基上方,构建一层网格,任何移动的操作都可以基于该网格进行计算。从而在编辑器模式下能够更方便进行调试
InstancedStaticMeshComponent
其是一种用于优化静态网格渲染性能的技术。InstancedStaticMesh允许你在场景中高效地渲染大量相同的静态网格对象。每个实例(Instance)共享相同的网格、材质和渲染属性,但可以具有不同的位置、旋转和缩放。
对于每一个网格,可以传入网个参数比如使用的材质,网格类性,碰撞类型和颜色等,进行初始化,然后所有网格初始化完成后调用AddInstanced实现网格的生成。
而为了体现每个网格的颜色,可以利用InstancedStaticMesh的SetCustomDataValue实现对自定义数值的控制,该自定义数值可以影响材质表现,而其需要一个索引即该网格在InstancedStaticMesh中的所有网格的索引。
所以对于每次AddInstance可以使用一个数组来维护当前Add后的网格索引,并且在每次Add之前可以查看当前索引是否已经具有网格,如果有可以删除后再Add
void AXGridMeshInst::AddInstance(const FIntPoint Index, const FTransform Transform, const TArray<ETileState>& states)
{
RemoveInstance(Index);
InstancedMeshComponent->AddInstance(Transform);
int temp = InstanceIncdexes.Add(Index);
FLinearColor colorins;
float isFilled = GetColorFromState(states, colorins);
//UE_LOG(LogTemp, Warning, TEXT("RGB: %f,%f,%f"),colorins.R,colorins.G,colorins.B);
InstancedMeshComponent->SetCustomDataValue(temp, 0, colorins.R, false);
InstancedMeshComponent->SetCustomDataValue(temp, 1, colorins.G, false);
InstancedMeshComponent->SetCustomDataValue(temp, 2, colorins.B, false);
InstancedMeshComponent->SetCustomDataValue(temp, 3, isFilled, false);
}
对每个网格参数的设置
新建了一个Actor类,GridVisual,其用来控制对InstancedMesh的调用,该类是生成网格的主要类,由于是动态生成网格,所以利用ChildActor组件来接受InstancedMesh,而不是直接声明一个类对象,这是因为一般类对象的构造都是在构造函数中已经利用CreateDefaultObject实现好了,如果想要动态生成可以参考。
而ChildActor组件相对来说比较简单,并且使用后ChildActor对象和GridVisual的关系为松耦合,其实际上可以理解为独立的,更改ChildActor不会影响GridVisual的逻辑。
在GridVisual类中,调用ChildActor中的Initial方法以及Add方法从而实现对于网格的创建。
为了能够使得生成的网格实体不要和地基重叠,还可以设置在Z轴方向上的偏移
void AXGridVisiual::InitializeGridVisual(AXGrid* XGrid)
{
if (!ChildActor_GridMeshInst) return;
XGridIns = XGrid;
XGridMeshInst = Cast<AXGridMeshInst>(ChildActor_GridMeshInst->GetChildActor());
if (!XGrid || !XGridMeshInst) return;
XGridMeshInst->InitializeGridMeshInst(
XGridIns->GetCurrentGridShape(XGridIns->GridShape)->FlatMesh, //通过Grid实例获取StaticMesh类型
XGridIns->GetCurrentGridShape(XGridIns->GridShape)->FlatBorderMaterial, //通过Grid实例获取材质
FColor::Black,
ECollisionEnabled::QueryOnly
);
//设置网格生成的中心点
FVector loc{ 0.0f,0.0f,0.0f };
SetActorLocation(loc, false);
SetOffsetfromGround(Offset);
}
void AXGridVisiual::UpdateTileVisual(FTileDataStruct* Data)
{
if (!XGridMeshInst) return;
XGridMeshInst->RemoveInstance(Data->Index);
//校验该网格类型是否是可以选择的类型 --> 不是障碍物或者为空
if (!IsTileTypeWalkAble(Data->Type)) return;
XGridMeshInst->AddInstance(Data->Index, Data->Transform, Data->States);
}
Grid类
该类是拖入到游戏场景中的类,相当于为了实现动态生成网格,使用了三层层级关系,主要就是想要降低耦合。在Grid类中同样使用ChildActor来实现对GridVisual类的接受,Grid类同样还接受了后面用来处理寻路的类。
为了能够动态生成网格,所以Grid类中必然会有几个参数,网格大小,网格沿X,Y方向的个数,以及是否根据环境生成合理的网格。
这些功能都主要体现在SpawnGrid中。
首先是初始化InstancedMesh所需要的数据,通过传入当前Grid类即可
XGridVisual->InitializeGridVisual(this);
SpawnGrid
生成就是沿着X,Y方向依次生成,所以首先需要通过中心点以及网格参数计算出左小角的起始点。然后通过一个双重for循环,计算出点(x,y)位置上的方格的Transform,然乎传入到AddGridTile中。
如果开启了依靠环境,那么还应当通过射线检测,判断当前地基的方格类型。
AddGridTile
调用GridVisual中的Update方法,传入当前该网格的参数。
void AXGrid::AddGridTile(FTileDataStruct* TileData)
{
GridTiles.Add(TileData->Index, *TileData);
if (!XGridVisual) return;
XGridVisual->UpdateTileVisual(TileData);
OnTileDataUpdated.Broadcast(TileData->Index);
}
动态生成
为了能够动态生成,使用到了ConstructionScript节点,在节点中获取ChildActor实例
Construction Script 是蓝图类中的一个特殊功能,它允许你在编辑器中设置和调整 Actor 的属性和组件。Construction Script 在你编辑 Actor 时会不断运行,以便实时更新和预览你的更改。这使得它非常适合用于设置和配置场景中的对象。Construction Script 的特点和用途 实时预览:允许你在编辑器中实时预览更改,方便进行调整和调试。动态生成和配置组件:可以在编辑器中动态生成和配置组件,自动化对象的创建和配置过程。初始化设置:为 Actor 设置初始状态和属性,确保在游戏开始时所有内容都已正确配置。
在C++代码中需要使用
#if WITH_EDITOR
virtual void OnConstruction(const FTransform& Transform) override;
#endif
来实现该功能
在ChildActor源码中,ChildActor的动态生成功能也是包含在
#if WITH_EDITOR
#endif
这样的宏中,用于在代码中包裹只在编辑器模式下编译和运行的部分。这使得你可以在 C++ 代码中编写特定于编辑器的逻辑,而不影响游戏在非编辑器(即运行时)模式下的行为
后续
创建UI界面,将TileSize,index等数据绑定到UI控件上。