public void Draw(SpriteBatch spriteBatch)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Vector2 mouseCoord = GetMouseTilePosition();
if (mouseCoord.X > 0)
{
spriteBatch.Draw(selection, tileRect = new Rectangle((int)mouseCoord.X * 64, (int)mouseCoord.Y * 64, 64, 64),
Color.White);
}
spriteBatch.Draw(tiles[index[x,y]].texture, tileRect = new Rectangle(x * 64, y * 64, 64, 64),
Color.White);
}
}
}
public Vector2 GetMouseTilePosition()
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (IsMouseInsideTile(x, y))
{
return new Vector2(x, y);
}
}
}
return new Vector2(-1, -1);
}
public bool IsMouseInsideTile(int x, int y)
{
MouseState MS = Mouse.GetState();
return (MS.X >= x * 64 && MS.X <= (x + 1) * 64 &&
MS.Y >= y * 64 && MS.Y <= (y + 1) * 64);
}
It is very cpu intensive. Is there a better way to do this code? Also I have a camera how can I change this to factor that in so I get the actual mouse position
Jon Skeet is right, you could directly call IsMouseInsideTile() instead of looping through your array several times. (Currently, you're checking where the mouse is in the whole tile array, for each tile, instead of only checking the current tile you're in).
public void Draw(SpriteBatch spriteBatch)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (IsMouseInsideTile(x, y))
{
spriteBatch.Draw(selection, tileRect = new Rectangle(x * 64, y * 64, 64, 64),
Color.White);
}
spriteBatch.Draw(tiles[index[x,y]].texture, tileRect = new Rectangle(x * 64, y * 64, 64, 64),
Color.White);
}
}
}
I'm sorry, it's all my fault, I submitted this code quickly earlier without double checking. This new version should improve your performance drastically.
Related
I'm using the following code to remove whitespace around an image.
static Bitmap TrimBitmap(Bitmap source)
{
Rectangle srcRect = default(Rectangle);
BitmapData data = null;
try
{
data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] buffer = new byte[data.Height * data.Stride];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
int xMin = int.MaxValue,
xMax = int.MinValue,
yMin = int.MaxValue,
yMax = int.MinValue;
bool foundPixel = false;
// Find xMin
for (int x = 0; x < data.Width; x++)
{
bool stop = false;
for (int y = 0; y < data.Height; y++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
xMin = x;
stop = true;
foundPixel = true;
break;
}
}
if (stop)
break;
}
// Image is empty...
if (!foundPixel)
return null;
// Find yMin
for (int y = 0; y < data.Height; y++)
{
bool stop = false;
for (int x = xMin; x < data.Width; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
yMin = y;
stop = true;
break;
}
}
if (stop)
break;
}
// Find xMax
for (int x = data.Width - 1; x >= xMin; x--)
{
bool stop = false;
for (int y = yMin; y < data.Height; y++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
xMax = x;
stop = true;
break;
}
}
if (stop)
break;
}
// Find yMax
for (int y = data.Height - 1; y >= yMin; y--)
{
bool stop = false;
for (int x = xMin; x <= xMax; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
yMax = y;
stop = true;
break;
}
}
if (stop)
break;
}
srcRect = Rectangle.FromLTRB(xMin, yMin, xMax , yMax);
}
finally
{
if (data != null)
source.UnlockBits(data);
}
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}
I'm trying to trim a Bitmap with Text Drawn on it.The Proper Image should look like this after trimming
But after trimming i get the following result ..the bottom portion clipped off
What i'm i doing wrong? Please advice..
This is actually a problem with Rectangle.FromLTRB !
Looking closer at the images you will find that you have actually lost only one row of pixels. (The strong magnification had me fooled for a while..)
The algorithm to determine the Height (and Width) of the rectangle is basically right, but off by one.
If you use this
srcRect = Rectangle.FromLTRB(xMin, yMin, xMax + 1 , yMax + 1);
or this:
srcRect = new Rectangle(xMin, yMin, xMax - xMin + 1 , yMax - yMin + 1);
it should work.
You can test with pen and paper: Say: 1st pixel row with a color = 4, first from bottom on a 10 pixel square: 8, that makes 5 not 4 net data: 4,5,6,7,8.
Do note that this issue is inherent in FromLTRB:
Rectangle myRectangle = Rectangle.FromLTRB(0, 0, 10, 10);
..results in a Rectangle with Height=10 even though 0..10 should cover 11 pixels rows! So the Right-Bottom coordinate is actually excluded from the result!!
I think the whole issue with rectangles being off by one stems from legacy ways to use a Pen with its alignment. When using the same rectangles to fill with a Brush all works as expected.
I am having a problem with displaying maps on screen. I am giving the developer the choice between square tiles and hexagonal tiles. The map is saved as a .txt
The square tiled map displays correctly as shown
where as the hexagonal map is upside down
I am completely stumped as to how to go about fixing the issue I have supplied the entire function bellow
void displayMap(){
//Draw Tiles on the Screen
if (useSquareTiles == true && useHexagonTiles == false) {
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
GameObject.Instantiate (mapArray [y] [x], new Vector3 (x, mapHeight - y, 0),
Quaternion.Euler (-90, 0, 0));
}
}
}
float hexWidth = 0.85f; //The width of the HexTiles
if (useHexagonTiles == true && useSquareTiles == false) {
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
GameObject.Instantiate (mapArray[y][x], new Vector3 (x * hexWidth, y + (0.5f * Mathf.Abs (x) % 1), 0),
Quaternion.Euler (-90, 0, 0));
}
}
}
}
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.
I have the following code for looping pixels in an image:
Bitmap myBitmap = new Bitmap(pictureBox1.Image);
for (x = 0; x < pictureBox1.Image.Width; x += 1)
{
for (y = 0; y < pictureBox1.Image.Height; y += 1)
{
Color pixelColor = myBitmap.GetPixel(x, y);
aws = pixelColor.GetBrightness();
}
}
The above works. What if i want to exclude an area of the image. The area is an area selected by the user,for example:
from x=200&y=30 to x=250&y=30
from x=200&y=35 to x=250&y=35
from x=200&y=40 to x=250&y=40
from x=200&y=45 to x=250&y=45
How i loop all pixels except the one i wrote above?
Thank you!
I think you can do something like the following
Rectangle rectToExclude = new Rectangle(200, 30, 250, 30);//rectangle to be excluded
Bitmap myBitmap = new Bitmap(pictureBox1.Image);
for (x = 0; x < pictureBox1.Image.Width; x += 1)
{
for (y = 0; y < pictureBox1.Image.Height; y += 1)
{
if (rectToExclude.Contains(x, y)) continue; // if in the exclude rect then continue without doing any effects
Color pixelColor = myBitmap.GetPixel(x, y);
aws = pixelColor.GetBrightness();
}
}
Maybe write a function to test x and y like this:
public static bool IsInArea(int testx, int testy, int x1, int y1, int x2, int y2)
{
if (testx > x1 && testx < x2 && testy > y1 && testy < y2)
{
return true;
}
else
{
return false;
}
}
and then use it like this:
BitmapData bmd = myBitmap.LockBits(new Rectangle(0, 0, myBitmap.Width, myBitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite,
myBitmap.PixelFormat);
int PixelSize = 4;
unsafe
{
for (int y = 0; y < bmd.Height; y++)
{
byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);
for (int x = 0; x < bmd.Width; x++)
{
if (!IsInArea(x, y, 200, 30, 250, 30))
{
int b = row[x * PixelSize]; //Blue
int g = row[x * PixelSize + 1]; //Green
int r = row[x * PixelSize + 2]; //Red
int a = row[x * PixelSize + 3]; //Alpha
Color c = Color.FromArgb(a, r, g, b);
float brightness = c.GetBrightness();
}
}
}
}
myBitmap.UnlockBits(bmd);
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.)