Basic tile map in winforms - c#

I am making a program where you bassicly move from tile to tile in windows forms.
So in order to do that, I wanted to use panels each panel has a tag. To detect collision.
So I have an image of my map. and I divided into multiple tiles. However now I have to drag 900 tiles onto panels.
This isn't very effective in 2 ways. First loading 900 textures isn't really a smart idea. Also it would take ages. So i wanted to use a spritesheet or tilemap. But how would I do that in winforms. I believe I have seen some people use a grid view or whatever. However im not sure how to do what I want to do.
What would be the best solution?
Thanks in advance!

For any serious gaming project WinForms is not the best platform. Either WPF or XNA or Unity are able to deliver high performance use of DirectX.
But since you want to do it in Winforms here is a way to do it.
It creates a whopping number of 900 PictureBoxes and loads each with a fraction of an source image:
private void Form1_Load(object sender, EventArgs e)
{
int tileWidth = 30;
int tileHeight = 30;
int tileRows = 30;
int tileCols = 30;
using (Bitmap sourceBmp = new Bitmap("D:\\900x900.jpg"))
{
Size s = new Size(tileWidth, tileHeight);
Rectangle destRect = new Rectangle(Point.Empty, s);
for (int row = 0; row < tileRows; row++)
for (int col = 0; col < tileCols; col++)
{
PictureBox p = new PictureBox();
p.Size = s;
Point loc = new Point(tileWidth * col, tileHeight * row);
Rectangle srcRect = new Rectangle(loc, s);
Bitmap tile = new Bitmap(tileWidth, tileHeight);
Graphics G = Graphics.FromImage(tile);
G.DrawImage(sourceBmp, destRect, srcRect, GraphicsUnit.Pixel);
p.Image = tile;
p.Location = loc;
p.Tag = loc;
p.Name = String.Format("Col={0:00}-Row={1:00}", col, row);
// p.MouseDown += p_MouseDown;
// p.MouseUp += p_MouseUp;
// p.MouseMove += p_MouseMove;
this.Controls.Add(p);
}
}
}
When I tried it I was a bit worried about perfomance, but..
This takes under 1 second to load on my machine.
Starting the programm adds 10MB to VS memory usage. That is like nothing.
For a fun project this will do; for best performance one might use Panels but these will have to be filled and refilled in the Paint event. This solution saves you the hassle and since you don't change the tile picture all the time this works well enough.
Pleae note: I have added a Name and a Tag to each PictureBox, so you can later refer to it. These both contain info about the original position of the Picturebox. The Name looks like this: Col=23-Row=02 and the Tag is the original Location object.
Also: Dynamically added controls take a little extra to script since you can't create their method bodies in the designer. Instead you add them like above. In doing so Intellisense and the Tab key are your best friends..
I have added three event handlers for a few mouse events. When you uncomment them you will have to add the methods like e.g. this:
void p_MouseMove(object sender, MouseEventArgs e)
{
throw new NotImplementedException();
}
But maybe you want to use other events to play like Drag&Drop or keyboard events..
There are two ways to refer to these tiles. Maybe you want to try and/or use both of them: You can loop over the form's controls with a
foreach (Control ctl in this.Controls)
{ if (ctl is PictureBox ) this.Text = ((PictureBox)ctl).Name ; }
It tests for the right type and then casts to PictureBox. As an example it displays the name of the tile in the window title.
Or you can have a variable and set it in the MouseDown event:
PictureBox currentTile;
void p_MouseDown(object sender, MouseEventArgs e)
{
currentTile = (PictureBox ) sender;
}

Related

Dynamic drawing ants in winforms during execution of ant colony

