Mapping PerlinNoise to a Grid - c#

I am trying to generate a grid across my map and add nodes depending on the perlin noise value. Depending on the value obtained from the perlin noise at a location, I will add a new Node which will be of a certain type e.g. Mountain, Water etc to represent terrian. Here I am trying to make it so that if the value is > 0.5, this mean it's only mountains and so a black coloured cubes should surround the mountain areas, However, my black cubes do not match the mountain areas from the perlin noise and I cannot seem to figure out why I am going wrong. Would appreciate any insight into how I could go about achieving this.
private void LocateWalkableCells()
{
for(int z = 0; z < Height; z++)
{
for(int x = 0; x < Width; x++)
{
noise = GetNoiseValue(x, z);
if(noise > 0.5) {
grid[x,z] = new Node(new Vector3(x, 0, z), TerrainType.Mountain, 1);
}
else {
grid[x,z] = new Node(new Vector3(x, 0, z), TerrainType.Grass, 1);
}
}
}
}
private float GetNoiseValue(int x, int z)
{
int pos = (x * Width) + z;
return Mathf.Round(terrainGenerator.noiseArray[pos] * 10) / 10;
}
// Draw gizmos to visualize colour
void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(Width, 1, Height));
if(grid != null)
{
foreach(Node n in grid)
{
if(n.TerrainType == TerrainType.Grass)
{
Gizmos.color = Color.green;
}
else if(n.TerrainType == TerrainType.Mountain)
{
Gizmos.color = Color.black;
}
Gizmos.DrawCube(n.Position, Vector3.one * (nodeDiameter - .1f));
}
}
}
noiseArray is used for the vertices of the terrain in the following code:
vertices = new Vector3[(Width + 1) * (Depth + 1)];
noiseArray = PerlinNoise();
int i = 0;
for(int z = 0; z <= Depth; z++)
{
for(int x = 0; x <= Width; x++)
{
var currentHeight = noiseArray[i];
if(currentHeight > HeightThreshold)
{
currentHeight *= HeightMultiplier;
}
vertices[i] = new Vector3(x, currentHeight, z);
i++;
}
}
Output
Result from suggested answer
Still seems to miss some mountain areas, colouring green instead of black.

It think the issue is in
var pos = (x * Width) + z;
since x is you index on the width of the grid you would probably rather want
var pos = z * Width + x;
in other words you want to
skip z rows
each row has Width elements
then from there take the xth element
assuming your terrain is laid out row-wise.
Or if it is laid out column-wise (which is rather unusual but possible)
var pos = x * Height + z;
or in other words
skip x columns
each column has Height elements
then from there take the zth element
See also Converting index of one dimensional array into two dimensional array i. e. row and column
Update
Now that you have showed the terrain generation code it needs to be
var pos = z * (Width + 1) + x;
since the terrain array has actually Width + 1 elements per row.

Related

how to limit CreateCell c# procedural grid generation unity

I am learning C# for unity and could use some pointers.
I am following catlikecoding hex map tutorial but I have modified the grid for my own means.
http://catlikecoding.com/unity/tutorials/hex-map-1/
My goal is to create a pyramid of squares procedurally starting from a 7 * 7 grid. I am using a prefab plane
How do I place a limit on The CreateCell looped function so that cells with the (x,y) coordinates are not created when they meet the following expression
x + y > n - 1 where n = grid size (for example (6,1) or (5,6)
I have gotten as far as creating a rhombus of planes with the undesired planes below the ground plane.
The script is as follows.
public class HexGrid : MonoBehaviour {
public int width = 7;
public int height = 7;
public int length = 1;
public SquareCell cellPrefab;
public Text cellLabelPrefab;
SquareCell[] cells;
Canvas gridCanvas;
void Awake () {
gridCanvas = GetComponentInChildren<Canvas>();
cells = new SquareCell[height * width * length];
for (int z = 0 ; z < height; z++) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < length; y++)
CreateCell(x, z, y);
}
}
}
void CreateCell(int x, int z, int y) {
Vector3 position;
position.x = x * 10f ;
position.y = ((y + 1) - (x + z)) * 10f + 60f;
position.z = z * 10f ;
Cell cell = Instantiate<Cell>(cellPrefab);
cell.transform.SetParent(transform, false);
cell.transform.localPosition = position;
Text label = Instantiate<Text>(cellLabelPrefab);
label.rectTransform.SetParent(gridCanvas.transform, false);
label.rectTransform.anchoredPosition =
new Vector2(position.x, position.z);
label.text = x.ToString() + "\n" + z.ToString();
}
}
Grid so far
A quick solution would be to add an if statement before the part of the code that creates a cell. In this case the method CreateCell(). That if statement should have your logic in code. You would also have to create two variables for the size to check. For example:
public int tempX;
public int tempY;
void Awake () {
gridCanvas = GetComponentInChildren<Canvas>();
cells = new SquareCell[height * width * length];
for (int z = 0 ; z < height; z++) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < length; y++)
{
if (x + y < (tempX + tempY) - 1)
{
CreateCell(x, z, y);
}
}
}
}
}

