Bejeweled Clone flame gem upward movement troubles - c#

So, this is an interesting problem. I'm making a Bejeweled 3 clone with extra stuff, and I can't figure out how to make the gems above a flame gem explosion move upwards; the middle column moves upward farther than the outer two columns(a normal flame gem explosion is 3x3).
Here is the code I have so far:
for (int gy = i - 1; gy <= i + 1; gy++)
{
if (gy >= 0 && gy < gems.GetLength(0))
{
for (int gx = j; gx >= 0; gx--)
{
if (gy == i)
{
gems[gy, gx].MoveTowardsPosition(gems[gx, gy].Position.Swap() + new Vector2(0, -70), gemMoveSpeed * 1.8f, true, softMove: true);
}
else gems[gy, gx].MoveTowardsPosition(gems[gx, gy].Position.Swap() + new Vector2(0, -45), gemMoveSpeed * 1.8f, true, softMove: true);
}
}
}
The MoveTowardsPosition() method works fine in other places. This is not the problem. What the problem is is that I can't get the gems to move upwards(and in the correct location; i.e, they either move sideways, oddly, or upwards but in the wrong position. Also, i and j are the coordinates of the center of the flame gem explosion.
The other thing I should probably mention is that the visual position is separate from the position in the Gem array (gems[,]), and that the visual position is y, x whereas the gem array is x, y. I don't know how this arose, but it would be quite intensive to fix it.
Oh, and the Swap() extension method simply swaps the values in a Vector2.
If you need more information, just say so. This is my first time posting a question here, so if I've missed something, please tell me.

Okay, so I finally figured it out. Yay for persistence! Here's the code:
for (int gy = j - 1; gy <= j + 1; gy++)
{
if (gy >= 0 && gy < gems.GetLength(0))
{
for (int gx = i; gx >= 0; gx--)
{
if (gy == j)
{
gems[gx, gy].MoveTowardsPosition(gems[gy, gx].Position.Swap() + new Vector2(0, -70), gemMoveSpeed * 1.8f, true, softMove: true);
}
else gems[gx, gy].MoveTowardsPosition(gems[gy, gx].Position.Swap() + new Vector2(0, -45), gemMoveSpeed * 1.8f, true, softMove: true);
moveTimer = 0;
}
}
}
Turned out I had to not only reverse the gy and gx, but ALSO the i and j. I haven't fixed the swapped visual position/array position yet. I might do that in the future.

Related

why is my marching cubes algorithm so slow?

