The Making of The Legend of Edgar

The Making of The Legend of Edgar


History

I'd always wanted to write a 2D platforming game, and initially it was going to involve a knight entering a castle, to rescue a princess. Pretty standard stuff. I was going to have features like being able to drink potions to give him wings and fly for short periods of time, but this was just something I thought about, and never actually committed anything to paper.

SDL tutorials

After completing Blob And Conquer, Stephen stopped making games to pursue other interests. I decided to add some tutorials to the Parallel Realities pages to give people who were just starting out making games a bit of a leg up. They started out as very simple things, such as opening a window, displaying an image and playing a sound. I then added more tutorials to demonstrate moving a sprite around, firing bullets and dealing with collision detection, so someone could make a simple space shooter. I then did a tutorial on tile-based collision detection, essentially allowing someone to develop a platform game.

Halfway through one of the tutorials, I realised that I'd written enough to actually start making a fully fledged game of my own.

Starting out

I had a fairly good idea about what the game would be about and that the player should be able to return to previous maps, rather than treating them as separate levels that the player would progress through. Allowing the player to wield different weapons would allow for some weapon-based puzzles and also increase variety. An inventory system would therefore be needed. I went out and bought a notepad so I could jot down ideas as I came up with them, as well as sketching out what enemies should look like and design the maps. Sometimes I'd come up with an idea when I was out or at work so I'd either note it down in my phone or email it to myself, because otherwise I'd have forgotten about it by the time I got home. This only happened a couple of times but fortunately something else jogged my memory a couple of days later.

The Mine with the original Edgar sprite
A test of the Village map
An early version of the Forest
The first version of the King Grub
The notepad was about 50 pages or so, and by the time I'd finished making the game I'd filled most of the pages with maps, monsters and ideas. Not all of them made it into the game, but that was OK because there was enough variety in it by then.


The Forbidden Swamp map
Brain storming ideas
The Library map
Sketches for bosses


Entities

I treated everything in the game (enemies, items, weapons) as an entity, with a field denoting what its type was. Furthermore, every entity had a name which was used to determine what properties file to load up. Certain entities, such as keys and doors, also had additional information stored so that the player could identify what they were or what they required. For example, a door might require a item in the player's inventory called "Forest Cell Key".

The entity information would be saved in the map file and read in when the player entered that map.

I decided early on that the easiest thing to do would be to store an entity's information in a properties file. This would allow me to modify information without having to compile the code every time. The properties file would store information such as the type of entity, its sprite file, its animation file and its flags (such as whether it could fly, was invisible etc.). Some of this information would be permanently stored in the map file that it was added to (such as health), but other information would always be read from the properties file.

Sprites and animation

The first problem I encountered with writing the game was dealing with loading sprites and animation. Initially, I had a text file for each entity's sprites, which listed the sprite's filename and its id. I could then reference the sprite's id in the entity's animation file. The sprite file would contain:

0 gfx/edgar/edgar_walk_00.png
1 gfx/edgar/edgar_walk_01.png
2 gfx/edgar/edgar_walk_02.png
3 gfx/edgar/edgar_walk_03.png

Sprites 0 - 3 were for Edgar's walking animation. The corresponding animation file would then contain:

NAME WALK
FRAMES 4
0 8
1 8
2 8
3 8

Meaning that when the WALK animation was set, it would loop through 4 frames and display sprites 0 - 3. The number after each id is how many frames the sprite would be displayed for, before moving to the next one. The biggest problem with this approach is that the sprite numbers are hard coded and adding or removing frames would result in having to juggle everything around. I ditched this method and made the code determine what the sprite id would be when it loaded up the images. The sprite file then simply contained:

gfx/edgar/edgar_walk_00.png
gfx/edgar/edgar_walk_01.png
gfx/edgar/edgar_walk_02.png
gfx/edgar/edgar_walk_03.png

The animation file would stay the same. When the code loaded up the sprite, a unique value would be assigned to it and stored in an array. The animation file assumed that animation frame 0 would be the sprite on the first line of the sprite file. Frame 1, the second line, and so on. In the end, I ended up with something like this:

gfx/edgar/edgar_walk_00.png would be assigned to sprite index 56

Frame 0 of the WALK animation maps to sprite index 56.