How would I subdivide an arbitrary cube into smaller cubes?

I'm trying to write an algorithm that will split an arbitrary quad into smaller quads that all have the same x, y, and z scales (so, cubes). Right now I have code that splits quads into scaled down versions of themselves, but I'd like the ratio to be 1:1:1. How would I modify the code below to do that?
for (int x=0; x < 2; x++) {
for (int y=0; y < 2; y++) {
for (int z=0; z < 2; z++) {
GameObject newCube = Instantiate(gameObject);
newCube.transform.localScale = new Vector3(
newCube.transform.localScale.x/2,
newCube.transform.localScale.y/2,
newCube.transform.localScale.z/2
);
newCube.transform.position = new Vector3(
newCube.transform.position.x + ((x-0.5f) * newCube.transform.localScale.x),
newCube.transform.position.y + ((y-0.5f) * newCube.transform.localScale.y),
newCube.transform.position.z + ((z-0.5f) * newCube.transform.localScale.z)
);
}
}
Destroy(gameObject);
If I understood you correctly, you want to make squares from a rectangle (actually the 3D equivalent of those, but whatever).
So your inner squares must have a side, at most, half of the SMALLER side of the rectangle. And, since they are squares, all the sides must have the same size. So, you must find which is the smaller side of x, y and z, and create your cubes with all sides set to half of that value.
Putting that into your code:
for (int x=0; x < 2; x++) {
for (int y=0; y < 2; y++) {
for (int z=0; z < 2; z++) {
GameObject newCube = Instantiate(gameObject);
var cubeSize = Math.Min(oldQuad.x, Math.Min(oldQuad.y, oldQuad.z)) / 2;
newCube.transform.localScale = new Vector3(
cubeSize,
cubeSize,
cubeSize
);
newCube.transform.position = new Vector3(
newCube.transform.position.x + ((x-0.5f) * newCube.transform.localScale.x),
newCube.transform.position.y + ((y-0.5f) * newCube.transform.localScale.y),
newCube.transform.position.z + ((z-0.5f) * newCube.transform.localScale.z)
);
}
}
Destroy(gameObject);
Since you told nothing about how you want to position them, I keep that part the same.

Getting the height and width of a polygon on a certain plane

I'm currently using the following to apply a texture to a polygon formed by TriangleList
public static VertexPositionColorTexture[] TextureMapping(VertexPositionColorTexture[] vertices, float xScale, float yScale)
{
bool initialized = false;
float x, y;
float lX = 0, hX = 0, lY = 0, hY = 0;
for (int i = 0; i < vertices.Length; i++)
{
x = vertices[i].Position.X;
y = vertices[i].Position.Y;
if (!initialized)
{
hX = x;
lX = x;
hX = y;
hY = y;
initialized = true;
}
else
{
if (x > hX)
{
hX = x;
}
else if (x < lX)
{
lX = x;
}
if (y > hY)
{
hY = y;
}
else if (y < lY)
{
lY = y;
}
}
}
float width = (Math.Abs(lX) + Math.Abs(hX)) / xScale;
float height = (Math.Abs(lY) + Math.Abs(hY)) / yScale;
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].TextureCoordinate.X = vertices[i].Position.X / width;
vertices[i].TextureCoordinate.Y = vertices[i].Position.Y / height;
}
return vertices;
This currently works fine for a polygon that has points that all have Z=0 (example: (0,0,0) (0,10,0) (10,10,0) (10,0,0)) but doesn't work for any that are rotated or not flat along the z (example (0,0,0) (0,0,10) (0,10,10) (0,10,0)). The only solution I have come with is to get the plane that the polygon lies on (it will always be flat) and somehow rotate or translate the vertices in the above method to flatten it to the xy line to allow for the correct height and width to be determined. Anyone point me in the right direction, or suggest something else?
Solved this myself by re-writing and rotating the polygon to the z plane.

Algorithm to find rectangles

I have the following code:
int width = 10;
int height = 7;
bool[,] array1 = new bool[width, height];
string values =
"1100000000" +
"1100000011" +
"0001100011" +
"0001100000" +
"0001110000" +
"0000000110" +
"0000000110";
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
array1[x, y] = (values[x + y * width] == '1');
}
}
im looking for a algorithm that would extract Ranges where we have a 1.
so from this data we would get rectangles
(0,0,2,2),
(8,1,2,2),
(3,2,3,3),
(7,5,2,2)
the order of the rectangles do not matter!
But i have no idea how to do this any one got any pointers?
After reading Rusty Weber answer i came up with the following:
private static List<Rectangle> GetRectangles(bool[,] array)
{
List<Rectangle> rectangles = new List<Rectangle>();
for (int x = 0; x < array.GetLength(0); x++)
{
for (int y = 0; y < array.GetLength(1); y++)
{
if (array[x, y])
{
rectangles.Add(GetRectangle(array, new Point(x, y)));
}
}
}
return rectangles;
}
static Rectangle GetRectangle(bool[,] array, Point startLocation)
{
int maxX = int.MinValue;
int minX = int.MaxValue;
int maxY = int.MinValue;
int minY = int.MaxValue;
HashSet<Point> visitedLocations = new HashSet<Point>();
Stack<Point> pointsToGo = new Stack<Point>();
Point location;
pointsToGo.Push(startLocation);
while (pointsToGo.Count > 0)
{
location = pointsToGo.Pop();
if (!location.X.IsBetween(0, array.GetLength(0) - 1))
continue;
if (!location.Y.IsBetween(0, array.GetLength(1) - 1))
continue;
if (!array[location.X, location.Y])
continue;
if (visitedLocations.Contains(location))
continue;
visitedLocations.Add(location);
pointsToGo.Push(new Point(location.X + 1, location.Y));
pointsToGo.Push(new Point(location.X, location.Y + 1));
pointsToGo.Push(new Point(location.X - 1, location.Y));
pointsToGo.Push(new Point(location.X, location.Y - 1));
}
foreach (Point location2 in visitedLocations)
{
array[location2.X, location2.Y] = false;
if (location2.X > maxX)
maxX = location2.X;
if (location2.X < minX)
minX = location2.X;
if (location2.Y > maxY)
maxY = location2.Y;
if (location2.Y < minY)
minY = location2.Y;
}
return new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1);
}
public static bool IsBetween<T>(this T item, T start, T end)
{
return Comparer<T>.Default.Compare(item, start) >= 0
&& Comparer<T>.Default.Compare(item, end) <= 0;
}
COMMENT :: It might help me to answer your question if you have better defined coordinates. (0,0,2,2) isn't exactly Cartesian and it may need some explaining. Is this the top left corner followed by the widths?
Ok. The easiest to program way, in my opinion at least, to extract all possible rectangles from the graph is to have a recursively defined method that searches in a specific direction for the symmetric rectangle pattern. This however could end up being really slow so I hope that speed isn't a constraint for you. Looking at the style of code, I would say that this is a school assignment for either recursion or dynamic programming.
something along the lines of the following pseudocode
`
for i in width
{
for j in height
{
if(point[i,j] == 1)
{
potentials = searh_in_direction(i,j,graph,width,height,RIGHT,[[i,j]] )
listOfAllRects.append(potentials)
}
}
}
list_of_rectangle searh_in_direction(i,j,graph,width,height,direction, listofpoints )
{
nextdirection = direction.nextdirection; //Right -> down -> left-> up
//DEVELOP METHOD FOR RECURSION HERE THAT RETURNS ALL SETS OF 4 POINTS THAT
for every point in the direction of travel
if the point is the origional point and we have 4 points including the point we are looking at, we have a rectangle and we need to return
if point on direction of travel is a one travel on the next direction
posiblerects.append(searh_in_direction(i,j,graph,width,height,nextdirection , listofpoints.append(currentpoint)))
//after all points in direction have bee searched
return posiblerects.
}
`
I know that this code could be very confusing but that is the gist of what you need as a recursive element.
I will also note that I can already see several bugs in this code but I have run out of the 15 minutes that I said that I was going to spend on this post so you might have to pick them out yourself.
This gives you the same results you're looking for:
static void Main(string[] args)
{
string values =
"1100000000" +
"1100000011" +
"0001100011" +
"0001100000" +
"0001110000" +
"0000000110" +
"0000000110";
int width = 10;
int height = 7;
bool[,] array = new bool[width, height];
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
array[x, y] = (values[x + y * width] == '1');
List<Rectangle> rectangles = new List<Rectangle>();
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
if (array[x, y] && !Used(rectangles, x, y))
{
int rHeight = 1;
for (int rX = x + 1; rX < width && array[rX, y] && !Used(rectangles, rX, y); ++rX)
for (int rY = y + 1; rY < height && array[rX, rY] && !Used(rectangles, rX, rY); ++rY)
if (rY - y >= rHeight)
rHeight = rY - y + 1;
int rWidth = 1;
for (int rY = y + 1; rY < height && rY - y <= rHeight && array[x, rY] && !Used(rectangles, x, rY); ++rY)
for (int rX = x + 1; rX < width && array[rX, rY] && !Used(rectangles, rX, rY); ++rX)
if (rX - x >= rWidth)
rWidth = rX - x + 1;
rectangles.Add(new Rectangle(x, y, rWidth, rHeight));
}
}
}
foreach (Rectangle rect in rectangles)
Console.WriteLine(rect);
}
private static bool Used(IEnumerable<Rectangle> rectangles, int x, int y)
{
return rectangles.Any(r => r.Contains(x, y));
}
I made an adhoc Rectangle struct since I didn't reference System.Drawing, but you can pass a System.Drawing.Point to the System.Drawing.Rectangle.Contains() and get the same results.
Also, notice that the width of your array should actually be 10 and your indexing math was wrong. You should be multiplying y by the width, not the height.
It is not clear from the question if you really want rectangles that cover the 1's exactly, or if you want bounding volumes that can contain zeroes, but will cover all the 1's with a reasonably small number of rectangles.
Assuming you want rectangles to cover the 1's, and you don't need a perfect solution:
Make a temporary copy of the array.
Iterate over the temporary looking for 1's
When you hit a 1, begin a new rectagle that starts as 1x1, offset to that location ( e.g. covers just that 1 )
Expand that rectangle rightward so long as there is a 1 in the next cell
Expand that rectangle downards so long as the row below has 1's matching the width
of the current rectangle.
ONce you can't expand down any more, emit that recgantle, and clear all the 1's covered by that rectangle from the temporary
continue scanning for 1's starting with the cell directly after the top right corner of the current rectangle.
This will produce a decent covering - but by no means ideal. If you need a perfect covering - e.g. the guaranteed minimum number of rectangles then it is harder.

