As I mentioned before, Graham, Mike and I are working on a cross-platform, multiplayer puzzle game written in C# and backed by SDL. This is my second major project using SdlDotNet, so it's a very comfortable environment for me to be programming in - code just seems to fly out to make things happen. But, as always, I spot a few niggles where I stray into new territory.
Before I talk about the niggles, I want to tell you a bit more about the Brain Party project.
Graham's initial idea was to have a lift that transports players from one level to the next. When they start a level, they have a set period of time to find the next lift and get everyone onboard to move to the next level, otherwise they need to retry the level. The picture you probably have in your head is something like Gauntlet...
...but here's the first twist: to get to the next lift, you have to do things. For example, you might need to flick a switch, collect a door key, and so on. Again, you're probably thinking this is nothing special, but here's the second twist: most of the actions require co-operation. For example, to get player A through a door, player B might have to find an exercise bike and pedal it as fast as they can. To get player C to the room with a door key, player D might have to activate a teleporter. To get player E over the bridge, players A, B and C might all need to be holding mirrors that reflect a laser into an activation point, and so on.
Now here's the third twist: most of the actions (eg pedalling a bicycle) are completed through random minigames. This might be something as simple as tapping A and Z as fast as you can, Hyper Olympics style, or by answering basic maths questions, tapping keys at the right time (Dance Dance Revolution-style), whacking moles as fast as you can, sorting things into groups with your mouse, counting moving objects, etc. If you whack the moles fast enough, the door starts to open and player B can walk through to get the key card. But if you stop whacking moles well enough (or tapping A-Z fast enough, shooting enough clay pigeons, etc), the door will start to close and lock Player B (and the key) in.
The actual wandering around the map is sorta peripheral to the minigames. The idea is that people will need to be co-ordinated enough to work together, solving problems, to get to the end point. To add some extra tension, the game is probably going to be comprised of two teams, both trying to get to some object (eg a star or a crystal) that's in the middle of the map. Team A approaches from the left, team B from the right - they both have the same tasks to do, so it's a race to see which team can work together best.
So, is this all just a nice idea? Nope: we already have 1638 lines of code in there, which includes three minigames as proofs-of-concept. Client-server networking support is in there, mouse movement is in there, and the beginnings of our own little GUI toolkit is in there too. It runs just fine on Linux and Windows (and presumably any other OSes for which you can get Mono and SDL).
Here are some shots of the minigames in action, although please note that the graphics are just placeholders at this point - we're more interested in proving that the idea works!
Here you need to drag the red Marios (shamelessly stolen from the NES, but that'll change later, natch) into the red box on the left and the green Marios (Luigis, I guess), into the green box on the right. They all bounce around the screen at different speeds, so this is actually pretty tricky. In the bottom-right you can see Mike's progress bar widget.
Cursor key directions fly up from the bottom of the screen, and you need to press the right keys as they hover over the ghost images at the top. The background colour changes from green to red depending on how well you're doing (only because we don't have font support yet!)
This is one that everyone can recognise: click the red Marios (the "moles") as they pop up, but don't click the green Marios otherwise you lose points. This one took just 139 lines of code to program, and could be done in under 100 if you a) didn't use so many comments/so much whitespace, or b) didn't want a little flash to appear over the moles when you click them.
The ability to write a fun minigame in 140 lines is what makes Brain Party special: the main engine handles all the networking, drawing, synchronising and input gubbins, leaving minigame coders to handle the "fun" part.
The idea is that we ship with at least 50 different minigames to play, so that each time you try a task you get something different. I knocked out the mole whacking and colour sorting games in two hours each, and that includes having to write the back-end code to handle mouse motion, clicking, etc. One minigame should take a competent coder about an hour to write, so we hope that anyone and everyone could get involved simply because it's so easy.
For example, if you want to catch mouse clicks in your game, you just use this line:
The Brain Party engine will catch the click, offset it to your minigame's position (ie, 0,0 is the top-left of your minigame window rather than the top-left of the Brain Party window), then pass the event on to your MouseClick() function to handle. Each minigame gets two functions called every frame (ie, every time the game loop goes round): Tick(), and MiniGameRender(). In Tick() you get the chance to update your game - move any objects, set scores, etc. In MiniGameRender() you draw your game objects to the sfcGameWindow variable, which Brain Party then renders as your mini game. That's it - it's really easy. You could feasibly write a working minigame in under 40 lines of code.
So, where do we go from here? Mike is working on the widget set. As you can see in the screenshots, the minigames popup in a little white window of their own, which we'll make draggable soon enough. But we also want buttons and text boxes so that people can click and type in their minigames, or send messages to each other. Typing, of course, includes font support, so that's high on Mike's to do list too.
Graham is working on the inky black area behind the minigame windows: the map. Last night I put in the code to move your player around with the mouse - you just press and hold where you want your player to move, as in the game Diablo. Graham is going to be doing the code to handle drawing a floor, walls, doors, etc, plus collision detection and scrolling, level loading and a mini-map view so you can see where the other players are relative to you.
As for me, I'm kinda filling in the gaps. I put together the minigame framework that makes it so easy to write minigames, and also the networking code. I intend to continue writing more minigames, as right now each new minigame usually requires something new in the minigame framework to enable it to work properly. For example, the DDR clone required keyboard support, the Mario sorting game required dragging support, etc. If I get the time, I'll probably flesh out the level XML framework too.
So, that's an update on what we're working on right now. Now, I did mention I had some niggles, and I do, so <rant>...
Well, perhaps that's being unfair - I had a very particular requirement for a networking stack, and .NET simply didn't meet that requirement. What I wanted was an event-based networking system where I could write something like this:
That is, I wanted a socket that would call my function, Func_NewClient(), when a client connected, and would call Func_OnDisconnect() when a client disconnected, plus other functions such as receiving data, etc. I don't want to have to poll the socket every tick to see whether any data is waiting, because a lot of data might be coming in sometimes and I'd rather it was left to its own devices so as not to hold up the rest of the game.
Sadly, the basic .NET networking system uses such a method: you poll, you read, you deal with, then you poll again. For people with slightly higher-level brain functions, there are some event-based alternatives, but they are nothing like so straightforward as my example code above would require. Instead, you have to call BeginReceive() with your callback, then call EndReceive(), or BeginAccept() then EndAccept(), etc. It is, sadly, very painful, and very poorly documented.
After having written the Brain Party network stack using the Begin/End methods, I noticed it had added over 100 lines of rather nasty code to the main Brain Party file, which was essentially incomprehensible to people who didn't understand how the .NET sockets worked. So, I did what any sensible person would do in these circumstances: I created my own ClientSocket and ServerSocket classes with the networking code, then added my own events to them so that I can finally write the kind of code I wanted. Here is how my server code now looks:
When a new client connects, my socket calls OnNewClient(). When any client sends some data, the socket calles ParseClientMessage(). Behind the scenes there's still all the BeginReceive() and EndReceive() rubbish, but it's totally encapsulated so that people who don't care about sockets don't have to deal with them.
With that problem out of the way, me and .NET are back on good terms. Really, if you haven't given C# a go, you need to - it's such a smart programming language, and the Mono implementation is awesome. SDL is, of course, a joy to use, and mixing the two together is programming heaven as far as I'm concerned.