Thanks again for taking time to read this blog! Hopefully you can learn from a few of our mistakes and save yourself the pain and torment that we caused ourselves. I'd like to talk about scope and ambition, and why this is such an important topic. First of all, I'd like to introduce you to the team:
This handsome chap is me. I'm currently the game designer in charge of the aesthetic direction, story and game-play mechanics. I'm also doing the art and some of the music/sound design for the game. In programming, I specialize in the physics, geometry, collision, animation, interaction and hierarchical structure of code.
Name: Tom
Favourite Colour: new Color(255, 165, 0);
Hobbies: Long walks on the beach, Mojitos and Sex and The City.
This fine specimen is Steve. We've been besties since day one of university. Steve is an absolute programming maverick and can help consult me out of the most dire situations. He specializes in audio engineering, XML, file management, optimization, game flow structure and memory management.
Name: Steve
Favourite Colour: new Color(50, 205, 50);
Hobbies: Bare knuckle boxing, knitting, tap-dancing, horse impersonations, taxidermy.
We've learned a lot over the course of the past five+ years. For one, we learned that starting on your most ambitious title ever is not a good idea if you don't know how to code. Trust me on this one.
I've been making RPG maker games, board games and Game Maker games for over a decade, which has taught me a lot about design. However, when it came to programming something from scratch, I knew nothing up until university. Steve and I sat around in our little student hovel discussing game ideas. We wanted to create something really fun to play, but also something that wasn't ridiculously technical and over-ambitious, like Crysis for example. However, we were in for a shocker!
It turned out that making something as simple as the NES Mario début or even Tetris or Pong was well out of our scope, let alone the idea for this whole game. We were still figuring out how to draw rectangles on the screen and make them move. What would now take us a few minutes would take hours back then! When we finally did implement something we liked, such as a physics-based weapon, it was so horribly implemented that it just couldn't be worked with. So it's safe to say that we've re-hashed this project a good few times now.
|
My first EVER XNA game - all in a single CS sheet! |
The biggest problem with ambitious projects the the tendency to hit a brick wall. For example, if I wanted to hypothetically create an enemy that resembled a Lakitu in Mario...
|
A Lakitu, in case you've been living in a cave for the past 3 decades |
I could quite easily hard-code a fully functional Lakitu in an hour or maybe even less. I could code its movement patterns, behaviour, etc, and have it working pretty much the same as one you would see in a Mario title. Easy lemon something. However, everything in our game runs through specific hierarchies for a few reasons which I can get to a bit later. Because of these hierarchical dependencies, I would have to create a Lakitu which inherited from the Entity class that we devised. This class handles multiple death animations, gibs, responses to damage, bullets, passives, aura effects, has an inventory, complicated physics and more. To add that simple behaviour where the Lakitu swoops from left to right and pursues the player, occasionally dropping Spinies, is simple - but once I had to override the AI Director / Control Manager present in every entity, it soon became clear that this seemingly simple behaviour directly conflicted with the underlying code in the base it inherits from.
This presents us with a forked road situation. On one hand, we want to make a game with lots of enemies, so, for example, say an enemy has a reaction to a bullet, and that reaction is to die, we have to code the appropriate behaviour which causes the enemy to die. Now imagine we create 30 enemy types - We want all 30 to react in the same way to bullets, so do we write that exact same code 30 times? Hell no! We make a base class that has a method inside which handles bullet behaviour, and we only code it once, then we make those 30 enemies all inherit from that base class. It's simple enough, but once we start adding all the complex base behaviours shared by all enemies, we also limit what is now possible for child classes of the base to do, as they must now follow strict guidelines derived from the base class.
A huge problem that we run into from my personal experience, and the experience of many bloggers I read about, is that programming in itself is fairly easy. Once you understand it, you just create the logic and it does exactly as you tell it (eventually). The hard part with projects like these is doing it in the most efficient way and managing huge mammoths of code. This entails creating a harmonious balance of code that is:
1.) Optimized (runs quickly and efficiently + cheaper hardware can run it + more processing available to add more features)
2.) Cohesive (easy to change and work with + less/neater code on screen)
3.) Flexible (we want cohesive code, but we like flexibility that allows us to break out of these constraints if need be, so that we do not limit game-play variety)
As a general rule of thumb, the easier code becomes to work with, the easier it is to also fall into a false sense of security that your design will scale well. You have to hit a delicate balance between code that is nice to work with as developers, but that also doesn't require heavy CPU/GPU processing. If I was hypothetically creating a title for the PS4 which, when finished, ran as a constant steady 60fps, then why on earth would you bother optimizing it? It does what it says on the tin, and it does it well. The PS4 is not modular in design, so the frame-rate will not vary if it has a few to spare and is well tested. Optimization issues strike hard, like a vicious lemur, when your playability and frame rates inevitably take a hit, usually due to the huge budget next-gen graphics bla bla nonsense games. If the game was running at around 50fps, and you had not bothered with any optimization, then there would be an incentive to further optimize your code to hit that nice clean 60fps mark.
PC hardware is very different to consoles. If you have something running at 60fps on a PS4, you can guarantee with 100% certainty that the code will execute exactly the same on every PS4, whether it's a PS4 from Mumbai or Hong Kong, and that variation in game-play will be from only the inputs being fed to the game. People have such variation in PC specs that you're going to have to code in a way for PC that accounts for multiple screen resolutions, varying hardware and multiple control systems. Because of this, it's nice to get a rough idea of what PC hardware the majority of people are running. By creating a game that pushes the limits of graphics, you're limiting your audience to the "elite" players with the £2000+ Alienware PCs. On the converse, by dropping the bar too low, you're losing the interest of people who expect a certain standard of graphical quality. It's up to you in your design how you choose to approach this, but I'm one of those nerds who prefers a good frame-rate over nice graphics. Of course, if I had the money, I'd choose both.
Anyway, in regards to the cohesiveness / flexibility balance, there is no easy answer, but it certainly helps a lot to research design patterns. From my personal and fairly limited experience, I would say that you develop a "feel" for what works, and this "feel" can only be gained from hands-on experience - so get yourself involved in every programming jam and part-time project you can. In regards to code structure, would it be better to use a hash table lookup or a simple list? It depends on the context. Is it better to use inheritance or polymorphism? Depends on the context yet again! I can't tell you what works best, because if there was always a single best solution to all problems, then why would any of these tools exist?
What I would recommend is to not get wrapped up in these problems while you're entering the world of game development. You've probably heard this disheartening advice before, but it IS better to work on lots of small projects than to dive in head-first to the most ambitious project you could ever imagine. The beauty of this is that you don't hit these "brick walls". If you get stuck, you can probably find a really sloppy way of making it work. The code looks awful, for sure, but you've finished a project, you have something to show, and for the player, it does not matter how the code is structured when they are playing your finished project.
So to apply this in a real world example, let's say that you wanted to create an online twitch-based first-person shooter such as Quake or TF2. This is already a behemoth of a task. Let us have a look at just a miniscule fraction of some of the tasks involved with creating TF2:
1.) 3D models / rendering
2.) Shaders
3.) UDP networking
4.) Lag compensation
5.) Online user account management
6.) VAC Anti-cheat measures
7.) Menu systems
8.) Making eyelids move
9.) Making chins move
10.) Hats
12.) Hats
13.) HATS!!!!
|
My face when I play TF2 |
Oh yeah, did I mention the engine they used? They freaking built it themselves. (The Valve Engine, in case you've been exiled to the sewers)
There's probably another thousand things that could go on that list, but for simplicity's sake, we shall stick with these few! Rather than cracking on with that stupidly ambitious first project, you should create seven separate games. Each game could use just one of these elements. One could be a simple 3D display of an animated model, the next could be a simple menu-based RPG, another could be a simple client-server architecture. This way, each project will eventually be completed with enough perseverance, and you won't feel like a failure for not completing one single mammoth of a project which encompasses all of them at once. This also helps target the indie programmer's main weakness: MOTIVATION!
This also gives you the freedom to use sloppy code. Sometimes sloppy code is good just to help you get used to the way things work. I mean, it's not good, it's awful, and you can be ashamed, but it's a small project so you can worry about refining it later. Once you have all these moving parts in order, putting them together is the next big task.
I have found that just because all the pieces work separately, it does not mean that they'll integrate nicely together. This is where prototyping comes in to play. One you have all the pieces of the puzzle working nicely, the next thing is to piece them together into a well-oiled machine. I would always recommend re-writing your code. This may seem like a big middle finger to your hundreds of hours of hard work, but this gives you the opportunity to perfect your art, and also reflect in sheer disgust at how messy your code used to be. You also have to remember that the most time spent coding something from uncharted waters is simply figuring the problem out in your head. The actual physical pressing of keys to write code is just a physical manifestation of your logical reasoning in your brain, and the code produced a reflection of one's thoughts, preserved until the end of man? Who knows? Went off on a bit of a tangent there. On each successive run though, I can guarantee that you will write cleaner code than before, and that you will do it significantly faster than the first time. It's all good fun. I love programming!!!! AHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA!!!!!!
Since you cannot foresee the complications of your game (even if you think you can), it is a very smart idea to prototype. This is the process of putting all of these ideas together and then testing the product to see if your idea is actually fun or engaging. Along the way, you will also learn a million-and-one ways to structure your code better. Once you have tried a few of these ideas out and got a general feel for the game you want to make, it's again time to throw away all of your beloved code, no matter the sentimental value, and start over. I hope this has helped any newbies struggling with code structure and programming patterns!
This is our justification behind why we re-wrote the entire game engine. It's been a total pain, but we see it as a worthy long-term investment which will make future development of the game so much easier. I hope that some of you have at least got something from reading this and can hopefully dodge a few brick walls I hit myself.
|
The FEAR |