游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

万字长文,关于游戏中道具设定和产出条件的相关设计,下篇

发布时间:2015-12-18 09:43:17 Tags:,,

篇目1,解析锻造系统对于玩家和开发者的作用

作者:Sande

武器/道具锻造系统很早以前就是MMORPG中非常重要的一部分,它丰富了原本空洞而单调的游戏世界。然而,对于这样一种有效的娱乐手段,锻造系统的发展史却并非一帆风顺。以前的游戏总是充满各种刷任务机制,无所用处的组件,并强迫玩家使用无聊的锻造系统,甚至完全采纳毫无用处的交易技能。

锻造系统也可以是维系着玩家与开发者关系的一个“弹簧”。如果玩家自己创造的内容与游戏所提供的道具力量不均,那么锻造者和非锻造者就会认为锻造系统是无用的,而不再与其他玩家进行比较,并责怪开发者糟糕的设计。

如何才能避免这种矛盾?矛盾的根源在于,锻造对于开发者的作用与其对于玩家的作用是完全不同的。而平衡双方需求便是化解这种矛盾的好方法,并且能够帮助游戏创造出一个合适的锻造系统。

所以,锻造系统对于玩家有何作用?

有助于缓解刷任务现象

对于玩家来说这应该说是最重要的作用吧。玩家总是喜欢变化,如果一款游戏只是让玩家一整天都在打怪,而没有其它任务,他们肯定会对此感到厌烦并最终退出游戏。而锻造能够在原本的杀戮-抢夺-杀戮模式中为玩家提供替换式选择,让他们能够以不同的方式控制角色的发展。

同时开发者还应该确保锻造的易用性。这就意味着玩家能够同时拥有战斗技巧和锻造技巧,并且两者间不会产生相互抵触。玩家不需要非得从中做出选择。同时锻造还需要具有吸引力,不论是资料还是产品,都应包含有趣的选择。肯定没有哪个玩家希望用一种更无聊的刷任务机制取代原先的刷任务机制吧。

crafting(from massively.joystiq.com)

crafting(from massively.joystiq.com)

它是一种道具和货币来源

玩家可以从自己所创造的道具中获得更多可观的收益。甚至,如果玩家能够使用自己锻造的道具,他们便会萌生出一种自力更生感,即通过自己的努力而不断增强战斗技巧。

而对于开发者来说,锻造道具应该能够有效地说服玩家愿意花费他们辛苦所得的货币。这时候他们就需要想办法保持锻造产品与游戏掉落道具之间的平衡。一大策略 便是在锻造中包含非装备性质的选项。如锻造一些能够消耗的物品,如药剂,以确保需求的稳定,以及强化装备威力的功能,如宝石或者能够让锻造者提升道具(游 戏邦注:可同时适用于游戏掉落道具、玩家自己锻造的道具)等。按照这种方法,获得最佳装备的角色便能够同时地利用两种游戏玩法的优势。

吸引建造和社交型玩家

有些玩家喜欢游戏中的破坏,杀戮和掠夺;也有一些玩家更喜欢在城市中闲逛,与人交流并创造神奇的道具。他们会喜欢锻造所推动的相互依赖和相互交流,愿意与合作伙伴和好友分享并共同创造资源。

这些玩家便是这类游戏最重要的玩家基础,游戏应该想办法留住这些玩家。开发者应该努力创造一个具有吸引力且有趣的锻造系统,但是却不应该强求所有玩家不得不关注锻造系统。一些系统采用即时战斗类行动而取代无聊的进度条。但是这么做却适得其反,因为这会强迫玩家不得不在锻造道具时停止社交行为。相反地,锻造应该更加灵活,让玩家能够按照自己的节奏进行。而这些有趣的行动应该在玩家点击“创造”之前就表现出来,例如在选择特定资源,资料或使用方法的时候。

创造有效的自定义机制

锻造功能能够按照不同方式吸引不同类型玩家的注意。对于那些喜欢角色创造的玩家来说,交易技能能够让角色更加与众不同。对于社交游戏玩家来说,他们能够锻造装饰性道具去自定义角色和环境的外观。对于战斗策略型玩家来说,定制属于自己的装备能够增强角色的实力并提高战斗效率。

开发者总是会忽视自定义机制,因为这需要花费他们更多的额外成本。但是如此做法所造成的损失却是我们不容小觑的。如果锻造者能够定制角色或道具的外观,这可以进一步增强玩家的沉浸感。游戏掉落的道具只有原来的固定属性,而如果锻造道具的配方也只有一种固定属性,那么对玩家来说,锻造道具与游戏掉落道具之间并无多大区别,其吸引力也将大打折扣。

Vanguard-Crafting(from mmorpg.com)

Vanguard-Crafting(from mmorpg.com)

那么,锻造对于开发者来说有何作用?

强化游戏的合理性

让人悲哀的是,很多开发者只是因为外部需求而在游戏中添加锻造机制。如此心态最终只能够创造出糟糕的设计系统,并破坏了整款游戏的合理性。

在设计一款强调用户留存的游戏,开发者在设计锻造系统时更是应该仔细考虑玩家的想法。如果游戏的目标是吸引特定玩家类型,如休闲玩家或战斗型玩家,游戏可以适当放松锻造系统的作用,但是同时应该添加能够创造出同等利益的功能。如果锻造系统是游戏所必须的内容,开发者就应该关注预算分配等问题,而如果玩家不喜欢你的锻造系统,那就只是徒劳无功的做法。

它能让玩家投入更多游戏时间

MMO中的任何系统都必须对玩家具有吸引力,并让他们愿意长时间玩游戏。锻造系统其实也是玩家角色发展的体现,结合战斗进程更能够调动玩家的积极性,在游戏中投入更多时间。

但是如果使用不合理,这种理念将会导致玩家不得不在锻造系统中耗费更多时间。不必要的刷任务机制,较长的进度条以及对失败的严厉惩罚都会导致玩家在锻造中投入更多时间,但是同时却会导致玩家在游戏中产生更多挫败感。带有过多挫败感的玩家很可能随时停止玩游戏。为了避免这种情况,游戏应该确保玩家始终能够感受到某种意义上的进步。或许可以让他们按照配方上的材料或其他交易技能中的组件来锻造道具,或者通过经验值而非随机性的技能增长,让他们获得这种进步感。

创造玩家间的相互依赖感

作为一种多人游戏,MMO自然应该包含推动玩家交流与协作的机制。而锻造系统便具有这种功效。它让玩家能够为好友创造及增强道具能量,同时还能够与他们分享所得的资源和材料。

创造玩家间相互协作的一大方法便是引入一种包括稀有掉落道具(或由其他交易技能锻造材料)的配方,但这种配方必须具有特殊性,能够制造出更多有益的道具。它不需要特别要求玩家为了获得一些基本道具进行协作,更不能对玩家做出升级技能的要求,否则只会让玩家因更多时间和金钱投入而备受挫折。

创造强有力的经济系统

强大的经济系统能够提高游戏的用户粘性,而如果开发者允许游戏内部的货币交易,他们便有可能从中获得更多实在的利益。锻造系统是玩家间一种重要的交易方式。除此之外,它让毛皮,尖牙,宝石等能够用于商业交易的材料更凸显其价值,并有效缓解游戏经济系统中的通货鼓胀现象。

使用锻造推动经济发展的一大方法便是,引进可以让让所有玩家都受益的道具配方。其次就是提供多种材料的来源,例如交易商品,掉落的道具(稀有和普遍的),资源节点以及其它配方。设计适用于多种配方的材料以提升它们的需求量,同时增加仅适用于一种配方但却极有用处的掉落道具数量。

总结

上述内容并非创建优秀锻造系统的完整列表,其中要点也未必适用于同一款游戏。开发者应该在衡量玩家的游戏目标前优先考虑自己的目标,以便创造一种能够让大家各取所需的锻造系统,并最终制作出一款强大的游戏。

篇目2,分享为怪物掉落道具编写程序的经验

作者:Kyatric

动作游戏中的一个普遍机制就是让敌人在临死前掉落一些道具或者奖励。角色就可以拾取这些战利品从而增加自己的优势。这是包括RPG在内的许多游戏都有的一个机制,它给予玩家一个除掉敌人的动机——以及看到即时奖励时的一点兴奋感。

在本篇教程中,我们将查看这些机制的内在运行机制,以及如果用你所使用的编码工具/语言将其植入游戏中。

我使用Construct 2这种HTML5游戏开发工具来展示这方面的例子,但其使用工具并不局限于此。无论你使用哪种编程语言或工具,应该都能够植入同样的机制。

这些例子是由r167.2所制作,你可以在其软件免费版本中打开和进行编辑。你还可以下载Construct最新版本来操作这些例子。

基本机制

ScreenShot1(from gamedevelopment)

ScreenShot1(from gamedevelopment)

在敌人死时的那一刻(它的HP已经极大减少,或者接近于0)会调用一个函数。该函数的作用就是确定敌人是否存在掉落物品,如果有,又该掉落哪种道具。

该函数还可以处理掉落物品的可视化表现形式, 令其协同敌人在前方屏幕中生成。

看看以下的例子。

