So I'm trying to draw a layered tile based map, as the title says. I have this so far. The tiles i am using are (32, 32). It's currently drawing only 1 type of tile as the whole tile map. When it should be drawing out several different types of tiles to make a map.
for (int layers = 0; layers < map.Layers.Count; layers++) {
for (var i = 0; i < map.Layers[layers].Tiles.Count; i++) {
int gid = map.Layers[layers].Tiles[i].Gid;
if (gid != 0) {
int tileFrame = gid - 1;
int row = tileFrame / (map.Height / tileHeight);
float x = (i % map.Width) * map.TileWidth;
float y = (float)Math.Floor(i / (double)map.Width) * map.TileHeight;
Rectangle tilesetRec = new Rectangle(0, 0, 32, 32);
for (int j = 0; j < tileSets.Count; j++) {
for (int k = 0; k < tileSets[j].Tiles.Count; k++) {
spriteBatch.Draw(tileSets[j].Tiles[k].Image.Texture, new Rectangle((int)x, (int)y, 32, 32), Color.White);
}
}
}
}
}
This is not entirely my code this is someone elses that i have tried to build on apparently it worked for the person for drawing one layer. This is the original code:
for (var i = 0; i < _map.Layers[0].Tiles.Count; i++) {
int gid = _map.Layers[0].Tiles[i].Gid;
// Empty tile, do nothing
if (gid == 0) {
} else {
int tileFrame = gid - 1;
int row = tileFrame / (_tileset.Height / _tileHeight);
float x = (i % _map.Width) * _map.TileWidth;
float y = (float)Math.Floor(i / (double)_map.Width) * _map.TileHeight;
Rectangle tilesetRec = new Rectangle(_tileWidth * tileFrame, _tileHeight * row, 32, 32);
spriteBatch.Draw(_tileset, new Rectangle((int)x, (int)y, 32, 32), tilesetRec, Color.White);
}
}
Your problem is that for each tile position you seem to be drawing all tiles in all tilesets. The variables tileFrame, row and tilesetRec, which were used to render the specific tile in the original code, are not used in your first snippet.
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'd like to figure out how I can add a 1 pixel border in between each frame that my spritesheet creator generates, can anyone help?
The function below takes an array of images and turns them into a table filled with bitmaps.
The program is supposed to take a gif and turn it into multiple spritesheets, so that it can be displayed in a game engine. That part is working fine, but I'd like to add a 1 pixel border in between each frame.
public Bitmap[] combineFrames(Image[] images, int columns)
{
int rows = columns;
int imagesPerSheet = rows * columns;
int sheets = (int)Math.Ceiling((double)images.Length / imagesPerSheet);
Bitmap[] mapTable = new Bitmap[55]; //55 being the max #sheets
for (int x = 0; x < sheets; x++)
{
Bitmap bitmap = new Bitmap(images[0].Width * columns, images[0].Height * rows);
Graphics graphics = Graphics.FromImage(bitmap);
int L = 0;
int remainingNFrames = (images.Length - (imagesPerSheet * x));
if (remainingNFrames < imagesPerSheet)
{
L = remainingNFrames;
}
else
{
L = imagesPerSheet;
}
for (int i = 0; i <= L; i++)
{
int formulizedProduct = imagesPerSheet * (x) + (i);
if (formulizedProduct == images.Length) { break; } //should always be length - 1
Image image = images[formulizedProduct]; //16*x+i
int X = (image.Width * ((i - 1) % columns));
int Y = (image.Height * (int)((double)(i - 1) / columns));
graphics.DrawImage(
image,
X, //I feel like I'd have to adjust this part and the part below, but I'm not really sure.
Y
);
}
mapTable[x] = bitmap;
}
return mapTable;
}
Thanks.
My goal is to have a robot use this grid to create a map base off the information it collects from its surroundings. When the robot detects an object the square in front of it turns red. Currently I am stuck on how I can give each square an x and Y value for location purposes. Also when I scroll the screen the block sizes change, can someone provide help with that as well?
Rectangle rect = new Rectangle(700, 350, 50, 50);
g.DrawRectangle(myPen, rect); // Draws the Rectangle to the screen
e.Graphics.FillEllipse(myBrush, 700,350,50,50);`
for (int i = 0; i < 9900; i = i + 50)
{
rect = new Rectangle(0 + i, 0, 50, 50);
g.DrawRectangle(myPen, rect);
for (int j = 0; j < 9900; j = j + 50)
{
rect = new Rectangle(0 + i, 0 + j, 50, 50);
g.DrawRectangle(myPen, rect);
}
}
Here is a very quick example of how to do this using a 2d array. It was written in LINQPad, so it may look a little odd, but it should give you some leads. It allows you to store a map and look up values using x and y coordinates. You can use the CellInfo class to add any extra information about the cell that you need, beyond if it is blocking or not.
Ideally, you would want to wrap the entire array up in your own Map class, that abstracts away the details, and gives you a lot of helpful utility functions. For instance, if your map is extremely large, you may run out of memory. You could have the Map class only load smaller blocks of the map from files on disk as needed, or even make the map wrap around its self easily.
void Main()
{
var map = new CellInfo[10, 10];
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
{
map[x, y] = new CellInfo();
}
}
var rnd = new Random();
for (int i = 0; i < 20; i++)
{
map[rnd.Next(0, 10), rnd.Next(0, 10)].IsBlocked = true;
}
DrawMap(map).Dump();
}
public Bitmap DrawMap(CellInfo[,] map)
{
var img = new Bitmap(320, 320, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(img))
{
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
{
var cell = map[x, y];
Brush brush = cell.IsBlocked ? Brushes.Red : Brushes.White;
g.FillRectangle(brush, x * 32, y * 32, 31, 31);
g.DrawRectangle(Pens.Black, x * 32, y * 32, 31, 31);
}
}
}
return img;
}
public class CellInfo
{
public bool IsBlocked { get; set; } = false;
}
It produces the following output (varies each time it is run):
I would like to find an object on a bitmap using its HSV color. I know there already are some topics created about this issue, but they seem not to work on Android (I didn't find System.Drawing.Imaging on Android) or to be less efficient than the algorithm I already created:
public Rect FindObjectOnBitmap(Bitmap bm)
{
bm.GetPixels(pixels, 0, bm.Width, 0, 0, bm.Width, bm.Height);
// Get the matching pixels.
for(int x = 0; x<bm.Width; x++)
for (int y = 0; y < bm.Height; y++)
{
Color color = new Color(pixels[x + bm.Width * y]);
pixelsMatch[x,y] = (color.GetBrightness() > Settings.Tracker.blackFilter && color.GetSaturation() > Settings.Tracker.whiteFilter) && (Angle(Settings.Tracker.hue, color.GetHue()) < Settings.Tracker.sensitivity);
}
Rect objectRect = null;
int maxNbPixel = 0;
// for each matching pixel
for (int x = 0; x < bm.Width; x ++)
for (int y = 0; y < bm.Height; y ++)
if(pixelsMatch[x, y])
{
int nbPixel = 0;
Rect objectRectTemp = new Rect(x, y, x, y);
Queue<Pixel> zone = new Queue<Pixel>();
zone.Enqueue(new Pixel(x, y));
while(zone.Count != 0)
{
Pixel px = zone.Dequeue();
nbPixel++;
// Updates object position
if (px.x < objectRectTemp.Left)
objectRectTemp.Left = px.x;
if (px.x > objectRectTemp.Right)
objectRectTemp.Right = px.x;
if (px.y < objectRectTemp.Bottom)
objectRectTemp.Bottom = px.y;
if (px.y > objectRectTemp.Top)
objectRectTemp.Top = px.y;
// for each nearby pixel
for (int side = 0; side < 4; side++)
{
Pixel sidePx = px.Side(side);
if (sidePx.x >= 0 && sidePx.x < bm.Width && sidePx.y >= 0 && sidePx.y < bm.Height)
{
if (pixelsMatch[sidePx.x, sidePx.y])
{
zone.Enqueue(sidePx);
pixelsMatch[sidePx.x, sidePx.y] = false;
}
}
}
}
// Save the object if it is big enough.
if(nbPixel > maxNbPixel && nbPixel >= 4)
{
maxNbPixel = nbPixel;
objectRect = objectRectTemp;
}
}
return objectRect;
}
// Function comparing hues.
static float Angle(float deg1, float deg2)
{
if (Math.Abs(deg1 - deg2) > 180f)
return (360f - Math.Abs(deg1 - deg2));
else
return Math.Abs(deg1 - deg2);
}
However, I am reading a stream, and this algorithm is also not efficient enough since I get 0.1 fps on a 640x480 image... I get a good fps only if I create the bitmap with a smaller resolution than the real image (for example 40x30). That enables me to detect big or close objects with a bad precision (position and size).
How can I increase the efficiency enough, knowing I am on an ANDROID device...
Thanks in advance.
PS: Sorry for my poor English ^^
I am creating a game after working through a XNA 4.0 book. It will be 3D, but I am already stuck in creating the terrain...
UPDATE: Everything starting from here is an update...
Terrain Update:
public void Update(Matrix view, Matrix projection)
{
View = view;
Projection = projection;
World = Matrix.CreateTranslation(-Width / 2f, 0, Height / 2f);
}
Terrain Draw:
public void Draw(GraphicsDevice g)
{
effect.CurrentTechnique = effect.Techniques["ColoredNoShading"];
effect.Parameters["xView"].SetValue(View);
effect.Parameters["xProjection"].SetValue(Projection);
effect.Parameters["xWorld"].SetValue(World);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
//g.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3, VertexPositionColorNormal.VertexDeclaration);
g.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, indices.Length / 3);
}
}
The commented line is working, in the both cases I am able to see the terrain...
The following code is to initialize Vertex and Index Buffer:
private void SetUpVertices(GraphicsDevice g)
{
float currentH;
int currentI;
vertices = new VertexPositionColorNormal[Width * Height];
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
currentH = heightData[x,y];
currentI = x + y * Width;
vertices[currentI].Position = new Vector3(x, currentH , -y);
if (currentH < minH + (maxH - minH) / 3)
vertices[currentI].Color = Color.ForestGreen;
else if (currentH < maxH - (maxH - minH) / 3)
vertices[currentI].Color = Color.LawnGreen;
else
vertices[currentI].Color = Color.White;
}
}
SetUpIndices(g);
}
private void SetUpIndices(GraphicsDevice g)
{
indices = new int[(Width - 1) * (Height - 1) * 6];
int counter = 0;
for (int y = 0; y < Height - 1; y++)
{
for (int x = 0; x < Width - 1; x++)
{
int lowerLeft = x + y * Width;
int lowerRight = (x + 1) + y * Width;
int topLeft = x + (y + 1) * Width;
int topRight = (x + 1) + (y + 1) * Width;
indices[counter++] = topLeft;
indices[counter++] = lowerRight;
indices[counter++] = lowerLeft;
indices[counter++] = topLeft;
indices[counter++] = topRight;
indices[counter++] = lowerRight;
}
}
SetUpNormals(g);
}
private void SetUpNormals(GraphicsDevice g)
{
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].Normal = Vector3.Zero;
}
int[] index = new int[3];
Vector3 s1, s2, n;
for (int i = 0; i < vertices.Length / 3; i++)
{
for (int y = 0; y < 3; y++)
index[y] = indices[i * 3 + y];
s1 = vertices[index[0]].Position - vertices[index[2]].Position;
s2 = vertices[index[0]].Position - vertices[index[1]].Position;
n = Vector3.Cross(s1, s2);
for (int y = 0; y < 3; y++)
{
vertices[index[y]].Normal += n;
vertices[index[y]].Normal.Normalize();
}
}
FillBuffers(g);
}
private void FillBuffers(GraphicsDevice g)
{
VertexBuffer = new VertexBuffer(g, VertexPositionColorNormal.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
VertexBuffer.SetData(vertices);
IndexBuffer = new IndexBuffer(g, typeof(int), indices.Length, BufferUsage.WriteOnly);
IndexBuffer.SetData(indices);
g.Indices = IndexBuffer;
g.SetVertexBuffer(VertexBuffer);
}
I don't think, that there is a mistake, because it is working with the other line. Might there be an error with the .fx file I am using. If you think so, I am going to switch to BasicEffects...
(You might notice, that the code is from http://www.riemers.net/eng/Tutorials/XNA/Csharp/series1.php )
Thanks for your help...
Yours,
Florian
(Answer to original revision of the question.)
You're not setting your vertex buffer and index buffer onto the graphics device. These two lines of code (untested) should do what you need:
g.GraphicsDevice.Indices = indexBuffer;
g.GraphicsDevice.SetVertexBuffer(vertexBuffer);
Place them just after you set the parameters on your effect (ef), before the loop.
The vertex buffer provides the vertex declaration that the exception message is asking for.
Edit after question update: In your new version you're setting the vertex and index buffers - but it's in the wrong place. You need to set them onto the graphics device each frame. Your code would only work if nothing changes them after you set them in FillBuffers. But I'm guessing that stuff is being drawn outside your class's Draw method?
If that something else is a SpriteBatch, even it works using vertex buffers and index buffers. So it will reset your settings. (It's worth adding that it also sets render states - in which case you might need to see this article.)