- Home /
Random.range returning similar values
I want my characters to play a random idle animation at a random time. I am currently using such code (every character has this script):
function Start () {
nextIdle = Random.Range(30.0, 60.0);
animation.CrossFade("Breathe");
animation.wrapMode = WrapMode.Loop;
}
function Update () {
if (nextIdle <= 0) {
PlayRandomIdle();
nextIdle = Random.Range(30.0, 60.0);
}
else if (nextIdle > 0) nextIdle -= Time.deltaTime;
}
function PlayRandomIdle() {
var idl : int = Random.Range(1, 4);
var anim : String = "Idle";
anim += idl;
animation.CrossFade(anim, 0.7);
animation.wrapMode = WrapMode.Once;
}
However this results in the animations being played almost simultaneously. I guess this is due to the seed of the random function being linked to time (time since 1970 etc), but that doesn't really matter, what i want to know is how can i fix it so that I would really get random values. I know i could seed the generator myself, but what number should I use? Or is there a better way?
Answer by Bunny83 · Aug 19, 2012 at 01:58 PM
I'm not sure what's your actual problem. Do you have trouble with the timing when to change the idle animation, or do you have trouble with the idle animation selection? What does "played almost simultaneously" mean in this context? There is only one animation active on the same layer when you use Crossfade.
Generally those random numbers aren't actually random. It's a pseudo random sequence. However it looks totally random. If the start seed is the same, it will always produce the same sequence of numbers. That's why it's initialized with the current time so everytime you start your application you get a totally different sequence.
The numbers can really seen as random. "True" randomness has an equal distribution of all numbers in range only in infinity. If you want to pick 5 numbers from the range 0 to 4, it's possible to pick 5 times the number 2. That's random. Random doesn't mean it has to be different next time. That actually would be a sign for not being random.
If you want to cycle through your idle animations in a "random way", you can just add all indices to an array and scramble it with random so the order is random. In this case the sequence would stay the same since you always use the same random-sequence.
If you want to have a true random selection, but you want to ensure that it doesn't pick the same number twice in a row, just check for it manually:
var lastIdleAnimation : int;
var idleAnimationCount = 3;
function PlayRandomIdle()
{
var idl : int = Random.Range(0, idleAnimationCount);
if (idl == lastIdleAnimation)
idl = (idl+1) % idleAnimationCount; // just pick the next animation. If idl is the last one, wrap around
lastIdleAnimation = idl;
var anim : String = "Idle";
anim += (idl+1); // add one since your names start with 1 and not 0
// [...]
}
alternatively you can use a while loop and pick a new random number until it's different from the last one, but that's not very effective.
function PlayRandomIdle()
{
var idl : int = Random.Range(1, 4);
while (idl == lastIdleAnimation)
{
idl = Random.Range(1, 4);
}
lastIdleAnimation = idl;
var anim : String = "Idle";
anim += idl;
// [...]
}
As final note, keep in mind that Random.Range with integer values will not include the upper limit. So in your case
Random.Range(1, 4);
it will return one of those values: 1, 2, 3
B, a more Protestant way to express your first algorithm is just "add a random number one to one less than the total in question and wrap"
So if you want random numbers between 0 and 9 inclusive:
new = (old + (random from 1 to 9 inclusive) % 10)
eg in one line, always works,
new = (old + randomInclusive(1,count-1)) % count;
(IANAS but I'm pretty sure the distribution is still normal.)
(Sorry for the TYPO - fixed)
B, regarding your fourth paragraph. You're just describing the age-old problem of "dealing a deck of cards."
The best algo to explain to beginners is just:
Create a List of the 52 items. [see footnote]
Pick one randomly -- ie, use a random number between 0 and the current length
Actually delete that item form the List (your "deck" gets shorter each time)
has your list length reached zero? just recreate it as in step one
This can be done in one, or two, lines of code in all languages.
This will give you - well it's obvious what it gives you. This could be the "feel" you want depending on your situation.
NOTE - be aware that conceivably you might end and then start with the same number, when you swap between sets. If for some reason you want to further avoid that issue, on the "first one" use the technique I mention in the previous comment.
[footnote] (intriguingly you don't even have to set the values of the list to anything. but if it makes you feel more comfortable just set each value the same as the index. In the case of say "cards" you can just set each one to the relevant card, PNG or whatever the hell you want. As a programmer you gotta understand you're just using the power of List to suck out items, hooray.)
Regarding my actual problem, which you asked about: I have this script applied to all of the characters on the scene and what made me ask the question was that when I initially tested it all of the characters started animating at the same time (+- a couple of seconds maybe) so I thought that I might have misunderstood something. However it probably was just my bad luck :D
regarding all of the other stuff, yeah I'm familiar with all these concepts and I was even going to write something like that, but I thought I'd ask the question first just in case
you can check what values are being randomly created by adding a Debug line afterwards :
nextIdle = Random.Range(30.0, 60.0);
Debug.Log( this.gameObject.name + " random value = " + nextIdle );