0_ Owl is slain, and drops Lollipop.
1_ Goblin is slain, and drops Gold.
2_ Mastodon is slain.
3_ Mastodon is slain, and drops Gold.
4_ Squire is slain, and drops Lollipop.
5_ Owl is slain.
6_ ZogZog is slain, and drops Lollipop.
7_ Owl is slain.
8_ Mastodon is slain.
9_ Owl is slain.
10_ Goblin is slain, and drops Lollipop.
11_ ZogZog is slain.
12_ ZogZog is slain, and drops Lollipop.
13_ Squire is slain.
14_ Mastodon is slain.
15_ Boar is slain, and drops Lollipop.
16_ Mastodon is slain.
17_ Boar is slain, and drops Lollipop.
18_ Boar is slain, and drops Lollipop.
19_ Boar is slain, and drops Lollipop.
20_ Boar is slain, and drops Lollipop.
21_ Boar is slain, and drops Lollipop.
22_ Mastodon is slain, and drops Rocks.
23_ Boar is slain, and drops Lollipop.
24_ ZogZog is slain, and drops Lollipop.
25_ Squire is slain, and drops Lollipop.
26_ Boar is slain, and drops Lollipop.
27_ Squire is slain, and drops Gold.
28_ Owl is slain.
29_ Squire is slain.
30_ Squire is slain.
31_ ZogZog is slain, and drops Lollipop.
32_ Owl is slain, and drops Jewel.
33_ Squire is slain.
34_ Mastodon is slain, and drops Rocks.
35_ Owl is slain.
36_ Owl is slain.
37_ Owl is slain.
38_ ZogZog is slain.
39_ Goblin is slain.
40_ Mastodon is slain.
41_ Boar is slain, and drops Lollipop.
42_ Boar is slain, and drops Lollipop.
43_ ZogZog is slain, and drops Lollipop.
44_ Owl is slain.
45_ Owl is slain.
46_ Mastodon is slain, and drops Rocks.
47_ Squire is slain, and drops Gold.
48_ ZogZog is slain, and drops Lollipop.
49_ Squire is slain, and drops Gold.
50_ Goblin is slain, and drops Lollipop.
51_ Owl is slain.
52_ Mastodon is slain.
53_ Mastodon is slain, and drops Lollipop.
54_ Squire is slain, and drops Gold.
55_ Goblin is slain, and drops Lollipop.
56_ Mastodon is slain.
57_ ZogZog is slain, and drops Lollipop.
58_ Goblin is slain, and drops Lollipop.
59_ ZogZog is slain.
60_ ZogZog is slain.
61_ ZogZog is slain, and drops Lollipop.
62_ Boar is slain, and drops Lollipop.
63_ Goblin is slain, and drops Lollipop.
64_ Squire is slain, and drops Lollipop.
65_ Goblin is slain.
66_ ZogZog is slain, and drops Lollipop.
67_ Owl is slain, and drops Equipment.
68_ Boar is slain, and drops Lollipop.
69_ Boar is slain, and drops Lollipop.
70_ Squire is slain, and drops Gold.
71_ Owl is slain.
72_ Owl is slain.
73_ Goblin is slain, and drops Lollipop.
74_ ZogZog is slain, and drops Lollipop.
75_ ZogZog is slain, and drops Lollipop.
76_ Owl is slain, and drops Equipment.
77_ Goblin is slain, and drops Lollipop.
78_ Boar is slain, and drops Lollipop.
79_ Owl is slain.
80_ ZogZog is slain, and drops Lollipop.
81_ ZogZog is slain.
82_ ZogZog is slain, and drops Lollipop.
83_ Mastodon is slain.
84_ Owl is slain, and drops Equipment.
85_ Mastodon is slain.
86_ Squire is slain.
87_ Mastodon is slain.
88_ Boar is slain, and drops Lollipop.
89_ ZogZog is slain.
90_ ZogZog is slain, and drops Lollipop.
91_ ZogZog is slain.
92_ Mastodon is slain.
93_ Boar is slain, and drops Lollipop.
94_ Goblin is slain.
95_ Owl is slain, and drops Rocks.
96_ Mastodon is slain.
97_ ZogZog is slain, and drops Lollipop.
98_ Mastodon is slain.
99_ Squire is slain, and drops Lollipop.

这将执行一个创造100个随机怪物,并杀死它们,然后展示每个怪物是否掉落道具这一结果的批量过程。屏幕底部将展示有多少怪物掉落道具,以及掉落了多少种类型的道具。

这个例子用文本来解释是为了呈现了该函数背后的逻辑,以及展示该机制适用于任何类型的游戏,无论是踩踏敌人的平台游戏,还是上下视角的射击游戏,或是RPG。

让我们看看这个样本的运行方式。首先,怪物和掉落道具都有各自的阵列。以下是怪物阵列:

beast array(from gamedevelopment)

beast array(from gamedevelopment)

这是掉落道具阵列:

drops array(from gamedevelopment)

drops array(from gamedevelopment)

Index列中的X值是怪物或道具类型的独特标识符。例如,指数为0的怪物就是一只野猪。而指数为3的道具则是一个宝石。

这些阵列便于我们查找表格,它们包括每个怪物或道具的名称和类型,以及将允许我们确定稀有性或掉落率的其他值。在怪物阵列中,其名称后还有另外两个栏目:

掉落率是怪物被杀死时掉落一项道具的机率。例如,野猪被杀死时有100%的道具掉落率,而猫头鹰的这一机率仅为15%。

稀有性决定了这只怪物掉落某项道具的低概率。例如,野猪可能掉落某项稀有值为100的道具。现在,让我们看看drops阵列。我们可以看到岩石是拥有最大稀有值(95)的道具(游戏邦注:虽然这里的稀有值很高,但作者编写这一函数时,为更普遍的道具设置了更大的稀有值。也就是说,怪物掉落岩石的机率高于稀有值较低的道具。)

从游戏设计角度来看,这对于我们来说非常有趣。为了游戏获得平衡,我们并不希望让玩家过早接触太多装备或过多高端道具——否则,角色可能会过早变得太强大,游戏也就不再那么有趣了。

这些表格和数值只是一些例子,你可以根据自己的游戏系统和环境来进行调整,一切取决于你的系统平衡性。

让我们看看样本的伪代码:

CONSTANT BEAST_NAME = 0
CONSTANT BEAST_DROPRATE = 1
CONSTANT BEAST_RARITY = 2
CONSTANT DROP_NAME = 0
CONSTANT DROP_RATE = 1
//Those constants are used for a better readability of the arrays

On start of the project, fill the arrays with the correct values
array aBeast(6,3) //The array that contains the values for each beast
array aDrop(6,2) //The array that contains the values for each item
array aTemp(0) //A temporary array that will allow us what item type to drop
array aStats(6) //The array that will contain the amount of each item dropped

On button clicked
Call function “SlainBeast(100)”

Function SlainBest (Repetitions)
int BeastDrops = 0 //The variable that will keep the count of how many beasts did drop item
Text.text = “”
aStats().clear //Resets all the values contained in this array to make new statistics for the current batch
Repeat Repetitions times
int BeastType
int DropChance
int Rarity
BeastType = Random(6) //Since we have 6 beasts in our array
Rarity = aBeast(BeastType, BEAST_RARITY) //Get the rarity of items the beast should drop from the aBeast array
DropChance = ceil(random(100)) //Picks a number between 0 and 100)
Text.text = Text.text & loopindex & ” _ ” & aBeast(BeastType,BEAST_NAME) & “is slain”

If DropChance > aBeast(BeastType,BEAST_DROPRATE)
//The DropChance is bigger than the droprate for this beast
Text.text = Text.text & “.” & newline
//We stop here, this beast is considered to not have dropped an item.

If DropChance <= aBeast(BeastType,BEAST_DROPRATE)
Text.text = Text.Text & ” dropping ” //We will put some text to display what item was dropped
//On the other hand, DropChance is less or equal the droprate for this beast
aTemp(0) //We clear/clean the aTemp array in which we will push entries to determine what item type to drop
For a = 0 to aDrop.Width //We will loop through every elements of the aDrop array
aDrop(a,DROP_RATE) >= Rarity //When the item drop rate is greater or equal the expected Rarity
Push aTemp,a //We put the current a index in the temp array. We know that this index is a possible item type to drop
int DropType
DropType = random(aTemp.width) //The DropType is one of the indexes contained in the temporary array
Text.text = Text.text & aDrop(DropType, DROP_NAME) & “.” & newline //We display the item name that was dropped
//We do some statistics
aStats(DropType) = aStats(DropType) + 1
BeastDrops = BeastDrops + 1
TextStats.Text = BeastDrops & ” beasts dropped items.” & newline
For a = 0 to aStats.width //Display each item amount that was dropped
and aStats(a) > 0
TextStats.Text = TextStats.Text & aStats(a) & ” ” & aDrop(a,DROP_NAME) & ” “

首先,是用户行为:点其Slay 100 Beasts可以调用一个参数为100的函数,当然在真正的游戏中,你可能一次只会杀死一只怪物。

由此开始,调用SlainBeast函数。其目的展示一些文本,给予用户反馈让他们知道发生了什么情况。首先,它会清除BeastDrops变量以及aStats阵列。在真正的游戏中,你不可能会需要这些阵列。它也会清除Text,这样你就可以看到这个批量数据的结果。函数本身会创造三个数值变量:BeastType, DropChance和Rarity。

BeastType将成为我们涉及aBeast阵列的特定行,它实际上是玩家必须面对和杀戮的怪物类型。Rarity也同样取自aBeast阵列,它是该怪物应该掉落的道具稀有性,道具稀有值位于aBeast阵列中。

最后,DropChance则是我们随机从0到100挑选出来的一个数值(游戏邦注:多数编程语言都有一个从某个范围随机获取一个数值的函数,或者至少是从0到1获得一个随机数值,之后你可以将其简单地乘以100)。

在这种情况下,我们可以展示位于Text对象中的一点信息:我们已经知道会生成和杀死哪种怪物。所以,我们要将从aBeast阵列中随机挑选的当前BeastType的BEAST_NAME与当前的Text.text值连接起来。

接下来,我们必须确定某项道具是否应该掉落。我们可以通过对比来自aBeast阵列的DropChance值与BEAST_DROPRATE值来实现这一点。如果DropChance少于或者等同于这个值,我们就要掉落一个道具。

(注:你可以选择“多于或等同于这个值”的方法来编写这个函数,这只是一个数值和逻辑上的问题。但是,要保持算法的一贯性,不要半途更改逻辑——否则,你可能就会在调试或维护的时候产生问题。)

要用两行代码来确定某项道具是否掉落,首先:

DropChance > aBeast(BeastType,BEAST_DROPRATE)

这里的DropChance在少于或等同于当前BeastType中的DropRate,所以我们可以认为这意味着一项道具会掉落。为此,我们将运行当前BeastType“允许”掉落道具的Rarity对比,以及我们已经在aDrop表格中设置好的一些稀有值。

我们查看aDrop表格,查找每个索引以便找到其DROP_RATE是否大于或等同于Rarity。(记住,Rarity值越高,该道
具就越普遍)针对匹配该对比的每个索引,我们会将该索引推向一个临时阵列aTemp。

这DropChance远比DropRate更大,我们认为这意味着没有道具会掉落。由此开始,唯一展现的东西就是句子末尾的一个全角句号“.”在移向我们批次的下一个敌人之前,“[BeastType]被杀掉”。

另一方面:

DropChance <= aBeast(BeastType,BEAST_DROPRATE)

在循环的最后,我们应该至少在aTemp阵列中设置一个索引。(否则,我们就必须重新设计aDrop和aBeast表格了!)我们之后可以制作一个新的数值变量DropType,从aTemp阵列中随机挑选一个索引。这就是我们将会掉落的道具。

我们要在文本对象中添加道具名称,形成类似“BeastType被杀死,掉落一个DROP_NAME”的句子。之后,为了便于解释,我们要在多个变量统计表中添加一些数值(添加到aStats阵列和BeastDrop中)。

最后,在重复100次后,我们展示这些统计数据,怪物掉落道具的数量,以及每种道具掉落的数量。

另一个例子:形象化地掉落道具

让我们考虑另一个例子:

screenshot2(from gamedevelopment)

screenshot2(from gamedevelopment)

你可以从中看到,画面中产生了一个随机敌人。位于左侧的玩家角色可以创造一次发射攻击。当发射攻击命中敌人时,敌人就会死。

在这里,我们在之前的例子中所使用的相似函数可以决定敌人是否掉落了一些道具,并确定其掉落的道具类型。此时,它还会创造道具掉落的视觉形象,并更新屏幕底部的数值。

以下就是伪代码的部署:

CONSTANT ENEMY_NAME = 0
CONSTANT ENEMY_DROPRATE = 1
CONSTANT ENEMY_RARITY = 2
CONSTANT ENEMY_ANIM = 3
CONSTANT DROP_NAME = 0
CONSTANT DROP_RATE = 1
//Constants for the readability of the arrays

int EnemiesSpawned = 0
int EnemiesDrops = 0

array aEnemy(11,4)
array aDrop(17,2)
array aStats(17)
array aTemp(0)

On start of the project, we roll the data in aEnemy and aDrop
Start Timer “Spawn” for 0.2 second

Function “SpawnEnemy”
int EnemyType = 0
EnemyType = random(11) //We roll an enemy type out of the 11 available
Create object Enemy //We create the visual object Enemy on screen
Enemy.Animation = aEnemy(EnemyType, ENEMY_ANIM)
EnemiesSpawned = EnemiesSpawned + 1
txtEnemy.text = aEnemy(EnemyType, ENEMY_NAME) & ” appeared”
Enemy.Name = aEnemy(EnemyType, ENEMY_NAME)
Enemy.Type = EnemyType

Keyboard Key “Space” pressed
Create object Projectile from Char.Position

Projectile collides with Enemy
Destroy Projectile
Enemy start Fade
txtEnemy.text = Enemy.Name & ” has been vanquished.”

Enemy Fade finished
Start Timer “Spawn” for 2.5 seconds //Once the fade out is finished, we wait 2.5 seconds before spawning a new enemy at a random position on the screen
Function “Drop” (Enemy.Type, Enemy.X, Enemy.Y, Enemy.Name)

Function Drop (EnemyType, EnemyX, EnemyY, EnemyName)
int DropChance = 0
int Rarity = 0
DropChance = ceil(random(100))
Rarity = aEnemy(EnemyType, ENEMY_RARITY)
txtEnemy.text = EnemyName & ” dropped ”

If DropChance > aEnemy(EnemyType, ENEMY_DROPRATE)
txtEnemy.text = txtEnemy.text & ” nothing.”
//Nothing was dropped
If DropChance <= aEnemy(EnemyType, ENEMY_DROPRATE)
aTemp.clear/set size to 0
For a = 0 to aDrop.Width
and aDrop(a, DROP_RATE) >= Rarity
aTemp.Push(a) //We push the current index into the aTemp array as possible drop index

int DropType = 0
DropType = Random(aTemp.Width) //We pick what is the drop index amongst the indexes stored in aTemp
aStats(DropType) = aStats(DropType) + 1
EnemiesDrops = EnemiesDrops + 1
Create Object Drop at EnemyX, EnemyY
Drop.AnimationFrame = DropType
txtEnemy.Text = txtEnemy.Text & aDrop.(DropType, DROP_NAME) & “.” //We display the name of the drop
txtStats.text = EnemiesDrops & ” enemies on ” & EnemiesSpawned & ” dropped items.” & newline
For a = 0 to aStats.width
and aStats(a) > 0
txtStats.text = txtStats.Text & aStats(a) & ” ” & aDrop(a, DROP_NAME) & ” ”

Timer “Spawn”
Call Function “SpawnEnemy”

先分别来看看aEnemy和aDrop表格的内容:

aEnemy(from gamedevelopment)

aEnemy(from gamedevelopment)

aDrop table(from gamedevelopment)

aDrop table(from gamedevelopment)

与之前的例子不同,包含敌人的阵列命名为aEnemy,它还包括另一行数据ENEMY_ANIM,后者拥有敌人动画的名称。这样,当画面生成敌人时,我们就可以查看并自动化图像播放。

同样,aDrop现在包含16个而非6个元素,每个索引都涉及对象的动画帧——但我也可能拥有多个动画,就像针对敌人设置一样,需要确定其掉落道具是否也需要动画。

此时我们所需要的敌人和道具数量远超过之前的例子。但你可以看到,与掉落率和稀有值相关的数据仍然存在。有一个显著的区别就是,我们将计算是否存在掉落道具的函数所生成的敌人区别开了。这是因为,在真正的游戏中,敌人不会无动于衷地等着被玩家杀掉。

所以现在,我们有一个函数SpawnEnemy和另一函数Drop。Drop类似于我们在之前例子中处理道具掉落随机性的方式,但这次要用到一些参数:其中两个是屏幕上敌人的X和Y坐标,因为这正是我们想让道具生成掉落的地方,另一个参数就是EnemyType,这样我们就可以在aEnemy表格中查找敌人的名称,以及一系列角色名称,以便更快速地编写为玩家呈现的反馈。

Drop函数的逻辑与之前例子相似,变化最多的是我们呈现反馈的方式。这次我们不仅仅是呈现文本,我们还要在屏幕上生成一个对象,给予玩家一个视觉形象。

(注:为了在屏幕多个位置生成敌人,我使用了一个隐形对象Spawn作为参照,它可以持续左右移动。无论何时调用SpawnEnemy函数,它都会在Spawn对象当前坐标创造敌人,这样敌人就会出现在一系列水平位置上。)

最后要讨论的是何时调用Drop函数。我并不会直接在敌人死亡的时候触发它,而是在敌人淡出屏幕(这是敌人的死亡动画)时才执行这一操作。你当然可以在敌人仍在屏幕上可见的情况下调用掉落函数,这取决于你的游戏设计需求。

ScreenShot3(from gamedevelopment)

ScreenShot3(from gamedevelopment)

总结

在设计层面上看,让敌人掉落一些战利品可以为玩家提供对抗和摧毁敌人的动力。掉落的道具可以为玩家提供能量升级、状态或目标,可以是直接或间接的方式。

从执行层面上看,掉落道具可以通过一个由程序员来决定何时调用的函数来管理。该函数要根据杀死敌人类型来执行检查掉落道具稀有性的工作,也可以确定在屏幕何处生成道具。道具和敌人数据可以在类似阵列等数据结构中托管,并通过函数进行查找。

函数使用随机数字来确定掉落道具的频率和类型,由程序员来控制这些随机性,其查找的数据,并调这些掉落道具在游戏中的感觉。

希望本文能够让你更加理解如何在自己的游戏中设置怪物道具掉落的情况。

篇目3,如何在游戏设计中利用战利品掉落表

作者:Daniel Cook

许多游戏都带有战利品。通常情况下这些战利品的分配都是随机的。战利品掉落是特别常见的主题,但却也是每个设计师经常会觉得头疼的内容。以下是我在过去几年所遇到的最佳实践。

你的基本战利品表

这里的目标是为了基于特定几率掉落一组道具。假设当你打败一个敌人,你便有机会获得盾牌,稀有的剑,或者什么都没有。

例子

战利品表

道具:

名字:剑

重量:10

道具:

名字:盾

重量:40

道具:

名字:空

重量:50

设置

道具:你想要提供给玩家的一种道具。

战利品表:将一组道具放进战利品表中。这只是一部分道具。例如一个战利品表将包括:剑,盾,空。

重量:每个道具都带有掉落重量:从1到10000。例如一把剑的掉落率可能是10。

空道具:战利品表中会有一个道具是“空”,这意味着如果滚动到它,便不会掉落任何战利品。

loop drop(from 3dmgame)

loop drop(from 3dmgame)

滚动战利品

总概率:首先,计算战利品表中的所有重量。在上述例子中便是10+40+50=100。因为这些数值并不是百分比,所以它们并不需要加到100。

接下来分配每个道具的范围。剑=1至10,盾=11至50,空=51至100。

从1至100生成一个随机数。

将该数值与范围进行比较。这便能够决定到底会掉落哪种道具。

再次滚动:生成多个随机数值去模拟多次滚动。

所以玩家会如何看待它们?我们设置剑的掉落几率为10%,盾的掉落几率为40%,而什么都不会掉落的几率为50%。

作为设计师,我可以将空的重量改为100,而现在我将剑的掉落几率设为6.6%(10/150),盾的掉落几率为26%(40/150),什么都不会掉落的几率为66%(100/150)。

映射到其它常见的随机系统

这一系统只是在重申许多其它相似的随机性方法。这是训练你的设计师大脑在基于战利品表,纸牌或筛子上理解任何随机性问题间转换的有趣方法。

纸牌

想象你能够洗牌并获取的桥牌。

桥牌上的每种纸牌类型都是一种道具。

特定类型的纸牌数量便是道具的重量。

洗牌等同于为每种道具分配范围并生成随机数。

抽取纸牌等同于选择掉落的道具。

现在常见的桥牌都拥有52张牌,但如果是基于战利品表,你便可以不受约束地进行操作。你的桥牌拥有1000张各种类型的纸牌。或者它们可以提供与典型的扑克手所拥有的较小的桥牌。

筛子

筛子同样也能够映射到战利品表上。

每一个独立的筛子都是一张战利品表。

筛子的每一面(1至N)便等同于道具。

筛子的每一面都拥有重量“1”(除非你是在使用超重的筛子!)。

多次滚动筛子代表多次滚动同一个战利品表。所以2D6便等同于抽取一个带有6种道具的战利品表2次。

变量

既然我们定义了一个基本的战利品表,我们还可以做些什么?

变量:道具组合

你同样也可以掉落战利品组合。道具并不需要一定是单一的内容。例如我可以扩展它从而让玩家同时获得一个盾牌和一个生命药剂。

例子

战利品表

道具:

名字:剑

重量:10

道具:

名字:盾

名字:生命药剂 数值:2

重量:40

道具:

名字:空

重量:50

变量:总是掉落

常见的需求是标记一个道具从而提升它的掉落频率。这里存在一种惯例,即带有“-1”重量的道具将会更常掉落。

变量:可重复的随机性

有时候你会希望能够重复一个随机滚动。例如当一名玩家保存了游戏,并能在之后重新加载以避免糟糕的战利品掉落结果,这将导致非常折腾的玩家行为。而如果存在一种方法能够避免这种情况,所有玩家都会很高兴吧。

大多数临时的伪随机数生成程序都是使用一个种子值。只要你能够保存该种子值,你便能够再次运行随机数生成程序并获得同意的结果。

变量:无需改变而滚动

上述系统的问题在于玩家可能会一直滚到“空”。这也是玩家常常抱怨的结果。就像“我玩了3000多次却从未获得MegaGoldenLootGun!”。

在统计学中存在两种基本的抽样类型:

放回抽样:你将从列表中抽取数值然后在记录你所获得的数值后,你会将它们放回去。如此你便有可能在下次抽取时拥有同样的几率。

不放回抽样:你将从列表中抽取数值,并且在你记录之后便将其置于一边。如此你在下次抽取时抽到该道具的几率便会下降,而抽到剩下道具的几率便会增加。

