-1

I have created a procedurally generated 'tiled floor' in unity3d, using a block prefab asset and script as follows:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class Wall : MonoBehaviour
{
    public GameObject block;
    public int width = 10;
    public int height = 10;
    public int timeDestroy = 1;
    List<GameObject> blockList = new List<GameObject>();
    

    void Start(){

        for (int y=0; y<height; ++y)
        {
            for (int x=0; x<width; ++x)
            {
                Vector3 offset = new Vector3(x, 0, y);
                GameObject hello= (GameObject)Instantiate(block, transform.position + offset, Quaternion.identity);
                blockList.Add(hello);
            }
        }
        
        StartCoroutine(SelfDestruct());

    }

    void Update()
    {
        SelfDestruct();
    }
    
    IEnumerator SelfDestruct()
    {
        yield return new WaitForSeconds(timeDestroy);
    
        Destroy(blockList[UnityEngine.Random.Range(0,(width*height))]);
        }
    }


When I run the game, one of the 100 blocks has been destroyed, but then nothing happens. From my script, I was expecting one block to destroy every second, as defined by: yield return new WaitForSeconds(timeDestroy); where int timeDestroy = 1;

and repeat until all the blocks are gone - game over. How can I change my script so the 100 gameObjects are destroyed one after another, until none are left?

UnholySheep
  • 3,967
  • 4
  • 19
  • 24
JmeMcQ
  • 1

2 Answers2

0

you need to put your IEnumerator SelfDestruct(); in a while loop:

IEnumerator SelfDestruct()
{
    while (1<2)
    {
        Destroy(blockList[UnityEngine.Random.Range(0, (width * height))]);
        yield return new WaitForSeconds( timeDestroy );
    } 
}

that way it will resume destroying the blocks, and you need to remove the SelfDestruct(); in Update.

Ps. Thanks derHugo I have forgotten that ^^.

  • 1
    You mean `while(true)`? ^^ using `1<2` is the funniest way I have seen someone writing `true` so far ^^ also OP is still calling this each and every frame which is the main issue ;) – derHugo Apr 09 '22 at 20:34
  • You need to use `StartCoroutine` in order to start it as a Coroutine .. this is still c# though and calling it without executes everything until the first `yield` statement -> still destroys the objects – derHugo Apr 11 '22 at 06:24
  • Note that you will also have to remove the objects from the list otherwise you might try to destroy the same object twice – derHugo Apr 11 '22 at 06:25
0

A couple of issues here

  • you start a coroutine once but it doesn't really wait for anything (only at the end). There is no repetition anywhere. You probably wanted to use a loop there

  • you call the SelfDestruct every frame within Update which will execute everything until the first yield!

  • you potentially try to destroy the same object multiple times since you never remove them from your list.

Actually the entire thing can be one single coroutine!

I would also use Linq OrderBy to randomize the order of blocks once and then simply iterate and destroy them one by one in already randomized order.

Something like e.g.

using System.Linq;
...

public class Wall : MonoBehaviour
{
    public GameObject block;
    public int width = 10;
    public int height = 10;
    public int timeDestroy = 1;
    
    // Yes this is valid and Unity will automatically
    // run this as a Coroutine!
    private IEnumerator Start()
    {
        // Don't even need this as a field only locally
        // already pre-allocate the needed amount
        var blockList = new List<GameObject>(height * width);

        for (var y = 0; y < height; ++y)
        {
            for (var x = 0; x < width; ++x)
            {
                var offset = new Vector3(x, 0, y);
                var newBlock = Instantiate(block, transform.position + offset, Quaternion.identity);
                blockList.Add(new Block);
            }
        }
        
        // shuffle the blocks once
        var randomizedBlocks = blockList.OrderBy(blockInstance => Random.value);
        // then simply iterate them in already randomized order
        foreach (var blockInstance in randomizedBlocks)
        {
            yield return new WaitForSeconds (timeDestroy);

            Destroy(blockInstance);
        }

        // Now you additionally also have direct control when they are all destroyed
        Debug.Log("All blocks have been destroyed!");
    }
}
derHugo
  • 83,094
  • 9
  • 75
  • 115