XNA C# 2D Tile Engine - c#

I have decided to have a go at making a dungeon crawler game with the Xna framework. I am a computer science student and am quite familiar with c# and .net framework. I have some questions about different parts of the development for my engine.
Loading Maps
I have a tile class that stores the vector2 position, 2dtexture and dimensions of the tile. I have another class called tilemap that has a list of tiles that are indexed by position. I am reading from a text file which is in the number format above that matches the number to the index in the tile list and creates a new tile with the correct texture and position, storing it into another list of tiles.
public List<Tile> tiles = new List<Tile>(); // List of tiles that I have added to the game
public List<TileRow> testTiles = new List<TileRow>(); // Tilerow contains a list of tiles along the x axis along with there vector2 position.
Reading and storing the map tiles.
using (StreamReader stream = new StreamReader("TextFile1.txt"))
{
while (stream.EndOfStream != true)
{
line = stream.ReadLine().Trim(' ');
lineArray = line.Split(' ');
TileRow tileRow = new TileRow();
for (int x = 0; x < lineArray.Length; x++)
{
tileXCo = x * tiles[int.Parse(lineArray[x])].width;
tileYCo = yCo * tiles[int.Parse(lineArray[x])].height;
tileRow.tileList.Add(new Tile(tiles[int.Parse(lineArray[x])].titleTexture, new Vector2(tileXCo,tileYCo)));
}
testTiles.Add(tileRow);
yCo++;
}
}
For drawing the map.
public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
{
foreach (TileRow tes in testTiles)
{
foreach (Tile t in tes.tileList)
{
spriteBatch.Draw(t.titleTexture, t.position, Color.White);
}
}
}
Questions:
Is this the correct way I should be doing it, or should I just be storing a list referencing my tiles list?
How would I deal with Multi Layered Maps?
Collision Detection
At the moment I have a method that is looping through every tile that is stored in my testTiles list and checking to see if its dimensions are intersecting with the players dimensions and then return a list of all the tiles that are. I have a derived class of my tile class called CollisionTile that triggers a collision when the player and that rectangle intersect. (public class CollisionTile : Tile)
public List<Tile> playerArrayPosition(TileMap tileMap)
{
List<Tile> list = new List<Tile>();
foreach (TileRow test in tileMap.testTiles)
{
foreach (Tile t in test.tileList)
{
Rectangle rectangle = new Rectangle((int)tempPosition.X, (int)tempPosition.Y, (int)playerImage.Width / 4, (int)playerImage.Height / 4);
Rectangle rectangle2 = new Rectangle((int)t.position.X, (int)t.position.Y, t.width, t.height);
if (rectangle.Intersects(rectangle2))
{
list.Add(t);
}
}
}
return list;
}
Yeah, I am pretty sure this is not the right way to check for tile collision. Any help would be great.
Sorry for the long post, any help would be much appreciated.

You are right. This is a very inefficient way to draw and check for collision on your tiles. What you should be looking into is a Quadtree data structure.
A quadtree will store your tiles in a manner that will allow you to query your world using a Rectangle, and your quadtree will return all tiles that are contained inside of that Rectangle.
List<Tiles> tiles = Quadtree.GetObjects(rectangle);
This allows you to select only the tiles that need to be processed. For example, when drawing your tiles, you could specify a Rectangle the size of your viewport, and only those tiles would be drawn (culling).
Another example, is you can query the world with your player's Rectangle and only check for collisions on the tiles that are returned for that portion of your world.
For loading your tiles, you may want to consider loading into a two dimensional array, instead of a List. This would allow you to fetch a tile based on its position, instead of cross referencing it between two lists.
Tile[,] tiles = new Tile[,]
Tile tile = tiles[x,y];
Also, in this case, an array data structure would be a lot more efficient than using a List.

