Related
i'm a newbie and i am following a tutorial on procedural landmass generation. However, my plane does not look right. It has a lot of seams/cracks. Is there someone who can point me in the right direction?
Below is my MeshGenerator scripts:
public static class MeshGenerator
{
public static MeshData GenerateTerrainMesh(float[,] heightMap, float heightMultiplier, AnimationCurve heightCurve)
{
int width = heightMap.GetLength(0);
int height = heightMap.GetLength(1);
float topLeftX = (width - 1) / -2f;
float topLeftZ = (height - 1) / 2f;
MeshData meshData = new MeshData(width, height);
int vertexIndex = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
meshData.vertices[vertexIndex] = new Vector3(topLeftX + x, heightCurve.Evaluate(heightMap[x,y]) * heightMultiplier, topLeftZ - y);
meshData.uvs[vertexIndex] = new Vector2(x / (float)width, y / (float)height);
if (x < width - 1 && y < height - 1)
{
meshData.AddTriangle(vertexIndex, vertexIndex + width + 1, vertexIndex + width);
meshData.AddTriangle(vertexIndex, + width + 1, vertexIndex + 1);
}
vertexIndex++;
}
}
return meshData;
}
}
public class MeshData
{
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
int triangleIndex;
public MeshData(int meshWidth, int meshHeight)
{
vertices = new Vector3[meshWidth * meshHeight];
uvs = new Vector2[meshWidth * meshHeight];
triangles = new int[(meshWidth-1) * (meshHeight-1)*6];
}
public void AddTriangle(int a, int b, int c)
{
triangles[triangleIndex] = a;
triangles[triangleIndex+1] = b;
triangles[triangleIndex+2] = c;
triangleIndex += 3;
}
public Mesh CreateMesh()
{
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
return mesh;
}
}
You triangle indices are wrong, this is rather obvious since you have a bunch of triangle-shaped holes. Notably
meshData.AddTriangle(vertexIndex, + width + 1, vertexIndex + 1);
the second vertex of the second triangle is a constant value, and that is most likely incorrect
You should not need to keep a running total of vertexIndex, you should be perfectly able to compute the triangle indices from the grid indices:
var v1 = y * (width+1) + x; // You should have one more column of vertices than you have grid cells
var v2 = v2 + 1; // the vertex one column to the right
var v3 = v1 + width+1; // the vertex one row down
var v4 = v3 + 1;
meshData.AddTriangle(v1, v2, v4);
meshData.AddTriangle(v1, v4, v3);
You may need to invert the vertex order to ensure the normals are oriented correctly.
I Have problem to save my simulation into JSON File.
the basics I make a moving ship. in the world space there will be a probe button that will provide information about the ship. I want to save that information into JSON.
Anyone know how to do that. I am new in Unity.
In button () I want to save the ship information (eg. position, depthsea, windspeed, temperature,flow
public class test : MonoBehaviour
{
// Start is called before the first frame update
public GameObject Ship;
public GameObject hole;
public GameObject turtle;
public GameObject panelhole;
public GameObject panelturtle;
public RectTransform shipvalue;
//public RectTransform flowvalue;
//public RectTransform windspeedvalue;
//public RectTransform temperaturevalue;
//public RectTransform depthvalue;
public Vector3 offset;
public RectTransform Basicobject; //parent
//public static bool captureAllKeyboardInput = false;
private bool paneloff = false;
public float duration = 1;
public int[,] grid = new int[10, 16];
public float[,] depth = new float[4, 3]
{{1.6f, 2.3f, 3.5f },
{4, 5, 6.5f},
{7, 8, 6.5f},
{7, 8, 6.5f}};
public float[,] temperaturedata = new float[10, 16]
{{0, 0, 0, 0, 0, 0, 0, 22.5f, 22.7f, 23, 23.9f, 24, 26.3f, 26.4f, 26.4f, 26.3f},
{0, 0, 0, 0, 0, 0, 22.8f, 23.2f, 23.8f, 24.4f, 25, 24.3f, 26.5f, 26.5f, 26.5f, 26.6f},
{0, 0, 0, 0, 0, 22.5f, 23.1f, 24.8f, 25.3f, 25.7f, 0, 0, 26.7f, 26.3f, 26.2f, 26.6f},
{0, 0, 0, 0, 23.2f, 23.8f, 25.1f, 25.4f, 25.9f, 0, 0, 26.8f, 26.9f, 26.5f, 26.3f, 26.3f},
{0, 0, 24.5f, 23.3f, 23.9f, 24.5f, 25.7f, 25.6f, 26.8f, 0, 0, 26.9f, 27.1f, 26.6f, 26.4f, 26.4f},
{0, 24.1f, 23.9f, 24.9f, 25.4f, 25.5f, 25.9f, 27.4f, 27.2f, 0, 0, 27, 27.2f, 26.8f, 26.4f, 26 },
{27.4f, 27.7f, 27.3f, 26.2f, 26.2f, 25.9f, 27.5f, 27.7f, 27.3f, 0, 26.8f, 27.2f, 27.2f, 26.9f, 26.4f, 26.2f},
{28.5f, 29, 27.5f, 27.3f, 27.3f, 27.5f, 27.7f, 27.7f, 27.5f, 27.2f, 27.2f, 27.4f, 27.4f, 26.9f, 26.3f, 26.7f},
{28.5f, 27.6f, 27.1f, 27, 26.5f, 27.6f, 27.6f, 27.6f, 27.7f, 27.4f, 27.8f, 27.7f, 27.7f, 27, 27, 26.6f},
{28.5f, 27.6f, 25, 27.3f, 26.8f, 27.8f, 27.3f, 27.5f, 28.1f, 27.9f, 28, 27.6f, 27.7f, 26.9f, 27.1f, 26.8f}};
public float[,] flowdata = new float[10, 16]
{{0, 0, 0, 0, 0, 0, 0, 0.4f, 0.4f, 0.6f, 0.8f, 0.6f, 0.7f, 0.4f, 0.4f, 0.4f},
{0, 0, 0, 0, 0, 0, 0.3f, 0.5f, 0.5f, 0.8f, 0.8f, 0.8f, 0.6f, 0.6f, 0.5f, 0.5f},
{0, 0, 0, 0, 0, 0.4f, 0.7f, 0.7f, 0.7f, 0.7f, 0, 0, 0.9f, 0.6f, 0.4f, 0.4f},
{0, 0, 0, 0, 0.5f, 0.5f, 0.6f, 0.7f, 0.6f, 0, 0, 0.8f, 0.8f, 0.4f, 0.3f, 0.3f},
{0, 0, 000, 0.7f, 0.6f, 0.5f, 0.7f, 1, 0.8f, 0, 0, 0.9f, 0.5f, 0.3f, 0.1f, 0.3f},
{0, 0.5f, 0.7f, 0.6f, 0.8f, 0.8f, 1.3f, 0.9f, 0.5f, 0, 0, 0.8f, 0.3f, 0.1f, 0.2f, 0.2f},
{0.6f, 0.6f, 0.6f, 0.7f, 1.1f, 0.9f, 0.8f, 0.4f, 0.3f, 0, 0.9f, 0.6f, 0.2f, 0.2f, 0.2f, 0.2f},
{0.4f, 0.4f, 0.5f, 0.5f, 0.3f, 0.4f, 0.3f, 0.2f, 0.4f, 0.2f, 0.8f, 0.3f, 0.2f, 0.2f, 0.2f, 0.1f},
{000, 0.3f, 0.5f, 0.2f, 0.2f, 0.2f, 0.2f, 0.1f, 0.2f, 0.6f, 0.6f, 0.3f, 0.3f, 0.2f, 0.2f, 0.2f},
{000, 000, 0.1f, 0.4f, 0.3f, 0.3f, 0.2f, 0.2f, 0.1f, 0.2f, 0.3f, 0.1f, 0.2f, 0.2f, 0.2f, 0.2f}};
public float[,] windspeeddata = new float[10, 12]
{{0,0,0,0,0,0,0,4,4,4,3,3},
{0,0,0,0,0,0,4,5,4,3,3,3},
{0,0,0,0,0,5,5,5,5,3,0,0},
{0,0,0,0,4,4,4,4,4,0,0,3},
{0,0,3,0,4,4,4,4,4,0,0,3},
{0,4,4,4,4,4,4,4,3,0,0,3},
{4,4,4,4,4,4,4,3,3,0,3,4},
{5,4,4,4,4,4,3,3,3,3,4,4},
{4,4,4,4,4,4,3,3,3,3,4,4},
{5,4,4,4,4,4,4,4,4,3,3,3}};
int row, column, num1, num2;
int p1;
int p2;
int[] grid2 = new int[5];
public Text shiposition = null;
public Text depthtext = null;
public Text windspeedtext = null;
public Text temperaturetext = null;
public Text flowtext = null;
//direction = Ship.transform.rotation.z;
float LastMove;
float timeIn = 0.5f;
public Vector3 direction;
float zvalue;
[Serializable]
public class ShipData
{
public int grid;
public float depth;
public float windspeed;
public float temperature;
}
[SerializeField]
private ShipData JSON_ShipData = new ShipData();
public void SaveintoJson()
{
string data = JsonUtility.ToJson(JSON_ShipData);
System.IO.File.WriteAllText(Application.persistentDataPath + "/DataCenter.json", data);
Debug.Log("Saving as JSON" + JSON_ShipData);
Debug.Log(Application.persistentDataPath);
}
void Start()
{
p1 = 9;
p2 = 0;
grid[p1, p2] = 1;
panelhole.SetActive(false);
panelturtle.SetActive(false);
Debug.Log(grid[p1, p2]);
Debug.Log(shiposition.transform.position);
}
// Update is called once per frame
void Update()
{
//Debug.Log(Ship.transform.localEulerAngles.z);
zvalue = Ship.transform.localEulerAngles.z;
if (Input.GetKeyDown(KeyCode.RightArrow))
{
StartCoroutine(Forward());
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
StartCoroutine(Backward());
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
StartCoroutine(Rotate(Vector3.forward, -90, 1.0f));
Debug.Log(transform.rotation.eulerAngles.z);
}
if (Input.GetKeyDown(KeyCode.UpArrow))
{
StartCoroutine(Rotate(Vector3.forward, 90, 1.0f));
Debug.Log(transform.rotation.eulerAngles.z);
}
if (Time.time - LastMove > timeIn)
{
LastMove = Time.time;
}
if (Input.GetKeyDown(KeyCode.W))
{
StartCoroutine(Rotate(Vector3.forward, 90, 1.0f));
// position(grid);
// depthsea(depth);
}
if (Input.GetKeyDown(KeyCode.A))
{
if (p2 >= 0)
{
StartCoroutine(Backward());
grid[p1, p2] = 0;
grid[p1, --p2] = 1;
//position(grid);
//depthsea(depth);
}
else
{
Debug.Log("You can't move left!!");
}
}
if (Input.GetKeyDown(KeyCode.D))
{
// z = 0
if (p2 <= 12 && zvalue == 0)
{
StartCoroutine(Forward());
grid[p1, p2] = 0;
grid[p1, ++p2] = 1;
//position(grid);
//depthsea(depth);
Debug.Log(zvalue);
}
// z = 270
else if (p2 <= 12 && zvalue == 270)
{
StartCoroutine(Forward());
grid[p1, p2] = 0;
grid[++p1, p2] = 1;
// position(grid);
//depthsea(depth);
Debug.Log(zvalue);
}
//// z = 180
else if (p2 <= 12 && zvalue == 180)
{
StartCoroutine(Forward());
grid[p1, p2] = 0;
grid[p1, --p2] = 1;
// position(grid);
//depthsea(depth);
Debug.Log(zvalue);
}
//// z = 90
else if (p2 <= 12 && zvalue == 90)
{
StartCoroutine(Forward());
grid[p1, p2] = 0;
grid[--p1, p2] = 1;
// position(grid);
//depthsea(depth);
Debug.Log(zvalue);
}
else
{
Debug.Log("You can't move right any further!!");
Debug.Log(Ship.transform.localEulerAngles.z);
}
}
if (Input.GetKeyDown(KeyCode.S))
{
StartCoroutine(Rotate(Vector3.forward, -90, 1.0f));
//position(grid);
//depthsea(depth);
}
//WebGLInput.captureAllKeyboardInput = false;
}
private void position(int[,] grid)
{
for (int i = 0; i < grid.GetLength(0); i++)
{
for (int j = 0; j < grid.GetLength(1); j++)
{
if (grid[i, j] == 1)
{
// Debug.Log("x: " + i + " y: " + j + " Grid: " + grid[i, j]);
shiposition.text = "X : " + i + " " + "Y : " + j;
shiposition.text.ToString();
PlayerPrefs.SetString("position", shiposition.text);
PlayerPrefs.Save();
Debug.Log(shiposition.text);
}
}
}
}
public void windspeed(float[,] windspeeddata)
{
for (int x = 0; x < windspeeddata.GetLength(0); x++)
{
for (int y = 0; y < windspeeddata.GetLength(1); y++)
{
if (grid[x, y] == 1)
{
windspeedtext.text = "Windspeed Level :" + windspeeddata[x, y];
}
}
}
}
public void temperature(float[,] temperaturedata)
{
for (int x = 0; x < temperaturedata.GetLength(0); x++)
{
for (int y = 0; y < temperaturedata.GetLength(1); y++)
{
if (grid[x, y] == 1)
{
//Debug.Log(temperaturedata[x, y]);
temperaturetext.text = "Temperature :" + temperaturedata[x, y] + "C";
}
}
}
}
public void flow(float[,] flowdata)
{
for (int x = 0; x < flowdata.GetLength(0); x++)
{
for (int y = 0; y < flowdata.GetLength(1); y++)
{
if (grid[x, y] == 1)
{
flowtext.text = "Flow :" + flowdata[x, y];
}
}
}
}
public void depthsea(float[,] depth)
{
for (int x = 0; x < depth.GetLength(0); x++)
{
for (int y = 0; y < depth.GetLength(1); y++)
{
if (grid[x, y] == 1)
{
depthtext.text = "Depth :" + depth[x, y];
Debug.Log(depth[x, y]);
}
}
}
}
public void moveobject()
{
shipvalue.transform.position = Ship.transform.position;
//shipvalue.transform.position.y + 0.5f;
//flowvalue.transform.position = Ship.transform.position;
//windspeedvalue.transform.position = Ship.transform.position;
//temperaturevalue.transform.position = Ship.transform.position;
//depthvalue.transform.position = Ship.transform.position;
}
public void button()
{
position(grid);
depthsea(depth);
windspeed(windspeeddata);
temperature(temperaturedata);
flow(flowdata);
moveobject();
//value.transform.position = Ship.transform.position;
//string newposition = JsonUtility.ToJson(shiposition.text);
//System.IO.File.WriteAllText(Application.persistentDataPath + "PositionData.json", newposition);
//Debug.Log(shiposition.text);
}
The JSON export itself should work that way
However, never use + "/" for system file paths. Rather use Path.Combine which inserts the correct path separator automatically
System.IO.File.WriteAllText(Path.Combine(Application.persistentDataPath, "DataCenter.json", data);
Main Problem
You are never assigning any values into the JSON_ShipData so you will always export a JSON with default values!
So first what you want is to store a Vector2Int for the position
[Serializable]
public class ShipData
{
public Vector2Int position;
public float depth;
public float windspeed;
public float temperature;
}
Then you probably would want to update the content at certain places like e.g.
private bool TryGetPosition(int[,] grid, out Vector2Int position)
{
position = default;
for (int x = 0; x < grid.GetLength(0); i++)
{
for (int y = 0; y < grid.GetLength(1); j++)
{
if (grid[x, y] == 1)
{
// store into the returned out value
position = new Vector2Int(x, y);
shiposition.text = $"X : {x} Y : {y}";
shiposition.text.ToString();
PlayerPrefs.SetInt("positionX", position.x);
PlayerPrefs.SetInt("positionY", position.y);
PlayerPrefs.Save();
Debug.Log(shiposition.text);
return true;
}
}
}
return false;
}
Note that this is also more efficient since it doesn't iterate through the rest of your grid if it already has found the position
Repeat the same for your other methods and then use them like e.g.
public void button()
{
// Check if a position is found and if yes update the json data
if(TryGetPosition(grid, out var position)
{
JSON_ShipData.position = position;
}
// Repeat the same for your other values
SaveintoJson();
}
General notes:
Before doing stuff like
grid[p1, --p2] = 1;
You should always check if your are maybe getting out of bounds e.g. like
private bool TryGoToPosition(int[,] grid, Vector2Int from, Vector2Int to)
{
if(to.x >= 0 && to.x < grid.GetLength(0) - 1 && to.y >= 0 && to.y < grid.GetLength(1) - 1)
{
grid[from.x, from.y] = 0;
grid[to.x, to.x] = 1;
}
else
{
Debug.LogWarning("Reached grid border! -> Ignored");
}
}
Then I would simply work with a Vector2Int in general instead of setting some value in a grid to 0 or 1 like
Vector2Int gridDimensions = new Vector2Int(10, 16);
public ShipData currentShipData;
public ShipData JSON_ShipData;
private bool CanGoToPosition(Vector2Int to)
{
return to.x >= 0 && to.x < gridDimensions.x - 1 && to.y >= 0 && to.y < gridDimensions.y - 1;
}
void Start()
{
currentShipData.position = new Vector2Int(9, 0);
panelhole.SetActive(false);
panelturtle.SetActive(false);
Debug.Log(JSON_ShipData.position);
Debug.Log(shiposition.transform.position);
}
void Update()
{
......
if (Input.GetKeyDown(KeyCode.A))
{
if (CanGoToPosition(JSON_ShipData.position + Vector2Int.down))
{
currentShipData.position += Vector2Int.down;
}
else
{
Debug.Log("You can't move left!!");
}
}
...
// Then after you handled user input for movement update the other data
currentShipData.windspeed = windspeedData[JSON_ShipData.position.x, JSON_ShipData.position.y];
currentShipData.flow = flowData[JSON_ShipData.position.x, JSON_ShipData.position.y];
...
}
Then for the button press you simply only copy over the current values
public void button()
{
JSON_ShipData.position = currentShipData.position;
...
SaveintoJson();
}
I am building a program where I can convert black&white images to a terrain.
I have a working code, but because Unity has a limit on the number of vertices a mesh can have The code doesn't work for big images.
So, I tried to split the image to smaller images and create mesh for each part. it works, but I get weird spacing between my meshes and I am not sure why.
Code:
public static class MeshGenerator
{
private static Transform CreateGameObjectWithMesh(Mesh m)
{
var go = new GameObject();
go.AddComponent<MeshFilter>().mesh = m;
go.AddComponent<MeshRenderer>();
m.RecalculateNormals();
return go.transform;
}
public static void Generate(float[,] heightsData)
{
int width = heightsData.GetLength(0);
int height = heightsData.GetLength(1);
float topLeftX = (width - 1) / -2f;
float topLeftZ = (height - 1) / 2f;
// Just for testing, I am working with 256X256 texture and split it to 4 parts. (would be modified when working)
for (int i = 0; i < 4; i++)
{
var startX = (i % 2) * 128;
var startY = i < 2 ? 0 : 128;
StartMeshCreationThread(mesh =>
{
var t = CreateGameObjectWithMesh(mesh);
t.position = new Vector3(topLeftX + startX + 128f / 2f, 0, topLeftZ - startY - 128f / 2f);
}, startX, startY, 128, 128, heightsData,
new Vector2(topLeftX, topLeftZ), (x, y) => new Vector2(x / (float) width, y / (float) height));
}
}
// Would create a new thread in the future.
private static void StartMeshCreationThread(Action<Mesh> onFinishedCallback, int startX, int startY, int width,
int height, float[,] heightsData, Vector2 orgTopLeft, Func<int, int, Vector2> getUVPosition)
{
var meshData = new MeshData (width, height);
float topLeftX = (width - 1) / -2f;
float topLeftZ = (height - 1) / 2f;
int vertexIndex = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
var vertexPosition = new Vector3(topLeftX + x, heightsData[x + startX, y + startY] * 10f, topLeftZ - y);
meshData.AddVertexData(vertexIndex, vertexPosition, getUVPosition(x + startX, y + startY));
if (x < width - 1 && y < height - 1)
{
meshData.AddTriangle (vertexIndex, vertexIndex + width + 1, vertexIndex + width);
meshData.AddTriangle (vertexIndex + width + 1, vertexIndex, vertexIndex + 1);
}
vertexIndex++;
}
}
onFinishedCallback(meshData.CreateMesh());
}
private class MeshData
{
private readonly Vector3[] vertices;
private readonly Vector2[] uvs;
private readonly int[] triangles;
private int triangleIndex;
public MeshData(int width, int height)
{
vertices = new Vector3[width * height];
uvs = new Vector2[width * height];
triangles = new int[(width - 1) * (height - 1) * 6];
triangleIndex = 0;
}
public void AddVertexData(int index, Vector3 vertexPosition, Vector2 uvPosition)
{
vertices[index] = vertexPosition;
uvs[index] = uvPosition;
}
public void AddTriangle(int v1, int v2, int v3)
{
triangles[triangleIndex + 0] = v1;
triangles[triangleIndex + 1] = v2;
triangles[triangleIndex + 2] = v3;
triangleIndex += 3;
}
public Mesh CreateMesh()
{
var mesh = new Mesh {vertices = vertices, triangles = triangles, uv = uvs};
return mesh;
}
}
}
Result (for 256X256) texture:
(main source)
I have this code that draws the boxes on a Qbert board, how would i figure out how to detect what color blocks are stepped on?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace QBert
{
public class Map
{
public int[,] board;
public Color[] blockColors = new Color[] { Color.Blue, Color.Green }; //this makes it so it takes one time to step to get to green
Texture2D block;
public Map(Texture2D block) //draws the map of the blocks
{
this.block = block;
board = new int[8, 7]
{
{ 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ -1, -1, -1, -1, -1, -1, -1 },
}
;
}
public Vector2 GetSquareCoords(int x, int y) //cordinates of the block
{
int ofs = block.Width / 2;
ofs *= y % 2;
return new Vector2(x * block.Width + ofs, y * 96); // 96
}
public Vector2 GetSquareCenter(int x, int y) //method for to jump on the middle of a block
{
Vector2 coords = GetSquareCoords(x, y);
return new Vector2(coords.X + block.Width / 2, coords.Y + 32); //32
}
public Vector2 GetNextSquare(bool down, bool left, Vector2 position) //this is how you jump to a next square
{
// If on even row, right is directly below and left is below and to the left
// If on odd row, left is directly below and right is below and to the right
int next_x = 0, next_y = 0;
int x = (int)position.X;
int y = (int)position.Y;
if (down)
{
next_y = y + 1;
if (left)
{
next_x = x - 1; // -1
}
else
{
next_x = x;
}
}
else
{
next_y = y - 1;
}
if (y % 2 == 0)
{
if (left)
next_x = x - 1;
else
next_x = x;
}
else
{
if (left)
next_x = x;
else
next_x = x + 1; //+1
}
if (next_x < 0)
{
next_x += 1;
}
if (next_x > 6)
{
next_x -= 1;
}
if (next_y < 0)
{
next_y += 1;
}
if (next_y > 7)
{
next_y -= 1;
}
if (board[next_y, next_x] == 0)
{
return new Vector2(x, y);
}
else
{
return new Vector2(next_x, next_y);
}
}
public void Draw(SpriteBatch spriteBatch) //draws the blocks and colors of the block
{
int drawXOffset = 30;
int drawYOffset = 60;
for (int x = 0; x < 7; x++)
for (int y = 0; y < 7; y++)
{
Vector2 coord = GetSquareCoords(x, y);
if (board[y, x] > 0)
spriteBatch.Draw(block, new Rectangle(drawXOffset + (int)coord.X, drawYOffset + (int)coord.Y, block.Width, block.Height), blockColors[board[y, x] - 1]);
}
}
}
}
I am trying to have the code detect the number of blocks drawn so that I know when they are all a certain color.
I need to make it a certain color of a block to end the game.
Right now, i have it starting out as a Blue Block Color then changing to a Green Block, how would i make it detect that if all the green blocks are stepped on that the game ends?
Somewhere in your Update method, you will want something like this:
bool finished = true;
for (int x = 0; x < 7; x++)
{
for (int y = 0; y < 7; y++)
{
if (board != 0 && board != 2) // 2 is green
{
finished = true;
break;
}
}
if (finished)
break;
}
if (finished)
{
// Move to next level
}
I think somthing like this is what you want
public Vector2 GetNextSquare(bool down, bool left, Vector2 position)
{
int x = (int)position.X;
int y = (int)position.Y;
//...other code
if (blockColors[board[next_y, next_x]] == Color.Green)
{
//End
}
else if (board[next_y, next_x] == 0)
{
return new Vector2(x, y);
}
else
{
return new Vector2(next_x, next_y);
}
}
Usually you have some sort of data representing your game field and rendering code simply renders visual representation of the field. Your game code only works with internal field representation (i.e. in your case set of cubes objects with "Color" property).
You definitely can check color on the screen, but it will require significantly more effort.
#Jaview "what do you mean, can you show me an example how i can check?"
Here is an example how to get a pixel in XNA:
ResolveTexture2D backBufferData;
backBufferData = new ResolveTexture2D(
graphics.GraphicsDevice,
graphics.GraphicsDevice.PresentationParameters.BackBufferWidth,
graphics.GraphicsDevice.PresentationParameters.BackBufferHeight,
1,
graphics.GraphicsDevice.PresentationParameters.BackBufferFormat
);
Rectangle sourceRectangle = new Rectangle(Mouse.GetState().X, Mouse.GetState().Y, 1, 1);
Color[] retrievedColor = new Color[1];
graphics.GraphicsDevice.ResolveBackBuffer(backBufferData);
backBufferData.GetData<Color>(
0,
sourceRectangle,
retrievedColor,
0,
1);
selectedColor = retrievedColor[0];
I have fully mastered the art of Perlin Noise in 3D, and now I'm trying to use my same implementation for a 2D algorithm.
The problem seems to be in picking my gradient directions. In 3D I use 16 gradients in evenly distributed directions and this works great.
In 2D I figured I'd use 8 gradients. up, down, left, right, and the four diagonal directions.
Here is what I get:
The general look of the noise is always correct, but the edges of the squares don't quite match up.
I have also tried using other gradients or fewer gradients but get similar results.
Here in another example you can see that the edges do match up sometimes and the results are fine in that area -
When I don't use gradients and instead just interpolate between a value picked randomly at each of the 4 corners I get the right results, which is what makes me think it is the gradient part that is messing it up.
Here is my code:
//8 different gradient directions
private Point[] grads = new Point[] {
new Point(0, 1), new Point(1, 1), new Point(1, 0), new Point(1, -1),
new Point(0, -1), new Point(-1, -1), new Point(-1, 0), new Point(-1, 1),};
//takes the dot product of a gradient and (x, y)
private float dot2D(int i, float x, float y)
{
return
grads[i].X * x + grads[i].Y * y;
}
public float Noise2D(float x, float y)
{
int
ix = (int)(x),
iy = (int)(y);
x = x - ix;
y = y - iy;
float
fx = fade(x),
fy = fade(y);
ix &= 255;
iy &= 255;
// here is where i get the index to look up in the list of
// different gradients.
// hashTable is my array of 0-255 in random order
int
g00 = hashTable[ix + hashTable[iy ]],
g10 = hashTable[ix + 1 + hashTable[iy ]],
g01 = hashTable[ix + hashTable[iy + 1]],
g11 = hashTable[ix + 1 + hashTable[iy + 1]];
// this takes the dot product to find the values to interpolate between
float
n00 = dot2D(g00 & 7, x, y),
n10 = dot2D(g10 & 7, x, y),
n01 = dot2D(g01 & 7, x, y),
n11 = dot2D(g11 & 7, x, y);
// lerp() is just normal linear interpolation
float
y1 = lerp(fx, n00, n10),
y2 = lerp(fx, n01, n11);
return
lerp(fy, y1, y2);
}
I'm in a bit of a rush, but this might be helpful. I adapted Perlin's reference implementation to C#. For 2D, just use the 3D Noise() function with a fixed z parameter. (public static float Noise(float x, float y, float z) towards the end of the class.)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Diagnostics;
namespace GoEngine.Content.Entities
{
public class NoiseMaker
{
/// adapted from http://cs.nyu.edu/~perlin/noise/
// JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
private static int[] p = new int[512];
private static int[] permutation = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
static NoiseMaker()
{
CalculateP();
}
private static int _octaves;
private static int _halfLength = 256;
public static void SetOctaves(int octaves)
{
_octaves = octaves;
var len = (int)Math.Pow(2, octaves);
permutation = new int[len];
Reseed();
}
private static void CalculateP()
{
p = new int[permutation.Length * 2];
_halfLength = permutation.Length;
for (int i = 0; i < permutation.Length; i++)
p[permutation.Length + i] = p[i] = permutation[i];
}
public static void Reseed()
{
var random = new Random();
var perm = Enumerable.Range(0, permutation.Length).ToArray();
for (var i = 0; i < perm.Length; i++)
{
var swapIndex = random.Next(perm.Length);
var t = perm[i];
perm[i] = perm[swapIndex];
perm[swapIndex] = t;
}
permutation = perm;
CalculateP();
}
public static float Noise(Vector3 position, int octaves, ref float min, ref float max)
{
return Noise(position.X, position.Y, position.Z, octaves, ref min, ref max);
}
public static float Noise(float x, float y, float z, int octaves, ref float min, ref float max)
{
var perlin = 0f;
var octave = 1;
for (var i = 0; i < octaves; i++)
{
var noise = Noise(x * octave, y * octave, z * octave);
perlin += noise / octave;
octave *= 2;
}
perlin = Math.Abs((float)Math.Pow(perlin,2));
max = Math.Max(perlin, max);
min = Math.Min(perlin, min);
//perlin = 1f - 2 * perlin;
return perlin;
}
public static float Noise(float x, float y, float z)
{
int X = (int)Math.Floor(x) % _halfLength;
int Y = (int)Math.Floor(y) % _halfLength;
int Z = (int)Math.Floor(z) % _halfLength;
if (X < 0)
X += _halfLength;
if (Y < 0)
Y += _halfLength;
if (Z < 0)
Z += _halfLength;
x -= (int)Math.Floor(x);
y -= (int)Math.Floor(y);
z -= (int)Math.Floor(z);
var u = Fade(x);
var v = Fade(y);
var w = Fade(z);
int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, // HASH COORDINATES OF
B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; // THE 8 CUBE CORNERS,
return MathHelper.Lerp(
MathHelper.Lerp(
MathHelper.Lerp(
Grad(p[AA], x, y, z) // AND ADD
,
Grad(p[BA], x - 1, y, z) // BLENDED
,
u
)
,
MathHelper.Lerp(
Grad(p[AB], x, y - 1, z) // RESULTS
,
Grad(p[BB], x - 1, y - 1, z)
,
u
)
,
v
)
,
MathHelper.Lerp(
MathHelper.Lerp(
Grad(p[AA + 1], x, y, z - 1) // CORNERS
,
Grad(p[BA + 1], x - 1, y, z - 1) // OF CUBE
,
u
)
,
MathHelper.Lerp(
Grad(p[AB + 1], x, y - 1, z - 1)
,
Grad(p[BB + 1], x - 1, y - 1, z - 1)
,
u
)
,
v
)
,
w
);
}
static float Fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
static float Grad(int hash, float x, float y, float z)
{
int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
v = h < 4 ? y : h == 12 || h == 14 ? x : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
}
}
Update
Okay, I managed to create a working 2D version. Here's the class:
/// implements improved Perlin noise in 2D.
/// Transcribed from http://www.siafoo.net/snippet/144?nolinenos#perlin2003
/// </summary>
public static class Noise2d
{
private static Random _random = new Random();
private static int[] _permutation;
private static Vector2[] _gradients;
static Noise2d()
{
CalculatePermutation(out _permutation);
CalculateGradients(out _gradients);
}
private static void CalculatePermutation(out int[] p)
{
p = Enumerable.Range(0, 256).ToArray();
/// shuffle the array
for (var i = 0; i < p.Length; i++)
{
var source = _random.Next(p.Length);
var t = p[i];
p[i] = p[source];
p[source] = t;
}
}
/// <summary>
/// generate a new permutation.
/// </summary>
public static void Reseed()
{
CalculatePermutation(out _permutation);
}
private static void CalculateGradients(out Vector2[] grad)
{
grad = new Vector2[256];
for (var i = 0; i < grad.Length; i++)
{
Vector2 gradient;
do
{
gradient = new Vector2((float)(_random.NextDouble() * 2 - 1), (float)(_random.NextDouble() * 2 - 1));
}
while (gradient.LengthSquared() >= 1);
gradient.Normalize();
grad[i] = gradient;
}
}
private static float Drop(float t)
{
t = Math.Abs(t);
return 1f - t * t * t * (t * (t * 6 - 15) + 10);
}
private static float Q(float u, float v)
{
return Drop(u) * Drop(v);
}
public static float Noise(float x, float y)
{
var cell = new Vector2((float)Math.Floor(x), (float)Math.Floor(y));
var total = 0f;
var corners = new[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(1, 1) };
foreach (var n in corners)
{
var ij = cell + n;
var uv = new Vector2(x - ij.X, y - ij.Y);
var index = _permutation[(int)ij.X % _permutation.Length];
index = _permutation[(index + (int)ij.Y) % _permutation.Length];
var grad = _gradients[index % _gradients.Length];
total += Q(uv.X, uv.Y) * Vector2.Dot(grad, uv);
}
return Math.Max(Math.Min(total, 1f), -1f);
}
}
Call it like this:
private void GenerateNoiseMap(int width, int height, ref Texture2D noiseTexture, int octaves)
{
var data = new float[width * height];
/// track min and max noise value. Used to normalize the result to the 0 to 1.0 range.
var min = float.MaxValue;
var max = float.MinValue;
/// rebuild the permutation table to get a different noise pattern.
/// Leave this out if you want to play with changing the number of octaves while
/// maintaining the same overall pattern.
Noise2d.Reseed();
var frequency = 0.5f;
var amplitude = 1f;
var persistence = 0.25f;
for (var octave = 0; octave < octaves; octave++)
{
/// parallel loop - easy and fast.
Parallel.For(0
, width * height
, (offset) =>
{
var i = offset % width;
var j = offset / width;
var noise = Noise2d.Noise(i*frequency*1f/width, j*frequency*1f/height);
noise = data[j * width + i] += noise * amplitude;
min = Math.Min(min, noise);
max = Math.Max(max, noise);
}
);
frequency *= 2;
amplitude /= 2;
}
if (noiseTexture != null && (noiseTexture.Width != width || noiseTexture.Height != height))
{
noiseTexture.Dispose();
noiseTexture = null;
}
if (noiseTexture==null)
{
noiseTexture = new Texture2D(Device, width, height, false, SurfaceFormat.Color);
}
var colors = data.Select(
(f) =>
{
var norm = (f - min) / (max - min);
return new Color(norm, norm, norm, 1);
}
).ToArray();
noiseTexture.SetData(colors);
}
Note that I've used a couple of XNA structures (Vector2 and Texture2D), but it should be pretty clear what they do.
If you want higher frequency (more "noisy") content with fewer octaves, increase the initial frequency value that used in the octave loop.
This implementation uses "improved" Perlin noise, which should be a bit faster than the standard version. You might also have a look at Simplex noise, which is quite a bit faster at higher dimensions.
I had to change this:
n00 = dot2D(g00 & 7, x, y),
n10 = dot2D(g10 & 7, x, y),
n01 = dot2D(g01 & 7, x, y),
n11 = dot2D(g11 & 7, x, y);
to this:
n00 = dot2D(g00 & 7, x , y ),
n10 = dot2D(g10 & 7, x - 1, y ),
n01 = dot2D(g01 & 7, x , y - 1),
n11 = dot2D(g11 & 7, x - 1, y - 1);
Basically just subtracting 1 from the x and y where needed.
If you plug in a zero value for z into your 3D equation and simply follow the math through, removing terms, you'll see that you end up with a simpler equation in the end.
Your implementation looks kind of different to the one I'm using though.
Here's a comparison of a 3D and 2D function I'm using (in JavaScript):
noise3d: function(x, y, z)
{
// Find unit cube that contains point.
var X = Math.floor(x) & 255,
Y = Math.floor(y) & 255,
Z = Math.floor(z) & 255;
// Find relative x,y,z of point in cube.
x -= Math.floor(x);
y -= Math.floor(y);
z -= Math.floor(z);
// Compute fade curves for each of x,y,z.
var u = fade(x),
v = fade(y),
w = fade(z);
// Hash coordinates of the corners.
var A = p[X ] + Y, AA = p[A] + Z, AB = p[A + 1] + Z,
B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;
// Add blended results from 8 corners of cube.
return scale(
lerp(
w,
lerp(
v,
lerp(
u,
grad(p[AA], x, y, z),
grad(p[BA], x - 1, y, z)
),
lerp(
u,
grad(p[AB], x, y - 1, z),
grad(p[BB], x - 1, y - 1, z)
)
),
lerp(
v,
lerp(
u,
grad(p[AA + 1], x, y, z - 1),
grad(p[BA + 1], x - 1, y, z - 1)
),
lerp(
u,
grad(p[AB + 1], x, y - 1, z - 1),
grad(p[BB + 1], x - 1, y - 1, z - 1)
)
)
)
);
}
The 2D version involves fewer computations.
noise2d: function(x, y)
{
// Find unit square that contains point.
var X = Math.floor(x) & 255,
Y = Math.floor(y) & 255;
// Find relative x,y of point in square.
x -= Math.floor(x);
y -= Math.floor(y);
// Compute fade curves for each of x,y.
var u = fade(x),
v = fade(y);
// Hash coordinates of the corners.
var A = p[X ] + Y, AA = p[A], AB = p[A + 1],
B = p[X + 1] + Y, BA = p[B], BB = p[B + 1];
// Add blended results from the corners.
return scale(
lerp(
v,
lerp(
u,
grad(p[AA], x, y, 0),
grad(p[BA], x - 1, y, 0)
),
lerp(
u,
grad(p[AB], x, y - 1, 0),
grad(p[BB], x - 1, y - 1, 0)
)
)
);
}