This solution worked out really well and saved me a lot of hassle going forward. I then started adding more information into the sprite files, to create bounding boxes so that a sprite's collision box wasn't necessarily the whole image. This was used for a couple of enemies, such as the Floating Snappers.

Maps

The size of the maps in the game were hard-coded, since they were just massive arrays. This worked out OK to start with but before the first release I realized that the map size was too small. I increased the size and ended up making them bigger than they actually needed to be, but this was OK since having wasted space was better than having to go back and increase the map sizes halfway through the project.

The map file contained information about the name of the map, the tile set to use, the music file to play and the entities that were initially present in it. Whenever the player moved from one map to another, the game would store the entity information for the map the player had just left into a persistence file. Subsequently, whenever a player entered a new map, the game would check the persistence file to see if the player had already visited the map. If they had, then the game would load up the entity information from the persistence file, rather than the map file. This worked out pretty well in the end, the only problem being that if a player was running away from a monster and exited the map, upon returning to it, the same monster would still be bearing down upon them.

The map editor

When I first started out, I just used a text editor to place a few tiles into a file and added Edgar and a chicken (more on these later). When I was satisfied that it all loaded up OK it was time to make an editor, to allow me to place tiles and entities more easily. I put together a very simple editor that allowed me to toggle between placing a tile and placing an entity. I then added small features such as filling in an entire row with tiles and snapping an entity to the grid, rather than being able to place it freely. Once I'd placed all the tiles and entities I'd go into a text editor and adjust the data.

The minimalistic editor
The most important thing for me was to not get bogged down in the editor, so once I had something that (barely) worked, I left it alone and got on with making the game. I never intended the editor to be used by the player and decided it would be quicker to correct information in a text editor, than to spend the next 18 months adding widgets and text boxes to an editor. If I'd done that, I may well have given up years ago. I'd strongly advise anyone who just wants to make a small game to do the same. Just make a small editor to do the bare minimum.

Scripting

Scripting was possibly one of my most favourite parts of the game to code. It allowed me to make the characters walk around, talk, give the player objectives to complete, and so much more. As an example, here is the script run when Edgar encounters one of the hidden bosses, Azriel:

MUSIC FADE_DOWN 3000
CAMERA_SPEED 1
LIMIT_CAMERA 1184 128 1824 576 TRUE
WALK_TO EDGAR 1260 522 WAIT
WAIT 30
TALK ??? Who dares pillage the tomb of the great warrior Ivan?
WALK_TO EDGAR 1640 522 WAIT
WAIT 30
WALK_TO EDGAR 1295 522 WAIT
WAIT 30
SET EDGAR FACE RIGHT
TALK Edgar Who said that?
TALK ??? Insolent cur, you will suffer for your disrespect
SET AZRIEL PROPERTY MENTAL 1
ACTIVATE_OBJECTIVE AZRIEL
WHILE_NOT AZRIEL MENTAL 0
WAIT 60
TALK Azriel I, am Azriel
ACTIVATE_OBJECTIVE AZRIEL

Most of this text is probably self explanatory, but I'll go through them line by line:

Fade down the music over 3 seconds
Limit the speed of the camera
Limit the camera's position. TRUE means that Edgar is constrained to the camera's bounds, rather than the map's bounds
Make Edgar walk to the coordinates 1260, 522. WAIT means that the script will not continue until Edgar has reached the coordinates
Wait 30 frames (0.5 seconds) before continuing (1 second is 60 frames)
Display a dialog box. The name of the speaker is the next word and will be displayed in yellow. The rest of the text is the actual dialog. The script will wait until the dialog box is dismissed
Make Edgar walk to the coordinates 1640, 522. Wait until Edgar has reached these coordinates
Wait 0.5 seconds before continuing
Make Edgar walk to the coordinates 1295, 522. Wait until Edgar has reached these coordinates
Wait 0.5 seconds before continuing
Make Edgar face to the right
Display a dialog box
Display a dialog box
Set the property "Mental" of the entity called "Azriel" to 1
Set the "Active" property of the entity called "Azriel" to TRUE
Do nothing until the property "Mental" of the entity called "Azriel" is 0
Wait 1 second
Display a dialog box
Set the "Active" property of the entity called "Azriel" to TRUE