For uniform sets of tiles with standard widths and heights, it is quite easy to calculate which tiles are visible on the screen, and to determine which tile(s) your character is overlapping with. Even though I wrote the QuadTree in Jon's answer, I think it's overkill for this. Generally, the formula is:
tileX = someXCoordinate % tileWidth;
tileY = someYCoordinate % tileHeight;
Then you can just look that up in a 2D array tiles[tileX, tileY]. For drawing, this can be used to figure out which tile is in the upper left corner of the screen, then either do the same again for the bottom right (+1), or add tiles to the upper left to fill the screen. Then your loop will look more like:
leftmostTile = screenX % tileWidth; // screenX is the left edge of the screen in world coords
topmostTile = screenY % tileHeight;
rightmostTile = (screenX + screenWidth) % tileWidth;
bottommostTile = (screenY + screenHeight) % tileHeight;
for(int tileX = leftmostTile; tileX <= rightmostTile; tileX++)
{
for(int tileY = topmostTile; tileY <= bottommostTile; tileY++)
{
Tile t = tiles[tileX][tileY];
// ... more stuff
}
}
The same simple formula can be used to quickly figure out which tile(s) are under rectangular areas.
IF however, your tiles are non-uniform, or you have an isometric view, or you want the additional functionality that a QuadTree provides, I would consider Jon's answer and make use of a QuadTree. I would try to keep tiles out of the QuadTree if you can though.

Related

C# - Per Pixel Collision Detection

Here's my situation I'm making a 2D maze game(XNA 4.0). I have figured out that the best way to do collision detection is by using per-pixel detection. While searching it up on the internet I have found people explaining or showing code for two things colliding(i.e. mouse & player, player & player, two shapes). What I would like to do is have this collision detect whether the player collides with a wall or not(the background is black but the maze walls are white). Could someone explain how to do this or to give some sort of starting point with the code. Much Appreciated.
P.S. A link to a website or anything relating to my question would also be helpful
The best way to go about this CPU-intensive operation is checking for hitbox collision first, then the per-pixel collision.
Most of this code can be found in this helpful video.
static bool IntersectsPixel(Rectangle hitbox1, Texture2D texture1, Rectangle hitbox2, Texture2D texture2)
{
Color[] colorData1 = new Color[texture1.Width * texture1.Height];
texture1.GetData(colorData1);
Color[] colorData2 = new Color[texture2.Width * texture2.Height];
texture2.GetData(colorData2);
int top = Math.Max(hitbox1.Top, hitbox2.Top);
int bottom = Math.Min(hitbox1.Bottom, hitbox2.Bottom);
int right = Math.Max(hitbox1.Right, hitbox2.Right);
int left = Math.Min(hitbox1.Left, hitbox2.Left);
for(y = top; y< bottom; y++)
{
for(x = left; x < right; x++)
{
Color color1 = colorData1[(x - hitbox1.Left) + (y - hitbox1.Top) * hitbox1.Width]
Color color2 = colorData2[(x - hitbox2.Left) + (y - hitbox2.Top) * hitbox2.Width]
if (color1.A != 0 && color2.A != 0)
return true;
}
}
return false;
}
You can call this method like so:
if (IntersectsPixel(player.hitbox, player.texture, obstacle.hitbox, obstacle.texture))
{
// Action that happens upon collision goes here
}
Hope I could help you out,
- GHC
Create a matrix of bools representing your sprite and a matrix of bools representing your maze (the matrix representing your sprite needs to have the same dimensions as your maze).
then you can do something simple like iterate over all x-y coordinates and check whether or not they're both true
// as an optimization, store a bounding box to minimize the
// coordinates of what you need to check
for(int i = 0; i < width, i++) {
for(int j = 0; j < height, j++) {
if(sprite[i][j] && maze[i][j]) {
collision = true
//you might want to store the coordinates
}
}
}
If you want to be very fancy you can flatten your maze matrix and use bit operations

Having some issues with procedurally generating a map

I'm building a very simple 2d tile map. It just spawns a few different grass tiles in random locations here's what it looks like:
public int tileHeight; //y
public int tileWidth; //x
int tileHeightCounter;
int tileWidthCounter;
public GameObject[] floorTiles;
void Start(){
tileHeightCounter = 0;
tileWidthCounter = 0;
while(tileHeightCounter < tileHeight){
Vector3 tileSpawnPoint = new Vector3 (tileWidthCounter, tileHeightCounter, 0);
GameObject groundTiles = (GameObject)Instantiate (floorTiles[Random.Range(0, floorTiles.Length)], tileSpawnPoint, Quaternion.identity);
groundTiles.transform.parent = transform;
if (tileWidthCounter == tileWidth) {
tileWidthCounter = 0;
tileHeightCounter++;
}
tileWidthCounter++;
}
}
I've run into two problems- say your tileHeight is 10 and your tileWidth is also 10 then the map it generates should be a 10x10 with 100 total tiles randomly distributed.
Instead two weird things are occurring the first is that if your map is 10x10 it actually generates a 10x9 meaning it stops one layer short on the y axis. The second is a grid(grass tile) is being created at 0,0 but the rest of the map is being created with at least x being 1 meaning that the map has a single tile attached to the bottom left sticking out.
I'm a bit confused as to whats going on here or how to fix it. Any help is much appreciated.
The issue is that you're using a < less than, so once it actually hits the tileHeight it exits the loop one iteration too early. Make it <=.

