BLOG.CSHARPHELPER.COM: Make a picture tiles jigsaw game in C#
Make a picture tiles jigsaw game in C#
This example is a game sort of like a jigsaw puzzle. You load a picture, which is broken into tiles. You need to put the tiles in their proper positions to rebuild the image.
The program draws a grid on the background showing where the tiles belong. It outlines unpositioned tiles in black. Once you place a tile in its correct position, it is locked in that position, moved to the back, and outlined in white.
The program is simpler than you might imagine. It uses a Piece class to represent the current and "home" location of each piece. The program keeps the pieces in a List<Piece> named Pieces.
To draw the board, the program draws the background grid and then just loops through the pieces drawing them.
Unfortunately drawing images seems to be relatively slow so the program didn't redraw fast enough when the user was moving a piece and there were a large number of pieces. Each time the mouse moved, the program needed to redraw every piece and it was taking too long.
To avoid this problem, the new design uses a background image. When you start moving a piece, the program makes an image of the background without the piece that you are moving. Then instead of redrawing every piece, it only needs to redisplay this background and then draw the piece you are moving in its current position.
The following code shows the MakeBackground piece that draws the background image.
// Make the background image without MovingPiece. private void MakeBackground() { using (Graphics gr = Graphics.FromImage(Background)) { gr.Clear(picPuzzle.BackColor);
// Draw a grid on the background. using (Pen thick_pen = new Pen(Color.DarkGray, 4)) { for (int y = 0; y <= FullPicture.Height; y += RowHgt) { gr.DrawLine(thick_pen, 0, y, FullPicture.Width, y); } gr.DrawLine(thick_pen, 0, FullPicture.Height, FullPicture.Width, FullPicture.Height);
for (int x = 0; x <= FullPicture.Width; x += ColWid) { gr.DrawLine(thick_pen, x, 0, x, FullPicture.Height); } gr.DrawLine(thick_pen, FullPicture.Width, 0, FullPicture.Width, FullPicture.Height); }
// Draw the pieces. using (Pen white_pen = new Pen(Color.White, 3)) { using (Pen black_pen = new Pen(Color.Black, 3)) { foreach (Piece piece in Pieces) { // Don't draw the piece we are moving. if (piece != MovingPiece) { gr.DrawImage(FullPicture, piece.CurrentLocation, piece.HomeLocation, GraphicsUnit.Pixel); if (!GameOver) { if (piece.IsHome()) { // Draw locked pieces with a white border. gr.DrawRectangle(white_pen, piece.CurrentLocation); } else { // Draw locked pieces with a black border. gr.DrawRectangle(black_pen, piece.CurrentLocation); } } } } } } }
picPuzzle.Visible = true; picPuzzle.Refresh(); }
The code clears the Bitmap Background and draws a grid on it. For each piece, it copies part of the original image stored in FullPicture from the rectangle given by the piece's HomeLocation property to the rectangle given by its CurrentLocation property. Depending on whether the game is over or the piece is locked in its final position, the program then outlines the piece in white or black.
The following code shows the DrawBoard method that redraws the board after the Background is ready.
// Draw the board. private void DrawBoard() { using (Graphics gr = Graphics.FromImage(Board)) { // Restore the background. gr.DrawImage(Background, 0, 0, Background.Width, Background.Height);
using (Pen blue_pen = new Pen(Color.Blue, 4)) { gr.DrawRectangle(blue_pen, MovingPiece.CurrentLocation); } } }
picPuzzle.Visible = true; picPuzzle.Refresh(); }
This code draws the Background image onto the Board Bitmap. The picPuzzle PictureBox displays Board as its image.
Next the code draws the piece that the user is currently moving. It finishes by refreshing picBoard.
See the code for the details.
There's still room for many improvements.
Pieces with jigsaw-style edges rather than rectangles. That would let you fit pieces by shape and find the picture's edges.
Rotating pieces. That would be very advanced.
Pieces that stick together. When you join two pieces that belong together, it would be nice if they stuck so you could move them as a unit.
Faster building of Background image.
I have an idea about the last one. Instead of rebuilding the whole Background image, you could find the piece under the mouse and just redraw any other pieces that overlap with that one. I think that would be much faster. I'll leave it to you (email me if you try it) or when I have more time.
Comments