After this question (Show trail of moving pixel in C# WinForm project) for my personal ant colony project in c#, I'm trying to apply the solution second suggested solution: the one that combines drawing the trail into a bitmap and the new ants onto the surface.
[...]Application.Run(new ShowAnts());[...]
public partial class ShowAnts : Form
{
Bitmap bmp;
int j = 0;
public ShowAnts()
{
InitializeAntProgram();
InitializeComponent();
bmp = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
pictureBox1.Image = bmp;
}
public void RenderAnts(object sender, PaintEventArgs e)
{
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
while (j < 1000)
{
Map.EvaporatesPheromones();
foreach (Vector2D food in foodSrcs)
{
Map.SetMapPoint(food, 500);
}
foreach (Ant a in ants)
{
Brush c;
c = Brushes.DarkBlue;
if (a.role == AntRole.Scout)
{
a.Move(j);
c = Brushes.Red;
}
e.Graphics.FillRectangle(Brushes.DarkBlue, a.position.x, a.position.y, 1, 1);
G.FillRectangle(Brushes.Gray, a.position.x, a.position.y, 1, 1);
}
j++;
}
}
}
}
The code above shows the graphic attempt to draw the ant movement into a winform.
It works perfectly, but it shows only the final result. I would like to show the step by step evolution keeping the graphical trail information without reparsing my map info.
Please consider that a working console project on which I' developing this "graphic interface" already exists so:
some variables are set elsewhere (i.e.: food) in the project;the `a.Move(j);` refers to the ant logic itself (analysis, decision, new cell movement referring to the map array);the `j` counter is used to count steps and to set an arbitrary stop, but has no real use;I'm already storing into map array and some other variables all informations concerning pheromone, movement, positions etc.
Looking at your code and also the comments of the previous question, it seems that you are missing the part that would animate the movement. Instead you are looping inside what seems to be the Paint event.
Here is a quick fix for that. It adds a Timer that triggers the RenderAnts event, which seems to be hooked up to the pictureBox1.Paint handler..:
A few class level variables:
int counter = 0;
int limit = 1000;
Timer antTimer = new Timer();
Start code:
antTimer.Interval = 50; // <-- pick your speed !!
antTimer.Tick += (ss, ee) =>
{ pictureBox1.Invalidate(); counter++; if (counter > limit) antTimer.Stop(); };
antTimer.Start();
The speed is 50ms, which means 20 Ticks per second.
The Tick event is inlined with a tiny Lambda epression and has only one statement plus the loop logic. By Invalidating the pictureBox1 control its Paint event and thereby the RenderAnts event is triggered.
Also note that I called it a 'quick fix'. Usually you would discern between the rendering and the moving code of an animation; but in this case this fine difference doesn't matter much.
Now we change the RenderAnts method, taking out the loop:
public void RenderAnts(object sender, PaintEventArgs e)
{
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
Map.EvaporatesPheromones();
foreach (Vector2D food in foodSrcs)
{
Map.SetMapPoint(food, 500);
}
foreach (Ant a in ants)
{
Brush c = Brushes.DarkBlue;
if (a.role == AntRole.Scout)
{
a.Move(j);
c = Brushes.Red;
}
e.Graphics.FillRectangle(c, a.position.x, a.position.y, 1, 1);
G.FillRectangle(Brushes.Gray, a.position.x, a.position.y, 1, 1);
}
}
}
You also may want to add a Start/Stop Button. Also a TrackBar to change the speed..
Now you should be able to watch the progress of your ants at 20Hz, leaving grey trails.

Which is faster when animating the UI: a Control or a Picture?

