[SOLVED] C# - Trouble with Instantiate

So I’m working on making a ‘wilderness’ that generates new stuff as you travel through it. Basically I have a grid of biomes which I plan to later associate with a list of terrain prefabs that have slots for different types of items and…well you know. Big ideas! Anyway, things are going okay so far. But I got stuck on actually placing the terrain in the scene we’re moving into.

I have a game object in the ‘main menu’ screen of the game to which I’ve attached the following script that serves as the ‘wilderness handler’.

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


public class Environment : MonoBehaviour {

    public GameObject grassterr;
    public GameObject dirtterr;
    public GameObject pathterr;
    public GameObject makeTerr;

    private Scene myScene;
    private Scene newScene;
    private string curName;
    private string newName;
    private string newTerr;
    private int oldx;
    private int oldy;
    private int newx;
    private int newy;

    string[][] wilderness = new string[][] { // Set up terrain-type grid
        new string[] {"grass", "dirt", "path"},
        new string[] {"dirt", "path", "grass"},
        new string[] {"path", "grass", "dirt"}
    };

    string[][] tiles = new string[][] { // Associate scene names with grid coordinates
        new string[] {"0.0", "0.1", "0.2"},
        new string[] {"1.0", "1.1", "1.2"},
        new string[] {"2.0", "2.1", "2.2"}
    };

    void Awake() {
        DontDestroyOnLoad (transform.gameObject); // Keep this script around!
    }

    // Use this for initialization
    void Start () {
      
    }
  
    // Update is called once per frame
    void Update () {
      
    }

    public void ChangeScene(int x, int y) { // Called when moving to a new tile in the wilderness
        myScene = SceneManager.GetActiveScene (); // Get the current scene
        curName = myScene.name; // Scenes where this function might run are named 'x.y', so for example 1.2 or 5.3
        string[] coords = curName.Split('.'); // Convert the scene name to its x and y coordinates
        oldx = int.Parse (coords [0]);
        oldy = int.Parse (coords [1]);
        newx = oldx + x; // Determine where we're moving to
        newy = oldy + y;
        newName = tiles[newx][newy]; // Get the name of the new scene
        newTerr = wilderness[newx][newy]; // Determine the necessary terrain type
        if (newTerr == "grass") { // Associate makeTerr with the proper terrain prefab
            makeTerr = grassterr;
        }
        if (newTerr == "dirt") {
            makeTerr = dirtterr;
        }
        if (newTerr == "path") {
            makeTerr = pathterr;
        }
        SceneManager.LoadScene (newName, LoadSceneMode.Single); // Move into the next scene
        Instantiate (makeTerr, new Vector3(0,0,0), Quaternion.identity); // Place the terrain in the new scene
    }
      
}

Everything is good until that last line. It doesn’t actually create the proper terrain type and place it at origin with no rotation, which is what I THINK it’s supposed to do. Anyone have ideas on why this doesn’t work?

It doesn’t throw any errors, and I’ve properly associated the prefabs with the necessary variables. When I’m in play mode, it moves me into the right scene, but I just fall endlessly with no terrain. I tried pausing and checking the hierarchy to see if the terrain object is even there, and it’s not.

PS I know the three ifs is bad form, haha. I’m not worried about handling/fetching biome prefabs yet, and just using three basic ones to get the logic/mechanics down first.

As the docs state, the loading “completes the next frame”. This means that you’re instantiating your terrain in this scene, and then the next scene gets loaded.

Check that like this:

SceneManager.LoadScene (newName, LoadSceneMode.Single); // Move into the next scene
var instantiatedTerrain = Instantiate (makeTerr, new Vector3(0,0,0), Quaternion.identity); // Place the terrain in the new scene

Debug.Log("Spawned terrain in: " + instantiatetTerrain.scene.name);

Two solutions:
a) easy but kinda dumb: make the instantiated terrain DontDestroyOnLoad. You’ll have to delete it manually later
b) a bit harder but less dumb: wait until the scene has loaded and then spawn the terrain based on the current scene’s name. You’ll want to hook some script up to SceneManager.SceneLoaded

Oh thanks! I feel like I should have realized that. I guess I assumed since I had the line to load the scene prior to the instantiate line it would go to the new one, not the old one. I’ll work on that second option now!

Thanks so much for your help! Got home from work and went to fiddling and I now have it properly generating the terrains in the right spot. I ran into a bunch of questions about how exactly the SceneLoaded bit worked, so I’m going to put the final code snippet here in case anyone else has some of the same issues.

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


public class Environment : MonoBehaviour {

    public GameObject grassterr;
    public GameObject dirtterr;
    public GameObject pathterr;
    public GameObject makeTerr;

    private Scene myScene;
    private Scene newScene;
    private string curName;
    private string newName;
    private string newTerr;
    private int oldx;
    private int oldy;
    private int newx;
    private int newy;

    string[][] wilderness = new string[][] { // Set up terrain-type grid
        new string[] {"grass", "dirt", "path"},
        new string[] {"dirt", "path", "grass"},
        new string[] {"path", "grass", "dirt"}
    };

    void Awake() {
        DontDestroyOnLoad (transform.gameObject); // Keep this script around!
    }

    // Use this for initialization
    void Start () {
        SceneManager.sceneLoaded += OnSceneLoaded;
    }
   
    // Update is called once per frame
    void Update () {
       
    }

    void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
        if (scene.name == "mainmenu") {
           
        } else {
            curName = scene.name;
            string[] coords = curName.Split('.'); // Convert the scene name to its x and y coordinates
            newx = int.Parse (coords [0]);
            newy = int.Parse (coords [1]);
            newTerr = wilderness[newx][newy]; // Determine the necessary terrain type
            if (newTerr == "grass") { // Associate makeTerr with the proper terrain prefab
                makeTerr = grassterr;
            }
            if (newTerr == "dirt") {
                makeTerr = dirtterr;
            }
            if (newTerr == "path") {
                makeTerr = pathterr;
            }
            Instantiate (makeTerr, new Vector3(0,0,0), Quaternion.identity); // Place the terrain in the new scene
        }
       
    }

    public void ChangeScene(int x, int y) { // Called when moving to a new tile in the wilderness
        myScene = SceneManager.GetActiveScene (); // Get the current scene
        curName = myScene.name; // Scenes where this function might run are named 'x.y', so for example 1.2 or 5.3
        string[] coords = curName.Split('.'); // Convert the scene name to its x and y coordinates
        oldx = int.Parse (coords [0]);
        oldy = int.Parse (coords [1]);
        newx = oldx + x; // Determine where we're moving to
        newy = oldy + y;
        newName = newx.ToString () + "." + newy.ToString (); // Get the name of the new scene
        SceneManager.LoadScene (newName, LoadSceneMode.Single); // Move into the next scene
    }
       
}