I have a piece of code in C# that populates an array with values. In the Awake method I set the length of the values array, yet when I try to access it in the setSample method it returns an IndexOutOfRangeException. The
public int gridSize = 32;
public int width;
public int height;
public int featureSize = 32;
public float[] values;
public void Awake () {
width = gridSize;
height = gridSize;
float[] values = new float[6 * width * height];
Debug.Log("Array length: " + values.Length);
for (int y = 0; y < height; y += featureSize) {
for (int x = 0; x < width; x += featureSize) {
setSample(x, y, Random.value);
}
}
}
public void setSample (int x, int y, float value) {
Debug.Log("Array length: " + values.Length);
values[((x & (width - 1)) + (y & (height - 1)) * gridSize)] = value;
}
I added the Debug.Log() lines, which gave me the following output:
Array length: 6144
Array length: 0
IndexOutOfRangeException: Array index is out of range.
Both the methods and the variables are public, so I don't see why there should be any access issues. Why does the array change after I declare it? Is it because it is full of null values?
Instead of declaring again you should use existing values, inside the awake method.
change
From
float[] values = new float[6 * width * height];
to
values = new float[6 * width * height];
Related
Hi I am trying to add sets of coordinates to a single line of a List (so x, y as a single list line). I am currently generating the x and y coordinates as I break up an image and display the chunks in a grid. I am hitting a brick wall in being able to add both x and y to the same List line. Here is the code I have so far -
float sf = 1f;
int x = 0;
int y = 0;
int width = tileImage.Width / tileNumber;
int height = tileImage.Height / tileNumber;
int placeXValue = (width / 10);
int placeYValue = (height / 10);
int placeX = placeXValue;
int placeY = placeYValue;
Rectangle tileRect = tileImage.Bounds;
tileRect.Width = width;
tileRect.Height = height;
coordinates = new List<int>();
for (int i = 0; i < tileNumber; i++)
{
for (int j = 0; j < tileNumber; j++)
{
tileRect.X = x;
tileRect.Y = y;
_spriteBatch.Draw(tileImage, new Vector2((x + placeX) * sf, (y + placeY) * sf), tileRect, Color.White, 0, new Vector2(0, 0), sf, SpriteEffects.None, 0);
placeX += placeXValue;
x += width;
coordinates.Add(x,y);
}
x = 0;
y += height;
placeX = placeXValue;
placeY += placeYValue;
}
I found out how to do what I was trying to in the question. The first thing that I did was change my - private List<int> coordinates; - to a tuple list like this - List<Tuple<int, int>> coordinates; - This then allowed me to add my x and y elements to a new list by using the - coordinates.Add(new Tuple<int, int>(x,y)); - tuple functionality. Making this change has allowed me to add my generated coordinate pairs to my list.
I am attempting to create randomised terrain meshes as I have done so in the screenshot below:
However, the issue I am facing is when attempting to reduces the number of triangles and vertices (Level of Detail).
I understand that to do this I can just skip over vertices.
for example:
The above mesh is full detail in that the vertices are generated like so:
0->1->2->3->4->5->6->7->8->9->...
and to generate a lower level of detail i can skip vertices as long as the skipping of vertices does not exceed the length of vertices so i could do the following generation to lower detail:
0->2->4->6->8->10->12->14->16->...
or:
0->4->8->12->16->20->24->28->32->...
Using a 2D array and a nested loop makes this trivial as each iteration on 2D coordinates x/y can be incremented by the increment: 1, 2, 4, 8, however, i am dealing with 2D arrays in 1D format.
I have the following code which executes and almost correctly generates the above mesh in the screenshot above.
Unfortunately it does seem to be missing one line of of vertices on the top left (3d z axis) as seen below:
One caveat to the Execute(int, int) method below is that any access to the NativeArray which is not labeled [ReadOnly] will throw an exception if the array is accessing indexes outside of it's batch size.
public struct int6
{
public int a, b, c, d, e, f;
public int6(int a, int b, int c, int d, int e, int f) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; this.f = f; }
}
public class MeshGeneratorJob2
{
[ReadOnly] public static int width = 241;
[ReadOnly] public static int height = 241;
[ReadOnly] public static float topLeftX = (width - 1) / -2f;
[ReadOnly] public static float topLeftZ = (height - 1) / 2f;
[ReadOnly] public static NativeArray<float> heightMap = new NativeArray<float>(width * height, Allocator.TempJob);
public static NativeArray<float> heightCurveSamples;
public static NativeArray<float3> vertices = new NativeArray<float3>(width * height, Allocator.TempJob);
public static NativeArray<int6> triangles = new NativeArray<int6>((width - 1) * (height - 1), Allocator.TempJob);
public static NativeArray<float2> uvs = new NativeArray<float2>(width * height, Allocator.TempJob);
public void Execute()
{
for (int i = 0; i < vertices.Length; i += 5)
{
Execute(i, 5);
}
}
private void Execute(int startIndex, int count)
{
for (int vertexIndex = startIndex; vertexIndex < startIndex + count; vertexIndex++)
{
int x = vertexIndex % width;
int y = vertexIndex / width;
vertices[vertexIndex] = new float3(topLeftX + x, heightMap[vertexIndex] * 16.67f, topLeftZ - y);
uvs[vertexIndex] = new float2(x / (float)width, y / (float)height);
if (vertexIndex < triangles.Length && x < width - 1 && y < height - 1)
{
triangles[vertexIndex] = new int6(vertexIndex, vertexIndex + width + 1, vertexIndex + width,
vertexIndex + width + 1, vertexIndex, vertexIndex + 1);
}
}
}
}
I have come up with the following solution to this problem:
The first issue i solved was using a nested for loop y, x with y always starting at startIndex.
this, however, caused an issue as the vertexIndex could be higher than the length of the triangles length, so i calculated the current vertexIndex at the supplied startIndex as follows:
Here i introduced an incrementer value which increments both the x and y loops rather than y++, x++ however in this example incrementer is 1 which is essentially the same thing.
int vertexIndex = (int)(math.ceil((float)width / incrementer) * math.ceil((float)startIndex / incrementer));
however calculating the vertexIndex caused another issue which again caused out of bounds exceptions on setting the vertices.
This was due to the startIndex being incremented by count, where count was not the same as the incrementer.
To solve this I at the start of the method added the following code to round the startIndex up to the next incremental count if needed.
startIndex += startIndex % incrementer;
and altogether i then get the following code:
public struct int6
{
public int a, b, c, d, e, f;
public int6(int a, int b, int c, int d, int e, int f) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; this.f = f; }
}
public class MeshGeneratorJob2
{
public static int width = 241;
public static int height = 241;
public static float topLeftX = (width - 1) / -2f;
public static float topLeftZ = (height - 1) / 2f;
public static int increment = 1;
public static NativeArray<float> heightMap = new NativeArray<float>(width * height, Allocator.TempJob);
public static NativeArray<float> heightCurveSamples;
public static NativeArray<float3> vertices = new NativeArray<float3>(width * height, Allocator.TempJob);
public static NativeArray<int6> triangles = new NativeArray<int6>((width - 1) * (height - 1), Allocator.TempJob);
public static NativeArray<float2> uvs = new NativeArray<float2>(width * height, Allocator.TempJob);
public void Execute()
{
for (int i = 0; i < vertices.Length; i += 5)
{
Execute(i, 5);
}
}
private void Execute(int startIndex, int count)
{
startIndex += startIndex % increment;
int vertexIndex = (int)(math.ceil((float)width / increment) * math.ceil((float)startIndex / increment));
for (int y = startIndex; y < startIndex + count && y < height; y++)
{
for (int x = 0; x < width; x += increment)
{
vertices[vertexIndex] = new float3(topLeftX + x, heightMap[vertexIndex] * 16.67f, topLeftZ - y);
uvs[vertexIndex] = new float2(x / (float)width, y / (float)height);
if (vertexIndex < triangles.Length && x < width - 1 && y < height - 1)
{
triangles[vertexIndex] = new int6(vertexIndex, vertexIndex + width + 1, vertexIndex + width,
vertexIndex + width + 1, vertexIndex, vertexIndex + 1);
}
vertexIndex++;
}
}
}
}
My question is very similar to this, but I couldn't quite work it out, as the math needs to be a little different: https://gamedev.stackexchange.com/questions/65800/when-storing-voxels-in-chunks-how-do-i-access-them-at-the-world-level
I am trying to remove some faces of blocks. I'm having trouble working out how to get a block if it's outside the current chunk.
I have a Chunk class that keeps track of its own blocks. For testing, my chunks are 2 x 2, which holds 4 blocks each, all solid.
class Chunk {
private Blocks[,,] blocks;
public Vector3 position;
public Chunk(Vector3 world_pos){
position = world_position;
// Loops for populating blocks array...
...
// Create mesh
for(...){
for(...){
for(...){
Block block = GetBlock(x, y, z + 1); // Chance of this block being outside of this chunk
}
}
}
}
public Block GetBlock(int x, int y, int z){
...
// If block is not in this chunk, use global GetBlock instead
}
}
I have a method inside the chunk called "getBlock" that attempts to get a block based on the x, y, and z position. If the indexes are out of range, I then call a global "getBlock" which attempts to find the chunk and the block inside of it. This is where I am struggling.
This is my "getChunk" method (not my code, found in a tutorial), which seems to be fine.
public Chunk getChunk(int x, int y, int z){
float s = CHUNK_SIZE;
float d = CHUNK_DEPTH;
int posx = Mathf.FloorToInt(x / s) * CHUNK_SIZE;
int posy = Mathf.FloorToInt(y / d) * CHUNK_DEPTH;
int posz = Mathf.FloorToInt(z / s) * CHUNK_SIZE;
Chunk chunk = null;
this.chunks.TryGetValue(new Vector3(posx, posy, posz), out chunk);
return chunk;
}
Here is my global "getBlock".
public Block getBlock(int x, int y, int z){
Chunk chunk = this.getChunk(x, y, z);
Block block;
if(chunk == null){
block = Block_Types.AIR;
} else {
// Need to work out the block position I want here
int posx = x;
int posy = y;
int posz = z;
block = chunk.getBlock(posx, posy, posz);
}
return block;
}
Here is a picture if it helps:
Picture
Thanks
Divide the global position by CHUNK_SIZE and take the remainder as the chunk's local position.
You can use modulo (%) operator for that:
x = posx % CHUNK_SIZE;
y = posy % CHUNK_SIZE;
z = posz % CHUNK_SIZE;
e.g.
0 / 2 = 0; r = 0;
1 / 2 = 0; r = 1;
2 / 2 = 1; r = 0;
3 / 2 = 1; r = 1;
4 = 2 = 2; r = 0;
5 = 2 = 2; r = 1;
I wrote this code in C# language to create a list or record, the record contains an array named Y and a variable named xyx. The problem is how to initialize the variable xyx. ًWhen I run a program there appears a problem in defining this variable
Declaration:
public struct All_Frames
{
public int[,] Y;
public int xyx;
};
All_Frames Frame = new All_Frames();
List<All_Frames> Frames = new List<All_Frames>();
In main program:
Frame.Y = new int[width, height];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
Frame.Y[x, y] = (int)(0.299 * Red[x, y] + 0.587 * Green[x, y]);
Frames.Add(Frame);
How can I initialize the variable xyx?
The problem is how to initialize the variable xyx
All_Frames Frame = new All_Frames();
Frame.Y = new int[width, height];
Frame.xyx = 100; // Or another value
The same way you did with the array, you can access direct the member xyx...
Frame.xyx = 117;
This may helps
Frame.Y = new int[width, height];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
Frame.Y[x, y] = (int)(0.299 * Red[x, y] + 0.587 * Green[x, y]);
Frame.xyx = 1213; // Set your value here
}
Frames.Add(Frame);
If I am understanding your problem/question correctly, you are looking for a way to initialize the variable xyx at the time of the creation of the struct. I would add a constructor to the struct and initialize everything there. This way, you don't have to declare the struct and then have another call to initialize the array, just do it all in the constructor. Doing this, you can eliminate this line of code:
Frame.Y = new int[width, height];
and you can replace it with a call to the constructor
All_Frames Frame = new All_Frames(width, height, 1234); // or whatever value you want for xyx
public struct All_Frames
{
public int[,] Y;
public int xyx;
public All_Frames(int width, int height, int initXyx)
{
Y = new int[width, height];
xyx = initXyx;
}
}
So, I was trying to come up with my own simple implementation of Perlin/Fractal noise(I'm just adding the noise to itself resampled multiple times).
When it comes to the resampling part of the algorithm, to achieve lower frequency noise I'm using bilinear interpolation on a smaller part of the array to effectively "stretch out" that smaller part (i.e: look at a 16x16 part of a 256x256 sheet of noise, but treat it as if it was a 256x256 array of pixels)
The site I use (well, one of them) for reference is http: //lodev.org/cgtutor/randomnoise.html
From the site the first two pictures is the result I'm looking for but instead i get something like this (I used a 64x64 array):
Original
Resampled using x8 Zoom
All of the code I'm using is in Unity (I know there is a built-in Perlin-generator for it, but I wanted my own):
using UnityEngine;
using System.Collections;
public class NoiseGenerator : MonoBehaviour {
//Dimensions of the array
public int width;
public int height;
//RNG Stuff for reproducability
public bool useTimeAsSeed;
public string seed;
//The array the nopise gets stored in
int[,] noise;
//These are used in the editor to control if we zoon in or not, original image gets resized to 1/ratio
public bool zoom;
public int ratio=1;
//Function used to fill up the array with white noise (random numbers with a uniform distribution between 0-255)
void InitNoise() {
noise = new int[width, height];
if (useTimeAsSeed) {
seed = Time.time.ToString();
}
System.Random r = new System.Random(seed.GetHashCode());
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
noise[x, y] = r.Next(0,256);
}
}
}
//Function to smooth the noise when it's being resampled
/*
AB
CD
|when resampled
v
A--R---B
| X |
| |
| |
C--J---D
Uses bilinear Interpolation to get the wighted average of A B C and D, which are all neighbouring pixels in the original picture
Returns an int value between 0-255
*/
int SmoothNoise(float x, float y) {
float fractX = x - (int)x; //Fractional part of the X-coord, used as the weight for Lerping in the x-direction
float fractY = y - (int)y; //Fractional part of the Y-coord, used as the weight for Lerping in the y-direction
//Calculating the neigbouring pixels
//For edge values the respective "missing pixel" is filled in by the other end of the noise sheet
int x1 = ((int)x + width) % width;
int x2 = (x1 + width - 1) % width;
int y1 = ((int)y + height) % height;
int y2 = (y1 + height - 1) % height;
//Calculating R and J by Lerping the respective pixels
float R = Mathf.Lerp(noise[x1, y1], noise[x2, y1], fractX);
float J = Mathf.Lerp(noise[x2, y1], noise[x2, y2], fractX);
//Finally Lerp R and J to get the value for X
return (int)(Mathf.Lerp(R,J,fractY));
}
void Start() {
InitNoise();
}
//Easy way to display the array as a temporary solution until actual terrain display is implemented
void OnDrawGizmos() {
if (noise != null) {
for (int x = 0; x < width; x++){
for (int y = 0; y < height; y++){
float component = (float)noise[x, y] / 255;
if (zoom) {
//Using the result of SmoothNoise normalised to be between 0-1 we construct a color component to be used when displaying
component = (float)SmoothNoise((float)x/ratio, (float)y/ratio) / 255;
}
Gizmos.color = new Color(component,component,component);
Vector3 pos = new Vector3(-width/2 + x + .5f, 0, -height/2 + y + .5f);
Gizmos.DrawCube(pos, Vector3.one);
}
}
}
}
}