ILNumerics plot 2d chart

Hello I have 2d matrix data saved in the ILArray < double >. This matrix represents the weights of the neural network from one neuron and i want to see how the weights looks with ilnumerics. Any idea how can i do this? I find many examples for 3d plotting but nothing for plotting 2d image data representation.
Image data are currently best (simplest) visualized by utilizing ILSurface. Since this is a 3D plot, you may not get the optimal performance for large image data. Fortunately, ILNumerics' scene graph makes it easy to improve this with your own implementation.
The most simple attempt would take an ILPoints shape, arrange the needed number of points in a grid and let every point visualize the value of the corresponding element within the input matrix - let's say by color (or size).
private void ilPanel1_Load(object sender, EventArgs e) {
using (ILScope.Enter()) {
// some 'input matrix'
ILArray<float> Z = ILSpecialData.sincf(40, 50);
// do some reordering: prepare vertices
ILArray<float> Y = 1, X = ILMath.meshgrid(
ILMath.vec<float>(1, Z.S[1]),
ILMath.vec<float>(1,Z.S[0]),
Y);
// reallocate the vertex positions matrix
ILArray<float> pos = ILMath.zeros<float>(3, X.S.NumberOfElements);
// fill in values
pos["0;:"] = X[":"];
pos["1;:"] = Y[":"];
pos["2;:"] = Z[":"];
// colormap used to map the values to colors
ILColormap cmap = new ILColormap(Colormaps.Hot);
// setup the scene
ilPanel1.Scene.Add(new ILPlotCube {
new ILPoints() {
Positions = pos,
Colors = cmap.Map(Z).T,
Color = null
}
});
}
}
Obviously, the resulting points do not scale with the form. So the 'image' suffers from larger gaps between the points when the form size is increased. So, for a better implementation you may adapt the approach to utilize ILTriangles instead of ILPoints, in order to assemble adjacent rectangles.

Automatically Merging Ajacent Rectangles

I've been making a top-down shooter game in XNA that requires rectangular collision for the map.
The collision walls for a map is stored in a text file in the format of:rect[0,0,1024,8]
The values correspond to defining a rectangle (x, y, width, height).
I've been thinking that I could write a separate application that can illiterate through the data of the map image, find out the pixels that are black (or any color of the wall) and make rectangles there. Basically, this program will generate the rectangles required for the collision. Ideally, it would be pixel perfect, which would require something like a thousand rectangles each 1 pixel wide that covers all the walls.
Is there a possible way to detect which of these rectangles (or squares I should say) are adjacent to one another, then connect them into the a bigger (but still covering the same area) rectangle?
EG. Lets say I have a wall that is 10 by 2. The program would generate 20 different rectangles, each 1 pixel high. How would I efficiently detect that these rectangles are adjacent and automatically make a 10 by 2 rectangle covering the whole wall instead of having 20 different little pixel rectangles?
EDIT: I've worked out a solution that fits my purposes, for future reference, my code is below:
//map is a bitmap, horizontalCollisions and collisions are List<Rectangle>s
for (int y = 0; y < map.Height; y++) //loop through pixels
{
for (int x = 0; x < map.Width; x++)
{
if (map.GetPixel(x, y).Name == "ff000000") //wall color
{
int i = 1;
while (map.GetPixel(x + i, y).Name == "ff000000")
{
if (i != map.Width - x)
{
i++;
}
if (i == map.Width - x)
{
break;
}
}
Rectangle r = new Rectangle(x, y, i, 1);//create and add
x += i - 1;
horizontalCollisions.Add(r);
}
}
}
for (int j = 0; j < horizontalCollisions.Count; j++)
{
int i = 1;
Rectangle current = horizontalCollisions[j];
Rectangle r = new Rectangle(current.X, current.Y + 1, current.Width, 1);
while(horizontalCollisions.Contains(r))
{
i++;
horizontalCollisions.Remove(r);
r = new Rectangle(current.X, current.Y + i, current.Width, 1);
}
Rectangle add = new Rectangle(current.X, current.Y, current.Width, i);
collisions.Add(add);
}
//collisions now has all the rectangles
Basically, it will loop through the pixel data horizontally. When it encounters a wall pixel, it will stop the counter and (using a while loop) move the counter towards the right, one by one until it hits a non-wall pixel. Then, it will create a rectangle of that width, and continue on. After this process, there will be a big list of rectangles, each 1px tall. Basically, a bunch of horizontal lines. The next loop will run through the horizontal lines, and using the same process as above, it will find out of there are any rectangles with the same X value and the same Width value under it (y+1). This will keep incrementing until there are none, in which one big rectangle will be created, and the used rectangles are deleted from the List. The final resulting list contains all the rectangles that will make up all the black pixels on the image (pretty efficiently, I think).
Etiquette may suggest that I should comment this instead of add it as an answer, but I do not yet have that capability, so bear with me.
I'm afraid I am not able to translate this into code for you, but I can send you towards some academic papers that discuss algorithms that can do some of the things that you're asking.
Other time this questions has appeared:
Find the set of largest contiguous rectangles to cover multiple areas
Puzzle: Find largest rectangle (maximal rectangle problem)
Papers linked in those questions:
Fast Algorithms To Partition Simple Rectilinear Polygons
Polygon Decomposition
The Maximal Rectangle Problem
Hopefully these questions and papers can lead help you find the answer you're looking for, or at least scare you off towards finding another solution.

