Hi
Here’s a demo of a multiplayer 2D top-down shooter I’ve been working on. I like to think of it as the next-gen Counterstrike/Half Life The only things holding it back are my artistic and 2D (let alone 3D) graphics skillz. Requires java 5 or better.
http://www.keithwoodward.com/sydneyengine/SydneyEngineDemo.jnlp
Controls: WASD to move, mouse click to fire, mouse wheel to change weapon. page up/down to zoom. tilde for player hit points and r for reload.
It’s not much fun on your own since there’s no computer players. I’ve tested it on a LAN with 4 players and it worked well. I haven’t tried it on the internet yet - it only copes with maximum 1/2 a second lag using the default settings (but you can change them in the options menu).
Here’s the source: http://www.keithwoodward.com/sydneyengine/
If you’re interested, this is the long-winded story of how it copes with lag. Background: in the old buggy version I made a few months ago, when an event (like a mouse click) came from a client that was in the past, in the old version I would update the World by a negative time, and relied on the game logic being so clever that it would reverse everything. That was too difficult since reversing a bullet past the point that it was shot would kill the player that shot it, etc, so I gave up.
Then a good fellow gave me the idea of saving a copy of the World to go back in time. So what this game does is have 2 Worlds. The first World (which I call the head) is the one which is displayed on the screen and is kept up to the current time. The other world (the tail) is slightly behind the head in terms of the time that it is updated to - in my game it’s half a second behind the head. When a lagged mouse click comes through from a client, ithe event’s timeStamp is read by the server and other clients and they do headWorld.makeEqualTo(tailWorld). So the head world is made to be exactly equal to the tail (as in head.equals(tail), not head == tail) so the head World has effectively gone back in time by half a second. Then the head World is advanced up to the time of the mouse click, the event is applied, and the head world is advanced to where it should be and then it’s rendered! Smooth eh? 8)
To keep the tail world 0.5 seconds or a bit more behind the head world, periodically I call headWorld.makeEqualTo(tailWorld), then advance the head a little so that it’s 0.5 seconds behind the current time, then I do tail.makeEqualTo(head), then I advance the head world up to the current time.
And another benefit from this system which is quite neat - it allows synchronisation updates to be sent from the server to the clients. By that I mean that the server is able to make sure that its World is the one being seen by all of the clients. You see, every second or so the server sends its tail world to the clients and they do clientTailWorld.makeEqualTo(tailWorldFromServer). Then the clients do headWorld.makeEqualTo(tailWorld), and advance the head to where it’s meant to be to be displayed. So everyone is re-sync’ed every second.
But the devil is in the detail and it wasn’t easy to implement. This game uses reflection (http://java.sun.com/javase/6/docs/api/java/lang/reflect/package-frame.html) to do the makeEqualTo method and despite the reputation of java.lang.reflect to be slow, it isn’t the bottleneck at all (rendering is). By using reflection, I didn’t have to hard-code the makeEqualTo method so that it assigned every field properly, which would be difficult to do when adding new features all the time. If this games new features and bugs settle down then I could over-ride the default makeEqualTo method so that it uses code (object1 = object2 etc) instead of reflection (object1Field.set(Object obj1Parent, object2)), but I can’t see that becoming necessary.
Unfortunately the network API is still crappy but at least it works (I’ve been meaning to work out the issues I’ve had with Riven’s kindly-provided framework but I haven’t had time, maybe I’ll go with Sunset’s JGN API).
I’ve kind of dumped the idea of making a network game engine, at least until I’ve made a few games which work well and discovered what parts of the code are re-usable. But if anyone really wants to extend this game or make a cool RTS or something out of it then be my guest, grab the source.
A big problem is that there is no physics yet. When you crash into a wall you just stop (i do line-circle or line-line intersection testing and if there’s an intersection, the player or bullet just stops or explodes). I tried to get the Box2D physics API to work with my network API but it uses Enums (darned things) which I haven’t been able to get to work with my serialization code yet.
Any comments appreciated
Cheers,
Keith
PS: I did most of the game’s menus using the Netbeans GUI builder which isn’t bad (but it’s a bit fiddly and I struggled to get it to work with java 5 but in the end I won). The menu’s look and feel uses Kirril’s great Substance skin RavenGraphiteGlass. You can try other skins too in the game menu.