Scripting allowed me to quickly create new events or adjust them without having to recompile the code every time. There are scripting engines out there, such as Lua, but I didn't want to spend time learning them or get 75% of the way through the project only to discover that it doesn't support something unusual that I wanted to do.

Gameplay

One of the trickiest parts of the game was keeping it fresh. There are 26 maps in the game, so making sure that the player didn't start experiencing déjà vu, I had to come up with something unique about each map (or at least a couple of new enemies). For example, in the Laboratory I came up with the idea that the player could drink a potion which would turn Edgar into a monster that could swim through water. The Left Tower contained no enemies at all, but had some memory puzzles involving teleporters or endless corridors (if the player chose the wrong path they'd simply be warped back to the start, giving them the illusion that they were walking forever).

A couple of my favourite items were the Ice Spray, which created ice cubes in water that the player could use as floating platforms, and the potion that turned Edgar into a Slime.

Swimming through the water as a Slime
Creating platforms using the Ice Spray
Controls

Right from the start of the project I knew I wanted the game to have joystick support and configurable controls. I already had a PS2 joypad for playing MAME and I wanted didn't want to be hunched over a keyboard playing the game (with keys that I couldn't configure). Adding support was pretty straightforward and well received by players who appreciated being able to modify the controls to their own liking.

Enemies

I didn't want Edgar to be the sort of game that featured just 4 base enemies, simply colour swapped or tweaked for appearances later in on. I therefore did some brainstorming with my notepad and came out with as many unique enemy types as I could think of. Here are a few of my favourites:

Gazer


Judging from some of the comments I received, some people found this enemy quite tough. All it would do is fly back and forth and if it spotted the player, it would flash the screen. If the player was in its line of sight and were facing it then Edgar would be stunned for a few seconds. A Gazer would sometimes drop its eye when it was defeated, which was needed to progress further in the game.

Centurion



The Centurions weren't particularly tough, but they had a lot of energy and their attacks couldn't be blocked. The attack consisted of smashing the ground with their pile drivers, which would stun the player for a few seconds if they were standing on the ground. The safest strategy was to jump while attacking them.

Tortoises



The tortoises were great enemies. I wasn't planning on having more than one type, but after Edgar collected the Fire Shield, I decided to have another enemy that could spit fire. It then occurred to me that I could have a tortoise that spat fire and one that could produce ice. I then decided that I could have one with the powers of all three, that would feature as part of the optional quest.

Whirlwind



While harmless in itself, the whirlwinds could suck in the player and spit him out, removing any equipped weapons in the process. This could be quite annoying if the player was trying to attack another enemy at the same time. The whirlwinds were relatively weak and could only withstand a few hits, but they could reflect projects, effectively making the bow and arrow useless against them.

Chicken



While not strictly an enemy, the chickens feature as part of the first quest the player has to perform. Initially, the player had to round up all ten of them, which was incredibly tedious and wasn't received well. I then came up with the idea of requiring the player to only capture three of them, and if they caught more then they would be rewarded. Because of the response to the chicken quest, I decided that it would be fitting to feature them in the game's ending.

1st release

After a few months of writing the game, it was in a state that I could release a version to the public. Version 0.10 only had a couple of maps, and only one save slot. The response wasn't great, with much of the criticism being levied at Edgar's walking speed and having to catch ten chickens in such a massive map (the Village map was about twice the size that it is now). The blandness of the maps didn't help things, but that was something I was able to improve at a later date.

The Forbidden Swamp Guardian. Missing from version 1.0
The next problem I faced was that since the game featured a persistent world, releasing an update to the game could break a player's save game. I therefore had to either find a way to update a player's save, or tell them to start over (not a great solution). Fortunately, I was able to simply produce a patch file with some commands in it to patch a player's save game. When the player loaded up their save game, the code would check the version of the save and compare it to the version of the game. If the game's version was newer, then it would bump the save game's version and search for a patch file for that version. If one was found then it would apply the patch. It would then repeat this cycle until the version in the save file matched the one in the code. This worked incredibly well and apart from a couple of times when I made a few copy and paste mistakes, there were no problems.

Bosses

I decided I wanted to not only have regular bosses in the game, but secret ones, too. The first secret boss I created was basically just the Red Grub with a lot more energy and a couple of different attacks. The advantages to defeating the secret bosses was that the player could increase their hit points and, in some cases, obtain an item towards the optional quest.

