for() loop skipping the first entry. Scripting error?

Hey guys - thanks for looking at my question.

I'm making a small cardgame. I have a script set up that 'deals' the card at the beginning. It's supposed to 1. place the cards in the start position (for solitaire) and 2. assign a random cardTexture to it.

This works perfectly, but the loop seems to skip the very first card, which gets dealt without any material applied.

I'll walk you through my code:

  • depending on number of the card, get instantiated at a certain place
  • pick a random element of the cardtypes array (Two of Spades, for instance. It has the same order as the textures array)
  • create a material, apply the texture that corresponds to the cardtype
  • apply the material to the card and apply the cardtype as the name
  • delete the material and cardtype from the arrays so they wont be re-used

All these steps work for all cards, except the very first card (i=0) skips the applying the material step for some reason.. If I check the arrays at the end they are empty though..

function Start () {

    for(i=0;i<52;i++){

        if(i<23) Instantiate(card,Vector3(0,i*0.01,3),Quaternion.identity);
        else if(i<30) Instantiate(card,Vector3((12-2*(i-23)),0.00,-0.0),Quaternion.identity);
        else if(i<36) Instantiate(card,Vector3((12-2*(i-30)),0.01,-0.3),Quaternion.identity);
        else if(i<41) Instantiate(card,Vector3((12-2*(i-36)),0.02,-0.6),Quaternion.identity);
        else if(i<45) Instantiate(card,Vector3((12-2*(i-41)),0.03,-0.9),Quaternion.identity);
        else if(i<48) Instantiate(card,Vector3((12-2*(i-45)),0.04,-1.2),Quaternion.identity);
        else if(i<50) Instantiate(card,Vector3((12-2*(i-48)),0.05,-1.5),Quaternion.identity);
        else if(i<51) Instantiate(card,Vector3((12-2*(i-50)),0.06,-1.8),Quaternion.identity);

        //pick a rendom element from the cardtypes array
        var randomPick = Random.Range(0,51-i);

        //create a material and apply a texture from the cardTextures array - which has the same order as cardtypes. Also rename the card.
        var cardMaterial : Material = new Material(Shader.Find("Diffuse Detail"));
        cardMaterial.mainTexture = cardTextures[randomPick];        
        card.renderer.material = cardMaterial;
        card.name = ""+cardTypes[randomPick];

        //remove the element from the arrays
        cardTypes.RemoveAt(randomPick); 
        cardTextures.RemoveAt(randomPick);  

        yield new WaitForFixedUpdate ();
    }

    Debug.Log(""+cardTextures); //this is EMPTY so all textures are selected and deleted, why not applied? :S

}

Many thanks in advance!

for(i=0;i<52;i++){

    if(i<23) Instantiate(card,Vector3(0,i*0.01,3),Quaternion.identity);
    else if(i<30) Instantiate(card,Vector3((12-2*(i-23)),0.00,-0.0),Quaternion.identity);
    else if(i<36) Instantiate(card,Vector3((12-2*(i-30)),0.01,-0.3),Quaternion.identity);
    else if(i<41) Instantiate(card,Vector3((12-2*(i-36)),0.02,-0.6),Quaternion.identity);
    else if(i<45) Instantiate(card,Vector3((12-2*(i-41)),0.03,-0.9),Quaternion.identity);
    else if(i<48) Instantiate(card,Vector3((12-2*(i-45)),0.04,-1.2),Quaternion.identity);
    else if(i<50) Instantiate(card,Vector3((12-2*(i-48)),0.05,-1.5),Quaternion.identity);
    else if(i<51) Instantiate(card,Vector3((12-2*(i-50)),0.06,-1.8),Quaternion.identity);

I don't know if this is your problem (it affects that last card) but your final iteration doesn't fit any of these specifications. `i = 51` will be the last iteration and as you can see that doesn't meet any requirements.

And this line:

var randomPick = Random.Range(0,51-i);

if i = 51, then this will always equal 0. I'm not sure if that's what you wanted, but if another iteration happened to choose 0, then there will be no texture to go along with this.

I think the problem here is actually the order in which you create the card and apply the texture. You first instantiate a card from a prefab 'card' presumably declared elsewhere in the script:

if(i<23) Instantiate(card,Vector3(0,i*0.01,3),Quaternion.identity);

and then later change the material attached to the prefab:

card.renderer.material = cardMaterial;

whereas you really want to change the material attached to the card you just instantiated. What you are in effect doing is changing the prefab, so that the next time through the loop, this altered prefab is spawned. This explains why the first card doesn't get textured, but the rest do.

The easiest way to fix this is to swap the order in which you instantiate the card, and attach a texture, so that you attach the texture first. However, changing your prefab each iteration isn't an ideal method, and you would be better off saving a reference to the last spawned card and changing that material attached.

I've put the fixed code below. The changes are the definition of the SpawnCard var, and assigning the result of the instantiate calls to that var, this is the card which has just been created. Where you assigned the texture and name of the card, I've changed 'card' to 'SpawnedCard'. Hope this helps:

function Start () {

    for(i=0;i<52;i++){

        var SpawnedCard;
        if(i<23) SpawnedCard = Instantiate(card,Vector3(0,i*0.01,3),Quaternion.identity);
        else if(i<30) SpawnedCard = Instantiate(card,Vector3((12-2*(i-23)),0.00,-0.0),Quaternion.identity);
        else if(i<36) SpawnedCard = Instantiate(card,Vector3((12-2*(i-30)),0.01,-0.3),Quaternion.identity);
        else if(i<41) SpawnedCard = Instantiate(card,Vector3((12-2*(i-36)),0.02,-0.6),Quaternion.identity);
        else if(i<45) SpawnedCard = Instantiate(card,Vector3((12-2*(i-41)),0.03,-0.9),Quaternion.identity);
        else if(i<48) SpawnedCard = Instantiate(card,Vector3((12-2*(i-45)),0.04,-1.2),Quaternion.identity);
        else if(i<50) SpawnedCard = Instantiate(card,Vector3((12-2*(i-48)),0.05,-1.5),Quaternion.identity);
        else if(i<51) SpawnedCard = Instantiate(card,Vector3((12-2*(i-50)),0.06,-1.8),Quaternion.identity);

        //pick a rendom element from the cardtypes array
        var randomPick = Random.Range(0,51-i);

        //create a material and apply a texture from the cardTextures array - which has the same order as cardtypes. Also rename the card.
        var cardMaterial : Material = new Material(Shader.Find("Diffuse Detail"));
        cardMaterial.mainTexture = cardTextures[randomPick];        
        SpawnedCard.renderer.material = cardMaterial;
        SpawnedCard.name = ""+cardTypes[randomPick];

        //remove the element from the arrays
        cardTypes.RemoveAt(randomPick); 
        cardTextures.RemoveAt(randomPick);  

        yield new WaitForFixedUpdate ();
    }

    Debug.Log(""+cardTextures); //this is EMPTY so all textures are selected and deleted, why not applied? :S

}

Hi,

I would suggest you to do this line out of the for()

var cardMaterial : Material = new Material(Shader.Find("Diffuse Detail"));

I mean, before the for()

I don't see it in the code you have posted, but make sure you don't ever check anything like this:

if ( i ) {
  doSomething();
}

If i is equal to zero this will evaluate as false. I had a similar situation where one iteration of my loop was getting skipped because i represented an offset into an array that I looked up. I thought was making sure that the lookup function returned a valid result, but 0 was a valid result that was getting skipped.