Skip to main content

体素资源生成器

最近更新时间:2023-12-08

概述

体素资源生成器(Voxel Resource Generator)用于编辑星球的矿物资源,并读取相关配置,在星球不同的生态矿点上生成对应的矿石供玩家勘探采掘。

配置文件

配置资源文件路径(前置条件)

使用体素资源生成器(Voxel Resource Generator),首先需要配置属性定义表(Property Definition Table)资源表(Resource Table)以及资源节点表(Resource Node Table)的路径。资源表包含了我们需要用到的各类资源,比如金银铜铁等,资源节点表则用来配置各类矿石,每种矿石可能会包含多种资源,这些资源均从资源表中引用,而属性定义表则是用来说明资源表和资源节点表中各项属性的类型。此处可以先创建三个空的XML文件,然后将其放入%Resource%ter目录下。

TODO: 后续版本计划支持在编辑器里创建配置表,并且配置表可以放在项目的任意目录下。

image-20230721134928332

依次点击菜单栏配置(Config) -> 项目设置(Project Settings),在弹出的窗口中选择体素设置(Voxel Settings),然后分别将所创建的三个XML文件拖拽到资源属性定义路径(Res Prop Define Path)、资源路径(Resource Path)资源节点路径(Resource Node Path)中。

image-20230721135731093

配置完成:

image-20230721135823167

属性定义表

属性定义表(Property Definition Table )用于表示资源表(Resource Table)资源节点表(Resource Node Table)中的各项属性的类型,需要定义所用属性的类型,否则在编辑资源表和资源节点表时,未定义的属性不会显示,目前需要手动配置。

属性定义表(Property Definition Table )的示例如下:

<properties>
<resource name="resource_id" type="string"/>
<resource name="wuxing_metal" type="float2" mutable="true"/>
<resource name="wuxing_wood" type="float2" mutable="true"/>
<resource name="wuxing_water" type="float2" mutable="true"/>
<resource name="wuxing_fire" type="float2" mutable="true"/>
<resource name="wuxing_earth" type="float2" mutable="true"/>
<resource_node name="node_id" type="string"/>
<resource_node name="display_name" type="string" />
<resource_node name="type" type="string" />
<resource_node name="difficulty" type="float" />
<resource_node name="vobj" type="string" />
<resource_node name="resource_ids" type="string" />
<resource_node name="weight" type="float2" />
</properties>

表中标签为resource的条目表示的是资源表(Resource Table)用到的属性,标签为resource_node的条目表示的是资源节点表(Resource Node Table)用到的属性,每个条目至少要包含name和type,name表示属性名,type表示属性的类型。如果有mutable且该mutable为true时,则表示这个属性是游戏里可以动态修改的属性,比如wuxing_metal表示矿物的五行金属性,在被玩家采掘后这个属性可能会发生变化。

例如,示例中name为resource_id的条目,表示如果资源表(Resource Table)中出现了resource_id属性,那么这个属性的类型会被识别为string类型,并且这个属性不可编辑,而name为wuxing_metal的属性则为可编辑属性。

属性的类型目前共有如下17种可选:

invalid
bool
int
int2
int3
int4
float
float2
float3
float4
entity_id
int64
uint64
str_ptr
wide_str_ptr
string
buffer

资源表

资源表(Resource Table)的配置示例如下,其中每个条目都可以表示一种资源,资源表可以使用体素资源编辑器进行配置。

<?xml version="1.0" encoding="utf-8"?>
<data>
<resource
resource_id="bronze"
wuxing_metal="2,5"
wuxing_wood="1,5"
wuxing_water="2,5"
wuxing_fire="1,6"
wuxing_earth="4,7"
/>

<resource
resource_id="copper"
wuxing_metal="0,55"
wuxing_wood="2,15"
wuxing_water="2,5"
wuxing_fire="5,6"
wuxing_earth="0,7"
/>
</data>

resource_id属性是这个resource的唯一标识,不可重复。

wuxing_metal,wuxing_wood,wuxing_water,wuxing_fire,wuxing_earth属性表示一个范围,其中用逗号","分隔最小值和最大值,在生成具体的矿物时,矿物中resource实例的这几项属性会生成一个范围内的随机值。

资源节点表

矿点使用到的资源节点配置示例如下,资源节点表(Resource Node Table)可以使用体素资源编辑器进行配置。