Multiple textures on a heightmap with blending?

This is my first time here (With an account), I'm looking to make a height-map editor with XNA 4.0 (Somewhat similar to Earth2150's, if you've played it).
I've written a custom Effect File here: http://pastebin.com/CUFtB8Z9
It blends textures just fine, except it blends over the entire map.
What I really want is to be able to have multiple textures on my heightmap (Which i'll then blend with the nearest other texture) and I am looking for ways to do this.
I thought about assigning a float in my Vertex Declaration, then using an array of textures to "Assign" a texture to a specific vertex. But how would I go about getting my effect file to take in a different value for a texture on each vertex?
Sorry about not being very clear, here is my Draw code and my Vertex Declaration:
(Excuse the random number changing, It was my attempt to try and get each vertex to pick a random texture)
public void Draw(Texture2D[] TextureArray)
{
RasterizerState rs = new RasterizerState();
rs.CullMode = CullMode.None;
//rs.FillMode = FillMode.WireFrame;
EditGame.Instance.GraphicsDevice.RasterizerState = rs;
Random rnd = new Random();
foreach (EffectPass pass in EditGame.Instance.baseEffect.CurrentTechnique.Passes)
{
if (SlowCounter == 60)
{
EditGame.Instance.baseEffect.Parameters["xTexture"].SetValue(TextureArray[rnd.Next(0, 2)]);
EditGame.Instance.baseEffect.Parameters["bTexture"].SetValue(TextureArray[rnd.Next(0, 2)]);
SlowCounter = 0;
}
pass.Apply();
EditGame.Instance.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3, VP2TC.VertexDeclaration);
}
SlowCounter++;
}
public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate,0),
new VertexElement(20, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate,1),
new VertexElement(28, VertexElementFormat.Single, VertexElementUsage.BlendWeight,0),
new VertexElement(32, VertexElementFormat.Vector3,VertexElementUsage.Normal,0),
new VertexElement(44, VertexElementFormat.Color,VertexElementUsage.Color,0)
);
As I said in my comment, I'm not certain this is what you're looking for but I'll go ahead anyway.
I think what you probably want is described here.
Essentially you have a Vector 4 which stores the weights of each texture and then take a weighted average of all 4 textures weighted by the individual elements in the vector (acting as 4 blend weights).
If you want to blend textures without having to have a blend element for every single texture things get more fun.
You could have a single blend weight, which essentially picks the blending of 2 adjacent textures in order. So if you have:
Snow
Grass
Rock
Sand
Blend Weight = 0.5
Would pick a blend of Grass (0.25) and Rock (0.75). Blended in equal amounts (since it's halfway between them).
If you want lots of textures, your shader is going to become very cumbersome with ~50 texture samplers. If you really want this many textures you should consider a texture atlas or just procedurally generated virtual textures with the blending already done at generation time.

Categories