XNA Draw a filled circle

In another thread on XNA, Callum Rogers wrote some code which creates a texture with the outline of a circle, but I'm trying to create a circle filled with a color. What I have to modify on this code to fill the circle with color?
public Texture2D CreateCircle(int radius)
{
int outerRadius = radius*2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(GraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.Transparent;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f/radius;
for (double angle = 0; angle < Math.PI*2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
texture.SetData(data);
return texture;
}
Don't use a texture for stuff like this (especially for things being in one single color!) - also don't try to do it pixel by pixel. You've got 3D acceleration for a reason.
Just draw the circle similar to a pie using a triangle fan. You'll need the following vertices.
Center of the circle
x points on the circle's border.
The first two points will define a line between the center of the circle and its border. The third vertex will define the first polygon. Vertices 1, 3 and 4 will then define the second polygon, etc.
To get the points on the circle's border use the formulas from your example. The first angle will be 0°, the following ones multiples of (360° / points on circle). To get a full circle you'll need one additional point that matches the second point (the first point on the border).
Depending on the number of vertices on the circle you'll get different n-gons. The more vertices you use the rounder the shape will look (at some performance cost):
(Less than 2 vertices aren't possible as a polygon requires at least 3 vertices to be drawn.)
Total of 4 points (3 points on circle) will result in a triangle.
Total of 5 points (4 point on circle) will result in a square.
Total of 6 points (5 points on circle) will result in a pentagon
...
Actually the XNA example for drawing primites show how to draw a circle (or n-gon) using a triangle fan.
well for anyone who wants to do it pixel by pixel ... i made a solution based on the information given. In your 2d texture method add the following code to fill the circle. I'm making a game and wanted to be able to make circles different colors and sizes. So inside CreateCircle(int radius) method, add the following code after the outline has been created :
bool finished = false;
int firstSkip = 0;
int lastSkip = 0;
for (int i = 0; i <= data.Length - 1; i++)
{
if (finished == false)
{
//T = transparent W = White;
//Find the First Batch of Colors TTTTWWWTTTT The top of the circle
if ((data[i] == Color.White) && (firstSkip == 0))
{
while (data[i + 1] == Color.White)
{
i++;
}
firstSkip = 1;
i++;
}
//Now Start Filling TTTTTTTTWWTTTTTTTT
//circle in Between TTTTTTW--->WTTTTTT
//transaparent blancks TTTTTWW--->WWTTTTT
// TTTTTTW--->WTTTTTT
// TTTTTTTTWWTTTTTTTT
if (firstSkip == 1)
{
if (data[i] == Color.White && data[i + 1] != Color.White)
{
i++;
while (data[i] != Color.White)
{
//Loop to check if its the last row of pixels
//We need to check this because of the
//int outerRadius = radius * 2 + -->'2'<--;
for (int j = 1; j <= outerRadius; j++)
{
if (data[i + j] != Color.White)
{
lastSkip++;
}
}
//If its the last line of pixels, end drawing
if (lastSkip == outerRadius)
{
break;
finished = true;
}
else
{
data[i] = Color.White;
i++;
lastSkip = 0;
}
}
while (data[i] == Color.White)
{
i++;
}
i--;
}
}
}
}
// Set the data when finished
//-- don't need to paste this part, already given up above
texture.SetData(data);
return texture;
If you need to do it from scratch (though I'm guessing there are easier ways), change the way you perform the rendering. Instead of iterating through angles and plotting pixels, iterate through pixels and determine where they are relative to the circle. If they are <R, draw as fill color. If they are ~= R, draw as border color.
I know that I'm a little late, but I modified your code to fill in the center
public static Texture2D CreateCircle(GraphicsDevice importedGraphicsDevice, int radius)
{
int outerRadius = radius * 2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(importedGraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.Transparent;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f / radius;
for (double angle = 0; angle < Math.PI * 2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
//width
for (int i = 0; i < outerRadius; i++)
{
int yStart = -1;
int yEnd = -1;
//loop through height to find start and end to fill
for (int j = 0; j < outerRadius; j++)
{
if (yStart == -1)
{
if (j == outerRadius - 1)
{
//last row so there is no row below to compare to
break;
}
//start is indicated by Color followed by Transparent
if (data[i + (j * outerRadius)] == Color.White && data[i + ((j + 1) * outerRadius)] == Color.Transparent)
{
yStart = j + 1;
continue;
}
}
else if (data[i + (j * outerRadius)] == Color.White)
{
yEnd = j;
break;
}
}
//if we found a valid start and end position
if (yStart != -1 && yEnd != -1)
{
//height
for (int j = yStart; j < yEnd; j++)
{
data[i + (j * outerRadius)] = new Color(10, 10, 10, 10);
}
}
}
texture.SetData(data);
return texture;
}

Categories

Resources