Whew, I've just finished my biggest update yet, feature wise. I was surprised at how fast this one came out, but I'm quite pleased with the state of this product - it seems to be rock solid, fast, accurate and *gasp* even a little bit fun!
So, since this seems almost like a milestone release, I think I'll write a bit about what's been done. Most of the content of this post will be over on the project's page, too, which will be updated as I go on.
As you know, I had this project done a fair bit using my own custom rolled engine, but maintenance became just a tad extreme, and I figured that other people have already done stuff, so why should I have to reinvent the wheel? Some research found me Ogre
The hardest thing about changing to Ogre was the way the main loop is done. In my own system, every scene node had an overrideable "Update" function, where I implemented my AI and everything. Basically, I extended scene nodes for every type of actor there was. In Ogre, you can't really extend the scene node class, without overriding the scene manager as well (which I didn't want to do) - instead, "frame listeners" are used - a function is called every tick on a frame listener, and they have to update the scene node. So I wrote a wrapper around all this so I can simply register my own AI classes, and this handled all the keyboard and mouse events
While I was at it, I put in a game state manager and level loader - this time actually thinking about the transitions between levels. There's functionality in place now to put loading screens in (but not actually used), and properly clear any cache or anything that I have lying around. Woohoo!
The next big task was deciding on a physics engine. My first instincts told me to go for PhysX (aka Nx) - there were binding classes around to do some of the integration work for me, and it wasn't long before I had a dummy app in Ogre that I could throw boxes around and watch them smash each other up.
However, after trying to get it into my engine, it became apparent that the binding classes included were too restrictive - it tried to wrap every single function up, and so I was left with a half of a physics engine that I could actually use, with the overhead of the whole library, and it lacked very VERY important features - such as a simulation tick callback, or listeners.
I decided to scrap Nx and go for bullet. The first set of bindings I used was similar to the nx bindings - tried to wrap every function - so I had issues with that straight away. Then I found a second set, far more slim - more of a converter than integration. It allowed me to throw entities at it, and get back bullet representations of those - cylinders, boxes, spheres, convex hulls, trimeshes, you name it. Brilliant! I now have a physics engine.
Ahh, physics is difficult. I'll give a brief rundown on how things work here, but it's a very complicated topic. It took me several forum posts, hours of reading, and hours of "DARGH WHY ISN'T THIS WORKING" until I got it down. So here goes.
The bullet physics engine is governed by 4 parts - the world, dispatcher, solver and broadphase. The idea is we create a world (which holds physics objects) and attach a dispatcher to it (which stores and moves collision information around). We also need to set up the broadphase - a topic in and of itself, but this handles the broadphase part of the collisions (checks bounding boxes, or whatever - cull lots of objects quickly). The solver is responsible for resolving collisions - if a truck hits a tennis ball, what is the result?
So, how does one set up bullet physics? Simply:
- Set up the world, dispatcher, solver and broadphase
- Attach some form of internal callback function, to handle the results of the collision
- As the game is going, add collision objects/rigid bodies to the world. Each object should have a motionstate attached to it - which synchronises the scene node position/orientation with the physics object
- At each timestep, tell bullet to step the simulation
This will get everything set up, and even make things bounce of each other properly. However, that last bullet point is a doozy. I'll put what I think happens inside that step to clarify:
- Simulation step is started
- The broadphase goes through and finds all possible collisions, creating a contact manifold for each and throwing it at the dispatcher
- The dispatcher looks at each manifold, and performs a low-level collision detection on each primitive (ie. box, sphere), storing the information of any contact points in the contact manifold
- If dynamics is enabled, solves any collision responses, making each object interact
- Calls the internal tick function specified by the user, with the collision world as a parameter
- Here, the user must loop through the manifolds, find all the contact objects and points, and perform any custom collision responses (playing sounds, losing health, whatever). To make this step more possible, one can set up a user pointer (void*!!!), which can hold anything. I chose to make it hold a physics response interface which I can subclass, it works really well. I just need to cast it to this IPhysicsResponse, and call my Collide function. Yay!
And that's that! Physics was definitely the hardest thing to set up - harder than the graphics engine, I would say. The main reason it's so hard is because documentation is really, really bad. It's written by a bunch of academics, and it's more fun to make the physics engine than it is to document it. Oh well. The forums are absolutely awesome though, hurrah!
Once I got the physics sorted out, I could move on to implenting decent gameplay. Here's where things sped up somewhat. I was able to perfect the collisions between bullets and zombies, and stop the zombies running through each other, and add a magnetised weapon, and stuff. I tried to add a kinematic player controller (basically, the user is governed by physics rather than by moving it), but it seems I should wait until the next Bullet release for that.
My most recent additions to the engine include a config file, so I can much more easily tweak things like zombie health, cooldown rates and starting money, and sound. That's right, the sound that added so much to the previous version is now in this one! And improved - I now have a dying sound, 2 sets of music and a zombie hitting sound.
All in all, I'm quite pleased with my progress. Check it out
here (page removed), and let me know any suggestions/ideas/critcism/anything! Feedback is most welcome!