我会编辑这个以尽可能多地添加其他相关问题,尽管我希望OP能够解决我上面的评论.我作为一名专业的在线游戏开发者,多年来作为一个业余爱好者在线游戏开发者,多年来一直在说它的价值.
在线游戏意味着某种持久性,这意味着您有两种类型的数据 - 一种是由您设计的,另一种是由玩家在游戏过程中创建的.您很可能将两者都存储在数据库中.确保您有不同的表格,并通过通常的数据库规范化规则正确地交叉引用它们.(例如,如果你的玩家制作了一把大刀,你就不会创建一个包含剑的所有属性的整个新行.你可以在player_items表中创建一个带有每个实例属性的新行,并参考中的大刀行包含每个itemtype属性的item_types表.)如果您发现一行数据中包含您设计的某些内容以及播放期间播放器正在更改的某些内容,则需要将其标准化为两个表.
这实际上是典型的类/实例分离问题,并且适用于此类游戏中的许多事情:地精实例不需要存储它对于妖精(例如绿皮肤)的所有细节,只有相关的事情到那个实例(例如,位置,当前的健康状况).有时,构造行为有一个微妙之处,在这种情况下,需要根据类数据创建数据.(例如,根据地精类型的最大生命值设置地精实例的起始生命值.)我的建议是将这些硬编码到代码中,以创建实例并为其插入行.这些信息很少变化,因为在实践中很少有这样的值.(耗尽资源的初始分数,如健康,耐力,法力......这就是它.)
尝试找到一致的术语,将实例数据与类型数据分开 - 这将使您以后修补实时游戏并尽量不通过编辑错误的表来破坏玩家的辛苦工作时的生活更轻松.这也使得缓存变得更加容易 - 您通常可以缓存您的类/类型数据而不受惩罚,因为只有当您(设计人员)将新数据推送到那里时它才会发生变化.您可以通过memcached运行它,或者如果您的游戏有连续的过程(即不是PHP/ASP/CGI /等),可以考虑在启动时加载它们.
请记住,一旦您上线,从设计端数据中删除任何内容都会有风险,因为播放器生成的数据可能会引用它.在部署到实时服务器之前,在本地彻底测试所有内容,因为一旦它在那里,就很难将其删除.考虑如何能够以安全的方式将这些数据行标记为已删除 - 可能是布尔"实时"列,如果设置为false,则表示它不会显示在典型查询中.想想如果你禁用他们所获得的物品对玩家的影响(如果这些是他们付出的物品,那就加倍了).
如果不知道你想如何设计你的游戏,那么实际的制作方面就无法真正回答.数据库设计必须遵循游戏设计.但我会经历一个微不足道的想法.也许你会希望能够创建一个基本对象,然后用符文或水晶或其他任何东西来增加它.为此,您只需要项目实例和扩充实例之间的一对多关系.(请记住,您也可能有项目类型和扩充类型表.)每次扩充都可以指定项目的属性(例如,持久性,在战斗中完成的最大伤害,重量)和修饰符(通常作为乘数,例如1.1到增加10%的奖金).你可以在这里和这里看到我对如何实现这些修改效果的解释- 同样的原则适用于临时技能和法术效果,适用于永久物品修改.
对于数据库驱动游戏中的角色统计,我通常建议坚持每个统计数据的一个列(整数或浮点数)的天真方法.稍后添加列并不是一项困难的操作,因为您将要经常阅读这些值,您可能不希望一直在对它们执行连接.但是,如果你确实需要灵活性,那么你的方法很好.这非常类似于我在下面建议的技能水平表:许多游戏数据可以用这种方式建模 - 将一个事物的类或实例映射到其他事物的类或实例,通常用一些额外的数据来描述映射(在这种情况下,统计的价值).
一旦你设置了这些基本连接 - 实际上是以一种可能不方便你的代码的方式分离类/实例数据而导致的任何其他复杂查询 - 考虑创建一个视图或存储过程来执行它们场景,以便您的应用程序代码不再需要担心它.
当然,其他良好的数据库实践适用 - 当您需要确保多个动作以原子方式发生时使用事务(例如交易),将索引放在您最常搜索的字段上,在静默期间使用VACUUM/OPTIMIZE TABLE /以保持性能提升等
(这一点下面的原始答案.)
说实话,我不会将任务需求信息存储在关系数据库中,而是存储在某种脚本中.最终你对"需求"的想法采取了几种不同的形式,可以利用不同种类的数据(例如,等级,类,完成的先前任务,物品占有)和操作员(等级可能是最小值或最大值,某些任务可能需要一个项目,而其他人可能需要一个项目,等等)更不用说连词和分离的组合(一些任务需要满足所有要求,而其他任务可能只需要满足几个中的一个).这种事情在命令式语言中更容易指定.这并不是说你没有DB中的任务表,只是你没有尝试将有时任意的需求编码到模式中.我有一个requirements_script_id列来引用外部脚本.我想你可以将实际的脚本作为文本字段放入DB中,如果它也适合的话.
虽然技能要求适合于数据库,但考虑到学习技能的典型游戏系统,当您在某个班级中升级时,技能要求非常微不足道:
table skill_levels { int skill_id FOREIGN KEY; int class_id FOREIGN KEY; int min_level; } myPotentialSkillList = SELECT * FROM skill_levels INNER JOIN skill ON skill_levels.skill_id = skill.id WHERE class_id = my_skill ORDER BY skill_levels.min_level ASC;
需要技能树吗?添加列prerequisite_skill_id.等等.
我会编辑这个以尽可能多地添加其他相关问题,尽管我希望OP能够解决我上面的评论.我作为一名专业的在线游戏开发者,多年来作为一个业余爱好者在线游戏开发者,多年来一直在说它的价值.
在线游戏意味着某种持久性,这意味着您有两种类型的数据 - 一种是由您设计的,另一种是由玩家在游戏过程中创建的.您很可能将两者都存储在数据库中.确保您有不同的表格,并通过通常的数据库规范化规则正确地交叉引用它们.(例如,如果你的玩家制作了一把大刀,你就不会创建一个包含剑的所有属性的整个新行.你可以在player_items表中创建一个带有每个实例属性的新行,并参考中的大刀行包含每个itemtype属性的item_types表.)如果您发现一行数据中包含您设计的某些内容以及播放期间播放器正在更改的某些内容,则需要将其标准化为两个表.
这实际上是典型的类/实例分离问题,并且适用于此类游戏中的许多事情:地精实例不需要存储它对于妖精(例如绿皮肤)的所有细节,只有相关的事情到那个实例(例如,位置,当前的健康状况).有时,构造行为有一个微妙之处,在这种情况下,需要根据类数据创建数据.(例如,根据地精类型的最大生命值设置地精实例的起始生命值.)我的建议是将这些硬编码到代码中,以创建实例并为其插入行.这些信息很少变化,因为在实践中很少有这样的值.(耗尽资源的初始分数,如健康,耐力,法力......这就是它.)
尝试找到一致的术语,将实例数据与类型数据分开 - 这将使您以后修补实时游戏并尽量不通过编辑错误的表来破坏玩家的辛苦工作时的生活更轻松.这也使得缓存变得更加容易 - 您通常可以缓存您的类/类型数据而不受惩罚,因为只有当您(设计人员)将新数据推送到那里时它才会发生变化.您可以通过memcached运行它,或者如果您的游戏有连续的过程(即不是PHP/ASP/CGI /等),可以考虑在启动时加载它们.
请记住,一旦您上线,从设计端数据中删除任何内容都会有风险,因为播放器生成的数据可能会引用它.在部署到实时服务器之前,在本地彻底测试所有内容,因为一旦它在那里,就很难将其删除.考虑如何能够以安全的方式将这些数据行标记为已删除 - 可能是布尔"实时"列,如果设置为false,则表示它不会显示在典型查询中.想想如果你禁用他们所获得的物品对玩家的影响(如果这些是他们付出的物品,那就加倍了).
如果不知道你想如何设计你的游戏,那么实际的制作方面就无法真正回答.数据库设计必须遵循游戏设计.但我会经历一个微不足道的想法.也许你会希望能够创建一个基本对象,然后用符文或水晶或其他任何东西来增加它.为此,您只需要项目实例和扩充实例之间的一对多关系.(请记住,您也可能有项目类型和扩充类型表.)每次扩充都可以指定项目的属性(例如,持久性,在战斗中完成的最大伤害,重量)和修饰符(通常作为乘数,例如1.1到增加10%的奖金).你可以在这里和这里看到我对如何实现这些修改效果的解释- 同样的原则适用于临时技能和法术效果,适用于永久物品修改.
对于数据库驱动游戏中的角色统计,我通常建议坚持每个统计数据的一个列(整数或浮点数)的天真方法.稍后添加列并不是一项困难的操作,因为您将要经常阅读这些值,您可能不希望一直在对它们执行连接.但是,如果你确实需要灵活性,那么你的方法很好.这非常类似于我在下面建议的技能水平表:许多游戏数据可以用这种方式建模 - 将一个事物的类或实例映射到其他事物的类或实例,通常用一些额外的数据来描述映射(在这种情况下,统计的价值).
一旦你设置了这些基本连接 - 实际上是以一种可能不方便你的代码的方式分离类/实例数据而导致的任何其他复杂查询 - 考虑创建一个视图或存储过程来执行它们场景,以便您的应用程序代码不再需要担心它.
当然,其他良好的数据库实践适用 - 当您需要确保多个动作以原子方式发生时使用事务(例如交易),将索引放在您最常搜索的字段上,在静默期间使用VACUUM/OPTIMIZE TABLE /以保持性能提升等
(这一点下面的原始答案.)
说实话,我不会将任务需求信息存储在关系数据库中,而是存储在某种脚本中.最终你对"需求"的想法采取了几种不同的形式,可以利用不同种类的数据(例如,等级,类,完成的先前任务,物品占有)和操作员(等级可能是最小值或最大值,某些任务可能需要一个项目,而其他人可能需要一个项目,等等)更不用说连词和分离的组合(一些任务需要满足所有要求,而其他任务可能只需要满足几个中的一个).这种事情在命令式语言中更容易指定.这并不是说你没有DB中的任务表,只是你没有尝试将有时任意的需求编码到模式中.我有一个requirements_script_id列来引用外部脚本.我想你可以将实际的脚本作为文本字段放入DB中,如果它也适合的话.
虽然技能要求适合于数据库,但考虑到学习技能的典型游戏系统,当您在某个班级中升级时,技能要求非常微不足道:
table skill_levels { int skill_id FOREIGN KEY; int class_id FOREIGN KEY; int min_level; } myPotentialSkillList = SELECT * FROM skill_levels INNER JOIN skill ON skill_levels.skill_id = skill.id WHERE class_id = my_skill ORDER BY skill_levels.min_level ASC;
需要技能树吗?添加列prerequisite_skill_id.等等.
更新:
从评论来看,看起来很多人都有XML问题.我知道现在抨击它很酷,它确实有问题,但在这种情况下我认为它有效.我选择它的另一个原因是有大量的库可以解析它,这样可以让生活更轻松.
另一个关键概念是信息真的是非关系型的.所以是的,您可以将数据存储在一堆具有大量连接的不同表中的任何特定示例中,但这很痛苦.但如果我不断给你一个不同的例子,我打赌你必须无限修改你的设计广告.我不认为添加表和修改复杂的SQL语句非常有趣.因此,@ scheibk的评论已被投票,这有点令人沮丧.
原帖:
我认为在数据库中存储任务信息时可能遇到的问题是它不是真正的关系(也就是说,它并不真正适合表格).这可能就是您在设计数据表时遇到问题的原因.
另一方面,如果您将任务信息直接放入代码中,这意味着您必须编辑代码并在每次添加任务时重新编译.瘸.
所以,如果我是你,我可能会考虑将我的任务信息存储在XML文件或类似的东西中.我知道这是几乎任何事情的通用解决方案,但在这种情况下,这对我来说是正确的.XML实际上用于存储非关系和/或分层数据,就像您需要为您的任务存储的东西一样.
简介:您可以提出自己的架构,创建XML文件,然后以某种方式在运行时加载它(甚至将XML存储在数据库中).
示例XML:
60 - Get to Mordor
Throw Ring into Lava ... Profit
听起来你已经为通用面向对象设计(OOD)原则做好了准备.我将故意忽略上下文(游戏,MMO等),因为这与你如何进行设计过程无关.我给你的链接没有什么比解释哪些条款最有助于自己查找,IMO; 我会把那些用粗体.
在OOD中,数据库模式直接来自您的系统设计,而不是相反.您的设计将告诉您基本对象类是什么以及哪些属性可以存在于同一个表中(与对象的关系为1:1)与制作映射表的对象(具有1:n或n:m关系的任何关系) - 例如,一个用户有多个统计数据,因此它是1:n).事实上,如果你正确地完成了OOD,你将无法做出关于最终数据库布局的决定.
进行任何OO映射的"正确"方法是作为称为"数据库规范化"的多步骤过程学习的.其基本原理正如我所描述的那样:找到对象关系的"arity"(1:1,1:n,...)并为1:n和n:m 制作映射表.对于1:n,你最终会得到两个表,"base"表和一个"base_subobjects"表(例如你的"users"和"user_stats"就是一个很好的例子),带有"外键"(基数的Id) object)作为子对象映射表中的一列.对于n:m,最终得到三个表:"base","subobjects"和"base_subobjects_map",其中地图有一列用于基本ID,另一列用于子对象Id.在您的N个任务示例中,这可能是必要的,每个任务都有M个要求(因此需求条件可以在任务之间共享).
这是你需要知道的85%.其余的是如何处理继承,我建议你跳过,除非你是自虐.现在,在你开始编写内容之前,先弄清楚你希望它如何工作,剩下的就是蛋糕了.