Page 1 of 1

Creating games with C# - Part 2

#1 SixOfEleven  Icon User is offline

  • using Caffeine;
  • member icon

Reputation: 945
  • View blog
  • Posts: 6,342
  • Joined: 18-October 08

Posted 20 November 2009 - 08:12 PM

This is the much anticipated second tutorial on creating games with C#. Sorry it has been quite some time but I have had other projects that required my attention. I will not be using the old project, I will be creating a new project for this. I used Microsoft Visual C# 2008 Express Edition for this tutorial. I have not done anything that requires the .NET framework 3.5, other than the using statements for LINQ. If you are using an older version of C# you can simply remove those using statements.

To get started you will want to create a new Windows forms application. You can call it whatever you would like, I called mine CSharpGameProgramming. If you call yours something different you will have to adjust the namespace for the code.

I receive a comment on the first one on why I was using DLLImport to get the TickCount when I could have used Environment.TickCount. The reason was I was going to update this to use a timer with better accuracy. I was going to use DLLImport to import the high resolution timer. Instead I will use the Stopwatch class for the timer. I again will be making a class for the timer. To your project add a class called HiResTimer. I always like to give code to be read and then explain why I did what I did. This is the code for class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace CSharpGameProgramming
{
    class HiResTimer
    {
        Stopwatch stopwatch;

        public HiResTimer()
        {
            stopwatch = new Stopwatch();
            stopwatch.Reset();
        }

        public long ElapsedMilliseconds
        {
            get { return stopwatch.ElapsedMilliseconds; }
        }

        public void Start()
        {
            if (!stopwatch.IsRunning)
            {
                stopwatch.Reset();
                stopwatch.Start();
            }
        }

        public void Stop()
        {
            stopwatch.Stop();
        }
    }
}



The Stopwatch class is in the System.Diagonstics namespace so there is a using statement for that namespace. The only field in this class is an instance of the Stopwatch class called stopwatch. In the constructor I create a new instance of the Stopwatch class and call the Reset method of the Stopwatch class. There is a get only property of type long which returns the number of milliseconds that have elapsed since the timer was last reset. The first method is the Start method. What that method does is check to make sure that the stopwatch is not running. If it is already running resetting it and starting it over might not be a good idea. If it is not running I call the Reset method of the Stopwatch class and then the Start method of the Stopwatch class. The second method is the Stop method. The Stop method just calls the Stop method of the Stopwatch class.

What I will do next is modify the Program.cs file. Like in the last tutorial I will be using a game loop to control the flow of the game. In this version though I will be making a slight modification so that when the program exits all items of the form that can be disposed will automatically be disposed of. Change the main method of the Program.cs file to the following.

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    using (Form1 form1 = new Form1())
    {
        form1.Show();
        form1.GameLoop();
    }
}



By using the using statement for creating the form when the form is closed everything in the form that can be disposed of will be. Inside the using statement you need to make sure that the form is visible. You do this by calling the Show method. Next I call the GameLoop method which I will show you in a moment.

Before I get to the GameLoop method there are a number of fields that I want to add to the code of Form1. I will be using these fields in the game, which unlike my other tutorial, will do a little logic and a little rendering so you can see that the game is actually doing something. What you will want to do is add the following fields to the code of Form1.

HiResTimer timer = new HiResTimer();

long startTime;
long interval = (long)TimeSpan.FromSeconds(1/30).TotalMilliseconds;

Graphics graphics;
Graphics imageGraphics;

Image backBuffer;
Image blackScreen;

int clientWidth;
int clientHeight;

Rectangle image = new Rectangle(0, 0, 40, 50);
Point direction = new Point(1, 2);



I will need a timer to control the speed at which the game runs so there is a HiResTimer field. The next field is of type long and it will be used to determine the millisecond at which each game loop starts. The next field is also of type long. I use TimeSpan to set the frame rate at which the game will run. I use the FromSeconds method and pass in 1 over 30 and get the result in milliseconds. What I am aiming for here is a frame rate of about 30 frames per second. Next there are two fields of type Graphics which are used for drawing with GDI+. The first one is for actually drawing to the form. The second one is for drawing to a back buffer. To prevent flickering it is best to draw to a back buffer and then copy the back buffer to the form. The second one is for drawing to the form itself. I also have an Image field for the back buffer. I will frequently need to know the width and height of the client area so there are two fields to hold them. The next two fields will be used to render and preform game logic.

There is one more thing I want to do before I get to the GameLoop method. I want to initialize a few things in the constructor for Form1.

