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?
CodePudding user response:
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 ^^.
CodePudding user response:
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 withinUpdate
which will execute everything until the firstyield
!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!");
}
}