Tile map with character and interaction - 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. The goal is to be able to find creatures that randomly spawn somehow i don't how to do that.
So I have an image of my map. and I divided into multiple tiles. So using this code I create the pictureboxes needed for the tile map but I want to add a character that is one tile big how can I do that???
I also want to be able to move around that character tag how would i fix that.
And also give the tiles a tag so the character can't go there.
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);
}
}
}

Related

Windows Forms Bitmap vs. Drawing Speed

I have data in a multidimensional array of doubles and I want to draw it on my form in a grid (with different values being different colors). Write now I am using a Panel control and in its OnPaint() I do do the drawing by calling Graphics.DrawRectangle() for each entry in the array. The data changes very frequently, so I call Refresh() on the panel when it does, but this is quite slow and I get a lot of flickering, I can also see the drawing of each rectangle happen sequentially, and I know that parallel drawing of the rectangles is not possible, because it is not thread-safe.
Would constructing a Bitmap where each pixel is an entry in the array, and then setting a PictureBox to show the Bitmap (and scale its size up) be faster than using the panel and OnPaint?
Here is a sample of the code I am using:
namespace PredPreySim
{
public partial class SimulationForm : Form
{
Simulation simulation; // instance of the simulation class
bool running = true;
int simWidth = 30;
int simHeight = 20;
int cellSize = 15;
System.Threading.Thread t;
public SimulationForm()
{
InitializeComponent();
simulation = new Simulation(simWidth, simHeight, numHerbivores, 0.3);
graphicsTimeDelay = Convert.ToInt32(GraphicsTimeDelay.Value);
ResizePanel();
}
private void ResizePanel()
{
panel1.Width = simWidth * cellSize;
panel1.Height = simHeight * cellSize;
}
// draws the board
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = e.Graphics;
SolidBrush brush = new SolidBrush(Color.Green);
RectangleF rectangle = new Rectangle();
rectangle.Width = cellSize;
rectangle.Height = cellSize;
// draw all the plants
Color plantColor;
for (int i = 0; i < simWidth; ++i)
{
for (int j = 0; j < simHeight; ++j)
{
int r, g = 255, b;
r = b = 255 - (int)(255.0 * Math.Tanh(simulation.plants[i, j]));
plantColor = Color.FromArgb(100, r, g, b);
brush.Color = plantColor;
rectangle.Location = new PointF(i * cellSize, j * cellSize);
graphics.FillRectangle(brush, rectangle);
}
}
brush.Dispose();
graphics.Dispose();
}
private void StartStopButton_Click(object sender, EventArgs e)
{
if (running) // then stop
{
running = false;
StartStopButton.Text = "Start";
}
else // start
{
running = true;
StartStopButton.Text = "Stop";
t = new Thread(new ThreadStart(this.runSimulation));
t.Start();
t.Priority = System.Threading.ThreadPriority.Highest;
}
}
private void runSimulation()
{
while (running)
{
simulation.Update();
panel1.Invoke(new MethodInvoker(Refresh)); // makes the call thread-safe
}
t.Abort();
}
}
}

Seat reserving software: Drawing lots of Seats in C# instantly

I'm building Seat reserving software using C# and I am confusing how I draw lots of seats instantly.
I'm trying three way which is..
Using Usercontrol
public void DrawUsercontrol(int x, int y)
{
int space = 4;
int SeatLimit = 165;
int RowSeatLimit = 15;
for (var i = 1; i < SeatLimit; i++)
{
UserControl1 ctrl = new UserControl1();
ctrl.Size = new System.Drawing.Size(25, 25);
ctrl.Location = new Point(x + space, y);
if (i % RowSeatLimit == 0)
{
x = 1;
y = y + 25 + space;
}
x = x + 25 + space;
ctrl.label1.Text = i.ToString();
ctrl.label1.Click += new EventHandler(label1_Click);
panel1.Controls.Add(ctrl);
}
}
Using "Panel" control
public void DrawingPanel(int x, int y)
{
Panel myPanel = new Panel();
int width = 16;
int height = 16;
myPanel.Size = new Size(width, height);
myPanel.BackColor = Color.White;
myPanel.Location = new Point(x, y);
Label mylabel = new Label();
mylabel.Text = "4";
myPanel.Controls.Add(mylabel);
myPanel.BackColor = Color.YellowGreen;
// this.Controls.Add(myPanel);
panel1.Controls.Add(myPanel);
}
Using Graphics and draw Rectangle
public void DrawingSquares(int x, int y)
{
SolidBrush myBrush = new SolidBrush(System.Drawing.Color.Red);
Graphics graphicsObj;
graphicsObj = this.panel1.CreateGraphics();
Rectangle myRectangle = new Rectangle(x, y, 30, 30);
graphicsObj.FillRectangle(myBrush, myRectangle);
graphicsObj.Dispose();
}
I refer first option but it's too slow.
And how can I decide?
Your problem is that you are adding only one control at a time. Adding a control forces a full refresh (software GDI+ rendering is quite slow) of the parent panel (best case) and perhaps the whole form (worst case).
Try creating all your controls and adding them in one line using Panel.Controls.AddRange. This will only prompt one refresh.
You should also only add these controls when the form is first shown and when the number of seats change - it is an expensive (and relatively slow) operation.
Consider creating a UserControl for each seat so that you don't have to manage the seat labels and seat borders separately - this way you can just have one list. If you add the seats in order, the index of an item in the list will map to its seat number! You probably wont get a performance increase from this but your code will be easier to work with.