The secret bosses were a lot harder than the regular bosses and the game did not provide any hints as to how to defeat them. Something that I'm not a fan of is encountering a boss and all you need to do is shoot them until they die. Providing a small puzzle during the boss fight makes the encounter more interesting. Here are a few of my favourite bosses:

The Golem



The Golem was one of the first bosses that I added to the game. The sword was completely useless against it since it was made of stone, so the player had to resort to using the short range pickaxe instead. Fortunately, there were a couple of cranes in the arena that allowed the player to pick up the rocks that the Golem threw and drop them on its head, shattering it temporarily. The player could then attack the head to do additional damage, until it reformed. Eventually, I added a medal (more on this later) which the player could earn for defeating the Golem by just dropping rocks on it.

Mataeus



Mataeus is one of the hardest bosses in the game, simply because of his array of attacks and the chance of instant death if the player isn't careful. Initially he was a story boss, but was simply too hard. I changed him into an optional boss that the player could activate by touching the Black Book in the Library. Defeating Mataeus was necessary if the player wanted to fight the Black Book later in the game.

The Awesome Foursome



The Awesome Foursome only had a couple of attacks, but their difficulty came from the fact that they attacked in pairs. So whereas one might be charging up to launch a fireball at the player, the other could be dropping from the ceiling. In addition to this, when the player defeated one of them, their partner would teleport in and begin to heal them, meaning that the player needed to wear them both down at the same time if they wanted to ever defeat them.

The Black Book



The Black Book had the unique ability of being able to change into seven of the other bosses - the King Grub, Queen Wasp, Forbidden Swamp Guardian, the Blob, Gargoyle, and the Awesome Foursome. The bosses had considerably less energy than their original versions, but nonetheless it was still quite frantic.

Chaos



Chaos is the final boss of the optional quest, and certainly the toughest of the lot. When I first designed him, it took me about twenty attempts to defeat him, and even today he still poses a challenge. He's probably a bit unfair, but that's the point.

Two quests

A few months into the development I decided to add an optional quest to the game. The main quest in the game involves Edgar setting out to rescue his father, but an additional quest would involve Edgar seeking out and defeating an ancient dragon known as Chaos, who had been bound and imprisoned beneath the sorcerer's fortress. Chaos had been defeated, but not killed, by a warrior known as Ivan, who had long since passed away. Upon reading that Chaos has been slowly recovering his power and will soon be free, Edgar decides to subdue the creature once more. Finding Chaos isn't too difficult, but actually being able to survive his breath requires the player to create a special shield, by collecting a number of items. The tricky part comes from the fact that after certain events in the game, the required items are no longer available.

The Sewer Dweller. One of the hidden bosses in the optional quest
Graphics, music and sound effects

My first attempt at drawing Edgar was quite poor. I'd never tried to draw anything properly before, so it turned out to be quite a challenge. After a while I looked at some old arcade games, to see if I could copy the style. My next attempt worked out better and the result is what you see in the game now. My ability to draw did improve as I continued to develop the game, although some of the graphics really do need some work (the Phoenix in particular could do with re-drawing).

The Graveyard map featuring rain effects
I don't have an ear for composing a good song, so when it came to the music I looked on the ModArchive for some suitable tracks. Generally, I just typed in the name of the map I was trying to find music for, or emotion I was trying to impress upon the player. I think I must have listened to over 1,000 songs in the course of making Edgar, although for a lot of these, it would only take a couple of seconds to realise the song wouldn't be suitable. A couple of my favourite tracks would be the boss music, the Library music, and Chaos' theme.

For the sound effects, I looked mostly on FindSounds, before moving onto FreeSounds. Again, finding a suitable sound effect is quite a task and I didn't simply want to stuff in any old effect just for the sake of it.

Medals

Much like the Xbox's Achievements and the PS3's Trophies, I decided to add in medals, which the player could earn by performing different tasks in the game. There are 54 medals in the game, a fair few of them being hidden from the player, as they are story related. Many people decry Achievements and Trophies, saying that people only play games to earn them, but I think they add great replayability to a game.

Some of the medals the player can earn in the game
Internationalization