/I'm working with and testing on a computer that is built with the following:
{1 GB RAM (now 1.5 GB), 1.7 GHz Intel Pentium Processor, ATI Mobility Radeon X600 GFX}
I need scale / transform controls and make it flow smoothly. Currently I'm manipulating the size and location of a control every 24-33ms (30fps), ±3px. When I add a 'fade' effect to an image, it fades in and out smoothly, but it is only 25x25 px in size. The control is 450x75 px to 450x250 px in size. In 2D games such as Bejeweled 3, the sprites animate with no choppy animation.
So as the title would suggest: which is easier/faster on the processor: animating a bitmap (rendering it to the parent control during animation) or animating the control it's self?
EDIT:
Hey, I thought this was a helpful community, not one that down-rates questions that don't seem challenging! (And I've seen more ridiculous questions here with better ratings too!) Please drop me a line first before negatively rating my questions!
I managed to find some free-time in my heck-tick scheduled, to quickly whip up a new project. I'm sure my time could have been better spent else where but hopefully someone else in my shoes may find this of use out there...
The answer is: a Picture over a Control. When rendering a bitmap onto the canvas, there are very little events that will fire, if any. As for the control, it is filled with events - some chained, some looped, and the addition of recursion, so a simple 'LocationChanged' event wouldn't even cover the half of what actually is taking place under the hood.
What I would do for controls that have lots of dynamic animations applied to them during runtime, is to develop a two piece set: a control [rendering] template or active interface (for when the control is at a stand-still or before the play of an animation), and a the animating structure with basic defining properties such as the display image [the rendered control], the rectangle bounds, and any animation algorithms that may be applied latter.
Edit: As Requested, here are the before and after code examples:
// This is the triggering event of the translating animation
private void object_Click(object sender, EventArgs e)
{
// the starting point is at (75,75)
element.Transform(new Point(500, 250));
}
Before:
public class ControlElement : UserControl
{
private Timer tick;
private Point pT0;
public ControlElement() : base()
{
tick = new Timer();
tick.Interval = 30; // about 30fps
tick.Tick += new EventHandler(tick_Tick);
}
void tick_Tick(object sender, EventArgs e)
{
// get the new point from distance and current location/destination
this.Location = Utils.Transform(this.Location, pT0, 3);
if ((pT0.X - this.Location.X)+(pT0.Y - this.Location.Y) <= 0)
{
this.Location = pT0;
tick.Stop();
//this.Visible = true;
}
}
public void Transform(Point destination)
{
pT0 = destination;
//this.Visible = false;
tick.Start();
}
}
After: I create a class that holds a picture of what the control would look like using the DrawToBitmap feature. It still contains the same animation methods as above. I had to add the Location and LocationChanged elements since this class was no longer a control. If and when the actual control needed to be accessed, I would stop rendering and display an instance of the control it's self.
Here is the rendering call:
void element_LocationChanged(object sender, EventArgs e)
{
canvas.Invalidate();
}
void canvas_Paint(object sender, PaintEventArgs e)
{
if (element != null)
{
Bitmap bmp = new Bitmap(element.Display);
Pen p = new Pen(Color.FromArgb(128, 128, 128), 1);
e.Graphics.DrawImage(bmp, element.Location);
e.Graphics.DrawRectangle(p,
element.Location.X, element.Location.Y,
bmp.Width, bmp.Height);
}
}

Drawing using a bitmap on PictureBox in C#

my first question here. And I'm sure it's an easy one, but I can't find a solution anywhere, I've read lots and just can't comprehend how to what i need to do:
I have a *.gif file, that I want to store in System.Drawing.Bitmap, so that I have access to GetPixel() method
I want to have my drawing on some control (currently trying out PictureBox)
So far what I've got:
I create a template, Bitmap from file - and that works (map_t in code)
I create a bitmap that will actually be drawn (map)
I can set my PictureBox image to a bitmap
I can't change this bitmap. Or I can, but the result doesn't show
I thought I'd have to create Graphic from that bitmap, then change it. I have no idea what i'm missing.
Here's the code: MapCanvas is PictureBox on a form, everything is in MouseMove, because I was trying to make some circles around the pointer - didn't work so I ended up with this code, which doesn't work either. I have read every question I could find, nothing helps... Here's the code:
namespace Projekt_innowacje
{
public partial class MapForm : Form
{
Bitmap map_t;
Bitmap map;
public MapForm()
{
InitializeComponent();
map_t = new Bitmap("map.gif", true);
map = new Bitmap(map_t.Width, map_t.Height);
MapCanvas.Image = map;
}
private void MapCanvas_MouseMove(object sender, MouseEventArgs e)
{
Graphics canvas = Graphics.FromImage(map);
for (int i = 0; i <= map.Width; i++)
for (int j = 0; j <= map.Height; j++)
if (map.GetPixel(i, j) == Color.Red)
canvas.DrawLine(new Pen(Color.Blue), i, j, i + 1, j + 1);
// map.SetPixel(i, j, map.GetPixel(i, j)); // also doesn't work
//MapCanvas.Refresh();
MapCanvas.Invalidate();
canvas.Dispose();
}
}
}
The code you are showing will never modify the canvas/map image unless you are making some other changes to the map image that you're not sharing. In your loop you test map.GetPixel, however map is created as a blank image in the constructor, so none of its pixels will ever be Color.Red. Did you intend to test against your template instead?
Also, you may want to have a look at this question which talks about comparing colors. It suggests that for basic color comparisons to use the .ToArgb() method on the color structure since the == and Equals do more than just compare the color value.

Performance issue of DrawingVisual following cursor while using onMouseMove event