《俄罗斯方块》便使用了不放回抽样。每种俄罗斯方块都有自己的战利品表。每次你获得一个特殊组块时,它便会被移出列表。这种方法能够保证你在长时间等待长方形组块时将能获得它。

以下是关于你在战利品表中如何执行不放回滚动。

当你滚动一个道具时,将其的重量减少1。这也等同于将它的范围和最大范围减去1。

确保在玩家下次滚动时他们的战利品表已经进行了修改。

变量:保证特殊的掉落道具

有时候不放回滚动不够快,而你却希望保证战利品的掉落。暴雪便保证了特定稀有道具的掉落从而让玩家无需长时间地刷道具。

你可以只是提升重量,但是随着玩家多次玩游戏,他们会感受到获得某些有保证的道具的低频率与获得一种道具慢慢提升的几率之间的明确区别。

以下是关于如何执行有保证的掉落战利品。

当你滚动任何无保证的道具时,减少X%无保证的道具重量。

X=100/在有保证的道具掉落前滚动的最大数量。

确保在玩家下次滚动时他们的战利品表已经进行了修改。

例子

假设你想要在5个回合后剑能够频繁掉落,尽管它只拥有10%的掉落几率。

如此X=100/5或20%

所以每次当你滚到剑时,盾的重量便会下降8(40*0.2),而空的重量会下降10(50*0.2)。

在5个回合后,所有其它道具的重量将变成0,剑便会拥有100%的掉落几率。

变量:分等级的战利品表

战利品表通常都是新资源的来源。然而你很容易进入一种情境,即你掉落了太多或太少特殊资源。这时候一些限制将很有帮助。

一种解决方法便是使用不放回的分等级的战利品表。当一种特殊资源用尽时,玩家将不再获得该资源。我们在每日货币奖励中便使用了这一方法。我们想要每天派发100个货币,并且不会超过这一数值。但是我们也想将其作为战利品系统的一部分。

创造两个表:奖励和每日货币。

让主要的战利品表参照每日货币表。

当选择每日货币时,滚动列表并明确你获得了多少货币。

例子

战力品表:奖励

道具:

名字:剑

重量:10

道具:

名字:每日货币

重量:40

道具:

名字:空

重量:50

战利品表:每日货币

类型:不放回

更新率:每日

道具:

名字:货币,数值:1

重量:10

道具:

名字:货币,数值:10

重量:4

道具:

名字:货币,数值:50

重量:1

在上述例子中,玩家有40%的机会获得货币。然后我们将滚动每日货币表并看看它们是否能够基于10次奖励每次1个货币,4次奖励每次10个货币以及1次奖励每次50个货币而在一天中获得最多的100个货币。

当每日货币战利品表空了时,它们只有在隔天更新时才会再次被填满。

变量:有条件的掉落

有时候你会想要测试是否应该基于一些外部变量去掉落道具。在《 Realm of the Mad God》中,我们便想要避免未创造任何伤害而杀死boss的“搭便车者”获得战利品。所以在战利品表中,我们添加了检查。如果滚动到战利品表中的一种有价值的道具,我们便会检查玩家对敌人所造成的伤害是否超过X%。

你可以基于玩家的级别或敌人的级别改变战利品的有效性。就像我更倾向于使用多个较小的战利品表,并且系统非常灵活,足以让你能够轻松地使用一些较大的列表和条件去创造数据。

变量:编辑器

你可以基于以下外部逻辑修改掉落物的数量或重量。例如擅长收集的玩家能够获得比不擅长收集的玩家2倍多的特殊掉落道具。或者你可以修改重量。较高级别的角色的所有道具可能拥有-50%的重量,这远低于他们的级别。

其它使用

掉落物列表通常是用于掉落战利品,但我们也可以在其它地方发现它们。

程序生成:使用列表去创造武器或角色。

AI:使用列表去选择行为,如攻击或移动。

这可能有点愚蠢,但的确存在一些更好的方式去创造AI!一种方式便是将随机性当成任何系统的一阶模型。人类大脑是如何创造系统模型?我们为系统创造了观察报告。并注意到这些观察值重复出现的频率和趋势。在之后我们开始理解“为什么”会发生某些情况以及每个部分之间的临时关系。

在物理学中,我们经常会开玩笑地说,为了创造一只奶牛模型,即一个复杂的有机体,我们需要做的第一步便是“想象一只球形的奶牛。”通过创造一个简单的模型,我们便能够以最低成本生成有用的见解。

很多时候,掉落表其实就是一个复杂系统的以人类为中心的近似值。对于许多系统,大多数玩家的移动都不会超过一个基本的概率理解,所以创造更复杂的模型只会浪费时间。有效的游戏设计是创造模型去最小化必要级别以创造出理想的游戏体验。

考虑:《龙与地下城》便基于必要的战利品掉落表创造了完整的宇宙。这就是专注于最小化系统。

战利品掉落表并不是你需要的唯一工具,但在很多情况下,它却是一种很有效的工具。

程序生成思维实验

以下是使用掉落表的简单程序生成系统。存在许多其它方式能够做到这点,但这却是最需要你进行思考的方法。让我们假设你想要创造一个程序生成敌人。

一开始先创造独特的敌人列表。也许你的敌人是由移动类型,攻击类型,防御类型以及财宝类型所组成。

为每种类型创造战利品表。

基于强度提供给战利品表中的每种道具能量值。例如,刀的攻击可能较弱,那么它的能量值便是5。而较大的铁锤的能量值为15。

创造另一个战利品表。这是各种属性的修改内容。例如,“强大”将为攻击增加20%的数值。你也可以将攻击设为“弱”,这将减少50%的数值。

现在让我们生成一个敌人

设定一个目标:为你的生成敌人设定一个目标能量。假设你想要一个拥有40能量值的敌人。

滚动:滚动每个部分并将其添加到列表上。

分数:添加所有的能量值去获得一个分数。

调整:如果这些部分的总和超过目标值,那就为较低的能量部分添加一个攻击或滚动。如果总和低于目标值,那就为较高的能量部分添加一个攻击或滚动。

重复:重复这一过程直到你到达一个预期的错误门槛(远离能量40)或者你耗尽了你想要消耗的迭代数。

现在你便拥有一个程序生成敌人。对于这一基本系统你可以进行多次调整,但它大多数情况下都是有用的。作为练习,你可以想想:

排除列表:如果选择了列表中的两个部分,那就丢掉敌人再次滚动。

多重限制:基于多个标准进行评判的部分。需要注意的是,当你添加更多限制时,你便更加不可能聚集多重结果。

结论

任何时候都会出现关于随机性的讨论,并且也有许多次要问题会发挥作用。我建议你们能够阅读以下内容:

http://www.lostgarden.com/2012/12/understanding-randomness-in-terms-of.html

http://www.lostgarden.com/2012/12/understanding-randomness-in-terms-of.html

抵抗教条式的随机性。作为一个受过良好教育的设计师,你的美学选择应该是基于亲手实践。这里存在的一个经验法则便是,在你成功使用一种设计工具创造出一些成功游戏之前,你不能轻易批评这种设计工具。

篇目4,促进角色创造&资源管理的游戏道具类型

作者:Koobazaur

什么是“工具”道具?

在本文中,我将把工具定义为一种可获得的道具,即提供给玩家一种全新且非战斗能力去通过障碍。例如撬锁工具将帮助玩家打开门,爪钩将帮助他们攀升到一个全新高度,或者氧气罩将让他们能够长时间待在水里。

工具=能力[-]障碍

deus-ex-multitool(from gamingpoint.org)

deus-ex-multitool(from gamingpoint.org)

需要注意的是我特别排除了战斗工具,如武器或盾牌,因为我想专注于那些用于打开新的前进方向或避免障碍(如守卫,所以就包含了潜行工具)的工具。我同样也排除了在传统RPG风格游戏中通过升级而获得的能力。然而,《生化奇兵》的补养药却包含在内,因为它们的功能与道具很像。

典例:《杀出重围》,《神偷》,《羞辱》,《塞尔达传说》,《银河战士》,《生化奇兵》

工具的类型

着眼于各种游戏,我总结出了工具的以下特征:

获取——玩家是如何获取工具

给予——在某些点上工具是自动给予玩家(如《神偷》中的lockpics)

可发现的——可以在游戏世界中的各种地方发现它们(如在《银河战士》,《塞尔达传说》和《杀出重围》中)

独有的选择——可以排除其它内容而选择它(如《杀出重围》中的AUG升级道具)

可购买的——可以通过使用某些货币进行购买(如在《生化奇兵》,《羞辱》中那样)

永久性——玩家可以使用工具多长时间

无限的——能够无限期地使用(如《神偷》的lockpicks,《银河战士》)

一次性——使用数量有限,如果想再次使用就需要再次获取(如《杀出重围》中的多功能工具)

基于燃料——能够无限次地使用,但要求某些燃料或弹药的补给(如《生化奇兵》的补养药,《杀出重围》的AUG)

可升级性——可改变或完善?

静态的——始终保持一样(如在《塞尔达传说》和《银河战士》那样)

可升级的(通过货币)——通过使用一些货币而获得省级(如在《羞辱》中)

以下是一些例子:

super-metroid-items(from gamingpoint.org)

super-metroid-items(from gamingpoint.org)

《杀出重围》中的多功能工具=可发现的+一次性+静态的

《杀出重围》中的AUG=可发现的+基于燃料+可升级的(通过货币)

《羞辱》中的能量=可购买的(通过货币)+基于燃料+可升级的

《神偷》中的绳索或青苔箭=可发现的/可购买的+一次性+静态

《银河战士》的能量=可发现的+无限的+静态的

当然,游戏也可以基于单一规格混合多种道具特征——就像《生化奇兵》中的某些补养药便是给予的(如前面几个)而有些则是可发现的(非必要的)。

我能想到的最复杂的例子便是《杀出重围》的AUG——它们不只是要求燃料的可发现且一次性的选择资源,同时也可以通过完全不同的资源获得升级!

通过工具进行角色定制

工具能够提供最基本的角色创造方法。工具的实用性,永久性,和可升级性定义了玩家选择的灵活性和意义,以及他们的角色将具有多大的差别。

karas38_tempering_with_gears(from gamingport.org)

karas38_tempering_with_gears(from gamingport.org)

举个例子来说,因为工具的规格,《银河战士》具有非定制的线性角色创造—-你将在每次游戏的几乎同一个点上获得工具,并且不能在之后升级它们。基于所有目的,武器例外,“角色创造”总是会基于同样的方式发展。相反地,《杀出重围》的AUG更加定制化且两级化—-它们会要求你在两个会对之后游戏发展造成重要影响的内容中做出选择。

《神偷》的箭就是个有趣的例子——你可以在每个阶段购买它们,并且将看到各种不同的数量,在每个任务结束前你都可以自由地进行购买。《生化奇兵》允许玩家交换补养药,但限制了升级它们的资源。