In an effort to make the game available to as many people as possible, I added support for Gettext. This all worked out fine on Linux, but when I tried it out on Windows, I discovered that some languages displayed garbage. After trying a lot of different DLLs, I noticed that Frogatto had support for Russian, which was one of the languages that was corrupting in Edgar. I delved into Frogatto's source code and noticed that it was using its own implementation of Gettext. I contacted the developers and they helpfully explained that it was to avoid the very same issues that I was experiencing. Once I coded an implementation of my own, the problems went away.

The game running in Russian
Bugs

Of all the bugs that reared their ugly heads in the game, the worst had to be the map resetting bug. What would happen is that, after the player escaped from the Catacombs (a map where the player had very limited vision), the Fortress had reset itself, causing the player to end up getting trapped in the Catacombs again. I never encountered the bug myself, but numerous players reported it. I added reams of code to try and detect the bug and ran the game for hours at a time, making it randomly change levels, save the game, reload the game, but nothing reproduced the problem.

Finally one day I happened to be walking around one of the maps and I opened up the map editor to check something. I then tried to exit to another map and the game reported a fault and exited. I finally found out that some of the players had compiled the game themselves and had then run the map editor while navigating the catacombs. When the map editor had started up it had cleared out all the game's temporary data. Since the game could not find it anymore, it assumed that the player had not visited that map yet. I fixed the bug and since then no one has reported the resetting issue.

The Catacombs. Focus of the resetting bug
The other issue was compiling the binary release version of the game with GCC's O2 flag, which caused very subtle changes in the game's behaviour. I've since removed the flag as the game's really not demanding enough to warrant it.

The good

I'm pleased that I achieved so much and saw this project right through to the end. It's been a great way to stretch my imagination and watch the game evolve over time. I hope it's something I can look back on in a few years and be proud of. The game is a standalone story and I have no plans to ever write a sequel (though even if I did it wouldn't feature Edgar).

The bad

The game didn't work out exactly the way I wanted it to and it isn't especially popular, but I'm not too bothered about that part. There are a few bugs in the game that are slightly annoying, but don't detract from the gameplay so I may leave them as they are. Some of the feedback I received wasn't exactly encouraging and some of it is even slightly disturbing. I guess I know what it's like to have a stalker now...

Conclusion

The Legend of Edgar has been a great experience and I hope you've enjoyed playing it as much as I've enjoyed making it. 2.5 years is a very long time to spend on anything and I probably won't make anything on this scale again. In an environment where you cannot move for endless First Person Shooters, I wish there were more 2D platformers like Edgar.


Statistics

Development time: 2.5 years
Number of files: 8,343
Lines of code: 142,368 lines

10 comments:

  1. i've been following the game development a bit, and the current version is quite challenging. some hints on defeating the bosses might be nice. I'm currently stuck trying to get rid of the slime bouncing ball boss.
    First thing I did on the game was change the keys. using ctrl as a key just tends to accidently get odd keystrokes pressed which does fun things to windows :-). Happyland adventures is a nice 2d platformer, but unfortunately not open source, and only windows.

    ReplyDelete
  2. http://www.youtube.com/watch?v=gArQ8ZqzGdA

    ReplyDelete
  3. many thanks. I'll try using the tesla pack. The hints mentioned it, but I had difficulties getting it to do anything, so I'll give it another go. Any chance of a gargoyle demo as well. I get as far as the mini gargoyles and then can't do any more damage.

    ReplyDelete
    Replies
    1. it worked. great !. now for the gargoyle...

      Delete
    2. I got past the mini-gargoyles to the one with yellow spear, and got as far as the vertical multiple spear attack. hopefully that is the last attack for the gargoyles. It's very hard on the fingers. You must have worn out a keyboard or two setting/testing the difficulty level. :-)

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hey man, I really enjoyed reading this post! It ispired me some months ago to realize a platformer too. But I would to know your opinion about some technical stuffs (such as using OpenGL for 2D).

    ReplyDelete
  6. I am try to install the game on MacOs,
    I download the source code and install all the required dependency after that try to make the source
    it is return this error :

    msgfmt -c -o locale/ar.mo locale/ar.po
    make: msgfmt: No such file or directory
    make: *** [locale/ar.mo] Error 1

    ReplyDelete
    Replies
    1. Have you installed gettext? The program msgfmt is part of this package.

      Delete