public Form1()
{
	InitializeComponent();

	this.DoubleBuffered = true;
	this.MaximizeBox = false;
	this.FormBorderStyle = FormBorderStyle.FixedSingle;
	this.ClientSize = new Size(300, 300);

	clientWidth = this.ClientRectangle.Width;
	clientHeight = this.ClientRectangle.Height;

	backBuffer = (Image)new Bitmap(clientWidth, clientHeight);

	graphics = this.CreateGraphics();
	imageGraphics = Graphics.FromImage(backBuffer);
}



You should do things with the form after the call to InitializeComponent because until that is called nothing truly exists on the form. The first thing I do is set the DoubleBuffered property of the form to true to stop flickering. GDI+ is slow, really slow, and it slows down even more in larger areas. To help things along I set the form so it can't be maximized or resized. I then set the client size to 300 pixels by 300 pixels. I set the two fields for the width and height of the client area. Next I create the back buffer image by creating a new Bitmap and casting the result to an Image. The Graphics class does not have a constructor. You need to create them by other means. Controls have the ability to create instances of the Graphics class using the CreateGraphics method so I do that with the form. The Graphics class also has the ability to create an instance using the FromImage method and I do that for the back buffer.

Now it is time for the GameLoop method. The method is pretty much the same as in the last tutorial. This is the code.

public void GameLoop()
{
    timer.Start();

    while (this.Created)
    {
        startTime = timer.ElapsedMilliseconds;
        GameLogic();
        RenderScene();
        Application.DoEvents();
        while (timer.ElapsedMilliseconds - startTime < interval) ;
    }

}



What this does is first start the timer. It then loops while the form is open. Each time through the loop it sets the startTime field to be the number of elapsed milliseconds since the timer was started. It then calls a method called GameLogic which will control objects in the game. It is important to update objects in a game before rendering them. It then calls a method RenderScene that will draw the game objects. It then tells the form to process any events that are in the queue of events. The last part is what keeps the game running at a relatively constant speed. It just loops until the elapsed milliseconds in the timer minus the starting value is less than the interval or frame rate.

Like I said the GameLogic method is what controls the objects in the game. In this simple game there is only one object, a rectangle that floats around the window. This is the code for the GameLogic method.

private void GameLogic()
{
    image.X += direction.X;
    image.Y += direction.Y;

    if (image.X < 0)
    {
        image.X = 0;
        direction.X *= -1;
    }

    if (image.Y < 0)
    {
        image.Y = 0;
        direction.Y *= -1;
    }

    if (image.X + image.Width > clientWidth)
    {
        image.X = clientWidth - image.Width;
        direction.X *= -1;
    }

    if (image.Y + image.Height > clientHeight)
    {
        image.Y = clientHeight - image.Height;
        direction.Y *= -1;
    }

}



I use the direction field to handle moving the rectangle. I set the initial values to (1, 2). That means that X value is 1 and the Y value is 2. When you are working with graphics in 2D the X coordinate increases as move across the screen. The Y coordinate increases as you move down the screen. So at the start the rectangle will be moving right 1 pixel at a time and 2 pixels down at a time. If this was to continue the rectangle would eventually disappear. So I will do some bounds checks to see when the rectangle reaches the edges of the window. Checking for the left hand side and top of the form are the easiest cases. You know the X and Y coordinates of the rectangle because they are properties of the rectangle. To see if the rectangle will go off the left side of the screen you check to see if the X property is less than zero. If it is you set it to zero to keep it from going off the screen. Just staying in the same position is boring though. So what I do is multiply direction.X by -1 which will reverse the direction. To keep it from going off the top of the screen what you do I check to see if the Y property is less than 0. If it is set it 0 and then multiply direction.Y to reverse the direction the square is travelling. Checking for the right hand side is a little more difficult what you do is check to see if the X property plus the width of the rectangle is greater than the width of the client area. If it is set it the the X property to the width of the client area minus the width of the rectangle and again reverse the direction. Finally for the bottom of the window you check to see if the Y property of the rectangle plus the height is greater than the height of the client area. If it is set the Y property to the height of the client are minus the height of the rectangle and reverse the direction.

That just leaves the RenderScene method. This is where I actually draw something. This is the code for the RenderScene method.

private void RenderScene()
{
    imageGraphics.FillRectangle(new SolidBrush(Color.Black), 
        this.ClientRectangle);
    imageGraphics.FillRectangle(new SolidBrush(Color.Blue), image);

    this.BackgroundImage = backBuffer;
    this.Invalidate();
}



