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;
}
}
Related
I am relatively new to Terrain Generation in Unity, and am currently stuck in one place. I have followed Brackey's tutorial on terrain generation, and in that tutorial, he uses something like this:
float y = Mathf.PerlinNoise(x, z) * 2f;
To manipulate the height of the terrain. I also followed Sebastian Lague's tutorial on this. This is where I am stuck.
I want to use Sebastian Lague's Noise.cs file that he created (can be found on his GitHub) to manipulate the terrain height.
The reason is because this noise generator, rather than Mathf.PerlinNoise(), gives you a much better control over the texture it outputs. The problem is, Noise.cs will return a 2D float array, while Mathf.PerlinNoise() returns a 1D float value. Is there a way for Noise.cs to return a float value, just like Mathf's function?
Noise.cs:
using UnityEngine;
using System.Collections;
public static class Noise {
public static float[,] GenerateNoiseMap(int mapWidth, int mapHeight, int seed, float scale, int octaves, float persistance, float lacunarity, Vector2 offset) {
float[,] noiseMap = new float[mapWidth,mapHeight];
System.Random prng = new System.Random (seed);
Vector2[] octaveOffsets = new Vector2[octaves];
for (int i = 0; i < octaves; i++) {
float offsetX = prng.Next (-100000, 100000) + offset.x;
float offsetY = prng.Next (-100000, 100000) + offset.y;
octaveOffsets [i] = new Vector2 (offsetX, offsetY);
}
if (scale <= 0) {
scale = 0.0001f;
}
float maxNoiseHeight = float.MinValue;
float minNoiseHeight = float.MaxValue;
float halfWidth = mapWidth / 2f;
float halfHeight = mapHeight / 2f;
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
float amplitude = 1;
float frequency = 1;
float noiseHeight = 0;
for (int i = 0; i < octaves; i++) {
float sampleX = (x-halfWidth) / scale * frequency + octaveOffsets[i].x;
float sampleY = (y-halfHeight) / scale * frequency + octaveOffsets[i].y;
float perlinValue = Mathf.PerlinNoise (sampleX, sampleY) * 2 - 1;
noiseHeight += perlinValue * amplitude;
amplitude *= persistance;
frequency *= lacunarity;
}
if (noiseHeight > maxNoiseHeight) {
maxNoiseHeight = noiseHeight;
} else if (noiseHeight < minNoiseHeight) {
minNoiseHeight = noiseHeight;
}
noiseMap [x, y] = noiseHeight;
}
}
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
noiseMap [x, y] = Mathf.InverseLerp (minNoiseHeight, maxNoiseHeight, noiseMap [x, y]);
}
}
return noiseMap;
}
}
MeshGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class MeshGenerator : MonoBehaviour
{
Mesh mesh;
Vector3[] vertices;
int[] triangles;
public int xSize = 20;
public int zSize = 20;
int mapWidth;
int mapHeight;
int seed;
float scale;
int octaves;
float persistance;
float lacunarity;
Vector2 offset;
// Start is called before the first frame update
void Start()
{
// Initialize everything
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
void CreateShape()
{
// Creating the grid of vertices
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
// Setting vertex positions
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
//float y = Mathf.PerlinNoise(x * .3f, z * .3f) * 2f;
float y = Noise.GenerateNoiseMap(mapWidth, mapHeight, seed, scale, octaves, persistance, lacunarity, offset);
vertices[i] = new Vector3(x, y, z);
i++;
}
}
triangles = new int[xSize * zSize * 6];
int vert = 0;
int tris = 0;
for (int z = 0; z < zSize; z++)
{
for (int x = 0; x < xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert++;
tris += 6;
}
vert++;
}
}
void UpdateMesh()
{
// Clear mesh data, reset with above vars and recalculate normals
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
private void OnDrawGizmos()
{
if (vertices == null) return;
Gizmos.color = Color.red;
// Draw Vertex Gizmos
for (int i = 0; i < vertices.Length; i++)
{
Gizmos.DrawSphere(vertices[i], .1f);
}
}
}
I've figured it out, I would've had to put vertices[i] = new Vector3(x, y[x, z], z); Now I've already tried this, but I suppose visual studio bugged and did not save it properly. Anyways, yes, I just needed to use x/y to pick out my float.
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.
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;
}
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
}
}
}
I am using Unity 5 to create an isometric game. I have generated a grid of tiles and it works well. However, when I use two different tiles to fill in the grid (their image sizes are slightly different), I get gaps in between the tiles. The obvious solution would be to create the tiles so that they are all the same image size, but this would prevent me from creating anything on a tile that is larger than the size of a tile (eg. a tree).
Here are some images to demonstrate:
With only one type of tile:
With two types of tile:
This is the code I use to create the map:
private void CreateMap() {
float tileWidth;
float tileHeight;
int orderInLayer = 0;
SpriteRenderer r = floorTiles [0].GetComponent<SpriteRenderer> ();
tileWidth = r.bounds.max.x - r.bounds.min.x;
tileHeight = r.bounds.max.y - r.bounds.min.y;
for (int i = 0; i < map.GetLength(0); i++) {
orderInLayer += 1;
for (int j = 0; j < map.GetLength (1); j++) {
Vector2 position = new Vector2 ((j * tileWidth / 2) + (i * tileWidth / 2) + (tileWidth / 2), (j * tileHeight / 2) - (i * tileHeight / 2) + (tileHeight/ 2));
r = map[i,j].GetComponent<SpriteRenderer>();
r.sortingOrder = orderInLayer;
Instantiate(map[i, j], position, Quaternion.identity);
}
}
}
Any help would be greatly appreciated, I cannot seem to fix it!
You appear to be calculating a position for each of your tiles from scratch every time you create one. If you have 2 different sized tiles, then your calculation comes out different, hence the gaps in your tiles. This is because you're only using the width/height of the current tile, failing to take into account any previous tiles that may be a shorter/longer height/width.
Given you have varying heights AND widths you'll need a way to calculate the correct position for both to prevent gaps in the X and Y direction. I've mocked up something here, but it's untested. More of a concept(?) I guess.
float tileHeight = 0;
float tileWidth = 0;
Vector2 position = new Vector2(0,0);
Dictionary<int, float> HeightMap = new Dictionary<int, float>();
for (int iRow = 0; iRow < map.GetLength(0); iRow++)
{
position.x = 0;
orderInLayer += 1;
for (int jColumn = 0; jColumn < map.GetLength (1); jColumn++)
{
position.y = HeightMap[jColumn];
r = map[iRow, jColumn].GetComponent<SpriteRenderer>();
tileWidth = r.bounds.max.x - r.bounds.min.x;
tileHeight = r.bounds.max.y - r.bounds.min.y;
r.sortingOrder = orderInLayer;
position.x += tileWidth / 2;
position.y += tileHeight / 2;
Instantiate(map[iRow, jColumn], position, Quaternion.identity);
HeightMap[jColumn] = position.y;
}
}
I leave the best way of storing the height, or instantiating the contents of the HeightMap dictionary to however you see fit.