I'm working on a Dice Application in Unity that involves randomly rotating a dice model on instantiation and then dropping it. But in order for this application to be useful I need to guarantee that the Dice Roll is close enough to random to be used for a live tabletop game. The problem I'm getting is that after some testing the dice rolls are not random at all.
Here's some pictures of a a D20 I'm working with as it looks at instantiation.
Bottom view:
Top view:
Side view 1:
Side view 2:
Side view 3:
My first attempt at randomizing the dice rolls was to use this code:
m_Instance.transform.Rotate(UnityEngine.Random.Range(0.0f, 360.0f), UnityEngine.Random.Range(0.0f, 360.0f), UnityEngine.Random.Range(0.0f, 360.0f));
But after 250,000 test rolls, the data shows that it's almost twice as likely to land on a side face than it is the top or bottom faces
After doing some digging, I found a recommendation to use this code instead:
m_Instance.transform.rotation = Quaternion.Euler(UnityEngine.Random.Range(0.0f, 360.0f), UnityEngine.Random.Range(0.0f, 360.0f), UnityEngine.Random.Range(0.0f, 360.0f));
Which gave me the exact opposite results, where faces on the top and bottom of the dice were suddenly twice as likely as those on the sides.
So my mad-scientist approach to this problem currently is to randomly pick between the two rotation methods. So that way it's randomly decided if one face will be twice as likely as another.
int randomRange = UnityEngine.Random.Range(0, 2);
if (randomRange == 1)
{
m_Instance.transform.Rotate(UnityEngine.Random.Range(0.0f, 360.0f), UnityEngine.Random.Range(0.0f, 360.0f), UnityEngine.Random.Range(0.0f, 360.0f));
}
else
{
m_Instance.transform.rotation = Quaternion.Euler(UnityEngine.Random.Range(0.0f, 360.0f), UnityEngine.Random.Range(0.0f, 360.0f), UnityEngine.Random.Range(0.0f, 360.0f));
}
Which is getting me close, but not quite perfect.
But it's not quite close enough for my liking as some faces like 10 seem favored by both methods. So my question is: is there something wrong with my code that's making the rolls not as random as I'm imagining? And if not, is there a way of rotating a dice in Unity such that the result of the rotation is suitably random enough to be used in a potentially professional setting?
EDIT: Thanks for the response DMGregory! That's definitely looking better, but I'm still seeing that 10 and 11 are favored sides after a million test rolls. with both getting a > '5.8' percent chance of landing there. Perhaps there's something wrong with my implementation of your code?
public void RandomizeDice()
{
m_Instance.transform.rotation = UniformRandomRotation();
}
Quaternion UniformRandomRotation()
{
float x0 = UnityEngine.Random.value;
float theta1 = UnityEngine.Random.Range(0f, Mathf.PI * 2f);
float theta2 = UnityEngine.Random.Range(0f, Mathf.PI * 2f);
// Make this theta2 in the range (0, PI * 1) if you want w > 0
float r1 = Mathf.Sqrt(1f - x0);
float r2 = Mathf.Sqrt(x0);
return new Quaternion(
Mathf.Cos(theta2) * r2,
Mathf.Sin(theta1) * r1,
Mathf.Cos(theta1) * r1,
Mathf.Sin(theta2) * r2);
}
public void UnRandomizeDice()
{
m_Instance.transform.rotation = Quaternion.Euler(270, 270, 270);
}