i have somewhat implemented marching cubes in unity/c# (you dont need to know unity to help me though) and i cant stop feeling like i have made a big mistake in my code because it is so slow. i am already running it on a separate thread but it just takes ages to complete. please help me optimize my code.
private void _UpdateChunk()
{
lock (this)
{
// clear the tri, vert and uv lists
ClearMeshData();
// Loop through each "cube" in the terrain.
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
for (int z = 0; z < width; z++)
{
// Create an array of floats representing each corner of a cube and get the value from our terrainMap.
float[] cube = new float[8];
float[] strengths = new float[8];
for (int i = 0; i < 8; i++)
{
Vector3Int corner = new Vector3Int(x, y, z) + gamedata.CornerTable[i];
cube[i] = terrainMap[corner.x, corner.y, corner.z].BlockType;
strengths[i] = terrainMap[corner.x, corner.y, corner.z].Strength;
}
// Pass the value into the MarchCube function.
MarchCube(new Vector3(x, y, z), cube, strengths);
}
}
}
}
}
void MarchCube(Vector3 position, float[] cube, float[] strengths)
{
// Get the configuration index of this cube.
int configIndex = GetCubeConfiguration(cube);
// If the configuration of this cube is 0 or 255 (completely inside the terrain or completely outside of it) we don't need to do anything.
if (configIndex == 0 || configIndex == 255)
return;
// Loop through the triangles. There are never more than 5 triangles to a cube and only three vertices to a triangle.
int edgeIndex = 0;
Vector3 vert1 = new Vector3();
Vector3 vert2 = new Vector3();
float vert1sample = 0;
float vert2sample = 0;
float lerp = 0;
int indice = 0;
for (int i = 0; i < 5; i++)
{
for (int p = 0; p < 3; p++)
{
// Get the current indice. We increment triangleIndex through each loop.
indice = gamedata.TriangleTable[configIndex, edgeIndex];
// If the current edgeIndex is -1, there are no more indices and we can exit the function.
if (indice == -1)
return;
// Get the vertices for the start and end of this edge.
vert1 = position + gamedata.EdgeTable[indice, 0];
vert2 = position + gamedata.EdgeTable[indice, 1];
vert1sample = strengths[gamedata.EdgeIndexTable[indice, 0]];
vert2sample = strengths[gamedata.EdgeIndexTable[indice, 1]];
// Get the midpoint of this edge.
lerp = Mathf.Abs(vert1sample) / (Mathf.Abs(vert2sample) + Mathf.Abs(vert1sample));
Vector3 vertPosition = Vector3.Lerp(vert1, vert2, lerp);
// Add to our vertices and triangles list and incremement the edgeIndex.
vertices.Add(vertPosition);
triangles.Add(vertices.Count - 1);
if (getChunkVoxel(vert1 + chunkPosition) != 0)
{
uvs.Add(new Vector2(getChunkVoxel(vert1 + chunkPosition) - 1, 0));
}
else
{
uvs.Add(new Vector2(getChunkVoxel(vert2 + chunkPosition) - 1, getChunkVoxel(vert2 + chunkPosition) - 1));
}
edgeIndex++;
}
}
}
int GetCubeConfiguration(float[] cube)
{
// Starting with a configuration of zero, loop through each point in the cube and check if it is below the terrain surface.
int configurationIndex = 0;
for (int i = 0; i < 8; i++)
{
// If it is, use bit-magic to the set the corresponding bit to 1. So if only the 3rd point in the cube was below
// the surface, the bit would look like 00100000, which represents the integer value 32.
if (cube[i] < terrainSurface)
configurationIndex |= 1 << i;
}
return configurationIndex;
}
it appears that this is the part that slows my game down, help would be appreciated
i already made it faster by changing terrainpoint from a class to a struct but it is still very slow.
One main reason it is slow is that there is a lot of allocations in the loop putting a lot of pressure on the garbadge collector. There is currently 11 allocation per "cube" in the terrain in _UpdateChunk and up to 17 in MarchCube (possibly even more if the expressions like position + gamedata.EdgeTable[indice, 0] allocates a new vector). This is not reasonable. Many allocation are not needed. For example cube and strengths can be preallocated once for all the cubes in the beginning of _UpdateChunk. You do not need to allocate the vector in the expression to compute corner: you can just compute the components separately manually (or you can possibly preallocate the vector and reset its component when needed). The same thing applies for the new Vector3(x, y, z) can can be preallocated and set in the loop. Such an algorithm is computationally intensive so you should get away any overhead like virtual method calls and allocations/GC -- only low-level arrays accesses and mathematical operations should remains.
Note that some computations can be optimized. For example GetCubeConfiguration can be modified so to be branchless. Mathf.Abs(vert1sample) can be precomputed so not to compute it twice (though the compiler may already do that). I am also wondering if the expression like vertices.Add are efficient but this is dependent of the type of container which is not provided here.

Terrain tree instances cannot be set correctly