<?xml version="1.0" encoding="utf-8"?>
<data>
<resource_node
node_id="desert_rock1"
display_name="desert_rock1"
type="Material"
difficulty="5.5"
vobj="%Resource%voxel_res\objects\desert_rock3.vobj"
resource_ids="bronze,copper"
weight="1,10"
/>

<resource_node
node_id="desert_rock2"
display_name="desert_rock2"
type="Material"
difficulty="15.5"
vobj="%Resource%voxel_res\objects\desert_rock3.vobj"
resource_ids="copper"
weight="2,20"
/>
</data>

node_id属性是resource_node的唯一标识符,不可重复。

resource_ids属性表示这个resource_node使用到的resource资源,资源节点可以包含一个或者多个资源。目前这两个属性是必需的。

体素资源编辑器

项目设置(Project Settings)窗口中配置完资源属性定义路径(Res Prop Define Path)、资源路径(Resource Path)资源节点路径(Resource Node Path)后,在编辑器中依次点击菜单栏窗口(Windows) -> 游戏数据(Game Data) -> 体素资源(Voxel Resource),打开体素资源编辑器(Voxel Resource Editor)

image-20230728150717851

体素资源编辑器(Voxel Resource Editor):

image-20230809113530639

编号说明
1选择当前编辑的资源类型。
2展示了当前所选类型资源的所有条目。
3功能按扭。点击添加(Add)按钮可添加一个新的条目
点击保存(Save)按钮可保存当前类型的资源。

切换当前编辑的资源

打开体素资源编辑器(Voxel Resource Editor)后,默认的编辑界面为资源的编辑界面,点击上方的资源节点(Resource Node)按钮可以切换到资源节点的编辑界面。

image-20230810093745406

添加资源

点击添加(Add)按钮后,会在中间的列表中生成一个新的条目,其中的属性值均为默认值。

image-20230809113945581

删除资源

右击所需删除的资源的标题栏,点击删除(Delete)即可删除当前资源。

image-20230809130732183

编辑属性

可编辑资源和资源节点的各项属性。如果编辑的是resource_id属性,那么当前条目的标题栏也会相应变化。

image-20230809114108295

保存修改

点击下方的保存(Save)按钮可以将修改保存到配置文件。

image-20230809132814648

修改后的配置文件如下:

image-20230809135644729

体素资源编辑器(Voxel Resource Editor)窗口中对资源节点( Resource Node)的添加、删除、编辑和保存的操作与资源(Resource)一致。

使用资源

若要在场景中使用配置好的资源节点,需再逐级配置矿产资源和生态资源。

矿产配置

资源预览(Resource Preview)窗口空白处右击打开快捷菜单,依次点击创建资源(Create Res) -> 体素(Voxel) -> 体素矿点(Voxel Ore),创建一个体素矿点文件用于调用配置的资源节点。

image-20230721160127843

在弹出的窗口中输入名称后,点击确定(OK)按钮后完成创建。

image-20230721160257192

双击所要用到的矿点资源(.vore)文件,即可打开对应的编辑界面。

image-20230830112851287

该文件中没有配置任何资源,只有两条默认的属性,空矿概率(Nothing Probability)属性表示无任何矿石生成的概率,此处概率为80,随机种子(Random Seed)用于调整矿石的分布情况。

image-20230830113102351

点击添加矿点( Add Ore)按钮可添加一个条目,用于调用配置的资源节点。

image-20230830113330815

添加完毕后,需修改其名称(Name)属性,将其修改为资源节点表中存在的资源节点的node_id,然后可以修改概率(Probability)属性来配置这个矿石出现的概率,在下图示例中生成desert_rock1的概率为20,而生成desert_rock2的概率为10。

注意:矿产资源中的总概率可以不为100,例如下图的矿产资源在生成矿石时有80/110的概率什么都不生成,20/110的概率生成desert_rock1的矿石,10/110的概率生成desert_rock2的矿石。

image-20230830113540568

设置完成后,点击生成(Generate)保存(Save)按钮。

image-20230830113646665

多次点击生成(Generate),会发现生成的矿石的位置和形状都是固定的。若想要概率相同的矿点文件所生成的矿石分布各不相同,则需使用到随机种子(Random Seed)属性。随机种子(Random Seed)值为0时,表示未使用随机种子,值大于0时,不同的值都会让矿石的分布位置以及形状产生变化。

不同随机种子(Random Seed)值的效果:

Random Seed: 10
Random Seed: 12.555

生态配置

还需在生态中引用使用到的矿产。在资源预览(Resource Preview)窗口中选择要用到的生态,若没有生态文件,在资源预览窗口中右击打开快捷菜单,依次点击创建资源(Create Res) -> 体素(Voexl) -> 体素生态(Voxel Ecology),创建一个体素生态文件。

