0
\$\begingroup\$

I am making a 3D infinite runner game in Unity in C#. When the player has reached the end of the tile it should spawn another tile and then delete the previous tile. When the player reached the end of the first tile it deletes that tile and spawns another(just like it should), but when the player reached the second tile is does nothing. Does someone have a solution?

Thanks

Here is my code: Old:

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

public class Terrain : MonoBehaviour
{
    private List<GameObject> objs = new List<GameObject>();
    public GameObject terrain;
    public GameObject terrain2;
    public GameObject player;
    //private float z = 4130.875f;
    private float safeZone = 260.0f;
    private float tileLength = 260.0f;
    private int animTilesCount = 2;
    private Vector3 position;
    void Start()
    {
        objs.Add(terrain);
        objs.Add(terrain2);
        for (int i = 0; i < animTilesCount; i++)
        {
            spawnTile();
        }
    }
    void Update()
    {
        if ((objs.Count > 1) && (player.transform.position.z - objs[0].transform.position.z > safeZone))
        {
            spawnTile();
            DeleteTile();
        }
    }

    public void spawnTile()
    {
        position = objs[objs.Count - 1].transform.position;
        position.z += tileLength; //21, -13
        position.x += 21;
        position.y += -13;

        GameObject go = Instantiate<GameObject>(
                      terrain,
                      position,
                      terrain.transform.rotation);

        go.transform.SetParent(transform);

        objs.Add(go);
    }

    public void DeleteTile()
    {
        Destroy(objs[0]);
        objs.RemoveAt(0);
    }
}

New:

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

public class Terrain : MonoBehaviour
{
    [SerializeField]
    private List<GameObject> objs = new List<GameObject>();
    public GameObject terrainPrefab;
    public GameObject player;
    public GameObject terrain;
    public GameObject terrain2;
    //private float z = 4130.875f;
    private float safeZone = 260.0f;
    private float tileLength = 260.0f;
    private int animTilesCount = 2;
    private Vector3 position;
    void Start()
    {
        objs.Add(terrain);
        objs.Add(terrain2);
        for (int i = 0; i < animTilesCount; i++)
        {
            spawnTile();
        }
    }
    void Update()
    {
        if ((objs.Count > 1) && (player.transform.position.z - objs[0].transform.position.z > safeZone))
        {
            Debug.Log("Hello");
            spawnTile();
            DeleteTile();
        }
    }

    public void spawnTile()
    {
        position = objs[objs.Count - 1].transform.position;
        position.z += tileLength; //21, -13
        position.x += 21;
        position.y += -13;

        GameObject go = Instantiate<GameObject>(
                      terrainPrefab,
                      position,
                      terrainPrefab.transform.rotation);

        go.transform.SetParent(transform);

        objs.Add(go);
    }

    public void DeleteTile()
    {
        Destroy(objs[0]);
        objs.RemoveAt(0);
    }
}
\$\endgroup\$
  • \$\begingroup\$ Why do you still have terrain and terrain2 variables? Starting with just a single terrain already in obj will work, since you're adding more in your animTilesCount loop anyway. \$\endgroup\$ – DMGregory Aug 3 '18 at 14:35
0
\$\begingroup\$

You're trying to use the first tile in the map as your source for spawning new tiles.

GameObject go = Instantiate<GameObject>(terrain, ...

But the first tile in the map is also the first one you destroy when the player gets far enough.

Destroy(objs[0]);

Once it's destroyed, you no longer have a source tile to use for spawning new copies. So no future tiles can spawn.

So, you have two different jobs here. Separate them.

  • Source for spawning tiles: prefab

    Take one of your tile objects and drag it into your Project folder. This creates what's called a Prefab asset. It's an archetype of the object that exists outside your scene, and you can use it as the source object to copy from when you want to Instantiate new copies.

    Prefabs are also useful for batch-updating many similar objects across multiple scenes: update the prefab, and its spawned copies will inherit the changes.

    Add a public GameObject terrainPrefab; variable to your script, and assign your prefab object from the Project folder into it.

    Change your spawning line to use this prefab: Instantiate<GameObject>(terrainPrefab, ...

  • Tiles currently in your map: objs list

    Add a [SerializeField] attribute above your private List<GameObject> objs = new List<GameObject>(); line. This will make it show up in the Inspector, so you can add your initial level tiles to it directly.

    Now you can delete your terrain and terrain2 variables and the calls to Add() them on start. They're already part of the list because you put them there.

Now you're never using a scene object / list member as a spawning source, and you can't accidentally delete your source tile.

\$\endgroup\$
  • \$\begingroup\$ Still doesn't work(am getting the same first tile gets deleted but second doesn't get deleted) \$\endgroup\$ – ProgrammerProgrammerIOSAN Aug 3 '18 at 13:58
  • \$\begingroup\$ Works just fine when I test it locally, so your problem may be in some detail you haven't shared in the question. You're sure you added both of your starting tiles to your objs array? Try to reproduce the problem in a brand new project using just the one script. Then edit your question to include that version of the script and complete information about how you've set up the scene and configured your inspector parameters. This is called a minimal complete verifiable example. If we can't reproduce & verify your problem, we won't be able to help you. \$\endgroup\$ – DMGregory Aug 3 '18 at 14:11
  • \$\begingroup\$ I am very sorry. It works just fine. \$\endgroup\$ – ProgrammerProgrammerIOSAN Aug 3 '18 at 15:56
0
\$\begingroup\$

I can see at least one other problem with your code. When spawning a new tile, you set its rotation by accessing terrain.transform after destroying it. This would cause a null reference exception and fail to spawn the tile.

Are there any errors being printed to the console?

\$\endgroup\$
  • \$\begingroup\$ Yes:MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object. When I click on the error it brings me to the line where I say Instantiate...terrain.transform.rotation \$\endgroup\$ – ProgrammerProgrammerIOSAN Aug 3 '18 at 14:31
  • \$\begingroup\$ Updated the code \$\endgroup\$ – ProgrammerProgrammerIOSAN Aug 3 '18 at 15:23
  • \$\begingroup\$ I don’t suppose terrainPrefab and terrain are pointing to the same GameObject in the scene? \$\endgroup\$ – Ed Marty Aug 3 '18 at 17:52

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.