游戏数据库设计经验

2023-11-06

一、游戏模板数据库设计特点

    软件行业一般数据库设计原则,”保持数据的完整性一致性“,”避免数据冗余“,”范式设计“。但游戏领域的游戏模板表设计上还需要考虑这些特点

1.1、对游戏程序只读,游戏程序只需要考虑读取性能,不需要过多考虑修改性能

1.2、数据结构复杂,如果过于追求“去冗余”,则会导致表结构非常复杂,策划将难以填写

1.3、很多项目是采用Excel录入的,即使你设计的结构考虑了重用性,策划依然会冗余的填写

1.4、大量的与程序约定的参数潜规则,程序员往往为了系统的灵活性,而抽象出一些配置,会放入模板表中,而这些对策划来说是晦涩难懂的,建议做详细备注

1.5、程序事先读入内存,读取模板数据的方式,大部分的时候不会使用SQL的select,而常见的做法是事先读入内存,有的是实例化各种对象,有的是哈希表,有的数据数组,很少会在程序中嵌入一个SQL引擎去查询数据

所以游戏模板库的设计,一般会在传统数据库设计原则基础上,做很多的灵活设计,这是由于游戏产品的特殊性决定的

二、设计范式

    不管怎么说毕竟还是数据库设计,设计范式作为基本设计理念还是要重温一下
2.1、第一范式,确保每列保持原子性,即列不可分,比如这些信息:”张三、战士、5级、绿装、50攻击力….“ 我们设计这样的结构

玩家

职业

级别

装备

攻击力

张三

战士

5

绿色头盔

50攻击力

    但是这样装备字段里包括了多个信息,所以我们改成这样,就可以符合第一范式了

玩家

职业

级别

装备品质

装备

攻击力

张三

战士

5

绿色

头盔

50攻击力

2.2、第二范式,属性完全依赖于主键,比如有这样一个数据结构

玩家

职业

级别

装备品质

装备

攻击力

技能

技能图标

张三

战士

5

绿色

头盔

50攻击力

 普攻

a.png

    这里面包括了玩家、装备、技能3类信息,当一个玩家有多个装备,且有多个技能的时候,这张表难以表达清楚逻辑关系。所以改成这样3张表,这样可以清楚的表达

2.3、第三范式,属性和主键不能间接相关,比如上面的数据结构,装备的攻击力只与装备关系,技能图标只与技能关系,所以改成这样就可以了,独立一个描述装备表和技能表,然后玩家和他们之间建立关系表来表达他们直接的多对多关系。

三、游戏模板库设计

3.1、数组属性
    一般一个字段放一个属性值,如果要表达一个数组类的属性怎么设计呢?一般的设计是建立一个独立的表,方便各种select

     

但是游戏库往往不需要反向查询,只需要存储一个数组数据,这时我们可以采用简单的存储方案

这两种方案的选择,各有特点

    一般选择的话看需不需要考虑属性的扩展? 需不需要按属性分析? 需不需要对单个属性进行修改。比如一个玩家成就列表,我们可能就会放入一个字段,因为一般不会按成就查询且没有扩展属性。而玩家的任务我们会放入一个表,因为会扩展出接任务的时间、任务完成进度等属性

案例:

    场景对象表中需要描述对象的交互范围 填1代表圆形范围后面跟半径参数,填2代表矩形范围,后面跟宽、高参数。这样我们会选择一个属性的设计。(注意 这个vector<int>,在数据库里一般是varchar类型,我这个是概念设计图)

里面的数据大概是这样的

 

 

3.2、单主键还是复合主键

    首先看程序的要求,有的项目出于某种原因,强制要求所有表必须单主键。比如分布式数据库的要求,或为了方便程序实例化对象。如果项目可以允许复合主键的话,可以充分利用好这类设计。

    约束的设计意义在于是否可以帮助业务帮助程序识别一条记录,比如这张表

一般有3个设计方案

方案1、你可以选择加一个自增长的单键来设计,但这样对读取数据没有任何好处,只是多了一个冗余的id

方案2、如果“物品编号”是唯一标识的,你可以设置为单键,但似乎对索引没有任何好处,因为大部分情况下会按玩家编号查询,这里你还需要建立一个玩家编号的索引,还是冗余了一个索引数据,不过比上一个方案好

方案3、如果条件允许,可以设置为玩家编号、物品编号 的复合主键,既可以解决标识问题,又可以解决索引问题

