Simple 2D game making with WxWidgets - a narrative in parts

This forum can be used to talk about general design strategies, new ideas and questions in general related to wxWidgets. If you feel your questions doesn't fit anywhere, put it here.
Post Reply
Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Simple 2D game making with WxWidgets - a narrative in parts

Post by Moschops » Wed Feb 04, 2009 2:57 pm

In learning wxWidgets, I set myself the task of a very simple cross-platform 2D point and click game. It's actually starting to take shape now (and it's actually a one-step build in WinXP and the two flavours of linux I've tested, with the appropriate separate config files but the same source code, so I'm quite pleased with that), and there are aspects of it that I could do with discussing. Sound is currently done with openAl.

I don't want to pollute a forum if I don't have to, so before I start discussing aspects here, can anyone suggest a more appropriate place?

If this is the most appropriate place we can think of, I'll just start here.

As an example, the next thing I'm thinking about doing is some kind of cutscene; there are two ways I've thought of to do this. The first is to have the entire cutscene already in existence (as a sequence of images) which will be shown in sequence, with a few maybe worked on whilst on screen. The second method would be to have the user controls locked out and take advantage of already existing code to move sprites, play sound, and so forth.
Last edited by Moschops on Thu Feb 05, 2009 6:33 pm, edited 2 times in total.

Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Post by Moschops » Thu Feb 05, 2009 10:57 am

Well, in the absence of complaints, I'll just start mumbling to myself. If anyone ever wants to see any of the code, I'm happy to send it to them.

Having solved the issue of displaying an image, I set about creating what would be the first room. I set an image to be the background and added a sound that would be played on any left mouse click within the room; this enabled me to test the sound and the mouse event handling code. I'm using openAl with the (deprecated) ALUT framework - for my simple needs, it's currently very simple with no moving sounds or the like, but openAl gives me the chance to get more sophisticated later on.

The first aesthetic issue I could see was that the background was static - non-static backgrounds add a great deal to a game and I set about fixing this. The first room contains a few candles, and candle flicker is unobtrusive movement in the background that can add a lot to the atmosphere.

I'm developing on Linux (OpenSUSE 11.1 on a desktop machine and Linpus on my belovéd Aspire One) and I'm compiling on WinXP as well, as I want this to be a cross platform effort.

As such, I fired up Gimp. Candle flicker is very easy once you've got a candle image; by copying the flame of the candle and then mirroring that flame about the vertical axis, you can paste it back into the original image to create an image identical but with the candle flame slightly different - the work of a few seconds to create a surprisingly good two-frame flicker effect.

The initial effort took a few such candles and repeated the process. A wxTimer was created and set to trigger every half-second, upon which the background image was replaced with the alternative and repainted.

There were two problems here; one aesthetic and one technical. The aesthetic problem is that all the candles flicker together, which is too noticeable. To solve this, I can either create a stack of such images and call them in sequence, or I can keep a core image and have just the flames themselves redrawn on each interval, with a probability of being redrawn such that they will seem to flicker randomly to the user. I'm tending towards the second solution; it'll mean more code at this stage, but if I write it well I can use it as a general paint-over-background class that will be very useful later on.

The other problem was that whilst on Linux this was fine, the WinXP version displayed a frequent banded flicker every refresh - a wide, horizontal strip of discolouration across the image for a moment. Switching the wxPaintDC to a wxBufferedPaintDC did not fix this. I added the EVT_ERASE_BACKGROUND event to my event table and pointed it to a function that did nothing - in essence, the image is no longer erased before being redrawn, and the flicker disappeared.

Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Post by Moschops » Thu Feb 05, 2009 10:38 pm

Adding a Character to the Screen

I'd honestly forgotten how fun coding could be. I code an awful lot in my day job, developing and maintaining a piece of software that frankly I shouldn't be nursing from feature request to feature request; I should be putting it in a big wicker man and burning it whilst I dance naked in a circle around it with Jane Jensen.

Anyway, being the traditionalist that I am, I decided to start out by sticking Guybrush Threepwood into things. Here's the little feller:

Image

It's Guybrush from his Lechuck's Revenge days. The background of the first room is a souped up version of the Scumm Bar. I found it on the web at a page that sadly seems to have disappeared in recent days. If it was still there I could tell you who made it, but it's not, so I can't. I do recall that there was no problem with people using it themselves and I really would like to credit the artist who made it, so if anyone recognises it, please speak up. Guybrush, of course, is a LucasArts creation. Fortunately, they seem pretty fine with people putting up pictures of him.

Anyway, as you'll note, Guybrush is a little small. He needs scaling. I have an actor class, of which Guybrush is an instance. At any given moment, the function creating the display image can ask every actor in the image where they should be, and what they look like. The returned information is used as so:

Code: Select all

       dc.DrawBitmap( finalDisplayBitmap, 0,  0, true /* use mask */ );
       dc.DrawBitmap( wxBitmap(tempGuybrush), 250, 275, true /* use mask */ );
