DHTML Lemmings primer

By crisp on Sunday 30 May 2010 01:39 - Comments (10)
Categories: Javascript, Webdevelopment, Views: 29.158

It's been 7 years since I wrote DHTML Lemmings™. In internet-terms that's a lifetime; back in those days Firefox didn't exist yet (Mozilla Phoenix was on version 0.8 or something and IE6 was the dominant browser), we didn't have HTML5 or javascript libraries and web standards was just a grass roots movement. It was hard to make dynamic cross-browser things back then, yet I managed to create something that still works in browsers today.

I was an AS/400 programmer at that time but with a keen interest in web development, which eventually lead to me becoming a moderator of the web development forum on Tweakers.net (which I still am). Back in those days we held DHTML contests, and I went over the top in that last one recreating Lemmings™ in JavaScript.
Hype
The first online preview of DHTML Lemmings™ was an instant hit on the internet. I just presented it as a preview for the DHTML contest but it eventually got linked from everywhere. I was quickly running out of limits on my own web space and started hosting the game on Tweakers.net. That lasted until Stichting Brein started harassing me for this remake.
Effects on web development
I didn't notice the effect my 'experiment' had on general web development techniques until later. Almost a year after DHTML Lemmings™ saw the light Alistapart hosted an article on CSS sprites - a technique that I already used for DHTML Lemmings™ and which was later recognized as an official 'pattern' for dynamic image changes.

Nowadays using CSS sprites is mentioned as one of the things that improve clientside performance; having to download just one image instead of several saves a number of round-trips to the server and mitigates the need to pre-load other images to make some sort of state-change visible (like a mouse-over effect on a button).
The Lemmings™ sprites
Here are some of the sprites I made for DHTML Lemmings™. I made these by taking screenshots from the original Lemmings™ game (which I own):

http://tweakimg.net/g/l/lemming_fall_l.gif
http://tweakimg.net/g/l/lemming_fall_r.gif
http://tweakimg.net/g/l/lemming_float_l.gif
http://tweakimg.net/g/l/lemming_float_r.gif
http://tweakimg.net/g/l/lemming_walk_l.gif
http://tweakimg.net/g/l/lemming_walk_r.gif
Animation technique
The animation of the Lemmings™ is actually quite simple: it's just a timer that shifts the background-image of a Lemming™-object with every movement. Whenever a Lemming™ encounters an obstacle or changes state it merely changes the background-image used and enters another codepath.

Here's an example:
The code
Now the code used for the example above is a very simplified and improved version of the original code I wrote seven years ago. I'm going into the code only briefly, those of you that only know a thing or two using some libraries are going to be left out: learn proper JavaScript first! :P

I use some common utilities that are already present in libraries such as prototype:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Object.extend = function(dest, source, allowOverwrite)
{
    for (var prop in source)
    {
        if (source.hasOwnProperty(prop) && (allowOverwrite || !dest.hasOwnProperty(prop)))
            dest[prop] = source[prop];
    }

    return dest;
}

Object.extend(Function.prototype,
{
    bind: function()
    {
        var handler = this, args = Array.slice(arguments, 0), obj = args.shift();

        return function()
        {
            return handler.apply(obj, args.concat(Array.slice(arguments, 0)));
        }
    }
});

IE's background-caching problem
This used to be a big problem in my original code which only did changes on the background property of a Lemming™ object: IE seemed to re-download the background-image for every change I made on the background-position. I couldn't find a real solution at that time so I made my code take a different code-path for IE where I used used an <img> element positioned absolutely inside a fixed container instead of just a background-image. This slowed down the performance in IE, but it helped reduce the number of unnecessary requests to the server.

Later I found out that this could be solved with just a few lines of code:

JavaScript:
1
2
3
4
/*@cc_on
    try { document.execCommand('BackgroundImageCache', false, true); }
    catch(e) {}
@*/


I think that this has been resolved in IE7 or 8, but I just included this code ever since.
Initializing
First of all we need to initialize the playfield (determine width & height) and preload all images that may be used:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Lemmings()
{
    var preloadUrl = 'http://tweakimg.net/g/l/';

    Lemmings.preload = {};

    [['walk','b',8],['fall','b',4],['explode','s',16],['splut','s',16],['floatstart','b',6],['float','b',8],['confused','b',8]].forEach(
        function(lem)
        {
            Lemmings.preload[lem[0]] = {
                num: -32*(lem[2]-1),
                l: new Image(),
                r: new Image()
            };

            Lemmings.preload[lem[0]].l.src = preloadUrl+'lemming_'+lem[0]+'_'+(lem[1]=='b'?'l':'s')+'.gif';
            Lemmings.preload[lem[0]].r.src = preloadUrl+'lemming_'+lem[0]+'_'+(lem[1]=='b'?'r':'s')+'.gif';
        }
    );

    Lemmings.layout = document.getElementById('playfield');
    if (Lemmings.layout)
    {
        Lemmings.gridwidth = Lemmings.layout.clientWidth - 32;
        Lemmings.gridheight = Lemmings.layout.clientHeight - 32;

        for (var i = 1; i <= 10; i++)
            setTimeout(Lemming, i * 1000);
    }
}

