战利品表,随机地图和怪物-第一部分(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/loot-tables-random-maps-and-monsters-part-i-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 25 分钟阅读 - 12504 个词 阅读量 0战利品表,随机地图和怪物-第一部分(译文)
原文地址:https://www.codeproject.com/Articles/420046/Loot-Tables-Random-Maps-and-Monsters-Part-I
原文作者:Mike Barthold
译文由本站 robot-v1.0 翻译
前言
How to make Boss xy drop item abc and where does that rare mob come from?
如何制作Boss xy掉落物品abc,那稀有生物来自何处?
介绍(Introduction)
这是我关于CodeProject的第一篇文章,所以请对我好一点. ;)(This is my first article on CodeProject, so please be kind to me. ;))
您是否曾经想过这些战利品系统如何在当今的MMO(如WoW,Rift等)或H&S游戏(如<暗黑破坏神>)中工作?(Did you ever wonder how those loot systems work in today’s MMO’s like WoW, Rift, and many others or H&S games like Diablo?)
这是很长一段时间以来一直困扰着我的事情,直到我终于坐下来并开始思考这件事.尽管我认为必须满足这样一个系统的要求,但很明显,(This is something that ran through my head for a long time, until I finally sat down and started thinking logical about it. While I thought through the requirements such a system has to fulfill, it became clear that almost)**一切(everything)**从供应商出售的物品到怪物死后掉落的战利品,甚至是(that happens in those games, from the items a vendor offers for sale, to the loot a monster drops when it dies, and even the)**产卵(spawn)**的怪物(是稀有生物还是普通怪物,任何一种精英生物,无论什么)都可以放在同一罩下:它是一种随机产生的…(of monsters (is it a rare mob or a normal monster, any kind of elite creature, whatever) can be put under the same hood: it is a kind of random generated …)事情(thing).甚至随机生成的地图也不过是"一小段地图片段".(. Even random generated maps are nothing else than a “drop of map segments”.)
几乎每个游戏都有"有时"发生的事情.如果您对"(Almost every game has things that happen “sometimes”. If you are happy with code like “) if (new Random().Next(1,10) < 5) ...
“,那么您可能应该停止阅读-但是,如果您不想要更多,如果您只想"设计"发生事情的概率,如果您不想(” then you should probably stop reading - But if you want more than that, if you want to be able to just “design” probabilities for things to happen, if you do not want) if
…(…) else if
…(…) else if
…构造随机值,那么这里将是您的明珠.我很有信心前进.继续阅读!(… constructs running through random values, then this one here will be a pearl for you. I am quite confident. Go ahead. Read on!)
我将向您展示我对"掠夺问题"的一站式解决方案,即”(I will show you my all-in-one solution to the “loot-problem”, a class library called “) RDS
“(随机分发系统),它创建递归的战利品表,概率,结果集,并具有一组属性,可让您控制其行为.令我惊讶的是,该库比我最初认为的要小得多.这些类苗条,快速且易于理解; RDS似乎不仅仅是其各个部分的总和,仅需几行代码,您就可以创建出色的随机内容!(” (Random Distribution System) that creates recursive loot-tables, probabilities, result sets and has a set of properties that allows you to control its behavior. To my own surprise, the library was way smaller than I initially thought it would be when finished. The classes are slim, fast and easy to understand; somehow the RDS seems to be more than the sum of its parts. With only a few lines of code, you will be able to create fantastic randomized content!)
没有花哨的图形,没有设计师,只有为您创造战利品的核心代码.这完全取决于您和您的想象力,这些类可以为您做些什么,以及您可能要编写哪些设计师来创建这些表(基于SQL,基于File的文件,等等).如果您也可以在这里与这些课程分享一些想法,那将是很棒的.(No fancy graphics, no designers, just the core code creating loot for you. It’s totally up to you and your imagination, what those classes can do for you and which designers you might want to write to create those tables (SQL based, File based, whatever). Would be great if you’d share some of your ideas with those classes here, too.)
背景(Background)
在本文的第一部分中,我想深入研究其含义的理论,以开发RDS.然后第二部分将所有这些变为现实.因此,如果您想先了解它可以为您做什么,也许您想从第二部分开始,然后在第一部分中进行详细介绍.取决于您的个人喜好,但希望在第二部分中找到一些术语和方法没有第一部分的知识,您将无法完全理解.(In the first part of this article, I want to go into the theory of what it means, to develop a RDS. The second part will then bring all this to life. So if you want to see what it can do for you first, maybe you want to start with Part II and then go into the details here on Part I. Depends on your personal preferences, but expect to find some terms and methods in Part II that you will not fully understand without knowledge from this first part.) 让我们来看看我们(Let’s take a look at what we)**看到发生(see happen)**在游戏中,然后将其分解为技术术语.我将以两个几乎每个人都知道的游戏(<魔兽世界>和<暗黑破坏神>)为例,因此,当我描述事物时,可能会有一个构想.(in the game and then break it down to what that means in technical terms. I will concentrate on two games that almost everybody knows (WoW and Diablo) for my examples, so the chances are good you have a picture in mind when I describe things.) <魔兽世界>的例子:(Examples from WoW:) 当(普通户外)暴民死于魔兽世界时,战利品如下所示:(When a (normal outdoor) mob dies in WoW, the loot looks like this:)
-
3.56金(3.56 Gold)
-
3丝纹布(3 Silkweave cloth)
-
双手斧(绿色)(Two-Handed Axe (green)) 如果幸运的话,斧头可能是蓝色,甚至是史诗般的颜色.(If you are lucky, the Axe might be blue or even epic colored.) 从此下降可以看到的要求:(Requirements that can be seen from this drop:)
-
“黄金"掉落具有随机的"数量”(例如"介于2到6之间的黄金”,甚至更好):完全动态地基于包括区域级别,怪物级别,玩家级别的公式来确定黄金数量的范围).(“Gold” drop has a random “amount” (something like “between 2 and 6 gold”, or even better: completely dynamically based on a formula that includes the area level, monster level, player level to determine the range for the gold amount).)
-
物品数量是随机的(也可以是一个或两个Silkweaves->这是(Amount of items is random (could as well be only one or two Silkweaves -> this is)**不(not)**和黄金一样!尽管金币只是添加到您的角色钱包中,但布显然是三件(=三个对象的实例,称为" silkweave:cloth"或类似的东西),因为它们将被放入您的库存中,并且可以分成几堆较小的堆栈).(the same as the gold amount! While the Gold is just added to your characters wallet, the cloth is clearly three items (= three instances of an object called “silkweave:cloth” or something similar), as they will be put into your inventory and the stack can be split into smaller stacks).) 当您在实例化的地牢中杀死Boss时(无论是突袭还是5人实例),您都有一定的战利品和随机的战利品:(When you kill a boss in an instantiated dungeon (no matter whether this is a raid or a 5-man-instance), you have some guaranteed loot and some random loot:)
-
252金(252 Gold)
-
史诗装甲片1(保证)(Epic armor piece 1 (guaranteed))
-
史诗装甲2(保证)(Epic armor piece 2 (guaranteed))
-
稀有手工艺品配方(随机)(Rare crafting recipe (random))
-
0至3个随机魔法(绿色)物品(0 to 3 random magic (green) items) 从此下降中看到的要求:(Requirements seen from this drop:)
-
我们需要能够(We need to be able to)**保证(guarantee)**如果物品来自同一战利品表(史诗般的盔甲),则要放下的特定最小物品数量->不仅是最小物品数量,甚至是(a specific minimum amount of items to drop, event if they come from the same loot-table (epic armor pieces) –> Not only a minimum amount, even a)**计数(Count)**丢弃查询的数量(of drop queries needs to be possible)
-
我们需要能够随机计数(0至3个随机魔法物品)(We need to be able to have a random number of counts (0 to 3 random magic items)) 让我们将其放入一些代码属性中.(Let’s put this into some code properties.) 我们将需要一个包含"表"的类.命名吧(We will need a class that holds the “table”. Let’s name it)
RDSTable
.这就是Gamer所说的(. This is, what in Gamer’s terms is called a)LootTable
.这样的表将包含可以删除的项目(或更优:对象)的列表.没有太多细节,我们知道我们希望允许开发人员以虚拟方式放置(. Such a table will contain a list of items (or better: objects) that can drop. Without too much detail, we know that we want to allow the developer to put virtually)**任何(any)**项目在这样的表中,因此我们需要一个接口.我们选择名字(item in such a table, so we need an interface. We pick the name)IRDSObject
为了这.逻辑上的下一步是将表的内容声明为(for this. The next logical step is to declare the contents of our table as)IENumerable<IRDSObject> rdsContents;
在我们班上.现在我们可以将任意数量的对象放在要选择的列表中.到目前为止还不算难,对吧?(in our class. Now we can put any number of objects in a list to be picked. Not really hard so far, right?) 好了,我们需要了解什么(Ok, what do we need to know about such an)IRDSObject
?这里必须具备什么?我们知道,它将有一个(? What is a must-have here? We know, it will have a)probability
下降.我们知道,有一个(to drop. We know, there’s a)count
参与.我们知道,有可能让它掉落(involved. We know, it’s possible to have it drop)always
.但是,当它总是掉落时…相反,这不是一个好主意,包括一个开关,使该物品成为(. But when it drops always… as an opposite… isn’t it a good idea, to include a switch, to make the item a)unique
下降?只能是结果的一部分(drop? That it can be part of the result only)一旦(once)?是的,这个主意很好.我们添加.为了增加灵活性,我们将添加(? Yes, this idea is good. We add that. And to add flexibility, we will add an)enabled
属性也是如此,因此我们可以按需"关闭"表内容的某些部分,而无需修改表本身.(property too, so we can “turn off” parts of our table contents on demand, without modifying the table itself.) 在这一刻(At the moment),则我们的界面将如下所示(我在此处删除了注释,以使代码更紧凑.在可下载的源代码中,代码当然有完整记录):(, our interface will then look like this (I removed the comments here to keep the code more compact. In the downloadable source, the code is, of course, fully documented):) 所有属性都有名称前缀(All properties have the name prefix)rds
将它们一起使用IntelliSense并避免命名冲突,例如:(to have them together in IntelliSense and to avoid naming conflicts, as “)Count
“和”(” and “)Enabled
“在C#中是非常常见的名称.您可以随意重命名它们或使用显式接口实现.我个人更喜欢按前缀分组(因为我所有的文本框都以(” are quite common names in C#. Feel free to rename them or work with explicit interface implementation. I personally prefer grouping-by-prefix (as all my textboxes start with)txt
,我的列表框(, my listboxes with)lst
等).(, etc.).)
public interface IRDSObject
{
double rdsProbability { get; set; } // The chance for this item to drop
bool rdsUnique { get; set; } // Only drops once per query
bool rdsAlways { get; set; } // Drops always
bool rdsEnabled { get; set; } // Can it drop now?
}
机率如何运作(How Probability Works)
为什么概率是(Why is the probability a) double
?例如,如果玩家角色具有修饰符(例如万能的),则通过乘法和除法进行动态修改会更容易,例如(? Because it will be easier to modify it dynamically with multiplications and divisions, as an example, if the player character has modifiers (like the allmighty)**魔术师(MagicFind)**在暗黑破坏神(Diablo)中,每个物品的掉落概率可以在运行时与(in Diablo), the drop probability for each item can be multiplied dynamically at runtime with the)**魔术师(MagicFind)**角色的奖金.(Bonus of the character.)
概率既不是百分比值也不是绝对值.它是一个值,相对于表的其他值描述了被击中的机会.(Probability is neither a percent value nor an absolute thing. It’s a value that describes the chance of being hit in relation to the other values of the table.)
让我举一个简单的例子:(Let me give you a simple example:)
Item 1 - Probability 1
Item 2 - Probability 1
Item 3 - Probability 1
所有三个物品都有相同的掉落机会.(All three items will have the same chance to drop.)
Item 1 - Probability 10
Item 2 - Probability 5
Item 3 - Probability 1.5
总计为16.5-如果从此表中计算出16滴,则很有可能是项目1的10倍,项目2的5倍,也许是16(The sum of all is 16.5 - If you calculate 16 drops from this table, you will likely have 10 times Item 1, 5 times Item 2 and maybe the 16)日(th)将是单个项目3.(will be one single Item 3.)
你懂了?结果将只是一个随机值,并循环遍历表的内容,直到它达到比随机值大的第一个值.这是项目命中.我将解释的确切功能(和递归)(You get it? The result will just take a random value and loop through the contents of a table until it hits the first value that is bigger than the random value. This is the item hit. I will explain the exact functionality (and recursion) of the) Result
本文后面的方法.(method later in this article.)
建立表格(Building a Table)
好,那我们来看一下(Ok, then let’s take a look at our) RDSTable
类.如果我们也以此作为接口,我们可以使任何类成为(class. If we start with that as an interface too, we can make any class become a) RDSTable
在我们的游戏项目中.我们不想将太多的设计规则放在开发人员的肩膀上.如果他需要一些自己的基类来启用RDS,那么他将能够做到这一点.除了我们的内容(in our game project. We do not want to put too many design rules on the developer’s shoulders. If he needs some of his own base classes to be RDS-enabled, then he shall be able to do so. Beside the contents of our) RDSTable
,我们当然需要一个结果集.正如我们在上面的示例中看到的,它不止一个(, we will of course need a result set. As we have seen in the examples above, it’s more than one) IRDSObject
我们期望,所以(we expect, so the) Result
将是(will be an) IEnumerable<IRDSObject>
也一样(, too.)
现在是中心思想之一:如果(And now is one of the clou ideas: What if) IRDSTable
源自于(derives from) IRDSObject
?大!现在每个条目的内容(? Great! Now each entry in the contents of a) RDSTable
,可以是另一个(子)表!那是我们在这里遇到的头奖之一-我们使其递归!这样,我们可以设计"主题"表,例如,我们将所有史诗般的世界掉落放在一张桌子中(并且该表中的每个史诗物品都有其自己的概率),我们所有的稀有物品都放在第二张桌子中,所有绿色物品都放在第三张桌子中,在第四张桌子上都是白色的.然后,我们建立一个"主表”,其中包含这四个表作为子表,并且每个子表都有自己的一组属性,概率和值.(, can be another (sub)table! That’s one of the Jackpots we hit here - we make it recursive! This allows us, to design “Theme” tables, say, we put all epic world drops in one table (and each epic item in this table has its own probability), all our rares in a second table, all greens in a third and all white in a fourth table. We then set up a “Master Table” that contains those four tables as sub-tables, and each of those sub tables has its own set of properties, probabilities and values.)
因此,(So, the first shot of) IRDSTable
看起来像这样:(will look like this:)
public interface IRDSTable : IRDSObject
{
int rdsCount { get; set; } // How many items shall drop from this table?
IEnumerable<IRDSObject> rdsContents { get; } // The contents of the table
IEnumerable<IRDSObject> rdsResult { get; } // The Result set
}
的(The) Count
是(is part of the) IRDSTable
接口,而不是(interface and not of the) IRDSObject
,因为我们想问"该表应删除多少个条目?“而不是"该物品多久丢一次?".在上面的WoW示例(丝织衣服)中,我们可以假设所有"布料"物品都放在一张表中,它们的掉落概率是根据怪物等级公式动态计算的(丝绸掉落在20到30之间,而魔纹只掉落) (从31 ++起),将这个怪物等级不能掉落的布料类型的所有概率简单地设置为零.(, because we want to ask “How many entries of this table shall drop?” and not “How often does this one item drop?”. In the WoW example above (the silkweave clothes), we could assume, all “cloth” items are together in one table, their drop probabilities calculated dynamically based on a monster level formula (silk drops between levels 20 and 30, while mageweave drops only from 31++) that simply sets all probabilities to zero for cloth types this monster level can not drop.)
更多细节(More Details)
在将一些已知的内容放入接口之后,我们现在有了一个基础,可以在其中开始思考细节.(After we put some known things into the interfaces, we now have a base where we can start thinking about details.)
我们仍然缺少大量功能,我们无法告诉系统丢弃” 0到3个绿色项目",当项目丢失时我们无法对其进行控制(即,它们被结果评估"击中"),并且我们做到了不可能在结果计算之前立即修改概率.当然,在计算结果集之后,我们将无法控制它.(We are still missing tons of functionality, we can not tell the system to drop “0 to 3 green items”, we have no control on the items when they drop (i.e., they are “hit” by the result evaluation) and we do not have any possibilities to modify probabilities immediately before a result-calculation occurs. And of course, we do not have control over the result set after it has been calculated.)
我们想念的另一件事是类似金滴的东西.我们只能删除实现(Another thing we miss, is something like the Gold drops. We can only drop objects that implement) IRDSObject
.但是我们没有(. But we don’t have)价值观(values).首先让我们开始,因为它真的很简单.我们想要删除任何类型的值. “任何一种”?好吧,仿制药现在登上了舞台.我们添加一个(. Let’s start with this first, as it is really easy. We want to drop a value of any kind. “Any kind”? Well, generics jump onto the stage now. We add an) IRDSValue<T>
与我们的模型的接口,这将从(interface to our model, that will derive from) IRDSObject
也增加了一个(too and that adds a) T Value
属性.在这里,我们可以存储结果中的黄金数量.(property. This is, where we can store our Gold amount in a result.)
public interface IRDSValue<T> : IRDSObject
{
T rdsValue { get; }
}
现在,我们可以将整数,双精度数,字符串或任何其他对象添加为表中的"值".(Now we can add integers, doubles, strings or any other object as “values” to our tables.)
控制内容(Taking Control of the Contents)
这一步非常重要.我们需要扩大(This step is very important. We need to expand the) IRDSObject
与更多的东西互动.我们希望能够在计算结果之前对表中所有项目的概率进行运算,我们想知道,当结果计算器"击中"某项时,甚至,我们甚至希望有机会检查一下整个结果集,然后再返回给调用者.(interface with some more goodies. We want to be able to run over the probabilities of all items in the table before a result is calculated, we want to know, when an item is “hit” by the result calculator and maybe, we even want to have a chance to check the entire result set before it is returned to the caller.)
为此,我们向(To do this, we add some events to the) IRDSObject
界面,使我们可以控制这些事情.(interface, that give us control over these things.)
我在此代码片段中保留了有关事件的注释,它们对每个事件何时发生的解释都很好.(I leave the comments on the events in this code snippet, they explain very well, when each of those events will happen.)
/// <summary>
/// Occurs before all the probabilities of all items of the current RDSTable
/// are summed up together.
/// This is the moment to modify any settings immediately before a result is calculated.
/// </summary>
event EventHandler rdsPreResultEvaluation;
/// <summary>
/// Occurs when this RDSObject has been hit by the Result procedure.
/// (This means, this object will be part of the result set).
/// </summary>
event EventHandler rdsHit;
/// <summary>
/// Occurs after the result has been calculated and the result set is complete, but before
/// the RDSTable's Result method exits.
/// </summary>
event ResultEventHandler rdsPostResultEvaluation;
void OnRDSPreResultEvaluation(EventArgs e);
void OnRDSHit(EventArgs e);
void OnRDSPostResultEvaluation(ResultEventArgs e);
稍微多一点理论,然后我们将最终看到结果计算的代码,它将所有部分放在一起.(Just a little bit more theory, then we will finally see the code of the result calculation which will put all the pieces together.)
实施接口(Implementing the Interfaces)
该库的接口层次结构非常简单,如下所示:(The interface hierarchy for the library is very simple and looks like this:)
该库包含所有接口的完整实现.它们都被命名为它们的接口,没有前导"(The library contains a full implementation of all interfaces. They are all named as their Interfaces without the leading “) I
“, 所以(”, so the) RDSObject
类工具(class implements) IRDSObject
,(,) RDSTable
->(->) IRDSTable
, 等等.看一下附带的源代码和我制作的构造函数.(, and so on. Take a look at the attached source codes and the constructors I made.)
这些实现易于阅读和直接.(The implementations are easy to read and straightforward.)
Key
图书馆的课是(class in the library is the) RDSTable
包含(class which contains the) Result
RDS使用的计算实现.在以下各章中,我们将非常仔细地研究此核心功能.(calculation implementation that is used by RDS. We will take a very close look at this core functionality in the following chapters.)
使用RDS时,不必实现那些接口,只需将游戏对象和怪兽的基类派生自(When you use the RDS, you do not have to implement those interfaces, just make your baseclass for your game objects and monsters derive from) RDSObject
您就可以将它们中的每一个添加到任何结果集中.(and you will be able to add each of them to any result set.)
空值:RDSNullValue类(Null Values: The RDSNullValue Class)
我们有一个未解决的问题,就是” 0到3个绿色项目"功能.我不要(One open issue we have, is the “0 to 3 green items” functionality. I do not want the) Count
属性要随机化,我选择一种更好的方法.(property to be randomized, I choose a better approach.) Null
价值观!我们只创建一个名为(values! We just create a class called) RDSNullValue : RDSObject
可以添加到每个战利品表,并有自己的概率.这样,我们可以轻松解决此问题.我们使用(that can be added to each loot table and have its own probability. With this, we can easily solve this issue. We create the table for the green drops with a) Count
的(of) 3
然后添加一个(and just add a) RDSNullValue
以给定的概率返回表就什么都不返回.这就是实现" 0到3"的方式.(to the table with a given probability to just return “nothing”. This is how “0 to 3” is implemented.)
为简单起见,该表如下所示:(For simplicity, the table could look like this:)
Null - Probability 1
Green Item - Probability 2
因此,从理论上讲,每三滴(So, in theory, every third drop is a) null
放下-但当然,我们会查询所有三个都击中一个绿色项目的地方,而放下后会放下(drop - but of course, we will have queries where all three hit a green item and we will have drops, where) null
被击中两次甚至三次.您可以轻松地增加/减少(is hit twice or even three times. You can very easily increase/decrease the) null
-通过修改绿色项目或(-chance by modifying the probabilities of either the green item or the) null
值.(value.)
的(The) RDSNullValue
类非常简单,但是解决了很多问题,因为它允许我们在需要时放弃"所有内容".(class is very very simple but solves a lot of problems because it allows us to drop “nothing” when we need it.)
/// <summary>
/// This is the default class for a "null" entry in a RDSTable.
/// It just contains a value that is null (if added to a table of RDSValue objects),
/// but is a class as well and can be checked via a "if (obj is RDSNullValue)..." construct
/// </summary>
public class RDSNullValue : RDSValue<object>
{
public RDSNullValue(double probability)
: base(null, probability, false, false, true) { }
}
随机器(The Randomizer)
哦,这是一个主题,您可以在其中写书.计算机不能创建"真实的"随机数和所有这些东西.我不想在哲学讨论中过分详细.是的,它们不是真正的随机性,但是它们或多或少是不可预测的.无论如何,我决定放弃这个决定,于是我创建了一个(Oh this is a topic where you can write books about it. Computers are not able to create “real” random numbers and all that stuff… I do not want to go into too much detail about that philosophical discussion. It’s right yes, they are not real random, but they are more or less unpredictable. Anyway, I decided to put that decision away from me, and I created a) static
类,(class, the) RDSRandomizer
.默认情况下,它仅使用.NET(. By default, it just uses .NET’s) Random
类.如果您想使用(class. If you want to use the) RNGCryptoServiceProvider
来自的班级(class from the) System.Security.Cryptography
命名空间,您可以做到.的(namespace, you may well do it. The) RDSRandomizer
类允许交换通过(class allows to exchange the randomizer used via the) SetRandomizer()
方法.您应该问自己的唯一问题是:“我真的需要吗?".无论如何,在您的跑步游戏中,没人能说出怪物掉落的原因或距离"史诗般的物品"的程度.只要您不处理真钱赌博(例如扑克软件或赌场软件)…在"普通趣味游戏"中,标准随机分配器就是…嗯…(method. The only question you should ask yourself is: “Do I really need it?”. No one can tell anyway in your running game, why or how close to the “epic item” the drop of a monster was. As long as you don’t deal with real money gambling (like Poker Software or Casino Software)… in a “normal fun game”, a standard randomizer is… hmm…)足够随机(Random enough).(.)
为了允许开发人员更改所使用的Randomizer,该方法接受从.NET派生的任何类.(To allow the developer to change the Randomizer used, the method accepts any class derived from .NET’s) Random class
.几乎所有的方法(. Almost all methods of) Random
是(are) virtual
,因为微软有相同的想法:人们可能会希望改变这一点.因此,请随意创建自己的Randomizer,只要它来自(, because Microsoft had the same idea: People will maybe want to change this. So feel free to create your own Randomizer, as long as it derives from) Random
,就可以了,您可以将我的默认实现替换为(, you are fine, and you can replace my default implementation with the) SetRandomizer()
方法.(method.)
RDSRandom
在某些时候都有一些对大多数游戏有用的方法,下面是这些方法的快速概述:(has some methods that are useful in most games at any point, here is a quick overview of the methods:)
public static double GetDoubleValue(double max) // From 0.0 (incl) to max (excl)
public static double GetDoubleValue(double min, double max) // From min (incl) to max (excl)
public static int GetIntValue(int max) // From 0 (incl) to max (excl)
public static int GetIntValue(int min, int max) // From min (incl) to max (excl)
// Rolls a given number of dice with a given number of sides per dice.
// Result contains as first entry the sum of the roll
// and then all the dice values
// Example: RollDice(2,6) rolls 2 6-sided dice and the result will look like this
// {9, 5, 4} ... 9 is the sum, one rolled a 5, the second one a 4
public static IEnumerable<int> RollDice(int dicecount, int sidesperdice)
// A simple method to check for any percent chance.
// The value must be between 0.0 and 1.0, so a 10% chance is NOT "10", it's "0.10"
public static bool IsPercentHit(double percent)
使用这几种简单的方法,您可以轻松地在不依赖于游戏的游戏中完成大多数随机操作(With those few simple methods, you can easily do most of the random stuff in a game that does not depend on) RDSTables
,还有很多优点,那就是您可能已用自己的替换了默认的.NET Randomizer.(, with the great addition, that you might have replaced the default .NET Randomizer with your own.)
结果计算如何进行(How Result Calculation Works)
我认为现在是时候解释一下,在混乱变得过高之前,结果计算将如何在实现中起作用.所以我将展示(I think it is a good time now to explain, how result calculation will work in the implementation, before confusion gets too high. So I will just show what the) Result
确实可以帮助您发挥想象力.(actually does to help you with your imagination.)
我实施了(I implemented the) Result
是的,我知道,你们当中有些人会说,这很糟糕,但是说实话,我真的很喜欢.如果更适合您的样式,请随意将其转换为方法.(in a getter, yes I know, some of you will say, this is bad, but honestly, I really like that. Feel free to convert it to a method, if that fits your style better.)
的代码(The code of the) result
该方法有充分的文档记录,但是我将在代码后添加进一步的说明.(method is well documented, but I will add further explanations after the code.)
// Any unique drops are added here when they are hit.
// Anything contained here can not drop a second time.
private List<IRDSObject> uniquedrops = new List<IRDSObject>();
// Calculate the result
public virtual IEnumerable<IRDSObject> rdsResult
{
get
{
// The return value, a list of hit objects
List<IRDSObject> rv = new List<IRDSObject>();
uniquedrops = new List<IRDSObject>();
// Do the PreEvaluation on all objects contained in the current table
// This is the moment where those objects might disable themselves.
foreach (IRDSObject o in mcontents)
o.OnRDSPreResultEvaluation(EventArgs.Empty);
// Add all the objects that are hit "Always" to the result
// Those objects are really added always, no matter what "Count"
// is set in the table! If there are 5 objects "always", those 5 will
// drop, even if the count says only 3.
foreach (IRDSObject o in mcontents.Where(e => e.rdsAlways && e.rdsEnabled))
AddToResult(rv, o);
// Now calculate the real dropcount, this is the table's count minus the
// number of Always-drops.
// It is possible, that the remaining drops go below zero, in which case
// no other objects will be added to the result here.
int alwayscnt = mcontents.Count(e => e.rdsAlways && e.rdsEnabled);
int realdropcnt = rdsCount - alwayscnt;
// Continue only, if there is a Count left to be processed
if (realdropcnt > 0)
{
for (int dropcount = 0; dropcount < realdropcnt; dropcount++)
{
// Find the objects, that can be hit now
// This is all objects, that are Enabled and that have not
// already been added through the Always flag
IEnumerable<IRDSObject> dropables = mcontents.Where(e => e.rdsEnabled && !e.rdsAlways);
// This is the magic random number that will decide, which object is hit now
double hitvalue = RDSRandom.GetDoubleValue(dropables.Sum(e => e.rdsProbability));
// Find out in a loop which object's probability hits the random value...
double runningvalue = 0;
foreach (IRDSObject o in dropables)
{
// Count up until we find the first item that exceeds the hitvalue...
runningvalue += o.rdsProbability;
if (hitvalue < runningvalue)
{
// ...and the oscar goes too...
AddToResult(rv, o);
break;
}
}
}
}
// Now give all objects in the result set the chance to interact with
// the other objects in the result set.
ResultEventArgs rea = new ResultEventArgs(rv);
foreach (IRDSObject o in rv)
o.OnRDSPostResultEvaluation(rea);
// Return the set now
return rv;
}
}
分步说明:(Step-by-step explanation:)
-
唯一列表列表包含设置为的所有匹配项(The list uniquedrops contains all hit items that are set to)
rdsUnique = true
.(.) -
首先,我们称(First, we call the)
OnRDSPreResultEvaluation
当前表的所有条目的方法.在这一点上,您可以禁用条目,修改概率,而在随机化器选择"黄金值"之前,您需要执行任何操作.(method for all entries of the current table. This is the point where you can disable entries, modify the probabilities, whatever you need to do before the randomizer picks the “golden value”.) -
然后,所有项目(已启用)与(Then, all items (that are enabled) with)
rdsAlways = true
被添加到结果集中.不需要Randomizer …永远都是,但是:如果表有一个(are added to the result set. No Randomizer needed… always is always, BUT: If the table has, let’s say, a)Count = 5
并且您有2项设置为(and you have 2 items set to)rdsAlways = true
,这意味着将从表的其余部分中仅再选择三个项目,以避免超过最大落差5.您可以在代码中找到(, this means, only three more items will be picked from the rest of the table to avoid exceeding the drop maximum of 5. You find this in the code where)realdropcount
计算.(is calculated.) -
下一步是评估所有"可投放"项目.这些都是这些(Next step is to evaluate all “dropable” items. This is all those items, that are)
rdsEnabled = true
而不是设置为(and not set to)rdsAlways = true
,因为这些已被添加.(, because those have already been added.) -
然后,我们遍历剩余的项目数((We then loop through the remaining count of items ()
realdropcount
)并生成一个() and generate a)RDSRandom
每个人的价值.的(value for each of them. The)while
循环计数直到(loop counts up until the)runningvalue
超过(exceeds the)hitvalue
.这是我们的热门商品.它将被添加到结果集中,并且(. This is our hit item. It will be added to the result set, and the)OnRDSHit
为此项目触发了事件(此操作由(event is fired for this item (this is done by the)AddToResult
方法,说明如下).(method which is explained below).) -
最后,(At the end, the)
OnRDSPostResultEvaluation
为结果集中包含的每个项目触发.有时您可能需要查看结果集以对其进行修改,然后才能将其最终返回给调用者.(is triggered for each item contained in the result set. There may be times where you want to look over the result set to modify it before it is finally returned to the caller.)AddToResult
对这一切进行一些关键操作:(does some key action on all this:) -
当您建立了表表表结构时,它将创建递归.(It creates the recursion when you have set up a table-of-tables-of-tables-of-tables structure.)
-
它照顾(It takes care of)
rdsUnique = true
掉落(drops) -
它介绍了迄今为止尚未显示的(It introduces a so far not shown concept of the)
RDSCreateableObject
(稍后说明)((explained later))
private void AddToResult(List<IRDSObject> rv, IRDSObject o)
{
if (!o.rdsUnique || !uniquedrops.Contains(o))
{
if (o.rdsUnique)
uniquedrops.Add(o);
if (!(o is RDSNullValue))
{
if (o is IRDSTable)
{
rv.AddRange(((IRDSTable)o).rdsResult);
}
else
{
// INSTANCECHECK
// Check if the object to add implements IRDSObjectCreator.
// If it does, call the CreateInstance() method and add its return value
// to the result set. If it does not, add the object o directly.
IRDSObject adder = o;
if (o is IRDSObjectCreator)
adder = ((IRDSObjectCreator)o).rdsCreateInstance();
rv.Add(adder);
o.OnRDSHit(EventArgs.Empty);
}
}
else
o.OnRDSHit(EventArgs.Empty);
}
}
一步步:(Step-by-step:)
- 首先是唯一检查.如果是(First is the unique-check. If it is)
rdsUnique = true
且目前尚未包含在唯一列表中,请将其添加.如果已经包含,请跳过它(即(and not contained in the unique list so far, add it. If it is already contained, skip it (thats the)if (!unique || !contained)...
声明)(statement)) - 接下来是(Next is the)
NullValue
检查.一种(check. A)NullValue
不会添加到结果集中.(will not be added to the result set.) - 然后进行递归检查.如果命中项是另一个(子)表,(Then the recursion check happens. If the item hit is another (sub)table,)
.AddRange
该表的结果(一切再次发生…事件,命中,结果).(the result of this table (where everything happens again… events, hits, results).) - 如果不是表,请将其添加到结果中.(If it is not a table, add it to the result.)
在下一章中,我将解释(In the next chapter, I explain the)
IRDSObjectCreator
界面,这是系统非常重要的一部分.(interface which is a very important part of the system.) 作为(As the)RDSNullValue
可以被击中,我决定开除(can be hit, I decided to fire the)OnRDSHit
事件(event on the)NullValue
对象,即使在大多数情况下,(object too, even when in most cases, the default)null
值将被使用,但是它允许您导出自己的值(value will be used, but it allows you to derive your own)null
值,甚至在被击中时也会对此做出反应.考虑一下在游戏中出现任何机会时禁用游戏中的某些内容(value and even can react on it when hit. Think of disabling something in your game, when any dropchance)xy
导致(results in a)null
值,将其说成是"对某事做出反应(-value, speak it as “react on something that does)**不(not)**发生”.(happen”.)
IRDSObjectCreator接口(The IRDSObjectCreator Interface)
这是一件非常重要的事情.您将引用添加到您的表.因此,如果您多次查询一个表,结果集中总是返回相同的引用.当您掉落黄金或其他死者物品时,这并不重要.但是,当您放下诸如怪物或地图片段之类的生物时,这一点至关重要.当所有掉落的怪物都具有相同的参考标记时,我们可以使游戏英雄更容易.如果他杀死其中之一,他们都会立即死亡(This is one very important thing. You add references to your tables. So if you query a table multiple times, there are always the same references returned in the result set. This is nothing critical when you drop Gold or other dead things. But it is critical, when you drop something living, like a Monster or a Map segment. When all dropped monsters have the same reference, we make it easy for the hero of our game. If he kills one of them, they all die immediately)
.因此,当每个对象删除时,我们需要一个新的实例.这是此界面(或(. So we need a new instance of each of the objects when they drop. This is where this interface (or the) RDSCreatableObject
实现它的类).(class which implements it) comes into play.)
它仅提供一种方法:(It offers only one single method:) CreateInstance()
.这种方法当然是(. This method is of course) virtual
,因此它可以(并且应该)被覆盖.默认情况下,它只返回一个(, so it can (and should) be overwritten. By default, it just returns a) new()
它是对象类型的默认构造函数的名称.(of the default constructor of the type of the object it is.)
看一下代码(Look at the code of) RDSCreateableObject
为了更好的理解:(for a better understanding:)
/// <summary>
/// This class is a special derived version of an RDSObject.
/// It implements the IRDSObjectCreator interface, which can be used
/// to create custom instances of classes
/// when they are hit by the random engine.
/// The RDSTable class checks for this interface before a result is added to the result set.
/// If it is implemented, this object's CreateInstance method is called,
/// and with this tweak it is possible
/// to enter completely new instances into the result set at the moment they are hit.
/// </summary>
public class RDSCreatableObject : RDSObject, IRDSObjectCreator
{
/// <summary>
/// Creates an instance of the object where this method is implemented in.
/// Only paramaterless constructors are supported in the base implementation.
/// Override (without calling base.CreateInstance()) to instantiate more complex constructors.
/// </summary>
/// <returns>A new instance of an object of the type where this method is implemented
/// </returns>
public virtual IRDSObject rdsCreateInstance()
{
return (IRDSObject)Activator.CreateInstance(this.GetType());
}
}
如果除默认构造函数外还需要其他任何内容,则应重写此方法.(If you need anything other than the default constructor, you should override this method.) 现在您已经看到了RDS中所有的类和接口.对象模型也非常简单,如下所示:(Now you have seen all the classes and interfaces that are part of the RDS. The object model is very simple too, it looks like this:)
现在,请阅读本文的第二部分,该部分将重点介绍如何使用该库以及一些不错的示例,这些示例包括随机地图,怪物生成,物品掠夺以及甚至在游戏运行期间发生的随机事件.(And now, read Part II of this article, which concentrates on using this library with some nice examples of random maps, monster spawns, item loots and even random events happening during the runtime of a game.)
概要(Summary)
我们创建了一个RDS,使我们可以执行以下操作:(We created a RDS that allows us to do these things:)
- 删除任意数量的…(Drop any number of…)**东西(things)**具有递归结构的给定概率(with given probabilities in a recursive structure)
- 下降(Drop)没有(nothing)
- 当某些事情发生时对事件(或覆盖)做出反应(React on events (or overrides) when certain things happen)
- 模拟游戏行业大玩家的战利品行为(Simulate loot behavior of big players in the game industry)
- 添加值或引用,重新创建活动对象的实例(Add values or references, re-create instances of living objects)
- 用更复杂的东西替换default.net Randomizer的选项(The option to replace the default.net Randomizer with something more sophisticated)
- 基本上,您可以委托(Basically, you can delegate)**每一个(every)**RDS中游戏的随机决定和机会(random decision and chance in your game to RDS) 我们在制作和玩游戏时所需要获得的所有乐趣就在那里.到目前为止,您唯一还没有看到的就是一切如何实现.幸运的是,有第二部分将完全做到这一点!(All we need to have fun while making and playing our games is there. The only thing you have not seen so far is, how that all comes to live. Fortunately, there is a Part II, which will exactly do that!) 看看这个!(Check it out!) 继续第二部分(Continue with Part II) 这里(here) .(.) 你的(Yours,) 麦克风(Mike)
历史(History)
- 2012-07-13初稿完成(2012-07-13 First draft completed)
- 2012-10-05删除了虚假链接的笑脸(2012-10-05 Removed false linked smileys)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# .NET .NET4 .NET4.5 .NET3.5 新闻 翻译