Entropy's picture

Coding Projects: Lessons Learned

Some time ago I announced my LinkSphere project, with a deadline for alpha-release in mid-to-late May (for the sake of my friend's college music course, as he was doing the integral music).

That Alpha release was completed in early June, but it's clunky and buggy and poorly coded and I'm not particularly happy with it. I certainly don't feel like publishing that particular fruit of my labour.

LinkSphere was very much a learning project for me. When I started, I'd just about got my head around the concepts of OO programming, I'd finished blowing the dust off the mathematics I learnt during my Physics degree and started to polish it up a bit. I felt ready to start a slightly larger project.

Of course, understanding OO concepts and applying that knowledge efficiently are two different things. Coming to C# from a procedural background, the code that gradually built up was full of static methods, arrays of array-indices and long complicated control structure blocks. I did USE inherited classes, polymorphism and the like, it's just that I hadn't really got my head around the idea of fully OO design yet.

Also, the whole thing was unplanned from the start, aside from a few basic mind maps to figure out which order I should code things in.

As I continued to code, two things happened. firstly I built up more and more classes on this hodgepodge patchwork of existing classes and the scale of the project became proportional to the (over-)complexity of it. Secondly, I styarted to understand the ground rules of good OO design better.

Both of those things led the project inexorably to a single point: refactoring hell. Actual progress ground to a halt as I rewrote classes, renamed classes, objects, filenames and namespaces, re-re-re-re-rewrote classes, decided they were better as they'd started out, moved objects between existing projects in the same solution, changed naming conventions over and over again etc etc etc...

In the end, I *just* got the Alpha out in time, but it felt like more of a quick hack than it did a successfully written program, as the structure was still a complete mess (although some semblance of order had finally started to creep in). I haven't touched it since. The latest revision in my svn repo doesn't compile, and it won't until a certain amount of major refactoring has taken place. And then I'll have to iron out the bugs again...

So I learnt some very important lessons with LinkSphere.

1. I learnt what OO really MEANS. I don't need a long switch/case structure checking an entity type if I use polymorphic class inheritance and simply call entity.Foo(Bar bar). I don't need to store all my entity objects in arrays and then refer to them by array position if I just reference the objects themselves directly. I can save function call overheads if I use event delegates proerly, etc etc.

2. A project should be carefully planned beforehand, and to think about how it needs to be structured.

3. Refactoring, while obviously invaluable to a project needs to be approached with care. It should be taken at a slow steps (one thing at a time, thoroughly tested along the way, then commited to svn, rinse, repeat). It's also silly to waste much time worrying about whether a field is called number_of_channels or numberOfChannels. Consistency is nice, and it pays to follow a convention, but it's not worth going through an sizeable project changing everything to follow a given convention when you could actually be doing something productive X-D.

4. Don't take on your friends' deadlines. :-p

Going forward, I built up a GameEngine project when "designing" LinkSphere which I'll continue to build as I code more projects. I've written a couple of messing around projects since and improved on it, and will continue to do so.

I love a good RogueLike, (NetHack is the one thing I'll keep on coming back to) and one of Objarni's texture fonts inspired me a few weeks back. I'm brewing ideas for a game with text-based graphics and a few fun OpenGL effects thrown in.

I haven't started coding anything yet, though. I've been thinking about structure first, jotting things down in idle moments. When I do start, I'll be building a framework of empty classes first, to see what it looks like.

One question, for design reference...

If I have a tree of inherited classes for every item that could exist in the game (eg. ProjectilePistol : ProjectileGun : Gun : Weapon : Item), how might I select one of these classes at random to create on object from?

One solution I thought of was to create a list of "dummy" objects from which one could be selected at random. I suppose with this method, I'd want a system of properties which return lists of all "Weapons", or "ProjectileGuns" etc as well as "Items" , but this starts to feel very messy. Are there any suggestions for a better way to proceed?


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

Consider posting this to the game design forum (the has little to do with the original blog post now, and it's information that would generally useful to be searchable).

Do you have a class which is responsible for spawning monsters?

Game game = new Game(...);
Player player = new Player(game);
MonsterFactory monsterFactory = new MosterFactory(game);
Monster monster = monsterFactory.SpawnMonster(...);

Now you don't have to pass Game objects every time you spawn a monster.

I don't think hooking monsters directly to the player.UpdateMove event is a good idea. How about this:

  • Game hooks Player.UpdateMove event.
  • Monster hooks Game.Update event (this is done via the MonsterFactory).
  • Player moves, raises UpdateMove event -> Game notified, raises Update event -> Monster notified.
nythrix's picture

Is this "refactoring madness" caused by today's dev tools capabilities? I remember coding a small cards game in C++ (latter rewritten into Delphi) but I don't recall going sideways or (god forbid) backwards often. Once I put down something it stayed like that unless it caused an issue. Granted, it was a much smaller project, but my coding skills were much funnier aswell. What is your experience, guys? Do you think it's related?

Inertia's picture

There's an asian proverb, which I like to think of when this topic arises:

Vision without action is a daydream.
Action without vision is a nightmare.

Both are not desirable (in the light to accomplish something) so you got to find a balance between the two. It's easy to get stuck in analysis-paralysis and getting nothing implemented, but it's also easy to start hacking away losing sight of the big picture. I don't think there's any rule how to find this balance, maybe time constraints are the only relevant factor.

The better availability of programming libraries due to the internet and established Frameworks (like .Net) certainly have an impact on code quality and the desire for reuseability.

objarni's picture

nythrix: refactoring madness comes from the realisation that the requirements, on both high-level (user level) and low-level (implementation details) change over time, whether we like it or not. We learn things as we code, and start to dislike older code, thus we want to fix old code to suit our new understanding. Users change their minds all the time about what the software should do. Software development is an evolutionary process - not an algorithm. In a way, requirements and software is the same thing. That's why it is so darn hard to "nail those requirements" in advance.

Entropy's picture
Inertia wrote:

Vision without action is a daydream.
Action without vision is a nightmare.

Very apt. :-)