What this does is draw a base layer (finalDisplayBitmap - I'd better work on these names, they're already becoming irrelevant) and then draw on top of that every actor in the scene (in this case, finalDisplayBitmap is the bar image, which is swapped around every half second to make the candles flicker, and tempGuybrush is the actor object put into the scene). Right now there's no way to draw the actor partly behind scenery. That's something I'll work on.

Here, tempGuybrush is a wxImage object. This is to allow the necessary scaling and any other effectsI may choose to implement, only being turned into a wxBitmap when being drawn onto the PaintDC.

This is a somewhat simplistic model, but it does make it very easy to think about, which means I'll be able to easily abstract this behaviour into classes and watch it scale effortlessly for any number of actor objects in the scene.

Here's the outcome of the above code:


Image

That candle on the table behind Guybrush flickers, although obviously not in this still image.

I am a little concerned that if I'm not careful dealing with everything on screen (actors and interactive objects) may become unmanageable if I don't design the actor, object and image building classes well at this early stage, so I think I'll have a shot at organising those classes next and then work on having Guybrush move.

User avatar
Disch
Experienced Solver
Experienced Solver
Posts: 99
Joined: Wed Oct 17, 2007 2:01 am

Post by Disch » Thu Feb 05, 2009 11:22 pm

Moschops wrote: I am a little concerned that if I'm not careful dealing with everything on screen (actors and interactive objects) may become unmanageable if I don't design the actor, object and image building classes well at this early stage
I have some experience in game programming, and I can say without a doubt -- planning an organizational structure is THE most important part. I try to outline all the details I want on paper, figure out how everything is going to work, and figure out how they're going to interact. Then design my class hierarchies and data structures. All of this before writing a single line of code.

Trying to figure it out as you go almost always ends badly. I've had to abort countless projects because I wanted to skimp on the planning stage and had to keep hacking in new additions until the code was a complete unmanagable nightmare.


I guess this could be said for any kind of program though.

Also... game programming in wx... without wxGLCanvas? You're mad, I dare say.

Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Post by Moschops » Thu Feb 05, 2009 11:27 pm

Some call it madness, I like to call it... well, madness I guess.

This is not so much about the final software as the fun of making it, so I'm just throwing together whatever seems most fun at the time of writing. I will be forced to think about the organisation, but actually now that I've got one room, one sound, one actor, thinking about that is suddenly a lot more fun than it looked before I started.

I shall look into this "wxglcanvas" of which you speak - I am literally sitting here with a PC and a copy of the book to flick through, chucking things together as they form inside my head.

Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Post by Moschops » Sat Feb 07, 2009 7:56 pm

Background, Foreground, Actor

So, having created means to place an actor image on the background, I set about having it located according to the mouse click. Previously, mouse clicking triggered a sound, but now that I knew the sound functions were working well, I could take that out and put in something useful.

The actor class has two location related variables at the moment:

Code: Select all

    std::vector<int> locationTarget;
    std::vector<int> locationCurrent;
They both hold two values, identifying a pixel location in the background image. When the user clicks the mouse, the location of the pointer is loaded into the locationTarget.

Currently, the value is then instantly copied to locationCurrent as well; locationCurrent controls where to draw the image and I have yet to implement the "walking" code, so the actor image simply teleports across the room.

Here's what I have on screen at this point:

Image

You will note that he is painted over the top of the background. This is less than ideal, as there as times he will need to appear to be behind things. Like that table over on the left.

I thought of a few ways to do this, and then went over to a gamedev irc channel to check that I hadn't missed anything. I found a level of conversation that made me remember why I gave up on irc channels for serious discussion long ago, and decided to implement a system of foreground objects.

The actor class was convenient, so I created a new actor called "table". Here's the image associated with this new actor:

Image

It's the table on the left, with some suitable transparency. I now have a new system of painting; the background, then the character actors, then the foreground actors. In essence, the foreground actors define a new foreground layer.

The final result is shown here:
Image

Guybrush is drawn in place, over the background, and then the table drawn over Guybrush, creating the final image in which Guybrush stands behind the table in a visually sensible manner.

In doing this, I tidied up the actor class code and was forced to start thinking about what will own what. At the moment, the main thread contains the actors and the background image, but I think it's time for a new "room" class which will contain all the information about what's inside it (i.e. will hold its own appearance and the actors within it). This makes a lot of sense from a design perspective, as it means I will be able to create independent rooms responsible entirely for their own contents.

Eventually, rooms may contain many foreground objects. I may then decide to give each room a single "foreground" actor, which would be an image the same size as the background image, which would be identical to the background image but with all pixels changed to transparent except for the pixels showing foreground objects. This would mean each room object had a background image, a foreground actor, and a list of actor objects. The foreground actor would in this case never need to move or be passed from room to room, so I may create a new class with much of the actor functionality removed, just to tidy things up a bit.

I think I may next try animating Guybrush, to get him walking on the spot, and then work on moving him across the screen in accordance with mouse clicks. Then, it'll be defining a walkable area and developing a path-finding algorithm. All these things already exist in the world, but where's the fun in just copy n' paste?

Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Post by Moschops » Mon Feb 09, 2009 12:24 am

