28. 内容支持Part4: 策划数据

为实现游戏逻辑,须要能够使用策划人员创建的游戏数据。 例如,对于道具的价格或属性信息而言,各个道具并不是通过单独的C++/C#类来实现的,而是依赖于从外部读取到的item策划文件。

为此,iFun引擎提供了用于读取游戏数据的 ResourceManager 接口。 该class的具体信息可在 此处 查看。

28.1. 读取策划数据

策划数据支持JSON文件、各项用tab区分的文件,以及存在于MySQL DB中的文件。

28.1.1. 当策划数据为JSON文件时,

如下所示,将在MANIFEST.json中写入JSON文件所在目录名称。

"ResourceManager": {
  "game_json_data_dir": "game_data"
}

iFun引擎将读取 game_json_data_dir 目录下的JSON文件。

Important

此时,各个文件的扩展名须为json、txt、text中的一种,且不分大小写。

Tip

在游戏服务器打包时,最好同时包含 game_json_data_dir 。因此,除了MANIFEST.json以外,最好也在CMakeLists.txt的 RESOURCE_DIRS 中列出。具体内容请参考 将游戏资源文件包含在package中

28.1.2. 当策划数据为tab分隔符文件时

与JSON类似,可以读取用tab分行的数据文件。MANIFEST.json的设置相同。

28.1.2.1. 文件条件

此时,文件须满足如下条件。

28.1.2.1.1. 编码及扩展名
  • 文件须为ascii或utf-8,当为utf-8时,包含BOM。

  • 文件的扩展名须为.txt或.text。

28.1.2.1.2. 注释
  • 以//或#开头的行是注释。

28.1.2.1.3. 标头
  • 除了注释以外的其他首行代码均属于header,并用tab分隔来列出field名称。

28.1.2.1.4. 数据
  • Header之后的行属于data,用tab分隔,列出field名称。

  • header的field个数和data的field个数应相同。

  • 各字段前后的空格均删除。

  • 如果已删除空格的data field为null string,相应的field将成为null JSON object。

  • 非null的field可为整数、实数、字符串的形式。

28.1.2.2. 转换为JSON数据

拥有上述结构的文件可通过如下规则转换成JSON数据。

  • 每行为{“header1”: “data1”, “header2”: “data2”}的形式。

  • 转换后的行单元内容将成为数组的元素。

因为已转换成了JSON,所以在游戏中使用时,将运用 ResourceManager::GetJsonData(…) 函数。

28.1.2.2.1. 示例:tab分隔符文件的JSON转换

例如,如下所示的Item.txt文件最终将转换成下面的JSON。

// comment
// comment
Index   Name      Price   Description
0       힐링 포션   100.0    힐링해주는 포션. 힐링힐링~
1       단검        50.0    가장 기본이 되는 검.
[
  {
    "Index": 0,
    "Name": "힐링포션",
    "Price": 100.0,
    "Description": "힐링해주는 포션. 힐링힐링~"
  },
  {
    "Index": 1,
    "Name": "단검",
    "Price": 50.0,
    "Description": "가장 기본이 되는 검."
  }
]

28.1.3. 当策划数据以DB数据表形式存在时

28.1.3.1. 设置MANIFEST.json

按如下所示设置MANIFEST.json。

"ResourceManager": {
  "enable_game_data_mysql": true,
  "game_data_mysql_server": "tcp://localhost:3306",
  "game_data_mysql_username": "funapi",
  "game_data_mysql_password": "funapi",
  "game_data_mysql_database": "funapi",
  "game_data_mysql_tables": "game_data_table1,game_data_table2"
}

所记述的内容为DB访问信息和要读取的DB table名称。 要读取的DB table名称在 game_data_mysql_tables 中记述,并且可以列出多个数据表,用逗号(,)分开即可。

28.1.3.2. 转换为JSON数据

各个table通过以下规则转换成JSON文件。

  • Table的名称为JSON文件的文件名。

  • 各row以{“column1”: “data1”, “column2”: “data2”}的形式转换为JSON object。

  • 转换后的行单元内容将成为数组的元素。

值的读取方法使用 ResourceManager::GetJsonData(TABLE_NAME)

28.1.3.2.1. 示例:DB中策划数据的JSON转换

