在 C# 中什么更有效 - 硬编码 41 修饰符,或通过数学解决它?
What is more efficient in C# - hardcoding 41 modifiers, or solving that through maths?
我是一个真正的编码新手,想要将 Dungeons & Dragons OGL 机制移植到我的 Unity3D 项目中。
现在,我想在 C# 中为基本字符属性添加修饰符。
这些基于数字 10/11,即 20 面骰子的 "middle" 结果。
基本角色属性例如力量、智力、魅力等
您的统计数据高于 10 的越高,您获得的统计修正值就越高。 10 越低,您的修正值越低(负数)。
So, for 10 and 11 you have +0 (no modifer). Now, for:
12, 13 -> you get +1
14, 15 = +2
16, 17 = +3
18, 19 = +4
20, 21 = +5
22, 22 = +6 and so on (Character Attributes can go up to 40 and as low as 0 I guees)
For attributes below average, so below 10:
8, 9 = -1
6, 7 = -2
4, 5 = -3
2, 3 = -4
0, 1 = -5
现在,我可以在 ifs 上或像一个人一样在开关上对其进行硬编码。这是他的代码的一部分:
private string DetermineModNum(string value)
{
int score; //convert to int.
TryParse(value, NumberStyles.Integer, null, out score);
if (score > 10)
{
if (score % 2 == 0) //can be divided by 2
{
switch (score)
{
case 12:
return "1";
case 14:
return "2";
case 16:
return "3";
case 18:
return "4";
case 20:
return "5";
case 22:
return "6";
case 24:
return "7";
case 26:
return "8";
case 28:
return "9";
case 30:
return "10";
}
}
else
{
switch (score)
{
case 11:
return "0";
case 13:
return "1";
case 15:
return "2";
case 17:
return "3";
case 19:
return "4";
}
}
}
else
{
if (score % 2 == 0) //can be divided by 2
{
switch (score)
{
case 10:
return "0";
case 8:
return "-1";
}
}
else
{
switch (score)
{
case 9:
return "-1";
case 7:
return "-2";
}
}
}
return null;
}
这是不完整的,只是他的角色生成器的一部分。
但另一种方法可能是将属性除以 4。因此,我们可以将统计数据除以 4,而不是全部写下来。
例如:
强度:7(修饰符为 7 / 4 = 1,75,因此我们应将其四舍五入为 2 并将其更改为 -2)
智力:18(修改器将是 18 / 4 = 4,5,所以这只是 int 4)
在 Unity3D 游戏中哪种方法会更快?
此外,如果第二种解决方案更好,你能帮我展示一下如何在 C# 中最好地构建它吗?我只是在学习,真的可以在这里使用一些很好的例子。我需要低于 10 的修饰符为 -,高于 10 为 +,属性范围至少在 0 - 40 左右(将很快检查 SRD 中的最大属性)。
我需要很多基于不同事物的修饰符。有些会像上面一样简单和规则,有些有其他修饰符模式,比如每 2 级,但其他一些东西,比如 class 能力,会有自定义,但有时仍然是重复的模式。
所有机制都必须尽可能快,因为在战斗中所有内容都是实时计算的,我希望尽可能获得最佳性能。
Case 语句很长,而数学计算可能很难理解(尽管在您的情况下它们看起来很简单)。
一种非常灵活的方法是构建一个查找表 table,用答案填充它,然后通过简单的向上循环得到结果到数组中:
private static int[] ModNumByScore = new [] {
0, 0, 0, 0, 0, 0, 0,-2,-1,-1,
0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
5, 0, 6, 0, 7, 0, 8, 0, 9, 0,
10
};
private static string DetermineModNum(string value) {
int score; //convert to int.
if (!TryParse(value, NumberStyles.Integer, null, out score)) {
// Throw an exception: the string is invalid
}
if (score > 30 || score < 7) {
// Throw an exception: the score is invalid
}
return ModNumByScore[score].ToString();
}
现在您可以按照您希望的方式填充您的 table,您的代码的读者将能够通过查看 table 中的数字来判断每个分数返回的内容。
你可以通过简单的计算摆脱巨大的switch语句:
float value = Mathf.Floor(score/2) - 5;
return value.ToString();
虽然在这个阶段担心这样的优化是愚蠢的。在您有具体的东西或发现任何明显的性能问题后分析您的游戏。
编辑:如评论中所述。 Mathf.Floor
是不必要的。整数值将被自动截断(向下舍入)。
整数运算在现代 CPU 上非常快。另一方面,条件跳转可以清空执行管道并减慢执行速度。所以做数学运算可能比冗长的 switch 语句更快。与充满案例陈述的 2 屏相比,数学也将更简洁,更不容易出错。
但我想您几乎不会注意到不同方法之间的任何区别。别忘了:premature optimization is the root of all evil.
所以
int modNum = (score / 2) - 5;
显然是首选。请注意,这里执行了整数除法,它会自动截断结果。例如。 13 / 2 ==> 6
.
short
's (16-bit), int
's (32-bit) and long
s (64-bit) 都存储整数,即整数有没有小数部分。
性能差异可能可以忽略不计,数学很简单:
score = (score - score % 2) / 2 - 5;
您要做的第一件事是将成对的连续数字处理为相同的数字。这可以通过从原始数字中减去模数 2 轻松实现。
然后将结果除以 2 再次得到连续的数字。
之后你所要做的就是从中减去 5 得到你开始的方程式:
...
8, 9 => -1
10, 11 => 0
12, 13 => 1
...
我是一个真正的编码新手,想要将 Dungeons & Dragons OGL 机制移植到我的 Unity3D 项目中。
现在,我想在 C# 中为基本字符属性添加修饰符。
这些基于数字 10/11,即 20 面骰子的 "middle" 结果。
基本角色属性例如力量、智力、魅力等
您的统计数据高于 10 的越高,您获得的统计修正值就越高。 10 越低,您的修正值越低(负数)。
So, for 10 and 11 you have +0 (no modifer). Now, for:
12, 13 -> you get +1
14, 15 = +2
16, 17 = +3
18, 19 = +4
20, 21 = +5
22, 22 = +6 and so on (Character Attributes can go up to 40 and as low as 0 I guees)
For attributes below average, so below 10:
8, 9 = -1
6, 7 = -2
4, 5 = -3
2, 3 = -4
0, 1 = -5
现在,我可以在 ifs 上或像一个人一样在开关上对其进行硬编码。这是他的代码的一部分:
private string DetermineModNum(string value)
{
int score; //convert to int.
TryParse(value, NumberStyles.Integer, null, out score);
if (score > 10)
{
if (score % 2 == 0) //can be divided by 2
{
switch (score)
{
case 12:
return "1";
case 14:
return "2";
case 16:
return "3";
case 18:
return "4";
case 20:
return "5";
case 22:
return "6";
case 24:
return "7";
case 26:
return "8";
case 28:
return "9";
case 30:
return "10";
}
}
else
{
switch (score)
{
case 11:
return "0";
case 13:
return "1";
case 15:
return "2";
case 17:
return "3";
case 19:
return "4";
}
}
}
else
{
if (score % 2 == 0) //can be divided by 2
{
switch (score)
{
case 10:
return "0";
case 8:
return "-1";
}
}
else
{
switch (score)
{
case 9:
return "-1";
case 7:
return "-2";
}
}
}
return null;
}
这是不完整的,只是他的角色生成器的一部分。 但另一种方法可能是将属性除以 4。因此,我们可以将统计数据除以 4,而不是全部写下来。
例如:
强度:7(修饰符为 7 / 4 = 1,75,因此我们应将其四舍五入为 2 并将其更改为 -2)
智力:18(修改器将是 18 / 4 = 4,5,所以这只是 int 4)
在 Unity3D 游戏中哪种方法会更快?
此外,如果第二种解决方案更好,你能帮我展示一下如何在 C# 中最好地构建它吗?我只是在学习,真的可以在这里使用一些很好的例子。我需要低于 10 的修饰符为 -,高于 10 为 +,属性范围至少在 0 - 40 左右(将很快检查 SRD 中的最大属性)。
我需要很多基于不同事物的修饰符。有些会像上面一样简单和规则,有些有其他修饰符模式,比如每 2 级,但其他一些东西,比如 class 能力,会有自定义,但有时仍然是重复的模式。
所有机制都必须尽可能快,因为在战斗中所有内容都是实时计算的,我希望尽可能获得最佳性能。
Case 语句很长,而数学计算可能很难理解(尽管在您的情况下它们看起来很简单)。
一种非常灵活的方法是构建一个查找表 table,用答案填充它,然后通过简单的向上循环得到结果到数组中:
private static int[] ModNumByScore = new [] {
0, 0, 0, 0, 0, 0, 0,-2,-1,-1,
0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
5, 0, 6, 0, 7, 0, 8, 0, 9, 0,
10
};
private static string DetermineModNum(string value) {
int score; //convert to int.
if (!TryParse(value, NumberStyles.Integer, null, out score)) {
// Throw an exception: the string is invalid
}
if (score > 30 || score < 7) {
// Throw an exception: the score is invalid
}
return ModNumByScore[score].ToString();
}
现在您可以按照您希望的方式填充您的 table,您的代码的读者将能够通过查看 table 中的数字来判断每个分数返回的内容。
你可以通过简单的计算摆脱巨大的switch语句:
float value = Mathf.Floor(score/2) - 5;
return value.ToString();
虽然在这个阶段担心这样的优化是愚蠢的。在您有具体的东西或发现任何明显的性能问题后分析您的游戏。
编辑:如评论中所述。 Mathf.Floor
是不必要的。整数值将被自动截断(向下舍入)。
整数运算在现代 CPU 上非常快。另一方面,条件跳转可以清空执行管道并减慢执行速度。所以做数学运算可能比冗长的 switch 语句更快。与充满案例陈述的 2 屏相比,数学也将更简洁,更不容易出错。
但我想您几乎不会注意到不同方法之间的任何区别。别忘了:premature optimization is the root of all evil.
所以
int modNum = (score / 2) - 5;
显然是首选。请注意,这里执行了整数除法,它会自动截断结果。例如。 13 / 2 ==> 6
.
short
's (16-bit), int
's (32-bit) and long
s (64-bit) 都存储整数,即整数有没有小数部分。
性能差异可能可以忽略不计,数学很简单:
score = (score - score % 2) / 2 - 5;
您要做的第一件事是将成对的连续数字处理为相同的数字。这可以通过从原始数字中减去模数 2 轻松实现。
然后将结果除以 2 再次得到连续的数字。
之后你所要做的就是从中减去 5 得到你开始的方程式:
...
8, 9 => -1
10, 11 => 0
12, 13 => 1
...