试验

此外,工具的规格也影响了玩家能够做试验的次数。在此他们会提出一个问题“在致力于某一工具前我是否能够给予基本形式去测试每种工具?”

在《杀出重围》中,一旦玩家做出一个增强选择,他便丧失了一个机会——他将永远不会知道自己错过了什么。所以你并不能真正进行“试验”。

另一方面,《神偷》在某些点上提供给你至少每种类型的箭中的一个,如此你便能够在花钱辛苦获得道具前对它们进行试验。

我们还可以看到《生化奇兵》采取了一种中立的方法,即基于基本形式提供给你几乎每一种补养药,让你决定升级哪一个。《羞辱》也是如此——玩家并不是基于每种能力开始游戏,但是他们却可以通过游戏赚到足够的货币去获取基于基本形式的每种工具。

bioshock-loot-glint(from gamingpoint.org)

bioshock-loot-glint(from gamingpoint.org)

作为资源的工具

在我看来,资源也是一种你能够发现,获得或购买的工具或货币/燃料,它们是有限供应或者是一次性的。包括一般弹药,《杀出重围》中的电池或《生化奇兵》中的药剂。

当工具变成资源时情况会变得更有趣,它将引进一种管理和探索元素。例如《杀出重围》中的多功能工具或《神偷》中的绳索/青苔箭便是如此。

相反地,《塞尔达传说》中的大多数工具(游戏邦注:如爪钩或盾牌)便不属于资源—-它们在游戏世界中只有一个,并且不要求管理。炸弹则是例外,它们在游戏世界中是一次性的且可发现的。

thief-dark-project-rope-arrows(from gamingpoint.org)

thief-dark-project-rope-arrows(from gamingpoint.org)

资源管理和探索

当工具作为或依赖于资源时,它们将能提升游戏玩法的深度——基于你的游戏表现以及做出何种选择,你将不断“获得”并“失去”能力。

再一次地,《杀出重围》的多功能工具和lockpick,《神偷》的箭或《塞尔达传说》的炸弹都是很棒的例子——它们都是探索的一种奖励。

相反地,《银河战士》的工具或《塞尔达传说》的爪钩虽然能够推动探索但却不能基于自身去“鼓励”它——你必须找到它们去推动股市的发展,所以你不能从技术上进行“探索”,只能走在预先设定好的设计之路。

平衡道具的难度

适当地设计并平衡工具很难。解释并验证每一个选择也很难。《杀出重围》中的游泳升级或水中呼吸器便是很好的例子,很少用例是在否定它们。襄樊地,你也会无法忍受某种过度设计的工具—-《羞辱》中一种闪光能够让你非常有效地完成每一个任务,从而导致其它工具变得无用。

此外,当工具是资源或依赖于资源时,我们将很难去平衡它们的数量,特别是考虑到玩家技能的不同以及随机探索的能力时。一位资深的《神偷》玩家将能够基于过多的箭而完成一个任务。但新手却只能勉强维持下来。不同的难度级别能够缓解这种情况,但每个设置中仍然存在着设计问题。

篇目1篇目2篇目3篇目4(本文由游戏邦编译,转载请注明来源,或咨询微信zhengjintiao)

篇目1,The Role of Crafting (Part I)

Sande

In this article, game designer Brendon Trombley looks at the various ways crafting adds to player experience in MMOs and how developers can use an understanding of these roles to build better crafting systems.

Since early on, crafting has been a major staple in the design of MMORPGs. Without some sort of crafting system, most online worlds would seem empty, nothing more than hack’n’slash mechanics wrapped around a leveling curve. Yet, for such a peaceful pastime, crafting has a sordid history. Past games have been fraught with resource grinds, wasted components, forced participation in crafting systems, useless recipes, and sadly, sometimes even useless tradeskills entirely.

Crafting can even be a source of strain between player and developer. If the balance of power between player-created and dropped items isn’t carefully calibrated, crafters and non-crafters can feel useless and neglected compared to the other and will blame the developer.

How can all this strife be avoided? A source of conflict is that crafting’s role for developers can differ greatly from its role for players. Reducing this conflict by addressing the needs of both parties is a good step towards a designing a successful crafting system.

So what’s the role of crafting for players?

It breaks up the grind

This is probably the single most important aspect for players. They crave variety, and if they get bored with fighting monsters all day and don’t have anything else to do, they might as well just log out. Crafting offers a satisfying alternate activity to the regular pattern of kill-loot-kill by giving them a separate form of character advancement.

Keeping this in mind, developers should keep crafting accessible to all players. That means allowing players to take both combat and crafting skills without negatively impacting each other. Players shouldn’t have to choose between the two. Crafting should also be kept engaging and include interesting choices, whether in the selection of materials, or the choice of product to make. No one wants to replace a grind with an even more tedious grind.

It’s a source of items and cash

Players derive a great source of satisfaction earning a tidy profit from the items they personally created. Even better, if they can personally use the items, they get to feel self-reliant seeing their own efforts augment their combat skills.

For developers, this means of course that crafted items must be useful to convince players to spend their hard-earned cash on them. This is where the tricky balance must be struck between crafting and dropped items. A great strategy is to include non-equipment niches in crafting. For instance, crafted consumables like potions ensure a steady demand, and equipment augments such as slot gems or enchantments allow crafters to increase the power of items, dropped or crafted. In this way, the best-equipped character is one who has taken advantage of both styles of play.

It appeals to constructive and social play styles

Some players don’t find rampaging around the world, destroying, killing, and pillaging everything they encounter terribly fun. Some would much rather hang out in cities, socialize, and create cool items. They enjoy the interdependency and community that crafting promotes, sharing or creating materials with their guildmates and friends.

These players are an important part of the player base, and retaining them should be prioritized properly. Developers should keep them in mind by creating a crafting system that is engaging and fun, but not demanding of all the player’s focus. Some systems have included real-time combat-like actions to replace boring progress bars. This could be a mistake, because it forces the player to stop socializing while they craft. Instead, crafting should be more cerebral, taken at one’s own pace. The interesting action should happen before the player clicks ‘create’, such as in the choosing and collecting of specific resources, materials, or recipes to use.

It creates greater customization

This is a feature of crafting that can appeal to different player types in different ways. For those interested in character-building, taking a tradeskill is a way to further differentiate their characters. For social players, making decorative items allows them to tailor the appearance of their characters and environments. For the combat strategists, the ability to customize their equipment allows them to maximize their stats and effectiveness in battle.

Fostering customization is a facet that is often neglected by developers, being as it tends to come with extra overhead cost. However, the loss of the potential benefits should not be taken lightly. When crafters can tailor the stats or appearance of their items, it greatly increases their engagement in the activity. Dropped items, by their nature, come as they are. When recipes also have locked-in stats, it seems to be a missed opportunity to differentiate those items from drops.(source:gamedesignaspect)

So what’s the role of crafting for developers?

It adds legitimacy to the game

It’s a sad fact that many developers include crafting in their game simply because they are expected to by the outside world. This leads to poorly designed systems that are added seemingly as an afterthought and can end up dragging the game down.

When designing in a genre where player retention is vitally important, developers should think carefully about their audience while considering crafting systems. If the game is meant to appeal to a specific player type, for instance casual or combat-oriented players, it may be possible to leave out crafting but include features that create similar benefits. Otherwise, if it’s decided that including crafting is indeed required, it should have the proper attention and budget allocated to it or players won’t use it, making it a wasted effort.

It gets players to spend more time playing

The goal of any system in an MMO should be to engage players and keep them willing to play the game over a long period of time. Crafting offers a form of character advancement that, when combined with combat advancement, creates plenty of motivation to spend time in the game.

When taken too far, this concept may lead to the addition of artificial time-sinks to crafting systems. Unnecessary grinding, long progress bars, and harsh penalties for failure may require the player to spend more time crafting, but at the cost of increased player frustration. Too much frustration, and there’s a chance they will stop the activity entirely. Avoid this by ensuring there’s always a sense of progress for the player. Perhaps the items they grind are ingredients for recipes down the line or components for other tradeskills. Perhaps advancement is based on experience rather than random skill-point increases.

It creates interdependency between players

MMOs, being multiplayer games, should of course include mechanics that foster player interaction and cooperation. Crafting systems are an effective way to do this. They allow players to create and enhance items for others, and promote the sharing of resources and materials.

A great way to create more cooperation between players is to include recipes with rare dropped components or ingredients crafted from other tradeskills. However, these recipes should be special exceptions that produce extra-useful items. Don’t require too much collaboration for basic items and especially not for regular advancement in the skill, or players will become frustrated at the extra time and money costs.

It promotes a strong economy

A strong economy increases player engagement and, if the developer so desires, can be a source of income if real-money trading of in-game currency is allowed. Crafting is a major source of trade between players. Additionally, it creates value in all those skins, fangs, gems, and other drops that would otherwise be merchant fodder. Merchant-bought components can help remove currency from a mostly positive-sum money system, reducing inflation.

Use crafting to promote the economy by first and foremost making the results of recipes worthwhile for all kinds of players. Then, create a variety of sources for ingredients: merchants, drops (rare and common), resource nodes, and other recipes. Create demand by designing ingredients to be useful in multiple recipes, and maximize the number of drops that are useful in at least one recipe.

Final thoughts

This is by no means a comprehensive list of all the ways to create a successful crafting system. Nor should all the tips here be included in the same game. Developers should instead prioritize their goals while thinking carefully about the goals of their audience. That way, they can craft a system that satisfies the needs of everyone as best they can, creating a stronger game in the end.

篇目2,How to Code Monster Loot Drops

by Kyatric25

A common mechanic in action games is for enemies to drop some kind of item or reward upon dying. The character can then collect this loot to gain some advantage. It is a mechanic that is expected in a lot of games, like RPGs, since it gives the player an incentive to get rid of the enemies—as well as a small blast of endorphins when discovering what the immediate reward is for doing so.

In this tutorial, we’ll review the inner workings of such a mechanic and see how to implement it, whatever the type of game and the coding tool/language you might be using.

The examples I use to demonstrate this were made using Construct 2, a HTML5 game making tool, but are in no way specific to it. You should be able to implement the same mechanic whatever your coding language or tool is.

The examples were made in r167.2 and can be opened and edited in the free version of the software. You can download the latest version of Construct 2 here (since I started writing this article, at least two newer versions have been released) and mess around with the examples to your liking. The example CAPX source files are attached to this tutorial in the zip file.

The Basic Mechanic

Upon an enemy’s death (so, when its HP is less than or equal to zero) a function is called. The role of this function is to determine whether there is a drop or not, and, if so, the kind of drop it should be.

The function can also handle the creation of the visual representation of the drop, spawning it at the former screen coordinates of the enemy.

Consider the following example :