Im currently experimenting with Terrain trees but im stuck at manipulating their position. The following script as far as i know should work, it also transforms the position into local terrain coordinates yet nothing happens.
private void SetTrees()
{
var Trees_On_Terrain = Terrain.activeTerrain.terrainData.treeInstances;
for (int i = 0; i < Trees_On_Terrain.Length; i++)
{
Trees_On_Terrain[i].position = new Vector3(10f /
Terrain.activeTerrain.terrainData.heightmapResolution, 0f, 10f /
Terrain.activeTerrain.terrainData.heightmapResolution);
}
Terrain.activeTerrain.terrainData.SetTreeInstances(Trees_On_Terrain, true);
}
They are put into a near the (0,0,0) coordinate.
Please help!
The problem here is that you are dividing your x and y coordiantes with Terrain.activeTerrain.terrainData.heightmapResolution which is not correct.
Imagine your terrain heightmap resolution is 2049(power of two + 1), but your terrain dimension is 1250 * 500. You still gonna divide x and y by 2049 but you should rather by 1250 and 500.
You should use the sampled data
Terrain.activeTerrain.terrainData.heightmapWidthfor the x coordinate
Terrain.activeTerrain.terrainData.heightmapHeight for the y coordinate.
Corrected code snippet:
private void SetTrees()
{
var Trees_On_Terrain = Terrain.activeTerrain.terrainData.treeInstances;
for (int i = 0; i < Trees_On_Terrain.Length; i++)
{
Trees_On_Terrain[i].position = new Vector3(10f / Terrain.activeTerrain.terrainData.heightmapWidth, 0f, 10f / Terrain.activeTerrain.terrainData.heightmapHeight);
}
Terrain.activeTerrain.terrainData.SetTreeInstances(Trees_On_Terrain, true);
}

Using perlin noise field, to get random positions for objects to spawn

I am trying to make a system that uses a perlin noise field to generate random positions that are then saved, or saved as the are generated, to a list. Then using that list the game will use those positions to spawn in objects within the level. I think that I might be on the right track, but I might have a few things in the wrong places.
Here is a link to an image of all the things that I think are relevant:
the blue dots should be the saved locations that are added to the list. But i am only getting one position 5 times, which is the correct number of positions but they are all the same position that is the problem.
// --- Random Generation of Objects --- //
Color[] colourMap = new Color[mapWidth * mapHeight];
for(int y = 0; y < mapHeight; y++) {
for(int x = 0; x < mapWidth; x++) {
float currentHeight = noiseMap[x, y];
for(int i = 0; i < regions.Length; i++) {
// --- Random Gen of Asteroids --- //
if(currentHeight <= regions[i].height) {
colourMap[y * mapWidth + x] = regions[i].colour;
Vector3 ping = new Vector3(x, 0, y);
asteroids.Add(ping);
Debug.Log(asteroids[i]);
break;
}
}
}
}
Anyway thank you for the help and reading this far, please let me know if you need anything more.

Cutting a XNA model in half by removing vertices and indices

I am attempting to cut a loaded 3D model in half using MonoGame (which is extremely similar XNA). I do not need to do this in real time so performance is not a huge issue.
I get my vertices and indices using the ModelMeshPart as such.
Vector3[] vertices = new Vector3[part.NumVertices];
part.VertexBuffer.GetData<Vector3>(vertices);
short[] indices = new short[part.PrimitiveCount * 3];
part.IndexBuffer.GetData<short>(indices);
and then set them using
part.IndexBuffer.SetData<Vector3>(vertices);
part.VertexBuffer.SetData<short>(indices);
Prior to that though I take those arrays and try to empty out all vertices (and indices that refer to them) that are positioned behind the center Z location of the model as such.
float centerZ = modelMesh.BoundingSphere.Center.Z;
for (int i = 0; i < indices.Length; i += 3)
{
short index0 = indices[i];
short index1 = indices[i + 1];
short index2 = indices[i + 2];
Vector3 vert0 = vertices[index0];
Vector3 vert1 = vertices[index1];
Vector3 vert2 = vertices[index2];
if (vert0.Z > centerZ && vert1.Z > centerZ && vert2.Z > centerZ)
{
vert0 = Vector3.Zero;
vert1 = Vector3.Zero;
vert2 = Vector3.Zero;
indices[i] = short.MinValue;
indices[i + 1] = short.MinValue;
indices[i + 2] = short.MinValue;
}
}
but in the end I get something that looks like this rather than a model cut in half. I am completely new to games programming and my comprehension of vertices and indices is still extremely poor. Obviously I am missing something really fundamental, any help would be sincerely appreciated.
short.MinValue is -32767 since short is signed, so you shouldn't be using that, since the indice value for the vertice can't be negative. You should use = 0 or ushort.MinValue. Also keep in mind that this method will not cut the model in half perfectly, triangles perpendicular to the center will still remain.
You can try something like this:
if (vert0.Z <= centerZ || vert1.Z <= centerZ || vert2.Z <= centerZ)
{
if (vert0.Z > centerZ) vert0.Z = centerZ;
if (vert1.Z > centerZ) vert1.Z = centerZ;
if (vert2.Z > centerZ) vert2.Z = centerZ;
}
else
{
indices[i] = 0;
indices[i + 1] = 0;
indices[i + 2] = 0;
}
Edit:
also this part looks wrong:
part.IndexBuffer.SetData<short>(vertices);
part.VertexBuffer.SetData<Vector3>(indices);
you should pass vertices to the VertexBuffer and indices to the IndexBuffer. And you should probably use ushort on indices, since theres no reason a indice would be negative.