I'm busy with a small application in which I want to display information at the location of the cursor when it hoovers over a Canvas. The Canvas in question is a custom one (inherited from Canvas) which provides functionality to add DrawingVisuals (as shown in basically every tutorial on displaying large amounts of geometric shapes on a canvas).
I would like to display a vertical line and horizontal line as well as the local coordinates (p in the code below) which are directly derived from the canvas coordinates (v). At the moment I'm rendering these objects at position (0,0) and use offset during the OnMouseMove event to update their location.
The horizontal and vertical lines are rendered in the DrawingVisual _cursor and the location in local y,z-coordinates in _info.
private void oCanvas_MouseMove(object sender, MouseEventArgs e)
{
#region 1. Get location data
System.Windows.Vector v = (System.Windows.Vector)e.GetPosition(oCanvas);
// point in YZ coordinates
BSMath.DoubleXY p = new BSMath.DoubleXY();
p.X = (oCanvas.OriginY - v.Y) / oCanvas.ZoomFactor;
p.Y = (oCanvas.OriginX - v.X) / oCanvas.ZoomFactor;
#endregion
#region 2. Update cursor and info
if (oSettings.ShowInformation)
{
_info.Info = p.X.ToString("0.0") + " | " + p.Y.ToString("0.0");
_info.Render(0, 0);
_info.Visual.Offset = v;
}
// move cursor
_cursor.Visual.Offset = v;
}
Using the mousemove event seems to be creating a lot of overhead and I can see that there are issues tracking the mouse movements when I move the mouse quickly.
Can anyone recommend a better way of creating the same effect?
example http://www.iccg.be/test/images/canvas.jpg
Edit:
I investigated it a bit further and the problem seems to occur when the resolution of the canvas is bigger. If it is a 600x400 canvas then there is no delay, but when it is around 1000x800 I get the problem with delays when hoovering. The performance also improves if I use user drawn crosshairs instead of the lines that have the full width/ height of the canvas.
I recently have built something similar and haven't had any performance issues.
Did it the very simple way by adding the stuff directly on the canvas.
The drawn items are in a second canvas behind the mouse position canvas. Both reside in a Grid.
This is for sure not the most sophisticated way to solve this, but it works quite well for me.
Here's the code:
private Point _previous;
private Point _current;
private Line _xLine;
private Line _yLine;
private TextBlock _displayTextBlock;
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_current = e.GetPosition(myCanvas);
if (_previous != _current)
{
if (_xLine == null)
{
_xLine = new Line() {X1 = 0, X2 = myCanvas.ActualWidth, Stroke = new SolidColorBrush(Colors.Black)};
_yLine = new Line() {Y1 = 0, Y2 = myCanvas.ActualHeight, Stroke = new SolidColorBrush(Colors.Black)};
_displayTextBlock = new TextBlock();
myCanvas.Children.Add(_xLine);
myCanvas.Children.Add(_yLine);
myCanvas.Children.Add(_displayTextBlock);
}
_displayTextBlock.SetValue(Canvas.TopProperty, _current.Y);
_displayTextBlock.SetValue(Canvas.LeftProperty, _current.X);
_displayTextBlock.Text = _current.X.ToString() + " | " + _current.Y.ToString();
_xLine.Y1 = _current.Y;
_xLine.Y2 = _current.Y;
_yLine.X1 = _current.X;
_yLine.X2 = _current.X;
_previous = _current;
}
}

Drawn rectangle appears only on last instance of my userControl

I have a userControl which has some programmatically drawn rectangles. I need few instances of that userControl on my form (see the image). The problem is that only the last instance will show the drawn shapes!
I guess it has something to do with drawing surface or the Paint event handler
In case it might help, here's some of the code I use in my control:
private void MyUserControl_Paint(object sender, PaintEventArgs e)
{
showHoraireMaitresse();
Rectangle rec = showDisponibilités();
var b = new SolidBrush(Color.FromArgb(150, Color.Blue));
e.Graphics.FillRectangle (b, rec);
showOccupation();
}
private void showHoraireMaitresse()
{
heureDebut = 8;
for (int i = 0; i < 14; i++)
{
//Label d'heure -> This shows just fine
addLabel(i, heureDebut);
//Rectangles d'heure -> This shows only in last instance
var rectangle = new Rectangle(180 + i * largeurDUneHeure, 14, largeurDUneHeure, 30);
surface.DrawRectangle(defaultPen, rectangle);
}
addLabel(14, heureDebut);
}
Thank you!
Without further information, I'm going to guess that 'surface' is static.
Trace through OnPaint and check which control is painting, and what the bounds are for 'surface'. Perhaps all the controls are painting the same exact rectangle.

Categories

Resources