Click the Slay 100 Beasts button. This will execute a batch process that creates 100 random beasts, slays them, and displays the result for each beast (that is, whether the beast drops an item, and, if so, what kind of item). Statistics at the bottom of the screen display how many beasts dropped items, and the how many of each type of item was dropped.

This example is strictly text to show the logic behind the function, and to show that this mechanic can be applied to any type of game, whether it is a platformer on which you stomp on the enemies, or a top-down view shooter, or an RPG.

Let’s look at how this demo works. First, the beasts and drops are each contained in arrays. Here’s the beast array:

Index (X)
Name (Y-0)
Drop rate (Y-1)
Item rarity (Y-2)
0 Boar 100 100
1 Goblin 75 75
2 Squire 65 55
3 ZogZog 45 100
4 Owl 15 15
5 Mastodon 35 50

And here’s the drops array:
Index (X)
Name (Y-0)
Item rarity (Y-1)
0 Lollipop 75
1 Gold 50
2 Rocks 95
3 Jewel 25
4 Incense 35
5 Equipment 15

The X value (the Index column) for the array acts as a unique identifier for the beast or item type. For example, the beast of index 0 is a Boar. The item of index 3 is a Jewel.

These arrays act as lookup tables for us, containing the name or type of each beast or item, as well as other values that will allow us to determine the rarity or the drop rate. In the beast array, there are two more columns after the name:

Drop rate is how likely the beast is to drop an item when slain. For example, the boar will have a 100% chance to drop an item when killed, whereas the owl will have a 15% chance to do the same.

Rarity defines how uncommon the items that can be dropped by this beast are. For example, a boar will be likely to drop items of a rarity value of 100. Now, if we check the drops array, we can see that the rocks is the item with the biggest rarity (95). (Despite the rarity value being high, due to the way I programmed the function, the bigger the rarity number is, the more common the item is. It has more chances to drop the rocks than an item with a lower rarity value.)

And that’s interesting to us from a game design perspective. For the balance of the game, we don’t want the player to get access to too much equipment or too many high-end items too soon—otherwise, the character might get overpowered too early, and the game will be less interesting to play.

These tables and values are just examples, and you can and should play with and adapt them to your own game system and universe. It all depends on the balancing of your system. If you want to learn more on the subject of balancing, I recommend checking out this series of tutorials: Balancing Turn-Based RPGs.

Let’s now look over the (pseudo)code for the demo:

CONSTANT BEAST_NAME = 0
CONSTANT BEAST_DROPRATE = 1
CONSTANT BEAST_RARITY = 2
CONSTANT DROP_NAME = 0
CONSTANT DROP_RATE = 1
//Those constants are used for a better readability of the arrays

On start of the project, fill the arrays with the correct values
array aBeast(6,3) //The array that contains the values for each beast
array aDrop(6,2) //The array that contains the values for each item
array aTemp(0) //A temporary array that will allow us what item type to drop
array aStats(6) //The array that will contain the amount of each item dropped

On button clicked
Call function “SlainBeast(100)”

Function SlainBest (Repetitions)
int BeastDrops = 0 //The variable that will keep the count of how many beasts did drop item
Text.text = “”
aStats().clear //Resets all the values contained in this array to make new statistics for the current batch
Repeat Repetitions times
int BeastType
int DropChance
int Rarity
BeastType = Random(6) //Since we have 6 beasts in our array
Rarity = aBeast(BeastType, BEAST_RARITY) //Get the rarity of items the beast should drop from the aBeast array
DropChance = ceil(random(100)) //Picks a number between 0 and 100)
Text.text = Text.text & loopindex & ” _ ” & aBeast(BeastType,BEAST_NAME) & “is slain”

If DropChance > aBeast(BeastType,BEAST_DROPRATE)
//The DropChance is bigger than the droprate for this beast
Text.text = Text.text & “.” & newline
//We stop here, this beast is considered to not have dropped an item.

If DropChance <= aBeast(BeastType,BEAST_DROPRATE)
Text.text = Text.Text & ” dropping ” //We will put some text to display what item was dropped
//On the other hand, DropChance is less or equal the droprate for this beast
aTemp(0) //We clear/clean the aTemp array in which we will push entries to determine what item type to drop
For a = 0 to aDrop.Width //We will loop through every elements of the aDrop array
aDrop(a,DROP_RATE) >= Rarity //When the item drop rate is greater or equal the expected Rarity
Push aTemp,a //We put the current a index in the temp array. We know that this index is a possible item type to drop
int DropType
DropType = random(aTemp.width) //The DropType is one of the indexes contained in the temporary array
Text.text = Text.text & aDrop(DropType, DROP_NAME) & “.” & newline //We display the item name that was dropped
//We do some statistics
aStats(DropType) = aStats(DropType) + 1
BeastDrops = BeastDrops + 1
TextStats.Text = BeastDrops & ” beasts dropped items.” & newline
For a = 0 to aStats.width //Display each item amount that was dropped
and aStats(a) > 0
TextStats.Text = TextStats.Text & aStats(a) & ” ” & aDrop(a,DROP_NAME) & ” ”

First, the user action: clicking on the Slay 100 Beasts button. This button calls a function with a parameter of 100, just because 100 feels like a good number of enemies to slay. In a real game, it’s more likely that you will slay beasts one by one, of course.

From this, the function SlainBeast is called. Its purpose is to display some text to give the user feedback on what happened. First, it cleans up the BeastDrops variable and aStats array ,which are used for the statistics. In a real game, it’s unlikely you will need those. It cleans the Text as well, so that a new 100 lines will be displayed to see the results of this batch. In the function itself, three numeric variables are created: BeastType, DropChance, and Rarity.

BeastType will be the index we use to refer to a specific row in the aBeast array; it’s basically the kind of beast that the player had to face and kill. Rarity is taken from the aBeast array as well; it’s the rarity of the item this beast should drop, the value of the Item rarity field in the aBeast array.

Finally, DropChance is a number we randomly pick between 0 and 100. (Most coding languages will have a function to get a random number from a range, or at least to get a random number between 0 and 1, which you could then simply multiply by 100.)

At this point, we can display our first bit of information in the Text object: we already know what kind of beast spawned and was slain. So, we concatenate to the current value of Text.text the BEAST_NAME of the current BeastType we’ve randomly picked, out of the aBeast array.

Next, we have to determine whether an item shall be dropped. We do so by comparing the DropChance value to the BEAST_DROPRATE value from the aBeast array. If DropChance is less than or equal to this value, we drop an item.

(I decided to go for the “less than or equal to” approach, having been influenced by these live role players using the D&D King Arthur: Pendragon set of rules regarding dice rolls, but you could very well code the function the other way around, deciding that drops will only occur when “greater or equal”. It’s just a matter of numeric values and logic. However, do stay consistent all through your algorithm, and don’t change the logic halfway—otherwise, you could end up with issues when trying to debug or maintain it.)

So, two lines determine whether an item is dropped or not. First:
1

DropChance > aBeast(BeastType,BEAST_DROPRATE)

Here, DropChance is greater than the DropRate, and we consider this to mean that no item is dropped. From there on, the only thing displayed is a closing “.” (full stop) that ends the sentence, “[BeastType] was slain.”, before moving on to the next enemy in our batch.

On the other hand:
1

DropChance <= aBeast(BeastType,BEAST_DROPRATE)

Here, DropChance is less than or equal to the DropRate for the current BeastType, and so we consider this to mean that an item is dropped. To do so, we will run a comparison between the Rarity of item that the current BeastType is “allowed” to drop, and the several rarity values we have set up in the aDrop table.

We loop through the aDrop table, checking each index to see whether its DROP_RATE is greater than or equal to Rarity. (Remember, counter-intuitively, the higher the Rarity value is, the more common the item is) For each index that matches the comparison, we push that index into a temporary array, aTemp.

At the end of the loop, we should have at least one index in the aTemp array. (If not, we need to redesign our aDrop and aBeast tables!). We then make a new numeric variable DropType that randomly picks one of the indices from the aTemp array.; this will be the item we drop.

We add the name of the item to our Text object, making the sentence to something like “BeastType was slain, dropping a DROP_NAME.”. Then, for the sake of this example, we add some numbers to our various statistics (in the aStats array and in BeastDrops).

Finally, after the 100 repetitions, we display those statistics, the number of beasts (out of 100) that dropped items, and the number of each item that was dropped.
Another Example: Dropping Items Visually

Let’s consider another example:

Press Space to create a fireball that will kill the enemy.

As you can see, a random enemy (from a bestiary of 11) is created. The player character (on the left) can create a projectile attack. When the projectile hit the enemy, the enemy dies.

From there, a similar function to what we’ve seen in the previous example determines whether the enemy is dropping some item or not, and determine what the item is. This time, it also creates the visual representation of the item dropped, and updates the statistics at the bottom of the screen.

Here is an implementation in pseudocode :

CONSTANT ENEMY_NAME = 0
CONSTANT ENEMY_DROPRATE = 1
CONSTANT ENEMY_RARITY = 2
CONSTANT ENEMY_ANIM = 3
CONSTANT DROP_NAME = 0
CONSTANT DROP_RATE = 1
//Constants for the readability of the arrays

int EnemiesSpawned = 0
int EnemiesDrops = 0

array aEnemy(11,4)
array aDrop(17,2)
array aStats(17)
array aTemp(0)

On start of the project, we roll the data in aEnemy and aDrop
Start Timer “Spawn” for 0.2 second

Function “SpawnEnemy”
int EnemyType = 0
EnemyType = random(11) //We roll an enemy type out of the 11 available
Create object Enemy //We create the visual object Enemy on screen
Enemy.Animation = aEnemy(EnemyType, ENEMY_ANIM)
EnemiesSpawned = EnemiesSpawned + 1
txtEnemy.text = aEnemy(EnemyType, ENEMY_NAME) & ” appeared”
Enemy.Name = aEnemy(EnemyType, ENEMY_NAME)
Enemy.Type = EnemyType

Keyboard Key “Space” pressed
Create object Projectile from Char.Position

Projectile collides with Enemy
Destroy Projectile
Enemy start Fade
txtEnemy.text = Enemy.Name & ” has been vanquished.”

Enemy Fade finished
Start Timer “Spawn” for 2.5 seconds //Once the fade out is finished, we wait 2.5 seconds before spawning a new enemy at a random position on the screen
Function “Drop” (Enemy.Type, Enemy.X, Enemy.Y, Enemy.Name)

Function Drop (EnemyType, EnemyX, EnemyY, EnemyName)
int DropChance = 0
int Rarity = 0
DropChance = ceil(random(100))
Rarity = aEnemy(EnemyType, ENEMY_RARITY)
txtEnemy.text = EnemyName & ” dropped ”

If DropChance > aEnemy(EnemyType, ENEMY_DROPRATE)
txtEnemy.text = txtEnemy.text & ” nothing.”
//Nothing was dropped
If DropChance <= aEnemy(EnemyType, ENEMY_DROPRATE)
aTemp.clear/set size to 0
For a = 0 to aDrop.Width
and aDrop(a, DROP_RATE) >= Rarity
aTemp.Push(a) //We push the current index into the aTemp array as possible drop index