Just a quick one before I head off to bed.

He walks! Here's a captured animated gif showing Guybrush walking across in front of a table (and then disappearing, which is actually rewinding back to the start of the sequence). It's a little jerky (although in this gif most of the jerkiness is an artifact of the gif capture process - I'll see about adding an actual gif writing function to the code) and I think I'm going to have to spend some time making sure that he moves the right number of pixels with each step, but the core of the walking code is in place.


Image

In this case, Guybrush walked from his point of origin to the place where the mouse was clicked. I've reduced the size by 50% to save people's bandwidth.

When I wake up and come back, I'll explain how the code works.

Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Post by Moschops » Sat Mar 07, 2009 8:28 pm

Gosh, a month already. In other news, I quit my job and have a week left.

Anyway, walking code. Walking left, right, up or down is a six frame looping process. When the guybrush actor object is initialised, a vector of images is created for each walking direction.

Code: Select all

tempImage.LoadFile(wxT("./spriteSource/guybrush/walkright01.gif"));
   walkingRightImages.push_back(tempImage);
   tempImage.LoadFile(wxT("./spriteSource/guybrush/walkright02.gif"));
   walkingRightImages.push_back(tempImage);
    tempImage.LoadFile(wxT("./spriteSource/guybrush/walkright03.gif"));
   walkingRightImages.push_back(tempImage);
    tempImage.LoadFile(wxT("./spriteSource/guybrush/walkright04.gif"));
   walkingRightImages.push_back(tempImage);
    tempImage.LoadFile(wxT("./spriteSource/guybrush/walkright05.gif"));
   walkingRightImages.push_back(tempImage);
    tempImage.LoadFile(wxT("./spriteSource/guybrush/walkright06.gif"));
   walkingRightImages.push_back(tempImage);
Subsequently, the user will request a location to walk to, and a decision is made about what state the actor object is in. For example:


Code: Select all

if(locationTarget[0] > locationCurrent[0])
		{
			actorState = walkingRight;
		}
Right now it's pretty primitive in working out whether the state should be walking right or walking up in instances where the general direction of motion is both - however, on screen it doesn't look too bad.

Every 200 mS (i.e. frame rate 5Hz) the actor image is updated; moved if needs be, and a new image requested. The images are cycled within the actor class, like so:

Code: Select all

wxImage actor::returnWalkingTowardsImage (void)
{
	currentWalkingFrame++;
    if (currentWalkingFrame == walkingTowardsImages.size())
    {
       currentWalkingFrame = 0;
    }

    return walkingTowardsImages[currentWalkingFrame];
}
You'll note that nowhere is the number of images mentioned; this means that the walking right cycle could contain a different number of images to the walking left cycle, and the code would not care - the beauty of using a vector to store the images.

I've also been moving code out of the main loop and into the relevant classes, heading towards a properly encapsulated set of classes so that I can then bring in more rooms and more actors. Putting initialisation code into the main loop is fine to get things working, but now that things are underway I should properly abstract and encapsulate.

Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Post by Moschops » Mon Mar 09, 2009 1:36 pm

You know, as I look at that code above, I wonder if this isn't screaming out for some kind of linked list, with the last images linked back to the first.

On the one hand, the code I have at the moment works fine. On the other hand, it'd be kind of neat and fun to do it with a linked list and be able to do away with that manual bounds check. I'll look into it.

Moschops
Earned a small fee
Earned a small fee
Posts: 13
Joined: Sat Jan 31, 2009 8:00 pm

Post by Moschops » Mon Mar 23, 2009 5:44 pm

I've got a cold and I'm learning Smalltalk and Objective-C in anticipation of my next job.

As such, little dev for the moment. I need to start thinking seriously about plot, and maybe about suckering someone else into this endeavour. I could do with voices and artists. I'm going to go looking...

leiradella
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Sep 07, 2008 9:49 pm
Location: Rio de Janeiro, Brazil

Post by leiradella » Mon Jul 27, 2009 3:31 pm

Considering using some other library for your game, like SDL. If/when rooms start to get crowded, your framerate could drop.

Also, SDL has means to accelerate the drawing of images. For example, your foreground actor is the same size of the background, but with transparent pixels occupying a big piece of the image. Blitting it is a lot faster with RLE compression, which SDL does. I don't think wxWidgets does it.

I would also consider make everything its own classes, like the candles. Generally speaking, you could have an Actor class with some virtual methods every subclass class must implement, like Update() and Draw(). Then you subclass it to create Guybrush, the candles, other characters etc.

Then you can have an array of Actors and Update() everyone in sequence (which will change their position and animation status), and after that you Draw() everyone in sequence.

If you to draw things in a specific order, i.e. the tables, you can have more than one list of Actors, and draw each list in the same order. So your tables would be Actors in a list which is drawn after the characters.

Hope this helps.

Cheers,

Andre

Post Reply