What the RenderScene method does is first fill the back buffer with black using the FillRectangle method of the Graphics class. The overload that I used requires a Brush and a Rectangle. For the brush I created a solid black brush. For the rectangle I use the ClientRectangle property of the form. I then draw a solid blue rectangle using a solid blue brush and the rectangle for the image. I then set the BackGroundImage property of the form to the backBuffer. I then call Invalidate method to tell the form that it needs be be redrawn.

Is This A Good Question/Topic? 0
  • +

Replies To: Creating games with C# - Part 2

#2 lesPaul456  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 173
  • View blog
  • Posts: 729
  • Joined: 16-April 09

Posted 22 November 2009 - 05:29 PM

Another great tutorial. :^:
Was This Post Helpful? 0
  • +
  • -

#3 Adkins  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 66
  • View blog
  • Posts: 560
  • Joined: 27-October 09

Posted 15 December 2009 - 10:33 AM

So I am assuming we can expect part 3 tomorrow? I kid. Great tut though.
Was This Post Helpful? 0
  • +
  • -

#4 frostyraver  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 13
  • View blog
  • Posts: 195
  • Joined: 21-April 09

Posted 16 March 2010 - 12:49 AM

Fantastic tutorial, this is what I have been looking for, You can create buttons and have proper interaction.

If anyone is wondering the performance of this, I have attached an edited file, where you can add up to 1 million boxes, the FPS counter is broken.

It's a small game where you have 3 armies, and you bet on which colour army will be left, once a man is injured he leaves the battle field.

I always wanted to design something like this but never could get XNA style to work int he form, You'll see it starts to frame drop at 20000 boxes.

20 THOUSAND, the performance of this thing is awesome, if you took out the for-loops in my program and fixed the collisions then you'd have even better performance.

This is on the road to something great, i'm working on adding pictures to this and i'll probably make a PONG style game.

Get it here.

EDIT:
Here's the collision code I'm working on
        private void GameLogic()
        {

            for (int x = 0; x < currentBoxes; x++)
            {

                boxNumber[x].X += direction[x].X + rng.Next(minSpeed, maxSpeed);
                boxNumber[x].Y += direction[x].Y + rng.Next(minSpeed, maxSpeed);

                // Collision detection, scan if x hits y
                for (int y = 0; y < currentBoxes; y++)
                {
                    // Scans Y and X
                    if (boxNumber[x].IntersectsWith(boxNumber[y]) && showBox[x] == true && showBox[y] == true)// == direction[y].Y && direction[x].X == direction[y].X && showBox[x] == true)//direction[x].Y == direction[y].Y && direction[x].X == direction[y].X
                    {
                                               if (showBox[x] == true && showBox[y] == true)
                        {
                            boxCounter--;
                            showBox[x] = false;
                            showBox[y] = false;
                        }
                        if (boxColour[x].Color == Color.Blue && showBox[x] == true || boxColour[y].Color == Color.Blue && showBox[y] == true)
                        {
                            blueCount--;
                            showBox[x] = false;
                            showBox[y] = false;
                        }
                        if (boxColour[x].Color == Color.White && showBox[x] == true || boxColour[y].Color == Color.White && showBox[y] == true)
                        {
                            whiteCount--;
                            showBox[x] = false;
                            showBox[y] = false;
                        }
                        if (boxColour[x].Color == Color.Red && showBox[x] == true || boxColour[y].Color == Color.Red && showBox[y] == true)
                        {
                            redCount--;
                            showBox[x] = false;
                            showBox[y] = false;
                        }
                    }

                }

                WallCheck(x);
                winCheck();
            }

        }



But when fired up it deletes the scene! kinda nukes everything.