int DropType = 0
DropType = Random(aTemp.Width) //We pick what is the drop index amongst the indexes stored in aTemp
aStats(DropType) = aStats(DropType) + 1
EnemiesDrops = EnemiesDrops + 1
Create Object Drop at EnemyX, EnemyY
Drop.AnimationFrame = DropType
txtEnemy.Text = txtEnemy.Text & aDrop.(DropType, DROP_NAME) & “.” //We display the name of the drop
txtStats.text = EnemiesDrops & ” enemies on ” & EnemiesSpawned & ” dropped items.” & newline
For a = 0 to aStats.width
and aStats(a) > 0
txtStats.text = txtStats.Text & aStats(a) & ” ” & aDrop(a, DROP_NAME) & ” ”

Timer “Spawn”
Call Function “SpawnEnemy”

Take a look at the contents of the aEnemy and aDrop tables, respectively:

Index (X)
Name (Y-0)
Drop rate (Y-1)
Item rarity (Y-2)
Animation name (Y-3)
0 Healer Female 100 100 Healer_F
1 Healer Male 75 75 Healer_M
2 Mage Female 65 55 Mage_F
3 Mage Male 45 100 Mage_M
4 Ninja Female 15 15 Ninja_F
5 Ninja Male 35 50 Ninja_M
6 Ranger Male 75 80 Ranger_M
7 Townfolk Female 75 15 Townfolk_F
8 Townfolk Male 95 95 Townfolk_M
9 Warrior Female 70 70 Warrior_F
10 Warrior Male 45 55 Warrior_M
Index (X)
Name (Y-0)
Item rarity (Y-1)
0 Apple 75
1 Banana 50
2 Carrot 95
3 Grape 85
4 Empty potion 80
5 Blue potion 75
6 Red potion 70
7 Green potion 60
8 Pink Heart 65
9 Blue pearl 15
10 Rock 100
11 Glove 25
12 Armor 30
13 Jewel 35
14 Mage Hat 65
15 Wood shield 85
16 Iron axe 65

Unlike the previous example, the array that contains the enemy data is named aEnemy and contains one more row of data, ENEMY_ANIM, which has the name of the enemy’s animation. This way, when spawning the enemy, we can look this up and automate the graphical display.

In the same vein, aDrop now contains 16 elements, instead of six, and each index refers to the animation frame of the object—but I could have had several animation as well, as for the enemies, if the dropped items were to be animated.

This time, there are far more enemies and items than in the previous example. You can see, though, that the data regarding drop rates and rarity values is still there. One notable difference is that we have separated the spawning of the enemies from the function that calculates if there is a drop or not. This is because, in a real game, enemies would likely do more than just wait on screen to be slain!

So now we have a function SpawnEnemy and another function Drop. Drop is pretty similar to how we handled the “dice roll” of our item drops in the previous example, but takes several parameters this time: two of these are the X and Y coordinates of the enemy on screen, since that’s the place where we will want to spawn the item when there is a drop; the other parameters are the EnemyType, so we can look up the name of the enemy in the aEnemy table, and the name of the character as a string, to make it quicker to write the feedback we want to give to the player.

The logic of the Drop function is otherwise similar to the previous example; what mostly changes is the way we display feedback. This time, instead of just displaying text, we also spawn an object on screen to give a visual representation to the player.

(Note: To spawn the enemies on several position on screen, I used an invisible object, Spawn, as reference, which continually moves left and right. Whenever the SpawnEnemy function is called, it creates the enemy at the current coordinates of the Spawn object, so that the enemies appear and a variety of horizontal locations.)

One last thing to discuss is when exactly the Drop function is called. I don’t trigger it directly upon an enemy’s death, but after the enemy has faded away (the enemy’s death animation). You can of course call for the drop when the enemy is still visible on screen, if you prefer; once again, that is really down to your game design.

Conclusion

On a design level, having enemies drop some loot gives an incentive to the player to confront and destroy them. The items dropped allow you to give power-ups, stats, or even goals to the player, whether in a direct or indirect way.

On an implementation level, dropping items is managed through a function that the coder decides when to call. The function does the job of checking the rarity of the items that should be dropped according to the type of enemy killed, and can also determine where to spawn it on screen if and when needed. The data for the items and enemies can be held in data structures like arrays, and looked up by the function.

The function uses random numbers to determine the frequency and type of the drops, and the coder has control over those random rolls, and the data it looks up, to adapt the feel of those drops in the game.

I hope you enjoyed this article and have a better understanding of how to make your monsters drop loots in your game. I’m looking forward to seeing your own games using that mechanic.

篇目3,Loot drop best practices

by Daniel Cook

Many games have loot. Usually this drops randomly. Loot drops are a pretty mundane topic, but one that almost every designer runs into at some point. Here are some best practices I’ve encountered over the years. Many thanks to everyone who contributed to these tips and tricks.

Your basic loot table

The goal is to drop some set of items at a given probability. Let’s say when you defeat an enemy, you have a chance of getting shield, a rare sword or nothing at all.

Example

lootTable
item:
name: sword
weight: 10
item:
name: shield
weight: 40
item:
name: null
weight: 50

Setup

Item: An item is something you want give the player.

Loot Table: A set of items is put into a loot table. This is just a bucket of items. For example a loot table might include: Sword, Shield, Null.

Weight: An item has a drop weight: 1 to 10,000. For example a sword might have a drop rate of 10.

Null items: One of the items in the loot bucket is ‘null’ which means if that is rolled, no loot is given

Rolling for loot

Total probability: First, sum all the weights in the bucket. In the example above, that’s 10+40+50 = 100. They don’t need to add up to 100 since these aren’t percentages.

Next assign each item a range. Sword = 1-10, Shield = 11 to 50, Null = 51 to 100

Generate a random number from 1 to 100.

Compare that number to the ranges. That’s the item that drops.

Reroll: Generate multiple random numbers to simulate multiple rolls.

So what does this look like to the player? We’ve got a 10% chance of dropping a sword, a 40% chance of dropping a shield and a 50% chance of getting nothing.

As the designer, I could go in and change Null’s weight to 100 and now I’ve got a 6.6% (10/150) chance of dropping a sword, a 26% (40/150) chance off dropping a shield and a 66% (100/150) chance of dropping nothing.

Mapping onto other common random systems

This system is a simple restating of many other familiar methods of randomness. It is a fun superpower to train your designer brain to be able to switch between understanding any randomness issue in terms of loot tables, cards or dice.

Cards

Imagine deck of cards that you can shuffle and draw from.

Each type of card in the deck is an item.

The number of cards of a given type is that item’s weight

Shuffling the deck is equivalent to assigning each item to a range and generating a random number.

Drawing a card is the equivalent of selecting the item that drops.

Now a normal deck of cards has 52 cards, but with loot tables, you don’t need to operate with that constraint. Your decks could have 1000′s of cards and a vast array of types. Or they could have tiny decks that are the equivalent of a typical poker hand.

Dice

Dice also map onto loot tables.

Each individual dice is a loot table.

The sides (1-N) are items (labeled 1 through N)

Each side gets a weight of ‘1’. (Unless you are using weighted dice!)

Multiple dice can be represented as rolling the same loot table multiple times. So 2D6 is the equivalent of sampling a 6 item loot table twice.

Variations

Now that we’ve defined a basic loot table, what else can we do with it?

Variation: Items sets

You can also drops sets of loot. An item doesn’t need to be a single thing. For example, I could extend it so that the players gets a shield and a health potion if that option is selected.

Example

lootTable

item:
name: sword
weight: 10
item:
name: shield
name: healthPotion number: 2
weight: 40
item:
name: null
weight: 50

Variation: Always drop

A common need is to flag an item so it always drops. One convention is that items with weight ‘-1′ always drop.

Variation: Repeatable randomness

Sometimes you want to be able to repeat a random roll. For example, when a player saves a game and then is able to reload to avoid a bad loot drop, it can lead to very grindy player behavior. If there is an exploit that ruins the game for them, most will happily go for it.

Most contemporary pseudo random number generators use a seed value. As long as you can save that seed value, you can run the random number generator again and get the same result.

Variation: Rolling without replacement

The problem with the system above is that players may, through chance alone, always roll ‘null’. This is a common complaint by players. “I played that encounter 3000 times and never got the MegaGoldenLootGun!” This can happen.

In statistics, there are two fundamental types of sampling:

Sampling with replacement: You pull the numbers out of the bucket and then after you’ve recorded what you got, you put them back in. So you have the same chance of getting the same thing again in the next draw.

Sampling without replacement: You pull the item out of the bucket and once you’ve recorded it, you set it aside. You have a lower chance of getting that item again and thus a higher chance of getting the remaining items.

Tetris uses sampling without replacement. Each set of Tetris pieces is in a loot table. Every time you get a specific piece, it is removed from the bucket. That way they guarantee that you’ll always get a long piece if you wait long enough.

Here’s how you implement rolling without replacement in a loot table.

When you roll an item, reduce its weight by 1. This shorten its range by 1 and shortens the max range by 1 as well.

Keep the player’s modified loot table around for the next time you roll.

Variation: Guaranteeing specific drops

Sometimes even rolling without replacement isn’t fast enough and you want to guarantee a loot drop. Blizzard does this for certain rare drops so that players don’t grind for very long times.

You could just increase the weight, but a low chance of getting something with a guarantee can feel very different over multiple plays than a slowly increasing chance of getting an item.

Here’s how you implement guaranteed loot drops.

When you roll any non-guaranteed item, reduce all non-guaranteed items weight by X%

X = 100 / Max number of rolls you before the guaranteed items drop.

Keep the player’s modified loot table around for the next time you roll.

Example

Suppose you want the sword to always drop after 5 turns even though it it only has a 10% chance of dropping.

So X = 100 / 5 or 20%.

So every time you don’t roll the Sword, the weight for the Shield drops 8 (40*0.2) and the weight for null drops 10 (50*0.2)

After 5 turns, the weight for all the other items will be 0 and the sword will have a 100% chance of dropping.

Variation: Hierarchical loot tables

Loot tables are generally source for new resources. However, you can easily run into situations where you are dropping too much or too little of a particular resource. Some sort of constraints would be helpful.

One solution is to use hierarchical loot tables without replacement. When a particular resource runs out, the player doesn’t get any more. We’ve used this for our daily coin awards. We want to give out 100 coins a day, but no more. But we want to do it as part of the loot system.

Create two tables: Rewards and DailyCoins.

Have the main loot table reference the Daily Coins bucket.

When Daily Coins get picked, roll that table and see how many coins you get.

Example