案例:

    先讨论Dialog表,业务是“一次对话有多句话”,程序一般在读取的时候是按对话ID来一次读取的,所以这里直接设计了一个双主键来表达这个业务。

    再来看NPC功能表,业务是“一个NPC有多个业务功能,且业务功能会根据玩家的选择而进一步扩展”,为了达到这个“进一步扩展”需要设计一个用户表达树形结构的自关联,如果这个表是复合主键的话,会导致外键也需要是复合的,会产生冗余字段,另外有考虑到有可能存在多个NPC共用一套NPC功能的可能性(比如一个NPC在两个的场景中都会出现,或者两个NPC都有进多人副本的功能),所以这里设计了单主键

 

3.3、范围结构的表达

比如数据是这样的

分数下限

分数上限

奖励

0

100

5

101

200

10

 

 

一般来说有2种设计方法,

    一个是按一边维护比如按上限设计,这样在两段数据之间绝对不会有间隙,而且没有冗余数据,但是看起来需要结合上下文来理解。

    另一个是按上下限来设计,好处是程序写起来方便,读起来简单。但是这样可能存在间隙,比如100.5这种,特别是日期时间,策划不可能填精确到毫秒的时间。

案例:

    一个技能根据,技能等级会有不同的数值变化,而这些变化是按等级段来的,比如10级内都是+10点攻击力,20级内都是加30点攻击力。

    这个表主要是为程序服务的,等级也不可能出现小数也就没有间隙问题,所以冗余了上下限的属性,另外为了程序用起来方便还加了等级段顺序属性(其实上下限的值就是顺序的,所以是冗余的)。

 

3.4、树形结构的表达

比如数据是这样的

最基本的设计方案是"邻接表"

但是为了方便程序的查询或者修改,一般会有各种冗余设计方案,这里举3个例子

1、枚举路径,在数据中像路径一样把,每个节点的路径都写出来,优点是查询的时候只需要select like 查询就可以了,但缺点是修改节点之间的关系,可能需要重建相关的其他数据的路径,比如迁移一个子树到另一个根节点下,需要改变整个子树的路径。

2、冗余根节点,这个设计方案就是一般是用在只按整个树查询的情形下,按根节点读取起来方便

3、闭包表,这个设计完全是为了方便程序读取,有的程序为了在界面上展示一个树形结构会这样要求冗余数据,有时还会要求冗余一个层级的字段来表达是第几层的。但这种设计修改起来非常容易出错,且不易读。真的遇到需要这种设计,还是建议程序员自己在内存里生成这种结构,以保证数据维护的质量

案例:

     这里举个技能行为的例子,业务是“一个行为后,接下一个行为,但QTE成功后行为是另一个”,我们也可以按最传统的结构表达出这个业务,但策划在用Excel填写的时候不直观,所以在当前行为下记录了下一个节点,而不是上一个。这个设计很像数据结构里的二叉树结构,也方便程序的读取。

    另一个案例看刚才的NPC功能表,同样为了Excel维护方便,程序读取简单,采用了记录下一个节点的方案,但这里的可能存在多个下级功能,所以用了前面说的数组属性,来避免多一张关系表

 

3.5、对象的设计

比如有这样一个玩家类,有两个子类机器人、VIP玩家,继承与玩家。我们如何表达这种设计

这里列5种方案

1、单表。就是用一张表把所有属性放进去,优点易于维护,缺点会有冗余数据

2、独立实体表。就是完全分开两个不同的表,相同的部分,冗余到两张表里,这样缺点是如果子类多,容易产生很多类似的表,后续修改表结构起来需要同时维护。

3、类-表映射。就是和类一样设计3张表,优点是逻辑清晰,没有冗余数据,但维护数据起来需要改多张表

4、范型属性表。这个设计非常灵活扩展非常好,但对应的维护起来基本不用工具是很难维护的。实际工作中只用来解决未知的扩展用。

5、半结构化属性表。这个也是牺牲了维护性,追求扩展性的设计,大文本里一般是类似json,xml之类的,一般用在非常复杂的参数扩展上

案例:

    场景对象里有可以包括普通怪物、NPC、不可见的触发区域、城堡、宝箱等等。

    很多对象的属性是差不多的,而其中有几类对象是会自己移动的,比如巡逻怪,NPC等。所以我们的行为树和移动速度放直接放在主表里,方便共用与维护。

    而NPC又有许多特殊的属性,且会有进一步的扩展功能,所以这里建立了一个1对0..1的NPC扩展表。在表达这1对0..1关系一般有3种,比较常见的是在子表里和主表的键的数据一致,既是主键也是外键。另外就是子表里放主表的键,这样子表的键比较自由,可以用自增ID。另一种是在主表里放子表的键,数据会有冗余。这里为了方便策划维护所以采用了后者

 

3.6、多对多关系

