Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

2D Game Primer (Visual C#)

 

  This article is the first part of the Upgrade Your Game series of tutorials. It walks you through the basics of how 2D games work using Visual C#
The Z-Buffer

Difficulty: Easy
Time Required: 1-3 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions,

 

 

Visual C# Upgrade Your Game Series

 

Introduction

Welcome to the Upgrade Your Game series of tutorials. There are four tutorials, each covering how to write a simple computer game using Visual Studio Express products. Though the games are simple, each one teaches some game development techniques and builds on the last to improve your skills. The skills can be applied to more complex games including 3D games using Microsoft DirectX.

When you program games, you have to use math whether you want to or not. The simpler the game, the less math there is to learn, and 2D games use simpler math — but you cannot avoid it altogether. Today's top-selling 3D games are full of 3D vector math and complex simulations of physics. Fortunately, you do not need to know all of that for the simple games presented in this series.

This 2D primer covers some of the fundamentals of simple math needed for the games in this series.

Note: This documentation assumes that you have a basic knowledge of programming concepts and the Visual C# environment. You can learn more about these topics in the product documentation by clicking Help on the menu bar, and then clicking Contents, Index, or Search. You can also access Help by positioning the mouse cursor on language keywords or user interface elements such as windows or dialog boxes, and pressing F1.

2D Game Development Concepts

The Game Loop

Window Forms and ASP applications are often called "event-driven" applications — they sit doing nothing for most of the time waiting for an external stimulus, such as a button press from an end user or a Web service request from another application. This works really well for that kind of application.

Games, on the other hand, are generally not sitting doing nothing all the time. Even if the end user is not pressing a key or a button on the gamepad, the game must animate characters, perform AI functions, and play sounds and many other things depending on the game. You've probably done some of these background type tasks in Windows Forms applications using timer controls or background threads. But in general these don't scale to the computer game scenario, where there are multiple animations and multiple background tasks all happening at very different intervals.

The solution that most games use (and by "most games" I mean everything right up to the top-selling Xbox 360 titles) is usually called the "game loop." In pseudo code it looks something like this:

    While (game is running)
        Check for input
        Update all objects in the game 
        Draw all objects in the game

This might explain one reason why games almost always use 100% of your CPU even if they don't appear to be doing much. The faster your computer is, the faster this loop will run and the smoother the animation and the synchronization will be.

There is one slight problem with running a tight loop like that, at least in Windows. If you don't allow Windows to check for and process Windows messages and events, your application will be marked as "Not responding" by the operating system. You might have seen this in your applications if you have a loop that runs for a long time. You might also have solved it by putting a call to DoEvents into the loop, which allows Windows to process the events. This can work reasonably well if you know the loop will end eventually, but in this case the loop is the entire game and you need a better way.

The method I have chosen to use for these games is NOT the most efficient (for a full discussion on this under Managed DirectX see the MDX Game Loop FAQ), but it is the simplest, and none of the games covered here needs the more complex method.

private void TinyTennis_Paint(object sender, PaintEventArgs e)
{
    //Work out how long since we were last here
    in seconds
    double gameTime = _timer.ElapsedMilliseconds / 1000.0;
    double elapsedTime = gameTime - _lastTime;
    _lastTime = gameTime;
    _frameCounter++;
 
    //Perform any animation

    //Draw the game objects
 
   //Force the next Paint()
    this.Invalidate();
}
 
Sprites
Sprites are two-dimensional images that appear in a video game. Sometimes they are static images, but in general they are the building blocks for animation. They either move around the screen or change the image at regular intervals, giving a "flip book" kind of animation; or they do both, giving you characters like Mario and Donkey Kong that can move around the screen running and jumping.   Positioning a Sprite
Just like Windows Forms controls, sprites are placed at an x,y position on the form. 0,0 is the top left and ClientSize.Width, ClientSize.Height is the bottom right. Just like Windows Forms controls, sprites have a height and width too.

However, unlike Windows Forms controls, sprites are primarily designed to move and animate, often hundreds of times per second, to get the smoothest animation possible. So while a sprite can only be drawn at an integer x,y position, its ACTUAL position at a subsecond point in time could be 1.45, 12.67. It's important to remember this position so that the animation doesn't get choppy. A sprite position is stored using a floating-point Point structure PointF rather than the integer-based Point structure like Windows Forms controls. This means you can store the subpixel accuracy and then just convert to integer screen positions when you draw them. Vectors
The more game programming you do, the more you will come across the term "vector." Though I've avoided the term in this series for the sake of simplicity, it's worth noting that the concepts are used when the positions of the sprites are updated for animation.

Simply put, a vector is just an offset. So a vector of (4,2) means that from some start point you move 4 units in the x-axis and 2 in the y-axis.

You will see positions referred to as vectors. In this case, a position of (4,2) means an offset of (4,2) from the origin.

Vectors can be added together, so (4,2) + (2,4) gives you a total offset of (6,6).

Vectors can have negative components, so (-2,1) means move 2 units to the left on the x-axis and 1 unit down on the y-axis.

All of these concepts are used when moving a sprite around the screen.   Moving a Sprite
Sprites move with a certain velocity. Velocity is different from speed because velocity also contains information about the direction. For example, you could tell me you are driving at 60 mph, and I would guess that in one hour you would be 60 miles from the start point. But if you were driving round in a circle, that would not be the case. If I told you my velocity was 60 mph due east, then I would know for sure where you would end up.

Since your position is an x, y value you can store your velocity as another two-component vector (s, t): s is how fast you want to move on the x-axis, and t is how fast you want to move on the y-axis. If you add them together: (x,y) + (s,t) = (x+s, y+t), you get the new position of the sprite.

Example: Sprite is at (4,10) and has a speed of (1,0). (1,0) means the sprite will move right in the x direction only. (4,10) + (1,0) = (5,10). If you repeat this addition every second, then the sprite will move 1 pixel per second across the screen. You can see that any direction and speed can be represented just by changing the s, t values in that vector.

Time-Based Animation

Imagine a sprite that starts at x=0 and is moving to the right with a velocity of +10. If you walk through the game loop above, the sprite would have +10 added to its position each time the sprite was updated and would move from x=0 to x={some bigger number} where {some bigger number} is 10 times the number of times the loop has been run. But the question is, exactly how fast would it move? Well, it depends on how fast the loop runs, which depends on how fast your computer is running. This would mean that on your top-of-the-line gaming rig the sprite could cross the screen in half a second, but on your parents' four-year-old "we only use it for e-mail" computer it could take two seconds. Obviously this isn't something that is desirable in a computer game.

To fix this you need to think about what velocity really is. What does that +10 mean? Velocity is a measure of distance over a period of time, so you cannot think of the +1 as just a distance; it's really +1 pixel in some time that you need to define. So each time round the loop, you just need to know how long it's been since the last update and scale the movement by that time.

A sprite that has a velocity of +10 actually means you move +10 pixels per second. So if it's been half a second since the last frame, multiply that by the velocity and move 5 pixels. In reality the time between frames is fractions of a second. So on your fast gaming rig, where the loop runs very fast, the sprite will move with subpixel accuracy for some very smooth animation. On your parents' computer, where the loop only runs 5 times per second, the sprite will move 2 pixels each frame, but after 10 seconds the sprite will be in exactly the same place as it is on your computer.

The code will look something like this (notice that for accuracy elapsedTime is a double and therefore needs to be cast down to a float to match the PointF structure):

    //Move the sprite based on the velocity
    Location.X += Velocity.X * (float)elapsedTime;
    Location.Y += Velocity.Y * (float)elapsedTime;

The loop timings I mentioned here are very exaggerated — on most computers the loop will be running hundreds or thousands of times per second, meaning that your animations will be very smooth and accurate.

Sprite bounds

There are two ways to represent the rectangle containing the sprite:

  1. X, Y, Height, Width
  2. X, Y of top left corner and X, Y of bottom right corner

Whilst both of these are valid, you must usually find the first one in use. It's easier to update just one corner as the sprite moves around and calculate the other corner when it's needed. For tasks like collisions- and bounds-checking you need to calculate the bottom-right corner, so you will see X+Width and Y+Height as a very commonly used formula in games.

 
Simple Collision Detection
Collisions are a vital part of almost any game — they restrict where an object can move, whether an object has taken damage, and so forth.

Simple collisions can be described in terms of the positions and size of the sprites and the regions they need to collide with. Since everything is rectangular in this case, there is no need for any more complex collisions that need to be determined at the pixel level. All the collisions can be checked for by looking to see if the rectangles overlap or if the coordinates are outside of a certain range. Checking for rectangle overlap is quite simple.

private bool collide()
{
  return ! ( sprite1.Location.X > sprite2.Location.X + sprite2.Size.Width
    || sprite1.Location.X + sprite1.Size.Width  < sprite2.Location.X
    || sprite1.Location.Y > sprite2.Location.Y + sprite2.Size.Height
    || sprite1.Location.Y + sprite1.Size.Height < sprite2.Location.Y);
}

Follow the Discussion

  • PixelbathPixelbath

    kenren: Just like you'd do collision detection, only you'd check for collision on the MouseUp event, rather than every iteration.

    You're welcome in post mortem.

  • Website Scripts » Coding4Fun : Upgrade Your Game: Crusader (C#)Website Scripts » Coding4Fun : Upgrade Your Game: Crusader (C#)

    PingBack from http://websitescripts.247blogging.info/coding4fun-upgrade-your-game-crusader-c/

  • Software Information » Coding4Fun : 2D Game Primer (Visual C#)Software Information » Coding4Fun : 2D Game Primer (Visual C#)

    PingBack from http://softwareinformation.247blogging.info/coding4fun-2d-game-primer-visual-c/

  • Adam DAdam D

    This series of posts has been brilliant. As a beginner to games programming I've had many an 'ahha!' moment reading them. Well done and thanks.

  • LangfordLangford

    The paragraph explaining how you move a sprite by its velocity, multiplied by the time elapsed since the last 'frame': Best explanation I've heard on that subject, in any OOP language lol.

  • GuillermoGuillermo

    It was very educational! can you send me some c++ code for games, thanks

  • Clint RutkasClint I'm a "developer"

    The wonders of having Coding4Fun moved from one medium to another.

    On it, thanks for the heads up.

  • jimi hendrixjimi hendrix

    why are the links to the tutorial on top not working???

  • Ariful Ambia NomanAriful Ambia Noman

    very usefully article. I like it.

  • LoendalLoendal

    Any progress on when we might see the diagrams and images again?  This primer was very thourough and I'd love to put it to practice..

  • Clint RutkasClint I'm a "developer"

    Welcome to the Upgrade Your Game series of tutorials. In this article, we will develop a role-playing

  • Clint RutkasClint I'm a "developer"

    fixed, thanks for telling me about the links.  They broke when we moved the site over to the blogs.msdn.com platform

  • Coding4Fun : 2D Game Primer (Visual C#)Coding4Fun : 2D Game Primer (Visual C#)

    PingBack from http://blogs.msdn.com/coding4fun/archive/2007/02/20/1727608.aspx

  • GregGreg

    Doesn't this cause alot of paint events to occur?  Each time the paint event fires it schedules another one by calling Invalidate.  Since the sprite movement is timer based, that occurs infrequently, so most of the time it's refreshing the screen for no reason.

  • LexLex

    it can be solved, by adding a variable which indicates which objects needed to be painted again

  • SomeoneSomeone

    Hey, I created my own shooter Sprite for the SpaceBlitz starter kit, where could I find information on how to add my image in place of the default spaceship?

  • Clint RutkasClint I'm a "developer"

    @Somone, You can also do it without replacing/deleting, or otherwise manipulating the image.

    Go to the project and look for the .png that represents the one you want to change. For example if it is “ship.png” then that’s the one you want. Don’t do anything to it.

    Then take your new one – hopefully it is the same size or it could cause some minor issues if the size was used in calculations – and add it to the Content project in the same place as ship.png. Make sure to do add existing item in VS, don’t just copy it, or if you do make sure to include in project.

    Then, do a project wide search for “ship.png” or whatever the old one is called. Find the line where it says, something = Content.Load<Texture2D>(“some path\ship”); (note there is NO png file extension in the code!!)

    Comment that line out, copy and paste it below, and replace “ship” with the filename of your new one (no extension)

  • Clint RutkasClint I'm a "developer"

    @Someone, That is something you should be able to figure out on your own Smiley

  • Clint RutkasClint I'm a "developer"

    @Someone, That is something you should be able to figure out on your own Smiley

  • SomeoneSomeone

    Thank you so MUCH!!! Now, if you could show me the best way to icrease the number of bombs you ( and the Aliens ) can drop without having to wait for them to be removed from the screen, you would be a lifesaver  

  • SomeoneSomeone

    Ok, I've spent the last week working on my problem. I'm working on a system that limits the number of missiles in the game using similar methods to the ones used to limit the number of lives, but the  problem is that the missiles are not being removed from the screen until I another missile is fired (although the missile is no longer visible) this results in the aliens being destroyed one after another as they run into the invisible missile. Another problem I am having is that no matter how many missiles are on the screen, only the last one fired does any damaqe to the aliens ( In other word all except the last missile pass through lines and lines of aliens without doing a lick of damage ). Thanks for all the help.

  • Clint RutkasClint I'm a "developer"

    @Someone why don't you have a missle counter.  You add one when you fire it, subtract when one hits something or is off the screen.

    in your firing function, say "if (_currentMisslesOnScreen < MaxAmountOfMisslesOnScreen) { fireMissle(); _currentMisslesOnScreen++;}

  • jh1507jh1507

    Wow,

    I`m a kid and i sorta get it!

    (I program in C#)

    It makes sence about the volocity

    x,y cordinites etc.

    Good Tutorial!

  • jh1507jh1507

    But...

    I sorta got throw on pong when i look at the code to me it`s very confussing! can you please recomend any other free C# 2D Tutorials? Please.

    Anyway i found the 2D game primer a good intro...

  • SomeoneSomeone

    In reply to your last message I first want to make my situation understood. First, I created a bomb counter system similar to the one you discribed ( similar because I don't know what all that fireMissile() method would intail) Secondly, no matter how many bombs are on the screen at one time, ONLY the last bomb ( Ithe order they were dropped) does any damage to either the bases or the baseship. Finally, I am tring to make the aliens drop two bombs simultaneously at all times ,but what is currently happening is that two bombs are dropped roughly at the same time ( I have the random factor set to zero) the first of the two making no impression on anything it runs into ( speaking again of the last one fired being the only one that does any damage) ,and after both have disappeared it begins dropping bombs only one at a time. Thank you for all the time you have spent answering my questions; This has been wonderful resource.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.