lootTable: Rewards
item:
name: sword
weight: 10
item:
name: dailyCoins
weight: 40
item:
name: null
weight: 50
lootTable: dailyCoins
type: noReplacement
refreshRate: Daily
item:
name: coin, number: 1
weight: 10
item:
name: coin, number 10
weight: 4
item:
name: coin, number: 50
weight: 1

In the example above, a player has a 40% chance of getting coins. Then we roll the dailyCoins table and see that they can win a maximum of 100 coins a day with 10 awards of 1 coins, 4 awards of 10 coins and 1 award of 50 coins.

When the dailyCoins loot table is emptied, they’ll get nothing until it refreshes after a day.

Variation: Conditional drops

Sometimes you want to test if you should drop the items base off some external variable. In Realm of the Mad God, we wanted to avoid free riders getting loot for a boss kill without doing at least some damage. So in the loot table, we added a check. If a valuable item in the loot table was rolled, then we’d check to see if the player had done more than X% of damage to the enemy.

You could also build in switches for which loot is valid based off player level or even enemy level. I tend to instead use multiple smaller loot tables, but the system is flexible enough that you can easily architect your data with a few large tables and use of conditionals.

Variation: Modifiers

You can also modify the quantity or weight of a drop based off some external logic. For example, a player with a skill in harvesting could yield 2x as many of a particular item drop compared to a player without that skill. Or you could modify the weight. A high level character might have a -50% weight for all items marked lower than their level.

Other uses

Drop tables are commonly used for dropping loot. But I also find them useful in other areas.

Procedural generation: Use a table to build weapons or characters from components

AI: Use a table to select behaviors such as attacks or moves.

This may seem a little silly..surely there are better ways to model AI! However, one way to think about randomness is that it is a very rough first order model of any system. How does the human brain model a system? We make an observation about a system. We note the frequencies and tendencies for those observations to reoccur. It is only much, much later that we start to understand ‘why’ something happens or the causal relationship between parts.

In physics, we often joke that in order to model a cow, a complex biological organism, the first step is to ‘imagine a spherical cow’. By creating a simplistic, easy to work with model, we can often generate useful insights at a very low cost.

Many times, a drop table is a ‘good enough’ human-centric approximation of a complex system. For many systems, most players will never move beyond a basic probabilistic understanding so modeling more complexity is a waste of time. Efficient game design is an exercise in modeling elements only to the minimum level necessary to create the desired experience.

Consider: D&D modeled entire universes with what were essentially loot drop tables. That was a deliberate focus on minimizing systems that were in many ways just secondary flavoring to the core roleplaying.

A loot drop table isn’t the only tool you need, but in many scenarios, it is good enough.

Procedural generation thought experiment

Here’s a simple procedural generation system using drop tables. There are lots of other ways to do this, but this is more to get your brain thinking. Let’s say you want to build a procedurally generated enemy

Start by making a list of unique enemy parts. Maybe your enemy is made up of a type of movement, a type of attack, a defensive buff and a type of treasure.

Make loot tables for each one of those parts.

For each item in the loot table, give it a power value based off how powerful you think it might be. for example, a knife attack might be weak so it only has a power of 5. But a large hammer attack might have a power of 15.

Create another loot table of buffs. These are modifiers to various attributes. For example, ‘Strong’ boost a value on an attack by 20%. You can have debuffs as well ‘Weak’ might diminish a value by -50%. These have reduce the power value of a part.

Now let’s generate an enemy

Set a target: Set a target power for your generated enemy. Say you want an enemy of power 40

Roll: Roll each of the parts once and add them into a list.

Score: Add up all the power values to get a score.

Adjust: If the sum of the parts is over the target, add a debuff or roll for a lower power part. If it is under, add a buff or roll for a higher power part.

Repeat: Repeat this process until you hit a desired error threshold (distance from power 40) or you’ve exhausted the number of iterations you are willing to spend.

You now have a procedurally generated enemy. There are tons of tweaks you can do to this basic system, but it works most of the time. As an exercise, think about:

Exclusion lists: If two parts are picked that are on the list, throw the enemy away and reroll.

Multiple constraints: Parts are scored on multiple criteria. Note, the more constraints you add, the less likely you are to converge on a viable result.

Conclusion

Any time there’s a discussion of randomness, there’s a huge number of secondary issues that come into play. I recommend the following for further reading:

Understanding randomness in terms of mastery: http://www.lostgarden.com/2012/12/understanding-randomness-in-terms-of.html

Richard Garfield on Luck: https://www.youtube.com/watch?v=av5Hf7uOu-o

Resist being dogmatic about randomness. Be a broadly educated designer whose aesthetic choices are based on hands on experimentation. A good rule of thumb is that you can’t intelligently critique a design tool until you’ve made a couple games that use it successfully.

Anyway, this is just how I’ve done loot tables; a mundane part of any working designer’s life. I’m curious if other folks have other ways of managing loot (and randomness) that they love and live by.

篇目4,Game Items as Character Building and Resource Management

by Koobazaur

What is a “Tool” Item?

In this article, I am defining a tool as an acquirable item that provides the player with a new, non-combat ability to get past obstacles. For example a picklock might allow the player to open doors, a grappling hook may let them climb to new heights, or an oxygen mask stay longer underwater.

Tool = Ability [-] Obstacle

Note I am specifically excluding combat tools, such as weapons or shields, as I want to focus on tools that open up new paths or avoid obstacles (including guards, so Stealth tools are in). I am also not counting abilities acquired purely via upgrading stats in a traditional RPG fashion. However, I am still including tonics in Bioshock, as they function more like items.

Role Models: Deus Ex, Thief, Dishonored, Zelda, Metroid, Bioshock

Dimensions of Tools

Looking at a variety of games, I propose the following characteristics of a tool:

Acquisition – how the tool is acquired by the player

Given – the tool is automatically given to the player at some point (i.e. Thief’s lockpics)

Findable – it can be found somewhere in the world (i.e. Metroid, Zelda, Deus Ex)

Exclusive Choice – it can be chosen at the exclusion of others (i.e. Deus Ex AUG upgrades)

Purchasable – it can be purchased via some currency (i.e. Bioshock, Dishonored)

Permanence – how long does the tool stay with the player

Unlimited – can be used indefinitely (i.e. Thief’s lockpicks, Metroid)

Disposable – can only be used a finite amount of times and needs to be re-acquired (i.e. Deus Ex Multitools)

Fuel-based – can be used indefinitely but requires some fuel or ammo to use (i.e. Bioshock’s tonics, Deus Ex AUGs)

Upgradability – can the tool be changed or improved?

Static – always stays the same (i.e. Zelda, Metroid)

Upgradable (by finding) – can be upgraded by finding a newer version, or some other resources (i.e. Bioshock tonics, Deus Ex AUGs)

Upgradable (by currency) – can be upgraded by spending some currency (i.e. Dishonored)

Here’s a few examples:

Deus Ex multitools = Findable + Disposable + Static

Deus Ex AUGs = Findable + Fueled + Upgradable (by currency)

Dishonored Powers = Purchasable by currency + Fueled + Upgradable

Thief Rope or Moss arrows = Findable/Purchasable + Disposable + static

Metroid powers = Findable + Unlimited + Static

Of course, games can mix multiple characteristics in a single dimensions – some of Bioshock’s tonics are Given (like the first few forced on the player) while others Findable (non-essential).

The most complex case I can think of are Deus Ex AUGs – not only are they Findable, Exclusive Choice Resource that requires Fuel, but they can also be upgraded via a whole different resource. Crazy!

Character Customization via Tools

Tools provide the most basic method of character building. Tool availability, permanence and upgradability defines how flexible and meaningful player choices are, and how polarized their character becomes.

For instance, Metroid has linear character building with no customization due to the dimensions of its tools – you get the tools at almost exact points in each playthrough and cannot upgrade them after. With exception of weapons, for all intents and purposes, the “character building” always progresses the same way. Conversely, Deus Ex AUGs are far more customizable and polarized – they force you to choose between two options which have critical implications for the rest of the game.

An interesting case is Thief arrows – you can purchase them on each stage, in varying amounts, giving you a lot of freedom and a blank slate before each mission. Bioshock allows for similar hot-swappability of the tonics, but limits the resources for upgrading them.

Experimentation

Furthermore, tool dimensions also influence how much experimentation the player is allowed. The question posed is “do I get to test each tool in some basic form before committing to one?”

In Deus Ex, once an augmentation choice is made, an opportunity is lost – you will never know what you missed out on. Ergo, you can’t really “experiment.”

On the other hand, Thief gives you at least one of each type of arrow at some point so you get to play around and experiment with them before deciding which ones you want to fork your hard-earned (well, stolen) cash on.

We can see a middle-ground approach in Bioshock, giving you almost every tonic in its basic form and letting you decide which to upgrade. Another way of achieving that is seen in Dishonored, – player doesn’t start with every ability, but they can earn enough currency throughout the game to acquire each one in its basic form.

Tools as Resources

In my definition, a resource is an item or currency/fuel you find, receive, or purchase, which has a limited supply, and is disposable. So anything from generic ammo, to batteries in Deus Ex, or Bioshock’s potions.

Now things get interesting when a tool becomes a resource – it introduces an element of management and exploration discussed below. Such was the case for multitools in Deus Ex or rope/moss arrows in Thief.

Conversely, most Zelda tools (like a grappling hook or a shield) are not a resource – there is exactly one of each in the world and they don’t require management. Bombs are one exception, however, as they are disposable and findable throughout the world.

Resource Management and Exploration

When tools act as, or rely on, resources, they increases the depth of gameplay – abilities are constantly “obtained” and “lost” back and forth depending on how you play and what choices you make.

Again, Deus Ex multitools and lockpicks, Thief’s arrows, or Zelda’s Bombs are a great example – they both enable and are a reward for exploration.

Conversely, Metroid’s tools or Zelda’s grappling hook enable exploration but do not “encourage” it by themselves – you must find them to advance the story, so you aren’t technically “exploring”, just playing the predefined design path.

Difficulties in Balancing Items

It is tricky to design and balance tools properly. It’s hard to account and validate each choice. Such was the case of the swimming upgrades or aqualungs in Deus Ex, which had so few use cases they were a waste. Conversely, you can also overpower a tool by over-designing – a maxed out blink in Dishonored will let you accomplish every single objective extremely efficiently, making the other tools seem useless, if not downright too risky.

Further, when tools are or depend on resources, it becomes difficult to balance their amount as well, especially considering varying player skills and aptitude for random exploration. An experienced Thief player will end a mission with over-abundance of arrows. A novice may be barely scraping by. Different difficulty levels help alleviate this somewhat, but the desing problem still exists for each setting.

My Work

Curious about my own indie game that spawned this feature? Check out my DevBlog, currently working on a Slavic-Steampunk game of sneaking and investigation onboard a damaged Zeppelin, uncovering the secrets of its passengers.


上一篇:

下一篇: