在游戏开发设计中,玩家作为一个游戏对象,他们之间可能会存在多种关系,比如:
- 好友关系;
- 组队关系;
- 公会关系;
- 买卖关系;
- ……
无论是使用关系型数据库还是key/value型的NoSQL ,按照传统思路对于这些关系的存储结构实现大同小异。例如:玩家会有独立的好友关系数据块,数据块内部至少存储着好友唯一ID,可以通过好友ID索引到好友相关信息;公会存在唯一ID,通过这个ID拉取公会成员信息和其他信息;组队与公会类似…
传统数据库与图数据库思路比对
我们列举一下好友与公会的常规操作,在不考虑合法性校验的情况下:
操作 | 传统方式 | 图数据库方式 |
玩家A和B成为好友 | 玩家A和B分别在好友数据块增加对方ID | 两对象A和B之间增加边 |
玩家A和B解除好友 | 玩家A和B分别在好友数据块删除对方ID | 两对象A和B之间删除边 |
玩家A加入公会C | 玩家A记录所属公会ID,公会C在成员列表加入玩家A | 两对象A和C之间增加边 |
玩家A离开公会C | 玩家A删除所属公会ID,公会C在成员列表删除玩家A | 两对象A和C之间删除边 |
直观感觉上使用图数据库方式更简洁一些,传统方式若做得严谨一些可能还需要考虑事务的支持,根据数据库的选型不同会导致数据一先一后写入,需处理先写入成功后写入失败的场景。
为了凸显差异性,这次我们再假设一些更复杂的操作,比如在推荐系统中常见的场景,同样也是不考虑合法性校验的情况下:
操作 | 传统方式 | 图数据库方式 |
查看好友的好友 | 先取得玩家A的好友列表,遍历好友列表,取得每个好友的好友列表 | 边遍历 |
查找哪些好友的好友与你同公会 | 先取得玩家A的好友列表,遍历好友列表,取得每个好友的好友列表,在好友的好友数据过滤出同公会ID的玩家 | 边条件遍历 |
同样的,还是感觉上使用图数据库方式更简洁一些,那么具体简洁在哪里,下面拿用实际的例子来说明。
环境搭建
这里我们选择NebulaGraph作为图数据库演示软件,安装步骤可以查看官方文档,有多种安装方式供选择。我们假设有如下图示关系:
使用NebulaGraph的nGQL对上图进行点、关系建立:
//创建图空间game_test并切换
(root@nebula) [(none)]> CREATE SPACE IF NOT EXISTS game_test (vid_type=FIXED_STRING(128))
(root@nebula) [(none)]> use game_test
//创建两种边(EDGE),分别表示好友与公会从属关系,设置了建立关系时时间戳属性
(root@nebula) [game_test]> CREATE EDGE IF NOT EXISTS friend (tm timestamp)
(root@nebula) [game_test]> CREATE EDGE IF NOT EXISTS member (tm timestamp)
//创建两种Tag,分别用来标记玩家和公会,设置了唯一ID属性
(root@nebula) [game_test]> CREATE TAG IF NOT EXISTS player (uid int NOT NULL)
(root@nebula) [game_test]> CREATE TAG IF NOT EXISTS guild (uid int NOT NULL)
//创建点,玩家A、B、C、D和公会G
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS player (uid) VALUES "Player_A":(10001)
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS player (uid) VALUES "Player_B":(10002)
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS player (uid) VALUES "Player_C":(10003)
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS player (uid) VALUES "Player_D":(10004)
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS guild (uid) VALUES "Guild_G":(20001)
//使用边来连接点,表示关系。好友因为是相互关系,可以认为两点间好友关系存在互相指向的两条边
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS friend (tm) \
VALUES "Player_A"->"Player_B":(timestamp()),"Player_B"->"Player_A":(timestamp())
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS friend (tm) \
VALUES "Player_B"->"Player_C":(timestamp()),"Player_C"->"Player_B":(timestamp())
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS friend (tm) \
VALUES "Player_B"->"Player_D":(timestamp()),"Player_D"->"Player_B":(timestamp())
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS member (tm) \
VALUES "Player_A"->"Guild_G":(timestamp())
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS member (tm) \
VALUES "Player_C"->"Guild_G":(timestamp())
//查看好友关系
(root@nebula) [game_test]> MATCH ()-[e:friend]->() RETURN e limit 10
//查看公会关系
(root@nebula) [game_test]> MATCH ()-[e:member]->() RETURN e limit 10
至此,我们预设的环境准备完毕。
利用图数据库解决问题
- 查看好友的好友
假设现在需要对Player_A做好友推荐,希望推荐ta好友的好友(按照图例所示结果应该推荐Player_C和Player_D),可以使用这样的nGQL语句:
(root@nebula) [game_test]> GO 2 STEPS FROM "Player_A" OVER friend \
Where id($$) != "Player_A" YIELD dst(edge);
//结果
+------------+
| dst(EDGE) |
+------------+
| "Player_D" |
| "Player_C" |
+------------+
- 查找哪些好友的好友与你同公会
假设我们现在需要找到Player_A所属公会成员里,有哪些是Player_A好友的好友(结果应该是Player_C),可以使用这样的语句:
//假设已经知道Player_A属于公会是Guild_G
(root@nebula) [game_test]> GO 2 STEPS FROM "Player_A" OVER friend \
Where id($$) != "Player_A" YIELD dst(edge) AS dstid | \
GO FROM $-.dstid OVER member Where id($$) == "Guild_G" YIELD src(edge);
//结果
+------------+
| src(EDGE) |
+------------+
| "Player_C" |
+------------+
除了这几个例子,相似的需要处理关联数据的场景应该还有不少。比起传统处理过程中冗长的数据拉取筛选过程或者复杂的SQL语句,图数据库在处理关联数据上更清晰、简洁、效率更高。
写在最后
图数据库近10年来也是发展最快的数据库类型,国内游戏公司除了网易之外,目前曝光出来将此类型数据库用在游戏开发中的公司基本很少。
虽然图数据库设计的初衷不是为了解决游戏开发相关的一些问题,但随着游戏系统的复杂度、数据关联性不断增强,期待图数据库在游戏领域被广泛传播的那一天。
(全文结束)
转载文章请注明出处:漫漫路 - lanindex.com