在我的游戏中,我将使用随机值来选择玩家从宝箱中获得的奖励。问题是你可以快速保存和快速加载,这意味着他们可以不断重新加载以重新随机化,直到他们得到他们想要的东西。有什么方法可以获取我的
Random
对象的当前种子值,并可能在加载时返回到同一点,以便它们不能滥用随机化?
这是不可能的。
相反,您可以使用二进制序列化来序列化
Random
实例。Random
是 [Serializable]
,种子和内部状态将持续存在。
但是请注意,保存随机种子可以让你的玩家预测未来,如果你允许在战斗中保存,这将非常有用。
另请注意,用户仍然可以保存、打开宝箱、加载、执行生成随机数的操作,然后从宝箱中获取不同的物品。
不确定是否获取种子,但您可以保存为
Random
对象提供的值。请记住,有两个构造函数。第二个是 Random(Int32)
,所以如果您自己设置种子(一个足够简单的值是 Environment.TickCount),您可以在将该值传递给构造函数之前将该值存储在某处。如果您还没有阅读过,请查看 MSDN 文档:https://learn.microsoft.com/en-us/dotnet/api/system.random。
事实上,
Seed
未存储,因为它与初始化后的算法无关。它的衍生物之一 mj
存储在 SeedArray
中,您可以检查使用反射来比较两个 Random
实例:
int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
mj = MSEED - subtraction;
SeedArray[55]=mj;
因此,您所要做的就是检查
SeedArray
中的最后一个元素(索引 55)。这是唯一使用Seed
的地方。
[从已删除的问题中移动答案如何确定两个 Random 实例是否具有相同的种子?]
您可以将随机奖励计算为以下哈希函数:
此方法的优点是,无论您保存和重玩多少次,给定的宝箱在给定的游戏中始终会产生相同的奖励,即使宝箱以不同的顺序打开,或者以不同的顺序触发其他“随机”事件。订单。此外,每个箱子的奖励都独立于其他箱子的奖励,只要散列中使用的箱子的属性是独立的。
在下面的示例中,
GetRewardId
生成奖励ID作为游戏种子与箱子的x坐标异或的散列。它使用 Random
执行哈希,使用哈希输入作为 Random
对象的种子,并将第一个随机生成的数字作为输出。
private static int GetRewardId(int seed, float coord, int numRewards)
{
int tempSeed = BitConverter.ToInt32(BitConverter.GetBytes(coord), 0) ^ seed;
return new Random(tempSeed).Next(numRewards);
}
int seed = new Random().Next();
int numDifferentRewards = 5;
float xCoordinate = chest.Position.X;
int rewardId = GetRewardId(seed, xCoordinate, numDifferentRewards);
如果您的许多箱子可能在 sace 中对齐,您可能需要通过与 y 和/或 z 坐标进行异或来选择不同的属性,或使用其他维度。
我可能只是按照 MSDN 使用它:http://msdn.microsoft.com/en-us/library/ctssatww.aspx
Random(seed)
其中种子是我从存储中加载的一些值。
不幸的是,在 Microsoft 的参考实现中,无参数 ctor 的种子值甚至没有保存,更不用说公开访问了: http://referencesource.microsoft.com/#mscorlib/system/random.cs,bb77e610694e64ca
但是,正如您在参考实现中也可以看到的那样,您可以传入的值(可能应该 - 我知道我这样做),就像他们所做的那样,是:
Environment.TickCount
因此,将其保存到一个变量中,然后将该变量传递给带有 arg 的 ctor,现在您就知道了种子。 不是事后,但这应该足以满足您的意图。
这仅与切线相关,但如果有人想知道为什么
Random
没有名为 Seed
的属性或名为 GetSeed()
的方法,我愿意打赌这可能是出于安全考虑:您想向外界公开您的“随机”数字生成器的内部工作原理吗?绝对不是!否则,某些客户端代码可能会四处寻找,直到获得您正在使用的值,然后用它们做一些令人讨厌和意想不到的事情。
您可以创建自定义 Random 类来获取种子 公共类 DebugRandom { 私人随机随机; 私有 int 种子;
public static readonly int defaultSeed = Environment.TickCount;
public DebugRandom(int? seed = null)
{
this.seed = seed ?? defaultSeed;
random = new Random(this.seed);
}
public int Seed => seed;
public int Next()
{
return random.Next();
}
public int Next(int minValue, int maxValue)
{
return random.Next(minValue, maxValue);
}
public double NextDouble()
{
return random.NextDouble();
}
}
我建议您生成一个随机数并将其用作真正的随机数生成器的种子数。通过这种方法,您将获得一个实际上是随机数的种子号,您可以保存种子号以供进一步使用。