image-20230721162019652

在弹出的窗口中输入名称后,点击确定(OK)按钮完成创建。

image-20230721162129136

双击所要用到的生态文件(.veco),即可打开对应的生态编辑界面。

image-20230721162510903

点击该生态编辑界面中的矿点引用(Ore Reference)的下拉框,选择并配置所要用到的矿产资源,这样在该生态中就会生成对应的矿石了。

image-20230721163018390

或者也可以直接从管理矿点(Manage Ore)面板中,将所要用的矿产拖拽到生态编辑界面的矿点引用(Ore Reference)中来完成配置。

image-20230728152258191

拖拽完成:

image-20230728145214505

设置完成后,点击生成(Generate)保存(Save)按钮。

image-20230728145403457

相关接口与代码示例

接口

//获取资源节点
virtual const res_node* GetResNode(float local_x, float local_y, float local_z) const;
virtual const res_node* GetResNode(const char* name) const;

//查找周围半径的区域内的矿点(如果name不为空,则查找该名的矿点)
virtual void FindOresInSphere(
float local_x, float local_y, float local_z, float radius,
TArrayPod<ore_data_t, 8, TCoreAlloc>& arrOres, const char* name = 0) const;

//设置和获取矿物实例的各项属性
//将指定坐标处生成的矿物实例中的第res_index个resource的指定属性值修改为value
bool SetOreResValue(float local_x, float local_y, float local_z, unsigned int res_index, const char* property_name, float value)
//获取指定坐标处的矿物实例的第res_index个resource的指定属性值
float GetOreResValue(float local_x, float local_y, float local_z, unsigned int res_index, const char* property_name) const

//收集当前使用的所有ResNode
virtual bool CollectUsedResNode(TArrayPod<const char*, 8, TCoreAlloc>& arrResNodeNames) const;

代码示例1

//获取资源的resource_id属性
//通过GetResName接口获取,GetResName接收一个整型参数可以直接获取resource_ids中的第i个resource_id
pChunkMgr->FindOresInSphere(local_x, local_y, local_z, radius, arrOres, name.c_str());
//获取资源节点并遍历其中的资源
size_t ore_num = arrOres.size();
for (size_t i = 0; i < ore_num; i++)
{
//通过arrOres[i].name获取到node_id
const res_node* node = pChunkMgr->GetResNode(arrOres[i].name);

size_t res_num = node->GetResCount();
//遍历节点中资源
for (size_t j = 0; j < res_num; j++)
{
const char* resource_id = node->GetResName(i);
}
}

代码示例2

//获取某坐标范围内的矿点使用的资源节点及其相关属性,获取并设置矿物实例的属性

//设置坐标
float local_x = xxx;
float local_y = yyy;
float local_z = zzz;
float radius = r;

//获取voxel_terrain对象
LVoxelTerrain* pCurVoxelTerrain = m_pCurrentVoxelTerrain;
if (pCurVoxelTerrain == nullptr)
{
return;
}

LVoxelTerrainComponent* pCurVoxelTerComp = pCurVoxelTerrain->GetVoxelTerrainComponent();
if (pCurVoxelTerComp == nullptr)
{
return;
}

IChunkManager* pChunkMgr = pCurVoxelTerComp->GetChunkManager();
if (pChunkMgr == nullptr)
{
return;
}

result_string name = "";
TArrayPod<ore_data_t, 8, TCoreAlloc> arrOres;
//获取周围矿点
pChunkMgr->FindOresInSphere(local_x, local_y, local_z, radius, arrOres, name.c_str());

//获取资源节点并遍历其中的资源
size_t ore_num = arrOres.size();
for (size_t i = 0; i < ore_num; i++)
{
//通过arrOres[i].name获取到node_id
const res_node* node = pChunkMgr->GetResNode(arrOres[i].name);
//获取resource_node中的其他属性
int user_var_type = 0;
char display_name[Z_PARAM_NAME_SIZE];
char difficulty[Z_PARAM_NAME_SIZE];
node->GetAttributeData("display_name", user_var_type, display_name);
//此处difficulty获取到的是字符串,可以通过atof等接口再转化为浮点数
node->GetAttributeData("difficulty", user_var_type, difficulty);

size_t res_num = node->GetResCount();
//遍历节点中资源
for (size_t j = 0; j < res_num; j++)
{
const res_info* res = node->GetResInfo(j);

//获取该资源节点第j个resource的各项属性
XMFLOAT2 wuxing_metal = res->attributes.GetFloat2("wuxing_metal");
XMFLOAT2 wuxing_wood = res->attributes.GetFloat2("wuxing_wood");
XMFLOAT2 wuxing_water = res->attributes.GetFloat2("wuxing_water");
XMFLOAT2 wuxing_fire = res->attributes.GetFloat2("wuxing_fire");
XMFLOAT2 wuxing_earth = res->attributes.GetFloat2("wuxing_earth");
}
}

//获取周围所有的实体对象
CVarList args, result;
args.AddFloat(local_x);
args.AddFloat(local_y);
args.AddFloat(local_z);
args.AddFloat(50);
pChunkMgr->GetRangeVisual(args, result);

size_t entity_num = result.GetCount();
for (size_t i = 0; i < entity_num; i++)
{
PERSISTID ent_id = result.ObjectVal(i);
IEntity* ent = GetCore()->GetEntity(ent_id);

IVarTable* var_table = ent->GetCustoms();
//拥有该属性的实体对象是矿石
if (var_table->Exists("ore_position"))
{
IVar* var = var_table->GetValue("ore_position");
result_string pos_str = var->StringVal();
//pos_str存储的坐标是由逗号分隔的字符串,如"xxx,yyy,zzz",此处将其分割并存入数组以供使用
TArrayPod<result_string, 8, TCoreAlloc> postions = SplitStr(pos_str);

result_string str_pos1 = postions[0];
result_string str_pos2 = postions[1];
result_string str_pos3 = postions[2];

float pos_x = (float)atof(str_pos1.c_str());
float pos_y = (float)atof(str_pos2.c_str());
float pos_z = (float)atof(str_pos3.c_str());

//获取并设置矿物实例的属性
//获取resource_node的float2属性实际值
float weight = pChunkMgr->GetOreValue(pos_x, pos_y, pos_z, "weight");
pChunkMgr->SetOreValue(pos_x, pos_y, pos_z, "weight", 6.6);

//获取resource的float2属性实际值
//获取(pos_x,pos_y,pos_z)坐标处矿物实例的第0个resource的wuxing_metal值
float wuxing_metal = pChunkMgr->GetOreResValue(pos_x, pos_y, pos_z, 0, "wuxing_metal");
//将(pos_x,pos_y,pos_z)坐标处矿物实例的第0个resource的wuxing_metal值设置为5
pChunkMgr->SetOreResValue(pos_x, pos_y, pos_z, 0, "wuxing_metal", 5);
//再次获取实例的wuxing_metal值,以便查看是否修改正确
wuxing_metal = pChunkMgr->GetOreResValue(pos_x, pos_y, pos_z, 0, "wuxing_metal");
}
}

代码示例3

//获取当前星球所用的所有资源节点
LVoxelTerrain* pCurVoxelTerrain = m_pCurrentVoxelTerrain;
if (pCurVoxelTerrain == nullptr)
{
return;
}

LVoxelTerrainComponent* pCurVoxelTerComp = pCurVoxelTerrain->GetVoxelTerrainComponent();
if (pCurVoxelTerComp == nullptr)
{
return;
}

IChunkManager* pChunkMgr = pCurVoxelTerComp->GetChunkManager();
if (pChunkMgr == nullptr)
{
return;
}

TArrayPod<const char*, 8, TCoreAlloc> arrResNodeNames;
//获取所有用到的节点的node_id属性并存入arrResNodeNames,可以将node_id传入GetResNode接口获取对应res_node
pChunkMgr->CollectUsedResNode(arrResNodeNames);

代码示例4

//通过地表对象获取地下矿点的坐标,persistID为地表对象的id
//如果代码获取坐标失败(返回false)则说明这个可视对象不是资源的地表对象
IEntity* ent = GetCore()->GetEntity(persistID);
if (ent == nullptr)
{
return false;
}

IVarTable* var_table = ent->GetCustoms();
if (var_table == nullptr)
{
return false;
}

if (!var_table->Exists("ore_position"))
{
return false;
}

IVar* var = var_table->GetValue("ore_position");
result_string pos_str = var->StringVal();

CVarList positions;
string_utils::util_split_string(positions, pos_str.c_str(), ",");

if (positions.GetCount() < 3)
{
return false;
}

int x = std::stof(positions.StringVal(0));
int y = std::stof(positions.StringVal(1));
int z = std::stof(positions.StringVal(2));