Finally the networking begins!
There's not yet a fancy interface for selecting one's alias, or some spiffy way of informing the game whether you'll be hosting a game or joining one, but for now it's just a simple no-hassle console app query. Ugly but effective. Nothing is in stone yet, but I may be checking out the nifty CEGUI library for my future GUI needs.
(Dead sexy. Well, maybe not the 'sexy' part. Just, uh, 'dead'. [sad])
Since the IP to connect to is hardcoded at the moment (me! me!), most of you will be choosing 'C'. Based on said choice, the game will create either a GameServer or GameClient instance and get things rolling via the wonderful RakNet lib, which has been nothing but an asset thus far. [smile]
Long story (very long, gah!) short, the server and client do their jobs in listening and sending data in/out, can detect connections/disconnections, maintain a list of remote players, and such. I dislike how it sounds pretty darn simple when I do over the gist of what I've done here, but in the code it's a completely different story. I've been refactoring my head off today, removing a useless abstraction class called RemoteClient that resided between the network interface and the actual Player class. Useless!
The first step -- after boring initialization and foundation stuff -- was to get player creation/login occuring both: a) when a new player logs in, and b) when sending all existing players to a newly logged-in player. This didn't take long; Mushu, Ravuya, and DarkCampaigner assisted with the testing. (Thanks guys!)
Next up was basic movement. I know that movement prediction is a really crucial step, since its accuracy will determine the playability of the game. If I do a poor job, then bullets will appear to hit on some clients, and be clear missses for others. It's this kind of this that makes writing action games online a nightmare. I intend on keeping the server-authoratative system I had previously, since it proved not only the most accurate, but additionally prevented devious hackers from doing nefarious deeds. Anyways, basic movement consists of merely sending the player's position and rotation 4 times per second (every 250ms). The server simply bounces the message off to all other clients, and they update the remote player's sprite. Very simple, but it looks a heck of a lot better than everyone sitting on their collective arses. :P
(Click for bigger)
Fonts revisisted (and DirectX temptation!)
I don't care what anyone says: fonts/text-output in OpenGL is a huge pain in the backside. There's a smorgasboard of ways to do it, whether it be outline fonts, bitmap fonts, texture-mapped fonts, or some combination of those, but all are fairly equally a pain to implement. At least compared to the awesome D3DXFont interface that Direct3D provides.
And had I not chanced a conversation with Ravuya (GD.NET's active cross-platform (and by association OpenGL) proponent), I would have been spending part of tonight, tomorrow, a few days thereafter rewriting all of my graphic code to Direct3D. Phew.
Basically, I gave Ravuya's RavBitmapFont class another go. Apparently the constructor I used was deprecated -- it didn't like Targa images, or somesuch -- so I converted the image containing the relevant image data to a PNG, whipped in the new constructor, and BAM! It worked. Sort of. I tweaked things around a little and soon had it working exactly like it did when I was using the wgl bitmap fonts. Except now it was running several times faster. Wonderful.
In my thankfulness, I offered to beef up his class a bit. He was using plain intermediate calls (glBegin(GL_QUADS)) and such for each character, so I decided to give a shot at having it render via display lists. And so 30 minutes or so passed. I was disappointed to learn that my work resulted in a speed drop. Turns out for something as tiny as a single quad, DLs don't offer any real speed gain. All was not lost, however. I optimized several of his state-changing calls and now I'm getting fonts rendering even faster than the lightning speeds I was already getting.
If one looks at the above screenshot with me and Ravuya, it may be worth noting that when Skirmish was using Delphi+Direct3D, I was getting around 40 FPS with that much text and scenery going on. Now I'm getting upwards in the 130 FPS zone. This is a real thrill because earlier in development (with those SLOW rasterized bitmap fonts) I was really worried about performance being cruddy. Now I'm absolutely thrilled that speed shouldn't be a problem even on lower-end systems. System tests to come. [smile]
The idea of having collision groups has sped up my execution speed hugely, as well. The gist of it is, there are (will be) three types of game objects capable of collisions: players, map objects, and projectiles. My old collision detection loop looked something like:
for a = 0 to big_sprite_list.length-1
for b = a+1 to big_sprite_list.length-1
With much prettier C++ code, I assure you.
(As a cool sidenote, I find it very neat that my taking of Discrete Math/Geometry last earlier this year has had such great benefits for game development. My gained knowledge of permutations/combinations has been hugely useful in determining things like how many collision combinations can occur, and the vector math was also a huge boon. In short: stay in school!)
This code is bad, since not all sprites have a relevant collision result with other sprites. If there are 500 sprites in the game, we'd be seeing a hideous 124,750 iterations/checks. Gah.
So the new idea is that we store all sprites in one of three lists: playerList, mapobjectList, and projectileList. Map objects don't need to check collisions against any other sprite (they are purely passive), projectiles need to check against both players AND map objects (purely active), and players only need to worry about colliding with map objects (semi-active). So our total number of collision iterations falls down to:
(numPlayers * numMapObjects) + (numProjectiles * numPlayers) + (numProjectiles + numMapObjects)
// Which conveinently can't be factored :P
This has resulted in a big boost in performance -- in a sample case, 5000 versus 100,000+, gah! -- which also adds to the overall FPS at the end of the day.
Not really an optimization..
I (stupidly) found myself amazed today, when I ran Skirmish in Release mode (no Debug data generated/used) and my FPS suddenly doubled. Wow. In Delphi -- which until now has been my main gamedev medium -- never really gave a speed difference in Debug versus Release modes, so this was a whack in the forhead to me. Uh, the good kind of whack in the forhead. [smile]