The Lemming™ object
On to the actual Lemming™ object; it's pretty straight-forward:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function Lemming()
{
    if (this == window)
        return new Lemming();

    this.animate = function()
    {
        this[this.ani]();

        if (this.element)
        {
            this.imgleft -= this.imgleft == this.maximgleft ? this.imgleft : 32;
            this.element.style.backgroundPosition = this.imgleft+'px';
            this.element.style.top = this.top+'px';
            this.element.style.left = this.left+'px';

            setTimeout(this.animate, 70);
        }
    }.bind(this);

    this.top = -32;
    this.left = Math.floor(Math.random() * (Lemmings.gridwidth - 40)) + 20;
    this.dx = Math.random() > 0.5 ? 2 : -2;

    this.element = document.createElement('div');
    this.element.style.cssText = 'position:absolute;top:'+this.top+'px;left:'+this.left+'px;height:32px;width:32px;z-index:200;overflow:hidden;';
    this.element.onclick = this.click.bind(this);

    this.floater = Math.random() > 0.2;
    this.changeAni('fall');

    Lemmings.layout.appendChild(this.element);

    this.animate();
}


This creates a Lemming™ on invocation. It has a click-handler that will change its animation to 'explode', but it will start out as a 'falling' Lemming™

the this[this.ani](); will trigger this prototyped method:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fall: function()
{
    if (this.top < Lemmings.gridheight)
    {
        this.top += 2;
        this.anicounter++;

        if (this.top < Lemmings.gridheight)
        {
            this.top += 2;
            this.anicounter++;

            if (this.floater && this.anicounter > 16)
                this.changeAni('floatstart');

            return;
        }
    }

    this.changeAni(this.anicounter > 64 ? 'splut' : 'walk');
}


So movement is done by 2 pixels; in case of falling this will add another 2 pixels when the bottom has not been reached yet. After 16 movements the animation will either switch to a 'floater' (slower descent by using an umbrella), or continue falling. When the total fall-distance is too big the Lemming™ will 'splut'.

You can check out every behaviour in the sourcecode; it will speak for itself.

This isn't a true representation of how I wrote the game 7 years ago, and it doesn't explain how I manipulated the gamefield when Lemmings™ exploded, fell into a trap or reached the exit, but as the title states - this is just a 'primer'. If I were to write 'DHTML Lemmings™' at this point I would probably write it all differently...
The Game
For those of you that are just interested in playing the game online; fortunately there are working copies available on sites such as elizium.nu and many others. Those versions are not guaranteed bug-free, but it's no use emailing me about that - I don't control those sites and stopped supporting DHTML Lemmings™ already long ago.

Volgende: The internet anno 2010: all about the money? 06-'10 The internet anno 2010: all about the money?
Volgende: Heads or Tails? 05-'10 Heads or Tails?

Comments


By Tweakers user Jeoh, Sunday 30 May 2010 02:19

Wow. Zo speel je een simpel spelletje op het internet jaren terug, en zo lees je over de hele gedachte er achter. Bedankt voor het entertainment tijdens de tussenuren ;)

By Tweakers user John_Glenn, Sunday 30 May 2010 09:43

(Pssst: I don't think stichting Brein owns brein.nl :))

Wow, I never realised that was your work. Many thanks for lots of fun!! This primer is fascinating, too. Thanks!

By Tweakers user Woudloper, Sunday 30 May 2010 09:50

Unfortunately the revised Lemmings project Pumpkins never has been finished as this was also a cool remake of the Lemmings DHTML project you made.

Never noticed that you also invented the CSS Sprites solution. But there where more DHTML projects that where using some kind of solution like the Dynamic HTML Guru which also had a very nice site that was using DHTML.

By Tweakers user Afvalzak, Sunday 30 May 2010 12:30

Too bad the pumpkin version never worked, I just saw some great designs;)
It must be kinda cool to see your own game on several websites and being played by a lot of users;)

By Tweakers user WeeJeWel, Sunday 30 May 2010 14:34

Can I has JavaScript in meh blog pls?

Funny to read that you actually made that game, it's one of the most famous examples of webbased games.

By Tweakers user RoadRunner84, Monday 31 May 2010 09:15

Cool, ik speelde dit ook veel, vond het altijd jammer dat niet alle levels geimplementeerd waren in de versie die op spele.nl stond.
Ik heb deze een paar weken geleden nog aangehaald in een discussie over het nut/nood van flash voor het gebruikt van rich webcontent: "Waarom hebben we flash nodig als je zelfs lemmings in DHTML kunt maken?" Ik had geen idee dat het al zo oud was, en het loopt zo lekker soepel d:)b


By Tweakers user Spinal, Wednesday 9 June 2010 16:24

[mierenneuk]
straight-forward
[/mierenneuk]
;)

Lemmings is still great after all those years. Have you ever contacted DMA Design/Rockstar North/Mike Daily about the situation with Brein? I'm sure you could have reached an agreement with them.

By PHP Web Development Compa, Tuesday 20 July 2010 15:29

like your blog and thanks for sharing such a great information.

By ajakkes, Thursday 2 September 2010 09:52

So still no word from the copy-right holder about your project I understand?

Never felt the need to retry getting it up and running yourself?

Comments are closed