最常见的多对多关系就是采用关系表了,比如这样

    这种传统设计完全复合范式设计理念,扩展性非常好,可以在关系表上扩展其他属性,同时也方便各种SQL的select查询

    而在游戏模板表里,如果只是单纯的多对多关系,业务上不需要扩展的话,更多是采用数组方案。在策划填表上优势非常明显,非常利于策划的填写,而不需要频繁的在不同excel中切换

案例:

    场景对象在不同的状态下,对不同的阵营有不同的行为。比如我们有3个阵营友好、敌对、中立,另有3个状态普通、战斗、死亡,那就会有9种排列组合。

对象

阵营

状态

动作

A

友好

普通

动作A

A 友好 敌对 动作B

   我们在设计这个表的时候用了这个结构

    首先把阵营做为数组放在场景对象表上了,然后按不同状态定义不同的动作扩展属性。这个设计相当于把阵营解耦到对象表上了,这是因为业务上”非PVP玩法的时候的时候,很多对象对不同阵营是对应一个状态动作的组合“,这样避免了动作扩展属性数据冗余,填起来也方便。

四、模板库设计的其他经验

4.1、设计好了策划不一定会按设计的来填写

比如有个多语言的表,设计师是想,一段多语言的文字只要内容相同的情况下是可以复用的

而策划填的时候往往为了方便,每个都去新增一行,根本没有去看看表里是不是已经存在了可以复用。

这类现象在填表的时候非常普遍,有的是为了方便维护,有的是怕后续会发生变化会相互影响,但也有是没有去冗余意识

最好的情形是策划填表的时候发现冗余数据太多,找设计师改设计。

4.2、为了避免小数使用万分比,而不使用百分比

因为模板表程序用的时候可能会经过各种导表工具给程序用,用小数容易出现数值差异,特别是各种概率

4.3、建议使用设计工具维护一个数据字典,而不是依赖Excel

这里建议使用Power Designer,或 ER Studio之类的工具把表设计给存下来,方便设计师与技术、策划的交流

五、总结

    大家通过案例可以看到,实际在做游戏数据库设计时,是需要根据业务灵活的使用各种设计方案。而不是固定的按某个固定的设计范式进行设计,甚至在表达一个业务时需要同时结合多种设计方案。
    另外就是调整,任何设计都是可能需要调整的,即使完全满足当前设计需求,也有可能随着业务变化而需要设计调整,这个是永远避免不了的。
    没有完美的设计,好的设计师是在各种约束条件下找到一个设计平衡点,来达到业务目的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

游戏数据库设计经验 的相关文章

  • Inno Setup入门(二十一)——Inno Setup类参考(7)

    复选框 复选框 CheckBox 用于多个并不互斥的几个选项中作出一个或者多选择 例如字体可以有粗体 斜体和下划线 这三种状态可以任意组合 像这样的选项可以采用复选框实现 Pascal脚本中对应的类是TcheckBox 其定义如下 lt x