How can I use a Lambda/Action/Delegate in my code? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I have to demonstrate a use of Lambdas/Actions/Delegates within my program for my A-Level to make it more complex.
I know how to make them all and (kind of) what they do, but not when to use them.
Can anybody let me know where in my code I can use one, two or all of these? I wasn't sure if this was the right place for this, let me know if not.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//Command-Line Arguements for SizeX and SizeY
namespace prjT02L08_Predator_Prey
{
public partial class frmSim : Form
{
Point GridSize; //Creates a pair of X and Y co-ordinates
Random r = new Random(); //Used for any random number needed in the program
float FoodSpawnChance = 50; //The chance that food has of spawning
int SimTick = 0; //How long the program has been running for
int ImageScale = 7; //How much to scale the bitmap grid up for displaying
int SizeX = 60; //The number of pixels on the X axis
int SizeY = 60; //The number of pixels on the Y axis
bool StepDone = true; //Has one update of the simulation happened?
public frmSim() //Main constructor method
{
InitializeComponent();
GridSize = new Point(SizeX, SizeY); //Sets the size of the grid using the size of the X and Y axis
AStar.Grid = Node.MakeGrid(GridSize.X, GridSize.Y, 10);
for (int i = 0; i < 10; i++)
new Mitosis(r.Next(0, SizeX) /*Produces a random number between 0 and the size of the X axis*/,
r.Next(0, SizeY)/*Random number between 0 and the size of the Y axis*/); //Adds new Mitosis bacteria for the first time
for (int i = 0; i < 8; i++)
new Meiosis(r.Next(0, SizeX), r.Next(0, SizeY)); //Adds new Meiosis bacteria for the first time
chkNaturalSpawn.Checked = true; //Sets the food's natural spawn to true, so it randomly spawns
}
private void UpdateSim() //Updates the whole simulation
{
UpdateVars(); //Updates all the variables in the simulation
if (SimTick == 20) //If the simulation has run for 20 ticks,
for (int i = 0; i < 10; i++)
new VirusP(1, 1); //Creates a new virus at the X position 1, and Y position 1
if (chkNaturalSpawn.Checked == true) //If natural spawning has been selected
SpawnFood(); //then try to spawn food
Entity.UpdateAll(SimTick); //Updates all entities
}
private void UpdateVars() //Updates all the variables in the simulation
{
SimTick++; //Each timer tick, this variable is incremented
tmrSimClock.Interval = trcInterval.Value; //The gap between ticks is set based of the trackbar (Which has been reversed right-to-left)
if (chkNaturalSpawn.Checked == true) //Checks if natural food spawning is enabled
FoodSpawnChance = trcFoodSpawn.Value; //then if true, sets the chance of food spawning to the value of the trackbar
VirusP.DoubleStepChance = trcPred2Step.Value; //The chance of the Virus moving two places instead of one is set of the trackbar
}
private void SpawnFood() //Attempts to spawn food at a random location
{
//Chance to spawn based on FoodSpawnChance variable
if (r.Next(0, 1000) < (FoodSpawnChance * 100)) //Uses a random number to determine whether food can spawn or not
{
int x = r.Next(0, GridSize.X); //Sets the value of x to a random number between 0 and the value of the Gridsize.X
int y = r.Next(0, GridSize.Y); //Sets the value of y to a random number between 0 and the value of the Gridsize.Y
if (!AStar.Grid[x, y].IsWall) //Checks if the random position chosen isn't a wall
new Food(x, y); //then if true, food is spawned at that position
}
}
private void frmSim_Load(object sender, EventArgs e)
{
}
private void btnStep_Click(object sender, EventArgs e)
{
if (StepDone == true) //Checks if the previous update from this button has already been completed or not - Prevents slow down
{
StepDone = false;
UpdateSim(); //Updates the simulation once
DrawSim(); //Redraws the bitmap image to show a visual update
StepDone = true;
}
}
private void DrawSim() //Creates the bitmap of the grid which is dispalyed on the screen and scales it up
{
Bitmap bmp = new Bitmap(GridSize.X, GridSize.Y); //Creates the bitmap specifying the width and height of it
//These two for loops loop through every part of the grid:
for (int x = 0; x < GridSize.X; x++) //For every value in the height of the grid
{
for (int y = 0; y < GridSize.Y; y++)//and every value in the width of the grid
{
Color Colour = Color.Black; //Creates a new color used to set the pixel colour on the bitmap (Empty space is black)
foreach (Entity e in Entity.GetEntitiesAt(x, y)) //For every entity a the current location...
{
if ((e as Food) != null) //If it is Food, set the colour to green
Colour = Color.FromArgb(Colour.R, 255, Colour.B);
else if ((e as Mitosis) != null) //If it is bacteria Mitosis, set the colour to blue
Colour = Color.FromArgb(Colour.R, Colour.G, 255);
else if ((e as Meiosis) != null) //If it is bacteria Meiosis, set the colour to gold
Colour = Color.Gold;
else //If it's none of these, the only entity left is the Virus, set the colour to red
Colour = Color.FromArgb(255, Colour.G, Colour.B);
}
if (AStar.Grid[x, y].IsWall) //If that location is a wall, set the colour to white
Colour = Color.White;
bmp.SetPixel(x, y, Colour); //Set the pixel at position x and y to the colour chosen above
}
}
//Scales up the bitmap into a new bitmap
Bitmap bmpscale = new Bitmap(GridSize.X * ImageScale, GridSize.Y * ImageScale);
for (int x = 0; x < GridSize.X; x++)
{
for (int y = 0; y < GridSize.Y; y++)
{
for (int sx = 0; sx < ImageScale; sx++)
{
for (int sy = 0; sy < ImageScale; sy++)
{
bmpscale.SetPixel(((x * ImageScale) + sx), ((y * ImageScale) + sy), bmp.GetPixel(x, y));
}
}
}
}
this.CreateGraphics().DrawImage(bmpscale, new Point(10, 10)); //Draws the bitmap image at set co-ordinates on the form
}
private void tmrSimClock_Tick(object sender, EventArgs e) //Every time the timer updates
{
UpdateSim(); //Updates the simulation
DrawSim(); //Redraws the simulation
}
private void btnRun_Click(object sender, EventArgs e)
{
tmrSimClock.Enabled = !tmrSimClock.Enabled; //Put timer in opposite state
btnStep.Enabled = !btnStep.Enabled; //Put button in opposite state
if (tmrSimClock.Enabled)
btnRun.Text = "Running...";
else
btnRun.Text = "Run";
}
private void btnReset_Click(object sender, EventArgs e)
{
Entity.Entities = new List<Entity>(); //Recreates the list of entitites
SimTick = 0; //Restarts the simulation timer
tmrSimClock.Enabled = false; //Turns the timer off
for (int i = 0; i < 10; i++)//Recreates entities Mitosis and Meiosis
{
new Mitosis(r.Next(0, SizeX), r.Next(0, SizeY));
new Meiosis(r.Next(0, SizeX), r.Next(0, SizeY));
}
btnRun.Text = "Run";
}
private void chkNaturalSpawn_CheckedChanged(object sender, EventArgs e)
{
lblFood.Enabled = chkNaturalSpawn.Checked == true ? true : false; //Turnery Statement
trcFoodSpawn.Enabled = chkNaturalSpawn.Checked == true ? true : false; //If checked is true, return true, else return false
}
}
}
First, I've got to agree with Jon: make it tidy first. This is an example of lambda use by using an action as an anonymous delegate/lambda function. You may have to fiddle to get it to work
private void traverseBmp(Action<int, int> doIt)
{
for (int x = 0; x < GridSize.X; x++) //For every value in the height of the grid
{
for (int y = 0; y < GridSize.Y; y++)//and every value in the width of the grid
{
doIt(x, y);
}
}
private void DrawSim() //Creates the bitmap of the grid which is dispalyed on the screen and scales it up
{
Bitmap bmp = new Bitmap(GridSize.X, GridSize.Y); //Creates the bitmap specifying the width and height of it
//These two for loops loop through every part of the grid:
traverseBmp((x, y) =>
{
Color Colour = Color.Black;
//Creates a new color used to set the pixel colour on the bitmap (Empty space is black)
foreach (Entity e in Entity.GetEntitiesAt(x, y)) //For every entity a the current location...
{
if ((e as Food) != null) //If it is Food, set the colour to green
Colour = Color.FromArgb(Colour.R, 255, Colour.B);
else if ((e as Mitosis) != null) //If it is bacteria Mitosis, set the colour to blue
Colour = Color.FromArgb(Colour.R, Colour.G, 255);
else if ((e as Meiosis) != null) //If it is bacteria Meiosis, set the colour to gold
Colour = Color.Gold;
else //If it's none of these, the only entity left is the Virus, set the colour to red
Colour = Color.FromArgb(255, Colour.G, Colour.B);
}
if (AStar.Grid[x, y].IsWall) //If that location is a wall, set the colour to white
Colour = Color.White;
bmp.SetPixel(x, y, Colour); //Set the pixel at position x and y to the colour chosen above
});
//Scales up the bitmap into a new bitmap
Bitmap bmpscale = new Bitmap(GridSize.X * ImageScale, GridSize.Y * ImageScale);
traverseBmp((x,y) =>
{
for (int sx = 0; sx < ImageScale; sx++)
{
for (int sy = 0; sy < ImageScale; sy++)
{
bmpscale.SetPixel(((x*ImageScale) + sx), ((y*ImageScale) + sy), bmp.GetPixel(x, y));
}
}
});
this.CreateGraphics().DrawImage(bmpscale, new Point(10, 10)); //Draws the bitmap image at set co-ordinates on the form
}

Drawing Multiple Rectangles c#

What would be the best way to draw 25 rectangles (5*5) in c#?
I later need to be able to reach a specific rectangle and change its color, for instance change the color to red if the user inputs the incorrect word.
Would it be more suitable to create an array of rectangles in this case?
This is what i have so far
Graphics g = pictureBox1.CreateGraphics();
int x =0;
int y= 0;
int width = 20;
int height = 20;
for (int i = 0; i < 25; i++)
{
if (i <= 4)
{
g.FillRectangle(Brushes.Blue, x, y, width, height);
x += 50;
}
else if (i > 4)
{
y = 50;
g.FillRectangle(Brushes.Blue, x, y, width, height);
x += 50;
}
}
This should get you started, not the complete code. You will need to add a PictureBox control and use the default name (picurebox1). EDIT: Need to add a button too :)
public partial class Form1 : Form
{
public List<Rectangle> listRec = new List<Rectangle>();
Graphics g;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Rectangle rect = new Rectangle();
rect.Size = new Size(100,20);
for (int x = 0; x < 5; x++)
{
rect.X = x * rect.Width;
for (int y = 0; y < 5; y++)
{
rect.Y = y * rect.Height;
listRec.Add(rect);
}
}
foreach (Rectangle rec in listRec)
{
g = pictureBox1.CreateGraphics();
Pen p = new Pen(Color.Blue);
g.DrawRectangle(p, rec);
}
}
public void ChangeColor(Rectangle target, Color targetColor)
{
Pen p = new Pen(targetColor);
g.DrawRectangle(p, target.X, target.Y, target.Width, target.Height);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.D0: ChangeColor(listRec[0], Color.Red);
break;
case Keys.D1: ChangeColor(listRec[1], Color.Red);
break;
//..more code to handle all keys..
}
}
}
If you're not concerned with performance or the looks, then the easiest approach would be to create a List of Lists of Panels in your Form_Load event as one of the comments mentions.
List<List<Panel>> panelGrid = new List<List<Panel>>();
for (var i = 0; i < 5; i++)
{
var panelRow = new List<Panel>();
for (var j = 0; j < 5; j++)
{
panelRow.Add(new Panel());
// add positioning logic here
}
panelGrid.Add(panelRow);
}
Then you will be able to reference each individual one at a later stage...
If you have to use Graphics class (which is the better approach), then you should setup something similar however replacing Panel with a class of your own. Then in Form_Paint event you would iterate through the list of objects and render them.
class MyPanel
{
public Size size;
public Color color;
}
...
foreach (var myPanelRow in myPanelGrid)
foreach (var myPanel in myPanelRow)
g.FillRectangle(myPanel.color, myPanel.size); // this obviously won't work as is, but you get the idea
Then when you need to change a color, you do something like:
myPanelsGrid[0][0].color = Color.Blue;
myForm.Invalidate();
The second line will result in Paint in event being called again.

