I have created a practice project to figure out how to get a random animation to play every time a button is clicked until all the animations have played with no animation playing twice. Ten animations are assigned to anims[] in the inspector. So far I have come up with this script:
var anims : AnimationClip[];
var randomAnims : AnimationClip[];
var order = new ArrayList();
var currentClip : AnimationClip;
var chosenOne: int;
var orderString = new ArrayList();
function OnGUI()
{
if (GUI.Button (Rect (105, 457, 100, 22), "Next"))
{
PlayAtRandom();
}
}
function PlayAtRandom()
{
for (var i = anims.length - 1; i > 0; i--)
{
chosenOne = Random.Range(0,anims.length);
var next = i.ToString();
var ind = orderString.IndexOf(next);
if (ind < 0)
{
orderString.Add(next);
order.Add(i);
randomAnims[chosenOne] = anims[order*];*
*currentClip = randomAnims[chosenOne];*
*animation.Play(currentClip.name);*
*}*
*}*
*}*
*```*
_<p>When I press play, and press the "Next" button, it generates an error saying "ArgumentOutOfRangeException: Index is less than 0 or more than or equal to the list count." Pointing to line 26 "randomAnims[chosenOne] = anims[order*];".</p>*_
_*<p>What am I doing wrong here?*_
_*Thank you.</p>*_
3 Answers
3
If you're willing to rewrite your code, consider this example which uses shuffling and queues. It is still possible that two animations come after each other if the first order was a, c, b and the other order is b, a, c. However you can easily modify this code to know the last used animation and swap it for the one in the other end of the array. That way you're guaranteed to never have same animation appear twice in a row.
import System.Collections.Generic;
var anims : AnimationClip[];
private var queue : Queue.<AnimationClip>;
// Added for comment discussion.
private var lastAnim : AnimationClip;
function OnGUI()
{
if (GUI.Button (Rect (105, 457, 100, 22), "Next"))
PlayNext();
}
function PlayNext()
{
if (queue == null || queue.Count == 0)
CreateQueue();
// Changed for comment discussion.
lastAnim = queue.Dequeue();
animation.Play(lastAnim.name);
}
function CreateQueue()
{
Shuffle(anims);
Shuffle(anims);
Shuffle(anims);
// Added for comment discussion.
// Swap the first and last if the first is the same as
// was used last animation.
if (anims[0] == lastAnim)
{
var last = anims.Length - 1;
var tmp = value[0];
value[0] = value[last];
value[last] = tmp;
}
queue = new Queue.<AnimationClip>(anims);
}
function Shuffle(value : AnimationClip[])
{
for (var i = 0; i < value.Length; ++i)
{
var r = Random.Range(0, value.Length);
var tmp = value*;*
_value *= value[r];*_
_*value[r] = tmp;*_
_*}*_
_*}*_
_*```*_
randomAnims[chosenOne] = anims[order*];*
*```*
*<p>You must have arrays of inconsistent length. </p>*
*<p>You're using an index based of the length of <strong>anims</strong> but it's unclear if <strong>order</strong> is at least as long as anims, and if <em>any value in <strong>order</em></strong> is less than <strong>anims.length</strong>. Finally, your <strong>randomAnims</strong> also must be at least the size of <strong>anims</strong>. Are they?</p>*
_<p>Since you seem to be adding items to order procedurally, <strong>order.Add(i);</strong>, then try to index into it <strong>order*</strong>, and <strong>i</strong> starts as the highest index, this looks like the place you're getting errors. Consider this with literal values:</p>*_
_*```*_
_*order.Add(30);*_
_*randomAnims[chosenOne] = anims[ order[30] ];*_
_*```*_
_*<p>order only contains one item in the first iteration still you're trying to access the last...</p>*_
Change the line of code that reads...
chosenOne = Random.Range(0,anims.length);
to
chosenOne = Random.Range(0,anims.length - 1);
You are trying to get a random animation, but since Random.Range takes a 0 based value, you need to put in the -1, so it will make sure you always getting a range within the bounds of the array.
You said, "However you can easily modify this code to know the last used animation and swap it for the one in the other end of the array." This is exactly what I need help on! How would I go about doing that?
– anon69168037Yes! I do! :) Just keep a private variable that you set each time you dequeue. Then in CreateQueue you can see if the first item in the array is the one (just before creating the queue). If it is, then swap it with the last item. See the shuffle function where I swap each item with a random item how to swap a value. You need a temp value (tmp) just like I did.
– StatementCheck the updated code.
– StatementThis is very good. Thanks. I need to make sure that no animation that has played before ever plays again. So I guess the button text should change to "Stop" once it reaches the very end and when clicked, stops the current animation.
– anon69168037Yeah, you could just create a queue and run it until it's depleted. When its empty you've played all possible animations without any duplicates (assuming the array doesn't contain duplicates)
– Statement