例如,以下为名为Item的DB table,最终将转换成下面的JSON。

mysql> select * from Item;
+-------+----------+-------+----------------------------+
| Index | Name     | Price | Description                |
+------------------+-------+----------------------------+
| 0     | 힐링 포션| 100.0 | 힐링해주는 포션. 힐링힐링~ |
| 1     | 단검     | 50.0  | 가장 기본이 되는 검.       |
+------------------+-------+----------------------------+
2 rows in set (0.00 sec)
[
  {
    "Index": 0,
    "Name": "힐링포션",
    "Price": 100.0,
    "Description": "힐링해주는 포션. 힐링힐링~"
  },
  {
    "Index": 1,
    "Name": "단검",
    "Price": 50.0,
    "Description": "가장 기본이 되는 검."
  }
]

28.2. 使用策划数据

首先,已读取的JSON data可通过

ResourceManager::GetJsonData(…) 进行访问。在上述示例中,若存在名为Item.json的文件,则该JSON数据可按如下所示调用。

Ptr<const Json> items = ResourceManager::GetJsonData("Item.json");
BOOST_ASSERT(items && items->IsArray());

for (size_t i = 0; i < items->Size(); ++i) {
  const Json &item = (*items)[i];
  LOG(INFO) << "item name: " << item["Name"].GetString();
  LOG(INFO) << "item price: " << item["Price"].GetDouble();
}
JToken items = ResourceManager.GetJsonData ("Item.json");
Log.Assert (items != null && items.Type == JTokenType.Array);

foreach (JObject item in items) {
  Log.Info ("item name: {0}", item["Name"].Value<string>());
  Log.Info ("item price: {0}", item["Price"].Value<double>());
}

28.3. 为策划数据建立索引

为通过数据表形式读取的游戏数据建立索引,将非常便于管理,可通过key field轻松访问。 为此,iFun引擎提供了名为 ResourceManager::IndexJsonArray(…) 的函数。

Ptr<const IndexedJsonData> indexed_item_table;

bool MyGameServer::Install(const ArgumentMap &arguments) {
   ...
   Ptr<const Json> json = ResourceManager::GetJsonData("Item.json");
   indexed_item_table = ResourceManager::IndexJsonArray(*json, "Index");
   BOOST_ASSERT(indexed_item_table);
}

void OnItemUse(const Ptr<Session> &session, const Ptr<Json> &message) {
   ...
   int item_index = ...

   IndexedJsonData::const_iterator it = indexed_item_table->find(item_index);
   BOOST_ASSERT(it != indexed->end());
   const Json &item_info = *(it->second);

   int price = item_info["Price"].GetInteger();
   ...
}
public class Server
{
  Dictionary<int, JObject> the_indexed_item_table = null;

  public static void Install(ArgumentMap arguments)
  {
    ...
    JToken json = ResourceManager.GetJsonData ("data.json");

    the_indexed_item_table = ResourceManager.IndexJsonArray (
        (JArray) json, "Index");

    Log.Assert (indexed_item_table != null);
  }

  void OnItemUse(Session session, JObject message)
  {
    ...
    int item_index = ...
    JObject item_info = null;
    Log.Assert (the_indexed_item_table.TryGetValue(item_index, out item_info));

    int price = (int) data ["Price"];
    ...
  }
}

28.4. 策划数据的相关设置参数

  • game_json_data_dir: 以JSON形式存在的、包含游戏策划数据的目录路径。(type=string, default=””)

  • enable_game_data_mysql: 包含游戏策划数据的MySQL服务器连接信息。(type=bool, default=false)

  • game_data_mysql_server: 包含游戏策划数据的MySQL服务器连接信息。(type=string, default=”localhost:3306”)

  • game_data_mysql_username: 包含游戏策划数据的MySQL服务器用户名。(type=string, default=”funapi”)

  • game_data_mysql_password: 包含游戏策划数据的MySQL服务器密码。(type=string, default=”funapi”)

  • game_data_mysql_database: 包含游戏策划数据的MySQL数据库名称。(type=string, default=”game_data”)

  • game_data_mysql_character_set: 用于与游戏策划数据DB进行连接的character set。(type=string, default=”utf8”)

  • game_data_mysql_tables: 相当于游戏策划数据的MySQL数据表名称。通过逗号区分。(type=string)