Performance issue when adding controls to a panel in C#

I am creating a panel and then adding some labels/buttons to it to form a grid. The issue is that if I add more than say 25x25 items to the panel, there is a terrible performance hit. I can resize the form ok but when I scroll the panel to see all the labels the program lags, the labels/buttons tear or flicker, and sometimes it can make the program unresponsive. I have tried adding the controls to a "DoubleBufferedPanel" that I created. This seems to have no effect. What else could I do? Sorry for such a large code listing. I didn't want to waste anyone's time.
namespace GridTest
{
public partial class Form1 : Form
{
private const int COUNT = 50;
private const int SIZE = 50;
private Button[,] buttons = new Button[COUNT, COUNT];
private GridPanel pnlGrid;
public Form1()
{
InitializeComponent();
pnlGrid = new GridPanel();
pnlGrid.AutoScroll = true;
pnlGrid.Dock = DockStyle.Fill;
pnlGrid.BackColor = Color.Black;
this.Controls.Add(pnlGrid);
}
private void Form1_Load(object sender, EventArgs e)
{
int x = 0;
int y = 0;
int offset = 1;
for (int i = 0; i < COUNT; i++)
{
for (int j = 0; j < COUNT; j++)
{
buttons[i, j] = new Button();
buttons[i, j].Size = new Size(SIZE, SIZE);
buttons[i, j].Location = new Point(x, y);
buttons[i, j].BackColor = Color.White;
pnlGrid.Controls.Add(buttons[i, j]);
x = x + SIZE + offset;
}
x = 0;
y = y + SIZE + offset;
}
}
}
}
Also, the GridPanel class:
namespace GridTest
{
public class GridPanel : Panel
{
public GridPanel()
: base()
{
this.DoubleBuffered = true;
this.ResizeRedraw = false;
}
}
}
If you must add controls at run time, based on some dynamic or changing value, you might want to consider creating an image on the fly, and capturing mouse click events on its picturebox. This would be much quicker and only have one control to draw rather than hundreds. You would lose some button functionality such as the click animation and other automatic properties and events; but you could recreate most of those in the generation of the image.
This is a technique I use to offer users the ability to turn on and off individual devices among a pool of thousands, when the location in a 2-dimensional space matters. If the arrangement of the buttons is unimportant, you might be better offering a list of items in a listview or combobox, or as other answers suggest, a datagridview with button columns.
EDIT:
An example showing how to add a graphic with virtual buttons. Very basic implementation, but hopefully you will get the idea:
First, some initial variables as preferences:
int GraphicWidth = 300;
int GraphicHeight = 100;
int ButtonWidth = 60;
int ButtonHeight = 20;
Font ButtonFont = new Font("Arial", 10F);
Pen ButtonBorderColor = new Pen(Color.Black);
Brush ButtonTextColor = new SolidBrush(Color.Black);
Generating the image:
Bitmap ControlImage = new Bitmap(GraphicWidth, GraphicHeight);
using (Graphics g = Graphics.FromImage(ControlImage))
{
g.Clear(Color.White);
for (int x = 0; x < GraphicWidth; x += ButtonWidth)
for (int y = 0; y < GraphicHeight; y += ButtonHeight)
{
g.DrawRectangle(ButtonBorderColor, x, y, ButtonWidth, ButtonHeight);
string ButtonLabel = ((GraphicWidth / ButtonWidth) * (y / ButtonHeight) + x / ButtonWidth).ToString();
SizeF ButtonLabelSize = g.MeasureString(ButtonLabel, ButtonFont);
g.DrawString(ButtonLabel, ButtonFont, ButtonTextColor, x + (ButtonWidth/2) - (ButtonLabelSize.Width / 2), y + (ButtonHeight/2)-(ButtonLabelSize.Height / 2));
}
}
pictureBox1.Image = ControlImage;
And responding to the Click event of the pictureBox:
// Determine which "button" was clicked
MouseEventArgs em = (MouseEventArgs)e;
Point ClickLocation = new Point(em.X, em.Y);
int ButtonNumber = (GraphicWidth / ButtonWidth) * (ClickLocation.Y / ButtonHeight) + (ClickLocation.X / ButtonWidth);
MessageBox.Show(ButtonNumber.ToString());
I think you won't be able to prevent flickering having 625 buttons (btw, windows) on the panel. If such layout is mandatory for you, try to use the DataGridView, bind it to a fake data source containing the required number of columns and rows and create DataGridViewButtonColumns in it. This should work much more better then your current result ...

Categories

Resources