Tile Engine Collision Optimization

Alright, so today I decided to try to further optimize my collision detection code for my tile engine.
This is what I did:
Circle class checks if there are points within range. If there are, then check for collision between player and tile.
Code:
int tileWidth = 128;
int tileHeight = 128;
int[,] Layer3 = { 1, 1, 1, etc... };
int tileMapWidth = Layer3.GetLength(1);
int tileMapHeight = Layer3.GetLength(0);
Rectangle tile, tile2;
for (int x = 0; x < tileMapWidth; x++)
{
for (int y = 0; y < tileMapHeight; y++)
{
int wallIndex = Layer3[y, x];
if (wallIndex == 1) //Full-sized Tile Collision (128 x 128)
{
if (collisionCircle.Contains(new Vector2(x * tileWidth + (tileWidth / 2) + (int)Player.camera.Position.X,
y * tileHeight + (tileHeight / 2) + (int)Player.camera.Position.Y))) //+ tile / 2 is for centering the point
{
tile = new Rectangle(x * tileWidth + (int)Player.camera.Position.X, y * tileHeight + (int)Player.camera.Position.Y, tileWidth, tileHeight);
Collide(tile);
}
}
}
}
This would check throughout layer3 if there is a "1". If there is, assign rectangle and check for collision if point is inside collision radius.
Also, I checked this code(with a draw method), and I know it's working properly, at least the behavior.
I added in about 120,000(32 x 3888) tiles to try to make it lag, and before the code, it lagged a little bit. But after I added in the code, it lagged even more so.
I thought that since it would only check for collision between tiles(points) that are within the radius it wouldn't even remotely lag, but that's not the case...
Any help/ideas on how to optimize this would be great.
Thanks a lot,
Shyy
EDIT:
Cirlce.Contains() code:
public bool Contains(Vector2 Point)
{
return ((Point - position).Length() <= radius);
}
I used a circle because I've heard it's faster than using a rectangle.
Another possible optimization is instead of
return ((Point - position).Length() <= radius);
use
return ((Point - position).LengthSquared() <= radius * radius);
This is faster because Vector2.Length() has to perform a costly square root operation. Vector2.LengthSquared() does not have to perform that slow operation. The radius has to be multiplied by itself to account for the length from the vector being squared.
It sounds like you're trying to determine what tiles you don't need to use for collision with the player. Another optimization you could do is that if a tile at (X=5,Y=5) is above and to the left of the player, then you don't need to check a tile at (X=4,Y=4). Similarly if (X=5,Y=5) is below and to the right, (X=6,Y=6) is guaranteed to be too far as well. Try to determine when you've passed the player and no longer need to check collisions.
I suggest to loop only over visible tiles in screen to check collision using movement offset.
i will try something from my head..
for x as integer = 0 + offSetX to tilesInWidth + offSetX
for y as integer = 0 + offSetY to tilesInHeight + offSetY
if player.insideCircle(player.position, radius) '
object = layer(y,x);
if player.collideWith(object) then Collide()
end if
next
next

Categories

Resources