Something strange with BoundingBox in XNA - c#

I have strange problem with BoundingBox and things that in make.
For loop wont work correctly and cause problem that dont change variable.
for (int i = 0; i < thing.Length; i++)
{
for (int j = 0; j < thing.Length; j++)
{
if (thing[i].bb.Intersects(thing[j].bb) && i != j)
{
thing[i].spriteSpeed *= -1;
thing[j].spriteSpeed *= -1;
soundEffect.Play(0.2f, -1f, 0f);
}
}
}
But if i change j variable to static number, like zero, code will work fine.
for (int i = 0; i < thing.Length; i++)
{
for (int j = 0; j < thing.Length; j++)
{
if (thing[i].bb.Intersects(thing[0].bb) && i != 0)
{
thing[i].spriteSpeed *= -1;
thing[0].spriteSpeed *= -1;
soundEffect.Play(0.2f, -1f, 0f);
}
}
}
P.S. Thing is a struct that looks like:
struct Thing
{
public Texture2D myTexture;
public Vector2 spritePosition;
public Vector2 spriteSpeed;
public BoundingBox bb;
public Vector3 start, end;
}

The problem is that you are updating both objects both times.
Consider the case where you have two items in the list and their bounding boxen are overlapping. Passing through your loop you get:
i == 0, j == 0: Skip because (i == j)
i == 0, j == 1: Reverse direction of [0] and [1]
i == 1, j == 0: Reverse direction of [1] and [0]
i == 1, j == 1: Skip because (i == j)
Working through the sequence you have reversed the items twice, returning them to their original headings.
To prevent this, and incidentally reduce the number of tests required to process the full list of objects, the j variable should always start 1 higher than i, since all comparisons for i <= j are either already done or are invalid.
Try this code instead:
for (int i = 0; i < thing.Length; i++)
{
for (int j = i + 1; j < thing.Length; j++)
{
if (thing[i].bb.Intersects(thing[j].bb))
{
thing[i].spriteSpeed *= -1;
thing[j].spriteSpeed *= -1;
soundEffect.Play(0.2f, -1f, 0f);
}
}
}
This has the effect of reducing the number of comparisons by about half (actually (n^2-n)/2 if we're being precise) as well as removing all of the double reversals. Each possible combination of items in the list is tested only once.

Related

Problem with world generation in unity c#

I want to create a world generation using two noise maps (altitude noise and moisture noise). I create maps of these noises and set them in the inspector to values between 0 and 1.
I want to get result like this:
If Elevation < 1000
{
If Moisture < 50: Desert
Else Forest
}
And it seems that I did it as it should, but for some reason the generation does not work correctly:
Here is my code:
for (int x=0; x < tilemap.Width; x++)
{
for (int y = 0; y < tilemap.Height; y++)
{
// Get height at this position
var height = noiseMap[y * tilemap.Width + x];
var moisureHeight = moisureMap[y * tilemap.Width + x];
// Loop over our configured tile types
for (int i = 0; i < TileTypes.Length; i++)
{
var TileType = TileTypes[i];
for (var j = 0; j < TileType.MoisureValues.Length; j++)
{
// If the height is smaller or equal then use this tiletype
if (height <= TileType.Height)
{
if (moisureHeight <=TileType.MoisureValues[j].MoisureHeight)
{
tilemap.SetTile(x, y, (int)TileType.MoisureValues[j].GroundTile);
break;
}
}
}
}
}
}

Efficient way to find neighboring cells in a 2d array

I have a 2d array of class Tiles. While creating the playfield I have to generate all directly neighboring cells (horizontally, vertically, diagonally). I start by generating the field filling up each cell with a new Tile, than (when that is done) I loop through the 2d array to calculate the neighbors using this piece of loop:
int dnDistance= 1; //Direct Neighbor Distance.
for (int iMapY = 0; iMapY < playfieldHeight; iMapY++)
{
for (int iMapX = 0; iMapX < playfieldWidth; iMapX++)
{
for (int yOffset = -dnDistance; yOffset <= dnDistance; yOffset++)
{
for (int xOffset = -dnDistance; xOffset <= dnDistance; xOffset++)
{
if ((iMapX + xOffset >= 0 && iMapX + xOffset < playfieldWidth) && (iMapY + yOffset >= 0 && iMapY + yOffset < playfieldHeight))
{
if (!(yOffset == 0 && xOffset == 0))
{
playfieldTiles[iMapX, iMapY].dnTiles.Add(playfieldTiles[iMapX + xOffset, iMapY + yOffset]);
}
}
}
}
}
}
Using this method, I have to loop through the entire 2d array a second time, creating a for loop, in a for loop, in a for loop, in a for loop which sometimes is quite unclear. There has to be a better way for this, right?
I found a post that looks to be similar but not quite the same, or I don't understand it properly:
When it just works then it's fine !
Here's a little optimization that makes it easier to debug:
var playfieldHeight = 5;
var playfieldWidth = 5;
var playfieldTiles = new byte[playfieldWidth + dnDistance * 2, playfieldHeight + dnDistance * 2];
var len1 = playfieldWidth * playfieldHeight;
var len2 = dnDistance * 2 + 1;
for (var i = 0; i < len1; i++)
{
var ix = i % playfieldWidth;
var iy = i / playfieldWidth;
for (var j = 0; j < len2 * len2; j++)
{
var jx = j % len2 - dnDistance;
var jy = j / len2 - dnDistance;
Console.WriteLine($"x1: {ix}, y1: {iy}, x2: {jx}, y2: {jy}");
}
}
You now have only 2 loops, the field and the neighbors.
You could further optimize it with a single for but I believe readability will decrease (inside the loop).

Where's the mistake in my Spiral Matrix algorithm? Size of matrix = input N^2

So I'm creating a spiral matrix using C#.
A spiral array is a square arrangement of the first N^2 natural numbers, where the numbers increase sequentially as you go around the edges of the array spiralling inwards.
For example:
I'm supposed to do this using an algorithm however my final results look like this:
My code is below:
private static void FillMatrix (int[ , ] matrix, int n)
{
int positionX = 0;
int positionY = 0;
int direction = 0; // The initial direction is "right"
int stepsCount = n - 1; // stepsCount decrements after 3/2/2/2/2...
int stepPosition = 0; // 0 steps already performed
int counter = 1; // counter increments after every turn
for (int i = 1; i < n * n; i++)
{
matrix[positionY, positionX] = i;
//moving logic:
if (stepPosition < stepsCount)
{
stepPosition++;
}
else
{
counter++;
stepPosition = 1;
if (counter <= 3)
{
direction = (direction + 1) % 4;
}
else if (counter % 2 != 0 && counter >= 5 || counter == 4)
{
stepsCount = stepsCount - 1;
direction = (direction + 1) % 4;
}
}
// Move to the next cell in the current direction
switch (direction)
{
case 0:
// right
positionX++;
break;
case 1:
// down
positionY++;
break;
case 2:
// left
positionX--;
break;
case 3:
// up
positionY--;
break;
}
}
}
private static void PrintMatrix (int[ , ] matrix, int n)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
Console.Write("{0,3}", matrix[i, j]);
}
Console.WriteLine();
}
}
static void Main(string[] args)
{
int n;
Console.WriteLine("Please enter N: ");
bool checkN = int.TryParse(Console.ReadLine(), out n);
if (checkN)
{
int[,] spiralMatrix = new int[n,n];
FillMatrix(spiralMatrix, n);
PrintMatrix(spiralMatrix, n);
}
Console.ReadKey();
}
}
}
Any help much appreciated!
Your logic for deciding when to make a turn and how many steps to take has a bug, and it is more complicated than necessary. A better way of making a decision on when to turn is to check the matrix itself. Pre-fill the matrix with -1, then start filling it at the top-left corner. When you see -1, continue straight; if you reached one of the ends of the matrix, or the next position has -1 in it, then make a turn. This makes your stepPosition and stepCount variables unnecessary, and shortens your code quite a bit.
Another useful trick is turning right: rather than keeping a direction as a single variable, keep two "delta" variables - dx and dy
if (positionX < 0 || positionX == n || positionY < 0 || positionY == N || matrix[positionX][positionY] != -1) {
int temp = dy;
dy = dx;
dx = -temp;
}
positionX += dx;
positionY += dy;
I've solved this problem.
There was an issue with my algorithm. The number pattern for the size of sequential line when filling in a matrix from the outside in is N, N-1, N-1, N-2, N-2, N-3, N-3... and so on.
For example in a spiral matrix of N size 4 the pattern goes like this:
4 right.
3 down.
3 left.
2 up.
2 right.
1 down.
1 left.
I originally thought the pattern started:
3 right.
3 down.
3 left.
I forgot to include the one more element of movement resulting in a algorithm that wouldn't fill out correctly.
Once I changed my conditional statements to the following code it allowed for the correct output. To clarify I am supposed to be starting from 1 in my 0 element of the array. Apologies for the confusion.
Code below:
int positionX = 0;
int positionY = 0;
int direction = 0; // The initial direction is "right"
int stepsCount = n - 1; // stepsCount decrements after 1/2/2/2/2... turns
int stepPosition = 1; // 1 steps already performed
int counter = 0; // counter increments after every change in direction
for (int i = 1; i < n * n + 1; i++)
{
matrix[positionY, positionX] = i;
//moving logic:
if (stepPosition <= stepsCount)
{
stepPosition++;
}
else
{
counter++;
stepPosition = 1;
if (counter % 2 != 0)
{
stepsCount = stepsCount - 1;
direction = (direction + 1) % 4;
}
else if (counter % 2 == 0)
{
direction = (direction + 1) % 4;
}
}
The result is a much simpler way than checking for zero and turning based on that rule as it is absolutely infallable.
Example results below:

"Infinite" world problems

I'm creating a minecraft like voxel engine thing in xna, and have started about implementing "infinite" world but have ran into a few problems. One such problem is that the following line always seems to apply the oppisite (eg if the player moved X+ REGION_SIZE_X it would assign Direction.X_DECREASING instead of the expected Direction.X_INCREASING).
Thread regionLoader;
public void CheckPlayer()
{
Direction direction = Direction.MAX;
float distancetraveledx = player.Origin.X - player.Position.X;
float distancetraveledy = player.Origin.Y - player.Position.Y;
float distancetraveledz = player.Origin.Z - player.Position.Z;
if (distancetraveledx > Variables.REGION_SIZE_X)
{
direction = Direction.XIncreasing;
player.Position = new Vector3(player.Origin.X, player.Position.Y, player.Position.Z);
}
else if (distancetraveledx < -Variables.REGION_SIZE_X)
{
direction = Direction.XDecreasing;
player.Position = new Vector3(player.Origin.X, player.Position.Y, player.Position.Z);
}
else if (distancetraveledz > Variables.REGION_SIZE_Z)
{
direction = Direction.ZIncreasing;
player.Position = new Vector3(player.Position.X, player.Position.Y, player.Origin.Z);
}
else if (distancetraveledz < -Variables.REGION_SIZE_Z)
{
direction = Direction.ZDecreasing;
player.Position = new Vector3(player.Position.X, player.Position.Y, player.Origin.Z);
}
if (direction != Direction.MAX)
{
regionManager.direction = direction;
regionLoader = new Thread(new ThreadStart(regionManager.ShiftRegions));
regionLoader.Start();
}
}
So what this is doing is checking if the player has moved REGION_SIZE from it's origin and if it has, reset the component of the position which has moved over that boundary.
This then calls the following function:
public void ShiftRegions()
{
// Future and current arrays are to avoid moving elements twice (or maybe I am thinking about it in the wrong way...)
future_regions = new Region[(int)Variables.RENDER_DISTANCE, (int)Variables.RENDER_DISTANCE, (int)Variables.RENDER_DISTANCE];
for (short j = 0; j < future_regions.GetLength(0); j++)
{
for (short m = 0; m < future_regions.GetLength(1); m++)
{
for (short i = 0; i < future_regions.GetLength(2); i++)
{
future_regions[j, m, i] = new Region(world, new Vector3i(new Vector3(j, m, i)));
switch (direction)
{
// This appears to work as XDecreasing
case Direction.XIncreasing:
// If it is not at the back
if (j != future_regions.GetLength(0) - 1)
{
// Make the regions look like they are scrolling backward
future_regions[j, m, i] = regions[j + 1, m, i];
future_regions[j, m, i].Index = new Vector3i((uint)j, (uint)m, (uint)i);
// In the next call the vertex buffer will be regenerated thus placing the "blocks" in the correct position
future_regions[j, m, i].Dirty = true;
}
// If it is the front most region to which the player is traveling ing
if (j == 0)
{
// New the region setting the Generated flags to false thus allowing it to be regenerated
future_regions[j, m, i] = new Region(world, new Vector3i(new Vector3(j, m, i)));
}
// If it is at the back of the regions
if (j == future_regions.GetLength(0) - 1)
{
//store region
}
break;
case Direction.XDecreasing:
break;
}
}
}
}
generator.build(ref future_regions);
direction = Direction.MAX;
this.regions = future_regions;
}
This actually moves the regions which causes the scrolling effect and the new regions which appear at the front. Which doesn't seem to work.
I was thinking istead of actually "moving" the regions i could just assign different offsets and move in the world matrix but when I do so i just get a blue screen...
Here is the rest of the code:
Generator class function:
public virtual void build(ref Region[,,] regions)
{
for (short j = 0; j < regions.GetLength(0); j++)
{
for (short m = 0; m < regions.GetLength(1); m++)
{
for (short i = 0; i < regions.GetLength(2); i++)
{
OutputBuffer.Add("Generating...");
if (regions[j, m, i].Generated == false)
{
regions[j, m, i].Dirty = true;
regions[j, m, i].Generated = true;
for (short x = 0; x < Variables.REGION_SIZE_X; x++)
{
for (short y = 0; y < Variables.REGION_SIZE_Y; y++)
{
for (short z = 0; z < Variables.REGION_SIZE_Z; z++)
{
regions[j, m, i].Blocks[x, y, z] = new Block();
Vector3i relativeBlock = new Vector3i(new Vector3(x + Variables.REGION_SIZE_X * j, y + Variables.REGION_SIZE_Y * m, z + Variables.REGION_SIZE_Z * i));
if (x == 0 || x == 16 || x == 32 || z == 0 || z == 16 || z == 32 || y == 0 || y == 16 || y == 32)
{
regions[j, m, i].Blocks[x, y, z].BlockType = BlockType.dirt;
}
else
{
regions[j, m, i].Blocks[x, y, z].BlockType = BlockType.grass;
}
}
}
}
}
}
}
}
}
and the code that checks if the region's buffers need to be rebuilt:
public void Update(World world)
{
this.world = world;
for (short j = 0; j < regions.GetLength(0); j++)
{
for (short m = 0; m < regions.GetLength(1); m++)
{
for (short i = 0; i < regions.GetLength(2); i++)
{
if (regions[j, m, i].Dirty &&
regions[j, m, i].Generated)
{
regions[j, m, i].BuildVertexBuffers();
}
}
}
}
}
By the way, if Dirty is set to true this means that the buffers need to be regenerated.
Any ideas why this is not creating new regions at the front and why it is not scrolling properly?
EDIT: I was just thinking logically and that my idea with the changing the regions position in the array will not change it's world position, and like I said above with transforming them to the correct positions instead of copying them - that seems like the most logical step. Well the thing is I may have to copy some to different places in the regions array because the array may become spaghetti in no time if you just transform them...
Thanks, Darestium
Just looking at this part
float distancetraveledx = player.Origin.X - player.Position.X;
You're subtracting position from origin, you should probably reverse the order and do this
float distancetraveledx = player.Position.X - player.Origin.X;
For example, if the player has moved from (0, 0, 0) to (10, 0, 0), the player has moved 10 units in the X direction, but your 'distancetraveledx' would give you 0 - 10, or -10.
Unrelated to your question:
Your variables will be easier to use if you follow CamelCase, with the first letter lower or upper-case:
float distanceTraveledX = player.Position.X - player.Origin.X;
In C# the convention is to have class names with the first letter capitalized, and variables with the first letter lower-case, like such:
MyClass myClass;

Matrix Row - QuickSort recursion problem

I've adapted QuickSort Method to sort Array's Row.
Here's the code:
That one works fine
static void QuickSort(int lowBound, int highBound, int[] a)
{
int temp = 0;
int x = random.Next(lowBound, highBound);
int pivot = a[x];
int i = lowBound;
int j = highBound;
do
{
while (a[i] < pivot) i++;
while (pivot < a[j]) j--;
if (i <= j)
{
temp = a[i]; //changes an element smaller than the pivot...
a[i] = a[j];//... with the greater one
a[j] = temp;
i++; j--;
}
}
while (i <= j);
if (lowBound < j) { QuickSort(lowBound, j, a); }//recursion
if (i < highBound){ QuickSort(i,highBound, a); }
}
Here's the problematic method
static void QuickSortMatrix(int[,] a)
{
int n = a.GetLength(0);
int m = a.GetLength(1);
for (int i = 0; i < n; i++)
{
QuickSortRow(0, m - 1, i, a);
}
for (int j = 0; j < m; j++)
{
QuickSortRow(0, n - 1, j, a);
}
}
static void QuickSortRow(int lowBound, int highBound, int row, int[,] a)
{
int temp = 0;
int x = random.Next(lowBound, highBound);
int pivot = a[row,x];
int p = lowBound;
int q = highBound;
do
{
while (a[row,p] < pivot) p++;
while (pivot < a[row,q]) q--;
if (p <= q)
{
temp = a[row,p];
a[row,p] = a[row,q];
a[row,q] = temp;
p++; q--;
}
}
while (p <= q);
if (lowBound < q) { QuickSortRow(lowBound, q, row, a); }
if (p < highBound) { QuickSortRow(p, highBound,row, a); }
}
At first when the "for" loop is executed everything's ok bur for some reason when executed recursively the row that should be constant when calling the method goes outside the matrix boundaries.
Here's my array and rows reaches value of 4
int[,] matrix =
{
{7,8,9,10,11,5},
{3,6,4,16,22,4},
{7,9,17,8,3,21},
{24,7,11,19,3,4}
};
I hope my explanation was clear enough.
Could anybody advise me? What I'm missing here?
Thank you for your kind help!
BR
Stephan
n is the number of rows in the matrix (4)
m is the number of columns in the matrix (6)
In your second loop you are going 0..m and passing that value to the row parameter. It blows up because there are more columns in the matrix than rows. i.e. It tries to read matrix[4, 0].
Note: as far as I can tell you don't need the second loop because your rows are already sorted after the first loop. Remove that and it won't throw an exception.

Categories