I am new to procedural generation in Unity and I am making a simple game with a player in a plane that is procedurally generated, the requirement is that I am trying to bring up some walls simultaneously with the generation of the plane which is not working. The code so far for this is below:
public GameObject WorldPlane;
public GameObject Cube;
public GameObject walls;
private int radius = 5;
private int planeOffset = 10;
private Vector3 startPos = Vector3.zero;
private int XPlayerMove => (int)(Cube.transform.position.x - startPos.x);
private int ZPlayerMove => (int)(Cube.transform.position.z - startPos.z);
private int XPlayerLocation => (int)Mathf.Floor(Cube.transform.position.x / planeOffset) * planeOffset;
private int ZPlayerLocation => (int)Mathf.Floor(Cube.transform.position.z / planeOffset) * planeOffset;
Hashtable tilePlane = new Hashtable();
void Update()
{
generateWorld();
}
private void generateWorld()
{
if (startPos == Vector3.zero)
{
for (int x = -radius; x < radius; x )
{
for (int z = -radius; z < radius; z )
{
Vector3 pos = new Vector3((x * planeOffset XPlayerLocation),
0,
(z * planeOffset ZPlayerLocation));
if (!tilePlane.Contains(pos))
{
GameObject tile = Instantiate(WorldPlane, pos, Quaternion.identity);
tilePlane.Add(pos, tile);
}
}
}
}
if (hasPlayerMoved(XPlayerMove, ZPlayerMove))
{
for (int x = -radius; x < radius; x )
{
for (int z = -radius; z < radius; z )
{
Vector3 pos = new Vector3((x * planeOffset XPlayerLocation),
0,
(z * planeOffset ZPlayerLocation));
if (!tilePlane.Contains(pos))
{
GameObject Wall=Instantiate(walls, pos, Quaternion.identity);
GameObject tile = Instantiate(WorldPlane,pos,Quaternion.identity);
tilePlane.Add(pos, tile);
tilePlane.Add(pos, Wall);
}
}
}
}
}
private bool hasPlayerMoved(int playerX, int playerZ)
{
if (Mathf.Abs(XPlayerMove) >= planeOffset || Mathf.Abs(ZPlayerMove) >= planeOffset)
{
return true;
}
return false;
}
Here the cube is the player, the world plane is the prefab for the plane which should be generated, and the walls are the prefab for the game object that I am trying to instantiate with the plane, as of now the plane is getting generated without any issues. I really stuck with this, requesting help.
CodePudding user response:
This condition is alway true:
if (startPos == Vector3.zero)
Because you never change startPos
. Therefore, you already assigned a Tile to pos
via tilePlane.Add(pos, tile);
So in the lower if
you check this:
if (!tilePlane.Contains(pos)) // fails, because you already added a Plane above.
To solve this, you can set StartPos to something else than zero after running the double-loop once. So later, when the cube(player) moves, new tiles wall get generated using the lower if
.
But you should not add 2 entries to one hashtable-key. So this will throw an error:
tilePlane.Add(pos, tile);
tilePlane.Add(pos, Wall);
ArgumentException: Item has already been added. Key in dictionary: '(0.00, 0.00, 70.00)' Key being added: '(0.00, 0.00, 70.00)'
So you either need 2 Hashtables (1 for tiles, 1 for wall) or you need a struct/class to hold both.
Example:
class Helper {
public GameObject wall;
public GameObject tile;
}
then in your code:
GameObject Wall = Instantiate(walls, pos, Quaternion.identity);
GameObject tile = Instantiate(WorldPlane,pos,Quaternion.identity);
Helper help = new Helper();
help.wall = Wall;
help.tile = tile;
tilePlane.Add(pos, help); // add the class containing both to key `pos`.
Edit: Yes it's procedural. I added random rotation to the walls to make it a maze. (Wall is an empty GameObject with the wall as child, so that I could offset the wall so it rotates around the tiles center.)
Check it out:
Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlaneGeneration : MonoBehaviour
{
// Start is called before the first frame update
public GameObject WorldPlane;
public GameObject Cube;
public GameObject walls;
private int radius = 5;
private int planeOffset = 10;
private bool startAreaGenerated = false;
private Vector3 startPos = Vector3.zero;
private int XPlayerMove => (int)(Cube.transform.position.x - startPos.x);
private int ZPlayerMove => (int)(Cube.transform.position.z - startPos.z);
private int XPlayerLocation => (int)Mathf.Floor(Cube.transform.position.x / planeOffset) * planeOffset;
private int ZPlayerLocation => (int)Mathf.Floor(Cube.transform.position.z / planeOffset) * planeOffset;
Hashtable tilePlane = new Hashtable();
private void Start()
{
startPos = Cube.transform.position;
}
class Helper
{
public GameObject tile;
public GameObject wall;
}
void Update()
{
generateWorld();
}
private void generateWorld()
{
if (!startAreaGenerated)
{
for (int x = -radius; x < radius; x )
{
for (int z = -radius; z < radius; z )
{
Vector3 pos = new Vector3((x * planeOffset XPlayerLocation), 0, (z * planeOffset ZPlayerLocation));
if (!tilePlane.Contains(pos))
{
GameObject tile = Instantiate(WorldPlane, pos, Quaternion.identity);
Helper helper = new Helper();
helper.tile = tile;
tilePlane.Add(pos, helper);
}
}
}
startAreaGenerated = true;
}
if (hasPlayerMoved(XPlayerMove, ZPlayerMove))
{
for (int x = -radius; x < radius; x )
{
for (int z = -radius; z < radius; z )
{
Vector3 pos = new Vector3((x * planeOffset XPlayerLocation),0, (z * planeOffset ZPlayerLocation));
if (!tilePlane.Contains(pos))
{
int random0to3 = Random.Range(0, 4); // 0, 1, 2 or 3.
Quaternion randomRotation = Quaternion.Euler(0, random0to3 * 90, 0); // rotated 0°, 90°, 180° or 270° around Y-Axis.
GameObject Wall = Instantiate(walls, pos, randomRotation);
GameObject tile = Instantiate(WorldPlane, pos, Quaternion.identity);
Helper helper = new Helper();
helper.tile = tile;
helper.wall = Wall;
tilePlane.Add(pos, helper);
}
}
}
}
}
private bool hasPlayerMoved(int playerX, int playerZ)
{
if (Mathf.Abs(XPlayerMove) >= planeOffset || Mathf.Abs(ZPlayerMove) >= planeOffset)
{
return true;
}
return false;
}
}