I’m working on randomly generating some stuff in my game. So far, it’s going well! I’m able to select from a list of terrains based on biome type when I move into a new place. Now, I’m looking at scattering objects randomly around this newly-created terrain. For the moment, I’m just using cubes, but I’ve built what I believe will be necessary to do this with all kinds of things (trees, rocks, patches of flowers, whatever).
The only problem is, I’m having trouble referencing the new terrain object in order to give it to the script that will spawn the other objects on it. So here’s what I have. I have three scripts attached to a singleton I’m using as basically my ‘movement handler’ object. The first one’s short, it’ll be a reference wrapper for objects I may want to place.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AllObjects : MonoBehaviour {
// Here we store all possible objects, so we can attach them via the editor
public GameObject[] cubes;
public Dictionary <string, GameObject[]> items = new Dictionary <string, GameObject[]> ();
void Awake ()
{
items.Add ("cubes", cubes);
}
}
What this does is let me say, hey, choose a prefab from AllObjects.cubes and spawn that one. Which is great! That part’s working. Next, I have the spawner script that actually places the objects for me.
using UnityEngine;
using System.Collections;
public class SpawnObjects : MonoBehaviour
{
public Terrain terrain; // add current terrain
public GameObject objectToPlace; // this object will be placed on terrain
public int numberOfObjects; // number of how many objects will be created
public int posMin; // minimum y position
public int posMax; // maximum x position
public bool posMaxIsTerrainHeight; // the maximum height is the terrain height
private int numberOfPlacedObjects; // number of the plaed objects
private int terrainWidth; // terrain size x axis
private int terrainLength; // terrain size z axis
private int terrainPosX; // terrain position x axis
private int terrainPosZ; // terrain position z axis
// Create objects on the terrain with random positions
public void PlaceObject()
{
terrainWidth = (int)terrain.terrainData.size.x; // get terrain size x
terrainLength = (int)terrain.terrainData.size.z; // get terrain size z
terrainPosX = (int)terrain.transform.position.x; // get terrain position x
terrainPosZ = (int)terrain.transform.position.z; // get terrain position z
if(posMaxIsTerrainHeight == true)
{
posMax = (int)terrain.terrainData.size.y;
}
int posx = Random.Range(terrainPosX, terrainPosX + terrainWidth); // generate random x position
int posz = Random.Range(terrainPosZ, terrainPosZ + terrainLength); // generate random z position
float posy = Terrain.activeTerrain.SampleHeight(new Vector3(posx, 0, posz)); // get the terrain height at the random position
if(posy < posMax && posy > posMin)
{
Instantiate(objectToPlace, new Vector3(posx, posy, posz), Quaternion.identity); // create object
numberOfPlacedObjects++;
}
// numberOfPlacedObjects is smaller than numberOfObjects
if(numberOfPlacedObjects < numberOfObjects)
{
PlaceObject(); // place another one
}
}
}
This one should let me feed it the terrain object, the object to spawn, and the number of them that I want, and it’ll scatter them randomly across the terrain. I believe that this is also working great, though I haven’t really gotten to test it due to the last script, which has an error I can’t figure out. Here’s my main handler script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Environment : MonoBehaviour {
static Environment Instance;
public GameObject grassterr;
public GameObject dirtterr;
public GameObject pathterr;
public GameObject makeTerr;
public System.Random rnd = new System.Random();
Biomes biomes;
SpawnObjects spawner;
AllObjects items;
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;
private GameObject[] posTerrs;
string[][] wilderness = new string[][] { // Set up biome type grid
new string[] {"grass", "dirt", "path"},
new string[] {"dirt", "path", "grass"},
new string[] {"path", "grass", "dirt"}
};
void Awake() { // Make this a singleton (only one instance of this script allowed, ever)
if (Instance == null)
{
DontDestroyOnLoad (gameObject); // Keep this script around!
Instance = this;
}
else
{
Destroy (gameObject); // Delete if we already have one
}
}
// Use this for initialization
void Start () {
SceneManager.sceneLoaded += OnSceneLoaded;
}
// Update is called once per frame
void Update () {
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
biomes = GetComponent<Biomes> ();
spawner = GetComponent<SpawnObjects> ();
items = GetComponent<AllObjects> ();
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 biome as a string
posTerrs = biomes.terrains[newTerr]; // Get the list of possible terrain objects associated with our biome
int r = rnd.Next(posTerrs.Length); // Pick a terrain object from the list
makeTerr = posTerrs[r];
GameObject createdTerr = Instantiate (makeTerr, new Vector3(0,0,0), Quaternion.identity); // Place the terrain in the new scene
spawner.terrain = createdTerr;
spawner.numberOfObjects = rnd.Next (100);
spawner.objectToPlace = items.cubes [0];
spawner.PlaceObject (); // Place some cubes on the new terrain
}
}
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
}
}
So the problem here is in line 78, the Unity editor says ‘Cannot implicitly convert type UnityEngine.GameObject' to UnityEngine.Terrain’'. I tried changing the ‘GameObject’ declaration for the variable createdTerr to ‘Terrain’ instead and it gives the exact same error, so I’m a bit at a loss. Any ideas?