随机推荐

  • char*转LPCWSTR的两种方法

    char 转LPCWSTR的两种方法 MultiByteToWideChar mbstowcs MultiByteToWideChar 将char 类型转换为LPCWSTR类型可以使用MultiByteToWideChar函数 这个函数可以
  • min_sample_split 和min_sample_leaf区别

    所以基本上 min sample split是分割所需的最小样本数 例如 如果min sample split 6并且节点中有4个样本 则不会发生拆分 不管熵是多少 在 另一方面 min sample leaf基本上是叶节点所需的最小样本数
  • 【经典买点】MACD指标的八种买入形态图解

    MACD指标中的DIF和MACD DIF和DEA两线 按照其金叉时在零轴上 下的位置 和金叉前是否发生过死叉 死叉发生的位置 有八种形态图形 它们分别是 佛手向上 小鸭出水 漫步青云 天鹅展翅 空中缆绳 空中缆车 海底电缆和海底捞月 本文转
  • Linux之你容易忽略的计算机组成知识

    来自鸟哥的私房菜 1 南北桥 整个主板上面最重要的就是芯片组了 而芯片组通常又分为两个网桥来控制各组件的沟通 分别是 1 北桥 负责链接速度较快的 CPU 主存储器不显示适配器等组件 2 南桥 负责连接速度较慢的周边接口 包括硬盘 USB
  • 7-52 两个有序链表序列的交集 (20 分)(思路加详解尾插法)come Boby!

    一 题目 已知两个非降序链表序列S1与S2 设计函数构造出S1与S2的交集新链表S3 输入格式 输入分两行 分别在每行给出由若干个正整数构成的非降序序列 用 1表示序列的结尾 1不属于这个序列 数字用空格间隔 输出格式 在一行中输出两个输入
  • 浅谈5G 与4G的区别

    5G 顺势而生 应用广泛 包含诸多的进步 但未来依然可期 期望6G 7G 8G等等 前提 了解5G 技术 有必要了解一下 1G 2G 2 5G 3G 4G技术 1G 到4G之间的技术我们称之为蜂窝移动网路系统 正所谓长江后浪推前浪 一代更比
  • sql-labs 29 waf 绕过参数污染

    HTTP参数污染 HTTP Parameter Pollution 攻击者通过在HTTP请求中插入特定的参数来发起攻击 如果Web应用中存在这样的漏洞 可以被攻击者利用来进行客户端或者服务器端的攻击 waf服务器 tomcat 只解析重复参
  • 前端自适应布局

    在前端开发中 我们不可避免要面临适配问题 本文将介绍几种适配方式 一 px和em 1 1 px 1 2 em 二 rem 2 1 rem原理 2 2 rem如何计算的 2 3 rem使用 三 使用插件px2rem转换 3 1 原理和优点 3
  • MySQL笔记(五)使用python调用数据库进行操作

    python 访问数据库流程 在pycharm中下载pymysql 打开数据库视图 相当去navicat 设置数据库 使用python对数据库进行操作 Python2中使用的是MySQLdb模块 from pymysql import de
  • 图结构与图算法综述

    图结构与图算法综述 图结构以及图算法 无向图 有向图和网络能运用很多常用的图算法 这些算法包括 各种遍历算法 这些遍历类似于树的遍历 寻找最短路径的算法 寻找网络中最低代价路径的算法 回答一些简单相关问题 例如 图是否是连通的 图中两个顶点
  • Oracle数据库基础知识

    1 Oracle 数据库服务器体系所包含的三种主要结构是 内存结构 进程结构 存储结构 2 安装 11gR2 数据库要经过哪几个主要阶段 Grid基础架构安装 数据库软件安装 DBCA创建数据库 3 数据库实例所必须的后台进程包括 DBWn
  • QT——实战动态链接库调用

    如何在Debug模式下调用外部的动态链接库 首先在工程文件夹下 通过右键可以选择添加后 进入下图界面 选中外部库 点击下一步 选择所要调用的外部库文件debug生成的buliding文件里的libxxxxx a文件 如下图所示 点击下一步
  • 移动端中的坑和 vue中事件修饰符详解(stop, prevent, self, once, capture, passive)

    stop 是阻止冒泡行为 不让当前元素的事件继续往外触发 如阻止点击div内部事件 触发div事件 prevent 是阻止事件本身行为 如阻止超链接的点击跳转 form表单的点击提交 self 是只有是自己触发的自己才会执行 如果接受到内部
  • Eclipse 运行web项目 HTTP404错误

    Eclipse 引入web项目后 run as on server tomcat启动成功 但网页提示404 问题排查 404 服务器找不到资源 首先检查Eclipse部署路径 是否部署了资源文件 查找部署路径 发现该路经下只有一个WEB I
  • QT 计算两个日期时间差?(时间转时间戳)

    时间戳时间转换工具 时间换算工具 1 得出的结果单位是 天 不足一天为0 没有半天的说法 QDateTime time1 QDateTime fromString 2022 4 25 16 40 02 yyyy MM dd HH mm ss
  • CMake中option和cmake_dependent_option的使用

    CMake中的option命令为用户提供可以选择的布尔选项 boolean option 其格式如下 option
  • centos7.4中安装Apache服务

    安装Apache服务 大家好 今天我们在cenots7 4中安装一个web服务Apache 接下来我们先来简单了解一下Apache服务吧 Apache Http server是开源软件项目的杰出代表 基于标准的http网络协议提供网页浏览服
  • 黄鱼车

    本文转载至 http www zynews com news 2010 12 19 content 788498 htm 文 佘建民 有交关外地朋友问我迭个老上海 为啥上海人拿三轮脚踏货车叫作 黄鱼车 对这个疑问 现借 上海闲话 一角 讲讲
  • 【C++】内存管理初阶

    1 C C 内存管理 1 C C 内存分布 int globalVar 1 static int staticGlobalVar 1 void Test static int staticVar 1 int localVar 1 int n
  • 游戏数据库设计经验

    一 游戏模板数据库设计特点 软件行业一般数据库设计原则 保持数据的完整性一致性 避免数据冗余 范式设计 但游戏领域的游戏模板表设计上还需要考虑这些特点 1 1 对游戏程序只读 游戏程序只需要考虑读取性能 不需要过多考虑修改性能 1 2 数据