EDIT 2:
 private void GameLogic()
        {

            for (int x = 0; x < currentBoxes; x++)
            {

                boxNumber[x].X += direction[x].X + rng.Next(minSpeed, maxSpeed);
                boxNumber[x].Y += direction[x].Y + rng.Next(minSpeed, maxSpeed);
                if (mnuColDet.Checked == true)
                {
                    // Collision detection, scan if x hits y
                    for (int y = 0; y < currentBoxes; y++)
                    {
                        // Scans Y and X
                        if (y == x)
                        {
                            y++;
                        }

                        if (boxNumber[x].IntersectsWith(boxNumber[y]) && //boxNumber[x].Location.Equals(boxNumber[y].Location + boxNumber[y].Size)  &&
                            showBox[x] == true &&
                            showBox[y] == true)// == direction[y].Y && direction[x].X == direction[y].X && showBox[x] == true)//direction[x].Y == direction[y].Y && direction[x].X == direction[y].X
                        {

                            // If x and y are the same colour then do not run this code
                            if (boxColour[x].Color == Color.Blue && boxColour[y].Color == Color.Blue && showBox[y] == true)
                            {
                            }
                            else if (boxColour[x].Color == Color.White && showBox[x] == true && boxColour[y].Color == Color.White && showBox[y] == true)
                            {
                            }
                            else if (boxColour[x].Color == Color.Red && showBox[x] == true && boxColour[y].Color == Color.Red && showBox[y] == true)
                            {
                            }
                            else
                            {
                                // Blue
                                if (boxColour[x].Color == Color.Blue && showBox[x] == true)
                                {
                                    blueCount -= 1;
                                    showBox[x] = false;
                                }
                                if (boxColour[y].Color == Color.Blue && showBox[y] == true)
                                {
                                    blueCount -= 1;
                                    showBox[y] = false;
                                }
                                // White
                                if (boxColour[x].Color == Color.White && showBox[x] == true)
                                {
                                    whiteCount -= 1;
                                    showBox[x] = false;
                                }
                                if (boxColour[y].Color == Color.White && showBox[y] == true)
                                {
                                    whiteCount -= 1;
                                    showBox[y] = false;
                                }
                                // Red
                                if (boxColour[x].Color == Color.Red && showBox[x] == true)
                                {
                                    redCount -= 1;
                                    showBox[x] = false;
                                }
                                if (boxColour[y].Color == Color.Red && showBox[y] == true)
                                {
                                    redCount -= 1;
                                    showBox[y] = false;
                                }
                            }
                        }
                    }
                }
                WallCheck(x);
                winCheck();
            }

        }



NEVER MIND! I worked it out, the x was referring to y which was itself so I adding it to +1 to jump over itself and check the next dude :).


EDIT 3:

Code block 2 now has FULL collision detection with the size of the boxes, next is adding HP to them, if I could some how write onto the box i'd put how much HP they have each.

This post has been edited by frostyraver: 16 March 2010 - 05:46 AM

Was This Post Helpful? 0
  • +
  • -

#5 SixOfEleven  Icon User is offline

  • using Caffeine;
  • member icon

Reputation: 945
  • View blog
  • Posts: 6,342
  • Joined: 18-October 08

Posted 16 April 2010 - 08:24 AM

I should mention, where you set the interval for the game. You should do the following instead.

long interval = (long)TimeSpan.FromSeconds(1.0/30).TotalMilliseconds;



The reason is that FromSeconds receives a double but just doing 1/30 will preform integer division which will work out to 0 and not 30 fps. By using 1.0 or casting 1 to a float or double will give a fractional value for the seconds and not 0.

The next part will be coming in the not too distant future for those who are interested.
Was This Post Helpful? 0
  • +
  • -

#6 Guest_Daniel*


Reputation:

Posted 02 June 2010 - 01:05 AM

when i put it inside a panel i get flicker..
any way to fix this?

when i try to do
gamePanel.DoubleBuffered = true;


i get cannot access protected member blah blah blah

any help please?
by the way this is my first c# project so i really dont know much about it.
Was This Post Helpful? 0

#7 Guest_Daniel*


Reputation:

Posted 02 June 2010 - 01:24 AM

View PostDaniel, on 02 June 2010 - 12:05 AM, said:

when i put it inside a panel i get flicker..
any way to fix this?

when i try to do
gamePanel.DoubleBuffered = true;


i get cannot access protected member blah blah blah

any help please?
by the way this is my first c# project so i really dont know much about it.


okay so i made a subclass of panel that has doublebuffered true, and im still getting a lot of flickering.. i have no idea what the problem is.
the panel is 600x600, is this too big to work or somthing?
Was This Post Helpful? 0

#8 Guest_Daniel*


Reputation:

Posted 02 June 2010 - 01:30 AM

nevermind got it to work :D
typo lol
Was This Post Helpful? 0

#9 Guest_vil3*


Reputation:

Posted 04 January 2011 - 02:30 AM

i really need to say thx for this =) really easy to follow your code and it gives great understanding... are building an form application and by implanting this the "game" part now works fine...atleast in theory (i still need to implement the most of it). thx agian!
Was This Post Helpful? 0

#10 OhScee  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 12-May 12

Posted 12 May 2012 - 10:49 AM

Great tutorial~ :3
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1