简体   繁体   中英

How to handle a random spawner of obstacles in unity?

I'm trying to handle a object spawner but it isn't work as i wish. It should create a new object every time my timer pass the maxTime of the object, but it does it just once and in the wrong position (out of my range). Here is my code:

public class Spawner : MonoBehaviour {

    public float maxTime = 10;
    private float timer = 0;
    public GameObject obstacle;
    private float width = (Screen.width)/2; //It doesn't recognize width as the variable that refeers the width of the screen, so i tried it
    private float maxTime0 = 10;
    public int pontos = 5;
    public float score;

    void Start() {
        GameObject new_obstacle = Instantiate(obstacle);
        new_obstacle.transform.position = transform.position + new Vector3(Random.Range(-width, width), 0, 0);
    }

    void Update()
    {
        if(timer > maxTime) {
            GameObject new_obstacle = Instantiate(obstacle);
            new_obstacle.transform.position = transform.position + new Vector3(Random.Range(-width, width), 0, 0);
            if(new_obstacle.transform.position.y < (Screen.height - 7)){
                DestroyImmediate(new_obstacle, true);
            }
            timer = 0; 
            maxTime = 0.9f * maxTime;
        }
        score += pontos; 
        timer += Time.deltaTime; 
    }
}

It sounds like the main issue is that you spawn the object in pixel coordinates since Screen.width is the Screens width in pixels .

But what you rather want is spawning in the extends of the Camera 's frustrum.

Unity provides a way of finding that Frustrum Size At Distance .

Since you then also check

   if(new_obstacle.transform.position.y < (Screen.height - 7))

to destroy your object I bet your Update indeed spawns the object but immediately destroys them!

Reason: Your spawner is in World coordinates and probably always under the Screen.height - 7 which is in pixel coordinates!

Since you don't have this check in Start it is the only time that it "works" for you.

If you really need this check at all you would need to convert the position into screen pixel space using Camera.WorldToScreenPoint

Camera camera;

void Start() 
{
    camera = Camera.main;
    // assuming that your camera points exactly forward (2D game like)
    var distance = Mathf.Abs(transform.position.z - camera.tramsform.position.z);
    var frustumHeight = 2.0f * distance * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
    var frustrumWidth = frustumHeight * camera.aspect;

    // not anymore in pixel space but frustrum width in world coordinates
    // at the z distance of the spawner
    width = frustrumWidth / 2f;


    var new_obstacle = Instantiate(obstacle);
    new_obstacle.transform.position = transform.position + Vector3.right * Random.Range(-width, width);
}

void Update()
{
    if(timer > maxTime) 
    {
        var new_obstacle = Instantiate(obstacle);
        new_obstacle.transform.position = transform.position + Vector3.right * Random.Range(-width, width);

        if(camera.WorldToScreenPoint(new_obstacle.transform.position).y < Screen.height - 7)
        {
            DestroyImmediate(new_obstacle, true);
        }
        
        timer = 0; 
        maxTime = 0.9f * maxTime;
    }

    // as mentioned here multiply by Time.deltaTime in order to
    // convert from pontos per frame into pontos per second
    score += pontos * Time.deltaTime; 
    timer += Time.deltaTime; 
}

and yes then you could use a Coroutine which is often a bit easier to maintain .. a matter of taste actually

void Start() 
{
    camera = Camera.main;
    // assuming that your camera points exactly forward (2D game like)
    var distance = Mathf.Abs(transform.position.z - camera.tramsform.position.z);
    var frustumHeight = 2.0f * distance * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
    var frustrumWidth = frustumHeight * camera.aspect;

    // not anymore in pixel space but frustrum width in world coordinates
    // at the z distance of the spawner
    width = frustrumWidth / 2f;


    StartCoroutine(SpawnRoutine());
}

IEnumerator SpawnRoutine()
{
    // looks dangerous but is fine for Coroutines as long as you yield somewhere
    while(true)
    {
        var new_obstacle = Instantiate(obstacle);
        new_obstacle.transform.position = transform.position + Vector3.right * Random.Range(-width, width);

        if(camera.WorldToScreenPoint(new_obstacle.transform.position).y < Screen.height - 7)
        {
            DestroyImmediate(new_obstacle, true);
        }
       
        // In Coroutines a yield tells Unity to "pause" here, 
        // render this frame and continue from here in the next frame
        yield return new WaitForSeconds(maxTime);

        maxTime = 0.9f * maxTime;
    }
}

private void Update()
{
    score += pontos * Time.deltaTime; 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM