I am trying to make a procedural city generate but all of the buildings in the same group are facing the same direction (rotated the same) and are the same scale. I am trying to add variation in height and rotation so it looks more random. This is what I have so far but maybe I am going about this all the wrong way. I am new to programming so I appreciate any help you guys could give. Thank you!
public GameObject[] buildings;
public int mapWidth = 20;
public int mapHeight = 20;
int buildingFootprint = 45;
float randomY = Random.Range(-360f, 360f);
// Procedural Generation
void Start ()
{
float seed = Random.Range(0, 500);
for (int h = 0; h < mapHeight; h++)
{
for (int w = 0; w < mapWidth; w++)
{
int result = (int)(Mathf.PerlinNoise(w/3.0f + seed, h/3.0f + seed) * 50);
Vector3 pos = new Vector3(w * buildingFootprint, 10, h * buildingFootprint);
Vector3 rot = new Vector3(0, randomY, 0);
if (result < 5)
Instantiate(buildings[0], pos, Quaternion.Euler(rot));
else if (result < 10)
Instantiate(buildings[5], pos, Quaternion.Euler(rot));
else if (result < 15)
Instantiate(buildings[6], pos, Quaternion.Euler(rot));
else if (result < 20)
Instantiate(buildings[1], pos, Quaternion.Euler(rot));
else if (result < 30)
Instantiate(buildings[2], pos, Quaternion.Euler(rot));
else if (result < 40)
Instantiate(buildings[3], pos, Quaternion.Euler(rot));
else if (result < 50)
Instantiate(buildings[4], pos, Quaternion.Euler(rot));
}
}
}
It's because you set their rotation to Quaternion.identity
The identity rotation (Read Only).
This quaternion corresponds to "no rotation" - the object is perfectly
aligned with the world or parent axes.
Set it to random rotation like this (I'm assuming you want to rotate them on Y axis):
// getting random rotation
float randomY = Random.Range(-360f, 360f);
// assigning random rotation to Y axis
Vector3 rot = new Vector3(0, randomY, 0);
// instantiating random building on position `pos` and rotation `rot`, I used `Quaternion.Euler` to convert the Vector3 to Quaternion
Instantiate(buildings[index], pos, Quaternion.Euler(rot));
For scaling you need to decide if you want to scale them uniformly or you want to have random scale on every axis. It would look something like this:
// getting random scale
Vector3 scale = Vector3.one;
if(scaleUniform)
{
float randomScale = Random.Range(min, max);
scale = new Vector3(randomScale, randomScale, randomScale);
}
else
{
scale = new Vector3(Random.Range(min, max), Random.Range(min, max), Random.Range(min, max));
}
// instantiating random building on position `pos` and rotation `rot`, I used `Quaternion.Euler` to convert the Vector3 to Quaternion
GameObject building = Instantiate(buildings[index], pos, Quaternion.Euler(rot));
// set the scale
building.localScale = scale;
Ultimately you should end up with a code like this:
public GameObject[] buildings;
public int mapWidth = 20;
public int mapHeight = 20;
int buildingFootprint = 45;
bool scaleUniform = true;
// Procedural Generation
void Start ()
{
float seed = Random.Range(0, 500);
for (int h = 0; h < mapHeight; h++)
{
for (int w = 0; w < mapWidth; w++)
{
int result = (int)(Mathf.PerlinNoise(w/3.0f + seed, h/3.0f + seed) * 50);
float randomY = Random.Range(-360f, 360f); // get random rotation for Y axis
Vector3 pos = new Vector3(w * buildingFootprint, 10, h * buildingFootprint);
Vector3 rot = new Vector3(0, randomY, 0); // set the random Y rotation to a new Vector3
Vector3 scale = Vector3.one; // create a new Vector3 for scale
if(scaleUniform) // scale the object uniformly
{
float randomScale = Random.Range(min, max);
scale = new Vector3(randomScale, randomScale, randomScale);
}
else // or scale it randomly on every axis
{
scale = new Vector3(Random.Range(min, max), Random.Range(min, max), Random.Range(min, max));
}
int index = 0; // declare index here so you dont repeat yourself in ifs
if (result < 5) // now you only set index value instead of copying and pasting long code in each if else
index = 0;
else if (result < 10)
index = 5;
else if (result < 15)
index = 6;
else if (result < 20)
index = 1;
else if (result < 30)
index = 2;
else if (result < 40)
index = 3;
else if (result < 50)
index = 4;
GameObject spawnedBuilding = Instantiate(buildings[index], pos, Quaternion.Euler(rot)); // and finally instantiate the object
spawnedBuilding.transform.localScale = scale; // and set its rotation
}
}
}
Related
I use the following code to generate a Cube as a single mesh. My purpose is to generate a sphere from it by normalizing as I have shown in the commented line (I just have to do that to all those statements in the following lines). The problem here is that the mesh changes from a cube to a flat plane as I keep increasing the resolution (parameter given as public int resolution).
(This code was inspired by this video https://youtu.be/QN39W020LqU . But I am using the technique in my own way as given by the following code, so that I can generate a single mesh instead of a combination of 6 meshes, this is required for my work)
[code=CSharp]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sc_Planet : MonoBehaviour
{
[Range(2, 512)]
public int resolution = 2;
[Range(2, 256)]
public int radius = 10;
MeshFilter meshFilter;
void OnValidate()
{
Initialize();
}
void Initialize()
{
if (meshFilter == null)
{
GameObject meshObj = new GameObject("mesh_Planet");
meshObj.transform.parent = transform;
meshObj.AddComponent<MeshRenderer>().sharedMaterial = new Material(Shader.Find("Standard"));
meshFilter = meshObj.AddComponent<MeshFilter>();
meshFilter.sharedMesh = new Mesh();
}
int xmax = resolution + 1;
int ymax = resolution + 1;
float dx = 1.0f / resolution;
float dy = 1.0f / resolution;
Vector3[] vertsTop = new Vector3[xmax * ymax];
Vector3[] vertsRight = new Vector3[xmax * ymax];
Vector3[] vertsFront = new Vector3[xmax * ymax];
Vector3[] vertsBottom = new Vector3[xmax * ymax];
Vector3[] vertsLeft = new Vector3[xmax * ymax];
Vector3[] vertsBack = new Vector3[xmax * ymax];
for (int y = 0; y < ymax; y++)
{
for (int x = 0; x < xmax; x++)
{
float px = dx * x - 0.5f;
float py = dy * y - 0.5f;
int t = x + y * xmax;
//vertsTop[t] = new Vector3(py, 0.5f, px).normalized * radius;
vertsTop[t] = new Vector3(py, 0.5f, px);
vertsRight[t] = new Vector3(px, py, 0.5f);
vertsFront[t] = new Vector3(0.5f, px, py);
vertsBottom[t] = new Vector3(px, -0.5f, py);
vertsLeft[t] = new Vector3(py, px, -0.5f);
vertsBack[t] = new Vector3(-0.5f, py, px);
}
}
List<int> trianglesList = new List<int>();
for (int y = 0; y < ymax - 1; ++y)
{
for (int x = 0; x < xmax; ++x)
{
if (x % xmax != xmax - 1)
{
int f = x + y * xmax;
trianglesList.Add(f);
trianglesList.Add(f + 1);
trianglesList.Add(f + 1 + xmax);
trianglesList.Add(f);
trianglesList.Add(f + 1 + xmax);
trianglesList.Add(f + xmax);
}
}
}
List<Vector3> verts = new List<Vector3>();
Dictionary<Vector3, int> vdict = new Dictionary<Vector3, int>();
List<int> triangles = new List<int>();
int nextIndex = 0;
void addFace(Vector3 [] in_verts, List<int> in_triangles)
{
for(int i = 0; i < in_verts.Length; ++i)
{
if (!vdict.ContainsKey(in_verts[i]))
{
vdict.Add(in_verts[i], nextIndex);
verts.Add(in_verts[i]);
++nextIndex;
}
}
for(int i = 0; i < in_triangles.Count; ++i)
{
triangles.Add(vdict[in_verts[in_triangles[i]]]);
}
}
addFace(vertsTop, trianglesList);
addFace(vertsRight, trianglesList);
addFace(vertsFront, trianglesList);
addFace(vertsBottom, trianglesList);
addFace(vertsLeft, trianglesList);
addFace(vertsBack, trianglesList);
var mesh = meshFilter.sharedMesh;
mesh.Clear();
mesh.vertices = verts.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
}
}
[/code]
This code works in Blender (I used python to script it on Blender and it works very well for any resolution).
The only problem is that when I use this in Unity, the meshes become weird as I have shown in the images I have attached below.
At Resolution = 96 :
At Resolution = 122 :
At Resolution = 182 :
At Resolution = 344:
Why is this happening?
How should I correct it?
(I have also posted this in unity forums: Why cube mesh becomes a plane when in high resolution?)
Ok I found the answer. This is exceeding the limit of vertices on unity api for 16-bit based meshes. I had to change it to a 32-bit indexed mesh to correct it.
Details are in this docuemntaiton page : https://docs.unity3d.com/ScriptReference/Rendering.IndexFormat.html?_ga=2.9556401.501737799.1635227368-67181881.1629608252
I just had to add the code :
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
That was it.
Now I have an octahedron sphere and I woud like to add noise to it, but I can' t do it because I dont know to much of this,
I think I need to modify the vertex pos but i don't know how.
Here is the script that generate the vertices, If you need More, only Ask (;
Vector3[] vertices = new Vector3[(resolution + 1) * (resolution + 1) * 4 - (resolution * 2 - 1) * 3];
int v = 0, vBottom = 0, t = 0;
for (int i = 0; i < 4; i++)
{
vertices[v++] = Vector3.down;
}
for (int i = 1; i <= resolution; i++)
{
float progress = (float)i / resolution;
Vector3 from, to;
vertices[v++] = to = Vector3.Lerp(Vector3.down, Vector3.up, progress);
for (int d = 0; d < 4; d++)
{
from = to;
to = Vector3.Lerp(Vector3.down, directions[d], progress);
t = CreateLowerStrip(i, v, vBottom, t, triangles);
v = CreateVertexLine(from, to, i, v, vertices);
vBottom += i > 1 ? (i - 1) : 1;
}
vBottom = v - 1 - i * 4;
}
Depending on how exactly your noise should look like the simplest I can imagine would be to simply iterate through the vertices and slightly shift them around by random direction vectors with a random magnitude up to a certain maximum like e.g.
void Randomize(Vector3[] vertices, float maxDistance)
{
for(var i = 0; i< vertices.Length; i++)
{
// Generates a random vactor pointing in any possible direction
var randomDirection = new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized;
// Generate a random magnitude between 0 and given maxDistance
var randomMagnitude = Random.Range(0, maxDistance);
// Move the current vert the randomMagnitude along the randomDirection
vertices[i] += randomDirection * randomMagnitude;
}
}
Or alternatively you could only move on Vectors between the current vert position and the center (assuming Vector3.zero for now)
void RandomizeFromCenter(Vector3[] vertices, float maxDistance)
{
var center = Vector3.zero;
for(var i = 0; i< vertices.Length; i++)
{
var direction = (vertices[i] - center).normalized;
// Generate a random magnitude between -maxDistance and maxDistance
var randomMagnitude = Random.Range(-maxDistance, maxDistance);
// Move the current vert the randomMagnitude along the randomDirection
vertices[i] += direction * randomMagnitude;
}
}
I am using Unity3D and I have a polygon (Vector2 array) as well as points to check.
For the last few days I was searching for solutions including pnpoly and other algorithms. The problem is I have an inaccuracy of up to 0.001f because I project 3D Faces onto a 2D plane by multiplying with a Quaternion right after using TransformPoint (to get the world position of a mesh-vertex).
I don't know anything about the polygons because they are formed from a number of mesh-triangles - they can have any shape.
How could I deal with this extreme inaccuracy and find all points that are inside or on the boundaries of the polygon?
public static bool IsInsidePolygon(Vector2[] vertices, Vector2 checkPoint)
{
float[] vertX = new float[vertices.Length];
float[] vertY = new float[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
vertX[i] = vertices[i].x;
vertY[i] = vertices[i].y;
}
return IsInsidePolygon(vertices.Length, vertX, vertY, checkPoint.x, checkPoint.y);
}
public static bool IsInsidePolygon3(int nvert, float[] vertx, float[] verty, float testx, float testy)
{
int i, j = 0;
bool c = false;
for (i = 0, j = nvert - 1; i < nvert; j = i++)
{
if (((verty[i] > testy) != (verty[j] > testy)) &&
(testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
c = !c;
}
return c;
}
The solution was to find the Closest Distance to the Polygon and return true if the distance is within the margin, here is the whole code:
'''
public static float DistancePointLine2D(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
return (ProjectPointLine2D(point, lineStart, lineEnd) - point).magnitude;
}
public static Vector2 ProjectPointLine2D(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
Vector2 rhs = point - lineStart;
Vector2 vector2 = lineEnd - lineStart;
float magnitude = vector2.magnitude;
Vector2 lhs = vector2;
if (magnitude > 1E-06f)
{
lhs = (Vector2)(lhs / magnitude);
}
float num2 = Mathf.Clamp(Vector2.Dot(lhs, rhs), 0f, magnitude);
return (lineStart + ((Vector2)(lhs * num2)));
}
public static float ClosestDistanceToPolygon(Vector2[] verts, Vector2 point)
{
int nvert = verts.Length;
int i, j = 0;
float minDistance = Mathf.Infinity;
for (i = 0, j = nvert - 1; i < nvert; j = i++)
{
float distance = DistancePointLine2D(point, verts[i], verts[j]);
minDistance = Mathf.Min(minDistance, distance);
}
return minDistance;
}
public static bool IsInsidePolygon(Vector2[] vertices, Vector2 checkPoint, float margin = 0.01f)
{
if(ClosestDistanceToPolygon(vertices, checkPoint) < margin)
{
return true;
}
float[] vertX = new float[vertices.Length];
float[] vertY = new float[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
vertX[i] = vertices[i].x;
vertY[i] = vertices[i].y;
}
return IsInsidePolygon(vertices.Length, vertX, vertY, checkPoint.x, checkPoint.y);
}
public static bool IsInsidePolygon(int nvert, float[] vertx, float[] verty, float testx, float testy)
{
bool c = false;
int i, j = 0;
for (i = 0, j = nvert - 1; i < nvert; j = i++)
{
if ((((verty[i] <= testy) && (testy < verty[j])) ||
((verty[j] <= testy) && (testy < verty[i]))) &&
(testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
c = !c;
}
return c;
}
'''
I try to add trees to my terrain with the following code:
// Create Trees
for (int x = 0; x < terrainData.heightmapWidth; x++)
{
for (int z = 0; z < terrainData.heightmapWidth; z++)
{
if (GetBiome(x, z) == "Grasland")
{
int r = UnityEngine.Random.Range(0, 500);
if (r == 0)
{
Terrain terrain = GetComponent<Terrain>();
TreeInstance treeTemp = new TreeInstance();
treeTemp.position = new Vector3(x, 0, z);
treeTemp.prototypeIndex = 0;
treeTemp.widthScale = 1f;
treeTemp.heightScale = 1f;
treeTemp.color = Color.white;
treeTemp.lightmapColor = Color.white;
terrain.AddTreeInstance(treeTemp);
terrain.Flush();
}
}
}
}
the function GetBiome() works correctly, i checked that by placing the trees as GameObjects and it worked fine.
Is there something missing that i didn't thought of?
Because there's not a single tree generated.
The tree i want to generate is set up under PaintTrees:
Please read my notes as comments below, I hope this explains and resolves the issue.
I have tested this in unity to confirm.
// Create Trees
//make these float otherwise your position math below is truncated
for (float x = 0; x < terrainData.heightmapWidth; x++)
{
//heightmapHeight not heightmapWidth
for (float z = 0; z < terrainData.heightmapHeight; z++)
{
Terrain terrain = GetComponent<Terrain>();
int r = UnityEngine.Random.Range(0, 500);
if (r == 0)
{
TreeInstance treeTemp = new TreeInstance
//position is local and expects value between 0 and 1
treeTemp.position = new Vector3(x / terrainData.heightmapWidth, 0, z / terrainData.heightmapHeight),
treeTemp.prototypeIndex = 0;
treeTemp.widthScale = 1f;
treeTemp.heightScale = 1f;
treeTemp.color = Color.white;
treeTemp.lightmapColor = Color.white;
terrain.AddTreeInstance(treeTemp);
terrain.Flush();
}
}
}
A bit late but, instead of
int r = UnityEngine.Random.Range(0, 500);
if (r == 0)
do
int r = UnityEngine.Random.Range(0, 500);
if (r >= 0 && <= 1)
to check for range. Because you're checking for a impossible number it will always come close but never 0 for example 0.000230...f you will need to Math floor it or check for range like I did
I'm experimenting with Perlin Noise in C#. I managed to create a working 2D map generator.
But now I want to make my noise tileable. I want that the right edge of the map fits in the left edge of the map.
I'vw been searching for this on the internet and I have tried different things but I could not get it to work. Is there someone who knows a formula for this?
Here is my code:
public float[,] GenerateNoise(float[,] map, int xsize ,int ysize ,float scale, int seed,
int octaves, float persistance,float lacunarity)
{
Random rnd = new Random(seed);
float offsetX = rnd.Next(0, 100000);
float offsetY = rnd.Next(0, 100000);
if (scale <= 0)
{
scale = 0.0001f;
}
float minNoiseHeight = float.MinValue;
float maxNoiseHeight = float.MaxValue;
float[,] fallmap;
//fallmap = FalloffGenerator.GenerateFalloffMap(xsize);
Perlin Noise = new Perlin();
//perlin noise
for (int y = 0; y <= ysize - 1; y++)
{
for (int x = 0; x <= xsize - 1; x++)
{
float amplitude = 1f;
float frequency = 1f;
float noiseHeight = 0;
for (int i = 0; i < octaves; i++)
{
float sampleX = (xsize - x) / scale * frequency + offsetX;
float sampleY = (ysize - y) / scale * frequency + offsetY;
float PerlinValue = (float)Noise.perlin(sampleX, sampleY, 0) * 2 - 1;
map[x, y] = PerlinValue;
noiseHeight += PerlinValue * amplitude;
amplitude *= persistance;
frequency *= lacunarity;
// map[x, y] = map[x, y] - fallmap[x, y];
}
if(noiseHeight > maxNoiseHeight)
{
maxNoiseHeight = noiseHeight;
}else if (noiseHeight < minNoiseHeight)
{
minNoiseHeight = noiseHeight;
}
map[x, y] = noiseHeight;
//map[x, y] = map[x, y] - fallmap[x, y] ;
}
}
return map;
}