Click here to Skip to main content
11,642,613 members (71,955 online)
Click here to Skip to main content

Collision - A C# Game, part 1: parallax scrolling

, 16 Apr 2002 95.2K 1K 36
Rate this:
Please Sign up or sign in to vote.
In which I attempt to write a simple game in C#

Sample Image - Collision.jpg

Overview

As part of my ongoing adventures in C#, I decided to write a game, largely because people seem to like them, and I had an Asteroids game already written using C++/DirectX, and I figured that meant I had the program logic and the graphics, I just needed the parts I wanted to learn, such as handling keyboard input, resources, etc.  Someone else beat me to the punch though, so I decided to go for something a little more simple - a sideways scrolling game in which the object is simply to avoid oncoming asteroids. 

Parallax Scrolling

For those who don't remember, parallax scrolling was a method of making things look cool in the days prior to 3D.  Basically it involves scrolling a number of different bitmaps at differing rates, which gives the illusion of 3D, in the same way that two moving objects moving at the same rate would travel at different speeds if they were different distances from you.

Timing

The first step in an action based game is to make it run the same speed regardless of the computer it's running on.  Using C++, I would do this by catching WM_IDLE, and drawing my objects whenever the processor is idle, but moving them based on time elapsed since I last moved them.  In this manner the speed is always the same, and we get the highest frame rate possible for the processor.  A slower machine slows down the frame rate, not the game.

Well, I don't know if C# has an OnIdle message, although I know it's possible to catch the message using some fancy footwork as documented in the Petzold book.  However, for the sake of the exercise, I've decided to use a timer instead.  In C#, we set up a timer like this:

private Timer timer = new Timer();
timer.Tick += new EventHandler(OnTimer);
timer.Enabled = true;
timer.Interval = 1000/60;

The variable is set as a class member and the rest is done on initialisation.  We are defining an event handler for the timer, turning it on and setting it to go off 60 times a second.  A timer will only fire if the system is not busy, so we are not guaranteed that we will get precisely 60 shots a second.  To get a better degree of accuracy, we will use a DateTime object to time our timer.  We create a member DateTime called m_DateTime and set it using DateTime.Now.  Then at the start of our timer function we do this:

TimeSpan ts = DateTime.Now - m_DateTime;

if (ts.Milliseconds > 1000/m_nFPS)
{
	m_DateTime = DateTime.Now;
	m_DateTime.AddMilliseconds(ts.Milliseconds - (1000/m_nFPS));

In other words, if the time that has passed is more than the time between our desired frames per second value, we spring into actoin, do our drawing and also reset the variable to be Now() again, plus any remaining offset.  As we will see later, although I tested this code using some simple drawing, as soon as we scroll the bitmap it is moot, because after turning the timer off, so it goes flat out, I get about 2-3 fps.  C# is not fast enough for action games, or at least it does not provide an way I can see to perform the scrolling on the bitmap quickly.

Resources

The .NET platform has an interesting way of dealing with resources.  Basically a resource is added by selecting Project | Add Existing Item, and then by clicking on that item in the Solution Explorer, it can be changed to a Build Action of 'Embedded Resource, i.e. it becomes part of your .exe.  To load a bitmap resource, I use this line:

m_bmPlanets = new Bitmap(GetType(), "Planets.jpg");

Now my bitmap is loaded from the resources.  Depending on your default namespace, you may need to specify a namespace name, such as Collision in this case, in order to load a resource.  I must admit I spent an hour on this before it started to work, and I'm not sure what I did....

Tiling

My initial strategy was to create a system where the background was constantly changing, and so I've built two resource bitmaps, one that holds star patterns of the same resolution in a row, and one that holds a planet per tile, all tiles again the same size.  Then I built a bitmap that was a tilewidth wider than the screen, and filled it every time the screen had scrolled a tilewidth, drawing new images off screen so they scrolled into view.  I soon discovered it was very slow.  The only way I found to scroll a bitmap without writing an image filter was to make a clone, and copy it back to itself offset by a certain number of pixels.  For speed I now have a repeating system, where I draw both bitmaps twice over, and use the TranslateTransform to move the aspect of the Graphics object before drawing my two bitmaps.  Sadly, it is also very slow, leaving me to conclude that there is no quick way to do what I am trying to do.  The timing code I presented before is turned off in the demo, because it doesn't get used. 

Transparent Bitmaps

The other thing I found is that in order to draw the planets over the stars with transparency, I needed to use the ImageAttributes object.  Bitmaps have a MakeTransparent method, which takes a colour to make transparent, but as I used resources saved as a jpeg, I found this did not work.  JPEG is a lossy compression, which means what you get out is not what you put in.  Specifically, instead of all being hard black, my transparent area was in a range of black, such that I needed to filter 0,0,0, through to 35, 35, 35 to get the masking effect I needed.

ImageAttributes iattrib = new ImageAttributes();
iattrib.SetColorKey(Color.FromArgb(255, 0, 0, 0), 
                    Color.FromArgb(255, 35, 35, 35));
iattrib.SetWrapMode(System.Drawing.Drawing2D.WrapMode.Tile,
                    Color.Black, false);

gr.DrawImage(m_bmPlanetLayer, new Rectangle(0, 0, 640, 320), 
             m_nPlanetPos, 0, 640, 320, GraphicsUnit.Pixel, iattrib);

Where to from here?

Having found that GDI+ is too slow to do what I wanted, I have decided to simplify.  The next installment will have planets which I track on the screen, and a ship I can move in it.  It will not scroll the stars anymore, hopefully providing a speed increase sufficient to prepare for the last installment, where I will be doing a per pixel hit test to know when I've flown into a planet.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Christian Graus
Software Developer (Senior)
Australia Australia
Programming computers ( self taught ) since about 1984 when I bought my first Apple ][. Was working on a GUI library to interface Win32 to Python, and writing graphics filters in my spare time, and then building n-tiered apps using asp, atl and asp.net in my job at Dytech. After 4 years there, I've started working from home, at first for Code Project and now for a vet telemedicine company. I owned part of a company that sells client education software in the vet market, but we sold that and I worked for the owners for five years before leaving to get away from the travel, and spend more time with my family. I now work for a company here in Hobart, doing all sorts of Microsoft based stuff in C++ and C#, with a lot of T-SQL in the mix.

You may also be interested in...

Comments and Discussions

 
QuestionHow to scroll the screen in Y direction? Pin
GuruMaster29-Sep-03 21:59
sussGuruMaster29-Sep-03 21:59 
Generalspeed Pin
Goran Mitrovic19-Apr-02 1:54
memberGoran Mitrovic19-Apr-02 1:54 
GeneralRe: speed Pin
Christian Graus19-Apr-02 2:02
memberChristian Graus19-Apr-02 2:02 
GeneralFound the Idle event Pin
James T. Johnson18-Apr-02 10:42
memberJames T. Johnson18-Apr-02 10:42 
GeneralRe: Found the Idle event Pin
Christian Graus18-Apr-02 11:21
memberChristian Graus18-Apr-02 11:21 
GeneralRe: Found the Idle event Pin
James T. Johnson18-Apr-02 13:00
memberJames T. Johnson18-Apr-02 13:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150731.1 | Last Updated 17 Apr 2002
Article Copyright 2002 by Christian Graus
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid