Jetboard Joust Devlog #53 – Camera Obscura

In the unlikely event that you’ve been reading this blog from the beginning you might remember that the camera in Jetboard Joust has always been something of a bugbear for me.

Way back in the early stages of development I wasted countless hours trying to get a camera action that I was happy with but, after failing miserably (once I started to add enemies the camera tracking solution I described in the blog post above proved woefully inadequate), decided to go for the simplest solution of locking the camera to the player centre-screen and leaving it at that.

Thing is – this kind of worked. Maybe because the camera is horizontal-only and the player’s horizontal motion smoothly accelerates and decelerates with no sudden changes? The game was perfectly playable and there were no sudden camera movements that looked out of place or anything.

But… it was just a bit boring and I always thought I could make it better, so as I’m nearing ‘playable alpha’ stage I decide once more to revisit. I set up a separate ‘Camera’ class (rather than putting all the camera code in the main game class as I had before) and attempted to approach things a bit more methodically.

Though it probably seems blindingly obvious I had a bit of an epiphany on realising that camera motion is really comprised of two separate elements, the camera target (ie what you want to be the centre of attention) and the way the camera moves to that target.

I decided to tackle the camera motion itself first. Many online articles talk about Lerp smoothing as the de-facto way to deal with this so this seemed a good place to start. Rather than working with Lerp smoothing on the camera right away I tried applying it to some sprite motion so it was much easier to see what was going on. The results of this really require a separate post so I’ve explained the process in a small tutorial with some example code here. The net result was an extended Lerp class that moves an object smoothly towards a target no matter how often (and by how much) the target position changes.

Now I had a way of making the camera track smoothly I set about working on the camera target (what the camera is moving towards). This was a lot harder! My thought process here had three key stages…

1. Player Velocity
One of my main concerns about the camera being centred all the time was that, if the player was moving quickly, lookahead was limited to half the screen width. In ‘Defender’ (the main inspiration for Jetboard Joust) the camera seems to be focussed something like 25% of the screen width if front of the player so I started with similar approach. I took a maximum lookahead value, worked out the player’s current velocity relative to the maximum possible velocity, and then placed the camera target the same proportion of the maximum lookahead value in front of the player.

The end result was OK but, as it doesn’t take long for the player to accelerate to top speed, the camera was moving far too quickly to its farthest position. Jetboard Joust plays differently to Defender in that there is more ‘dogfighting’ style gameplay when dealing with enemies (rather than hurtling in one direction as fast as possible) and somehow the camera movement needed to reflect this. So I tried a different approach…

2. Directional Duration
For my second attempt I set up a variable that gradually increased/decreased in value depending on how long the player was travelling in a particular direction. So, from stationary, if the player was travelling left it would take approx five seconds for the value to reach -1.0 (the minimum possible) and the inverse for travelling right. I then multiplied the maximum lookahead value by this variable and set the camera target appropriately. If the player changes direction I set the variable to zero for a quick turnaround.

This worked much better than my first attempt – the camera seemed to track fairly naturally with a long lookahead distance if the player was travelling any distance left and right and not switching erratically in the midst of battle. There were a couple of problems with this approach though, I didn’t like the obvious lag as the camera tried to ‘catch up’ with the player when accelerating from stationary and too often I’d end up forced to do battle at the edges of the screen.

3. Enemy Location
For my third attempt I adopted an entirely different tactic. I totally ignored player movement and instead positioned the camera entirely based on the location of nearby enemies (limiting this at a maximum value to ensure the player remains onscreen). I tried two approaches here:

3a. Enemy Position
Firstly I tried positioning based on how many enemies were to the left or right of the player. So I’d start by dividing 1.0 by the number of enemies within range and then adding or subtracting this number to a variable based on whether each enemy was to the left or right of the player. The result was a number between -1.0 and 1.0 which gave an indication of where the action was relative to the player – this number was multiplied by the max lookahead value to give the camera target position.

This strategy had some interesting results in the way the camera swung towards the action but overall it was too unreliable, I’d often find my attention was on a particular enemy and the camera would suddenly swing away as there were more enemies behind me or something. So I tried something simpler…

3b. Closest Enemy
This time, based on the logic that 99% of the time the closest enemy would be the one the player was concentrating on in a dogfight I simply targeted the camera at the closest enemy to the player (whilst ensuring the player remained onscreen).

The result of this was strangely compelling! Although the camera lurched left and right far too much it made the gameplay seem more intense, dynamic, and somehow just more ‘fun’. I was really surprised about how much the gameplay was enhanced by this simple approach – I just needed to find a way to tone it down a bit.

4. All Of The Above
So what I did next was use all of the above approaches and apply a weighting to each of them, hoping that this would provide some kind of ‘happy medium’. Fortunately, with a bit of tweaking, it did!

I still felt I could improve things further though so I made this weighting dynamic. I fixed the weighting based on player velocity at 25% and based the remaining 75% on either the closest enemy (if any enemies are within range) or the directional duration.

Another tweak I made was to slightly bias the ‘closest enemy’ calculation based on the direction the player is facing so that enemies in front of the player are considered ‘closer’ (I reduce the distance by 75%) than those behind. This makes the camera more likely to stick on enemy the player is currently engaged with.

The result is a camera that doesn’t lag, gives a long lookahead if the player is covering a lot of distance (and no enemies are present), and pans to the centre of the action when a dogfight is in place. I think it works pretty well.

I’m particularly pleased with the way the camera snaps to the closest enemy. I don’t know if it’s still too motion-sickness-inducing (maybe) but it feels much more visceral, seems to help with gameplay, makes it easier to get combos, and, in certain situations, just makes the player feel like a complete badass! So I think I’m going to park the camera now and wait for user feedback before I go tweaking it further…

Dev Time: 2.5 days
Total Dev Time: approx 96 days

previous|next

mockup_3x
Camera Motion Based On Player Velocity

mockup_3x
Camera Motion Based On Duration Of Movement

mockup_3x
Camera Motion Based On Closest Enemy

mockup_3x
Camera Motion Based On All Of The Above #1

mockup_3x
Camera Motion Based On All Of The Above #2

Lerp Camera Motion Smoothing Tutorial and Example Code

Recently I’ve been revisiting at the way the camera works in Jetboard Joust – something that has been a continual thorn in my side throughout the project.

When researching how to make the camera movement better I came across many references to Lerp smoothing but I found few, if any, detailed explanations as to how this works in practice and (particularly) ways to get around its significant limitations. Cue this post, by the end of which we’ll have a class that’ll move an object smoothly towards a target point and ‘brake’ so it reaches it exactly no matter how often and by how much the target is changed.

I’m just concentrating on one axis of movement here for simplicity but this approach can easily be applied to 2D or 3D vectors. For those of you with short attention spans you can just download the final class here.

So… Lerp is a quasi-acronym for ‘linear interpolation’ and used to create smooth movement between two points. It’s often used to smooth out camera movement but can equally be applied to sprites or anything else in 2D or 3D space. The basic equation for Lerp is very simple, it takes a position and target, works out the difference between them and then returns a value a specified amount along that path. Only values >0 and <1 are generally meaningful.

public static float Lerp(float position, float target, float amount)
{
     float d = (value2 - value1) * amount;
     return value1 + d;
}

If you are using this equation to move an object and each frame call it with the object’s current position (keeping the other values the same) the object will move a smaller distance each frame which results in a ‘slowing down’ effect as the objects reaches its target. Here’s an example of this type of motion applied to a sprite. In this case the sprite is moving approx 480 pixels at a frame rate of 60fps with a Lerp value of 0.025.


Basic Lerp Smoothing

Now this looks pretty good already but, as you’ve probably figured out, there are issues. The first one is that using this approach the object will never actually reach its destination – this can result in a slightly jerky looking motion as it reaches the end of its trajectory as well as presenting potential issues if you want to do something when the destination is reached.

One way of resolving this is to implement a minimum amount of movement per frame. This can be anything you want but for many applications it makes sense for this to be one pixel.

Here’s a simple ‘Lerper’ class that implements this – each frame the amount of movement (LerpVelocity) is calculated and, if the absolute value of this is less the minimum velocity we clamp it at the minimum velocity. Of course this means that the object might now be in danger of moving beyond its target point so we check for this at well and make sure it doesn’t.

public static float Lerp(float position, float target, float amount)
public class Lerper
{
    public Lerper()
    {
        Amount = 0.025f;
    }

    // Returns the amount of movement at this stage of the lerp
    private float LerpVelocity(float position, float target)
    {
        return (target - position) * Amount;
    }

    public float Lerp(float position, float target)
    {
        float v = LerpVelocity(position, target);

	// If this is less than the minimum velocity then
        // clamp at minimum velocity
        if ( Math.Abs(v) 0) ? MinVelocity : 0 - MinVelocity;
        }

	position += v;
	// Now account for potential overshoot and clamp to target if necessary
        if ( (v= target))
        {
            position = target;
        }
        return position;
    }

    public float Amount
    {
        get;
        set;
    }

    public float MinVelocity
    {
	get;
	set;
    }
}

Here’s an example of this in action. As you can see it looks much better…


Lerp With Minimum Velocity Applied

Still we have limitations though if we’re looking for really smooth movement. The next glaringly obvious one is that Lerp is ‘ease out’ only, the object will be moving fastest at the start of the sequence which makes it look like it’s being fired from a slingshot rather than smoothly accelerating from rest.

To resolve this I’m going to apply an ‘Acceleration’ property to my Lerper class. This ensures that, if the object is increasing in speed, the increase doesn’t exceed a specified amount. This requires a variable to store the amount of movement each frame.

public class Lerper
{
    private float previous_velocity;

    public Lerper()
    {
        Amount = 0.025f;
        Acceleration = float.MaxValue;
    }

    // Returns the amount of movement at this staget of the lerp
    private float LerpVelocity(float position, float target)
    {
        return (target - position) * Amount;
    }

    // Returns the next position with lerp smoothing
    public float Lerp(float position, float target)
    {
        // get the amount to move
        float v = LerpVelocity(position, target);
        // don't allow increases in velocity beyond the specifed acceleration
        if ( v>0 && previous_velocity>=0 && v-previous_velocity>Acceleration )
        {
            v = previous_velocity + Acceleration;
        }
        else if (v < 0 && previous_velocity  Acceleration)
        {
            v = previous_velocity - Acceleration;
        }
        // If this is less than the minimum velocity then
        // clamp at minimum velocity
        if ( Math.Abs(v) 0) ? MinVelocity : 0 - MinVelocity;
        }
        // Remember the previous velocity
        previous_velocity = v;

        // Adjust the position based on the new velocity
        position += v;
        // Now account for potential overshoot
        if ( (v= target))
        {
            position = target;
        }
        return position;
    }

    public float Amount
    {
        get;
        set;
    }

    public float MinVelocity
    {
        get;
        set;
    }

    public float Acceleration
    {
        get;
        set;
    }
}

Here’s an example of this in action – now we have a nice ‘ease in’ to our Lerp motion. Here I’m using an acceleration value of 0.25f.


Lerp With Maximum Acceleration ‘Ease In’ Applied

There’s one other problem I’m going to address though. This ‘ease in’ effect works nicely if the object is initially at rest or moving in the same direction but if the object had previously been moving in the opposite direction we’re still going to get a nasty jolt. Here’s an example where I’m changing the destination to the opposite side before the previous Lerp has reached the end of its trajectory.


Sudden Changes In Direction Result in A Nasty Jolt

So we’re going to check for this in the code and again apply a maximum amount by which the velocity can change each frame. Remember that because we’re changing direction the absolute value of the current velocity at the point of change is the sum of the absolute value of both the current and previous velocities, ie a change in direction from 10 to -10 would result in a new velocity of -20.

A side-effect of this is that the object will probably end up moving away from the target until it’s gathered enough momentum to change direction. As the code that clamps to the target position assumes we are moving towards the target we have to max out the target value if this happens.

We also have a special case check for our minimum velocity here as we need to make sure that if the minimum velocity is applied its applied in the direction that moves towards the target (otherwise the object could get stuck moving in the wrong direction).

public class Lerper
{
    private float previous_velocity;

    public Lerper()
    {
        Amount = 0.025f;
        Acceleration = float.MaxValue;
        MinVelocity = 0;
    }

    // Returns the amount of movement at this staget of the lerp
    private float LerpVelocity(float position, float target)
    {
        return (target - position) * Amount;
    }

    // Returns the next position with lerp smoothing
    public float Lerp(float position, float target)
    {
        // get the amount to move
        float v = LerpVelocity(position, target);
        // don't allow increases in velocity beyond the specifed acceleration (ease in)
        if ( v>0 && previous_velocity>=0 && v-previous_velocity>Acceleration )
        {
            v = previous_velocity + Acceleration;
        }
        else if  (v  Acceleration )
        {
            v = previous_velocity - Acceleration;
            // we might actually end up moving away from the target
            // here in which case we adjust the target so we don't get
            // clamped to it later
            if (v  0 && previous_velocity  Acceleration )
        {
            v = previous_velocity + Acceleration;
            // we might actually end up moving away from the target
            // here in which case we adjust the target so we don't get
            // clamped to it later
            if (v > 0-MinVelocity)
                v = MinVelocity;
            else
                target = float.MinValue;
        }

        // If this is less than the minimum velocity then
        // clamp at minimum velocity
        if ( Math.Abs(v) 0) ? MinVelocity : 0 - MinVelocity;
        }
        // Remember the previous velocity
        previous_velocity = v;

        // Adjust the position based on the new velocity
        position += v;
        // Now account for potential overshoot and clamp to target if necessary
        if ( (v= target))
        {
            position = target;
        }
        return position;
    }

    public virtual void OnTarget(){}

    public float Amount
    {
        get;
        set;
    }

    public float MinVelocity
    {
        get;
        set;
    }

    public float Acceleration
    {
        get;
        set;
    }
}

Here’s what happens when you suddenly change the Lerp target with this code applied – as you can see we now have a nice smooth movement no matter how often we change the target value.


Lerp With Maximum Acceleration ‘Ease In’ Applied

And that’s pretty much it. You can download the final class file here – in the final version I’ve also added a MaxVelocity property, a delegate to be called when the object reaches its destination, and tidied up the code so its not quite so verbose.

If you are in the process of coding a camera for a 2D game I highly recommend you check out this excellent article.

If you are looking for something to move smoothly between two points over a specified number of frames then you’re probably better off using some kind of tweening rather than Lerp. Check out Robert Penner’s excellent tweening/easing functions or the ‘smoothstep’ algorithm approaches described here.

Hopefully this article is of use – if so please consider following me on Twitter.

Jetboard Joust Devlog #52 – Go Logo!!

Been really ill over Christmas with a very heavy cold/flu and it hasn’t got any better this year as it appears to have developed into sinusitis. Now at least I know why I can barely walk up the stairs without feeling like my head’s going to split open.

So it’s been tough trying to work this week – but I’ve managed to get some done! It was quite a relief not to have to dive right into coding but to start with a fairly significant art task instead – the design of the game logo.

I had an idea of the type of thing I wanted but, as with all art projects, I started by gathering reference material and trying to whittle down ‘stuff I like’ into ‘stuff I like but might have a vague hope of achieving with my limited skills in a reasonable amount of time’.

I eventually settled on four key reference logos…

1. National Petrol
The ‘National’ brand is long-since defunct but I have fond memories of it from my childhood as National petrol stations were the only place in the UK that you could buy Smurfs! I really the the dynamism and simplicity of the main pictogram which has an almost Soviet constructivist feel.

2. Go Jetters – CBeebies
I’d never heard of this TV show (my kids are too old) but I was looking after my niece and nephew over the holiday and they had an activity book from the series. The ‘jet’ reference is obviously relevant and again I like the simplicity. The oblique effect on the type is subtle but adds a lot of dynamics.

3. Trans Am – Ultimate Play The Game
OK so this one’s kind of horrible but cool at the same time. As a kid the ‘Ultimate’ games were like the holy grail of Spectrum gaming. I originally looked at the ‘Jet Pac’ logo for obvious reasons but thought it too heavy and overworked. There’s more of a sense of motion to the ‘Trans Am’ logo and I like the ‘silver dream machine’ feel to it – even if that 70s airbrushed effect is something I’d normally avoid like the plague. It works in context.

4. Asteroids – Atari
I’m a massive fan of the Atari arcade service manual art but the trouble with it as reference is that it’s very intricate and would take more time and skill than I have at my disposal to pull off effectively. The nice thing about the ‘Asteroids’ logo is that it uses a very straightforward typeface in a pretty straightforward manner yet still works extremely well.

I decided to start working in Illustrator rather than Photoshop and to restrict myself to pure black and white. Restrictions are good – and I knew that if I had something that looked good in a single colour it would be easy to add colour later whereas trying to retrofit a colour logo into black and white can be a nightmare. It’s a similar approach to the one I’m taking with the actual game art.

Once I had my reference in place I began experimenting with different typefaces*. Initially I thought I needed something that looked a bit ‘sci-fi’ but, despite finding some nice fonts, everything I tried looked either incorrectly proportioned or too gimmicky. The only typeface that seemed to hit the spot was Helvetica Neue Black Oblique which is very similar to the font used in the Asteroids logo. I rotated the letters anti-clockwise so that the uprights sat directly vertical, spaced the letters very tightly (so much so that some of them joined together) and finally felt like I had something I could work with. I also sheared the logo slightly so that it sat at an exact 25% gradient in order to make it easier to convert to pixel art.

Next step was to add something to make the logo seem more unique and give it more of a feeling of motion. I started by add some ‘wings’ to the tops of the ‘J’ characters based on the ‘winged helmet’ of the National logo. This worked right away! I tried some more curvaceous and illustrative alternatives as well but these all seemed too flouncy so the hard-edged brutalist approach won out ( a good job really as it would be a whole lot easier to realise as low-res pixel art). I also added similar ‘wings’ to the right side of the logo which seemed to balance better.

I then tried to increase the motion effect by adding Dyno-Rod style arrow-type shapes at the top-left of letters where there was a lot of negative space. As well as making the lettering seem more dynamic this also had the benefit of filling in the negative space, thus making the letters seems as if they were spaced more evenly.

Lastly I felt the logo needed something to kind of tie it all together a bit more so I experimented with various ‘underline’ effects and illustrative elements, eventually settling for a simple ‘double underline’ consistent with the National-style ‘wings’ on the letters. It doesn’t look like it but the little angled section on the right of the underline took a long time to get right!

Once this was in place I noticed that the ‘wings’ gave the type an almost 3D effect – I liked this (for some reason it reminded me a bit of the 20th Century Fox logo) so I tweaked the angles and lengths to exaggerate the effect.

At this point I was pretty happy with the vector version so I moved on to converting it to low-res pixel art. I began working at a resolution of 256 pixels wide as this seemed to scale nicely for the title screen.

The most time-consuming stage of creating the pixel art version was tidying up the original, rasterized, black and white version so that it looked as good as possible. I realigned all the angled lines so that any that run parallel ‘step up’ at the same time and also changed the angle at the end of the ‘wings’ so that this ran at 45 degrees which looked a lot tidier and didn’t really seem to affect the overall feel.

After this it was a matter of playing around with outlines, drop-shadows and highlights in Photoshop until I found something I liked then tidying it up manually. The final stage was to add some ‘shine’ to the letters (a bit like the ‘Trans Am’ logo) and a final bit of texture by manually dithering the edges of the ‘shine’ and adding some rivets.

I’m pretty pleased with the end result – it looks clear, dynamic, and has a kind of accidental retro/art-deco sci-fi quality that I like. Reminds me of ‘Flash Gordon’ or some of the work by French comic artist Moebius, neither of which are reference points to be ashamed of. The pixel art version could still do with more texture (possibly) but it was hard to do this with the limited palette I’d set myself, and these things can always be better so maybe I’ll revisit at some stage – for now though it’s time to get it integrated into the game!

* Whilst doing this I came across this really useful tool – it’s like Shazam for fonts and actually works (did for me anyway)!

Dev Time: 2 days
Total Dev Time: approx 93.5 days

previous | next

mockup_3x
Gathering Reference Material

mockup_3x
Unsuccessfully Experimenting With Typefaces

mockup_3x
The Final Flat Vector Version

mockup_3x
The Final Pixel Art Version (Click To See 2:1)


Two Day’s Work In Ten Seconds

Jetboard Joust Devlog #51 – Under Instructions

This has been another one of those tasks I’d been putting off for a long time. The main app framework, options menu, instructions, etc etc.

I already had a set of classes that handled all this stuff, including a menu, instructions, highscores, about pages and a bunch of other functionality but it was incredibly bloated and old. In fact, looking back at the original file I see I created the first version in 2003 so that’s 13 years ago! It’s been ported from JavaME and had all sorts of cruft in there to deal with the idiosyncrasies of ancient mobile phones plus maintaining backwards compatibility with titles that will (thankfully) never see the light of day again. It’s served me well but it was time to start again (more or less) from scratch – even though I knew that was to be a fairly painful process.

So I kept some classes that were written comparatively recently and were relatively bloat-free (the actual menu itself and the highscore management stuff) and got rid of everything else, adding the necessary code back in a piece at a time, refactoring it, and going through every line to trim all the bloat. It took me over a day just to get the thing to compile!

A part of the process I also streamlined all the graphics scaling to work with pixel-art games (where everything is drawn from one set of graphics rather than loading in a separate set for different screen-sizes) and improved some of the visual UI feedback, animations and tweening. There may be a bit too much ‘bounce’ in some of the tweens, I’m using an ‘exponentially decaying parabolic bounce’ tweening algorithm that is all over the web but I can’t find anything to specify the ‘amount’ of the bounce and the maths is a bit over my head. I will try and get my head around it at a later date.

And now I have a streamlined, bloat-free ‘main app’ class that manages the main menu, highscores and instructions. There is still some functionality I need to add (and some that has moved to new classes) but I have ditched almost 4000 lines of confusing and redundant code which is a good feeling.

One aspect that I spent a fair bit of time on was the formatting of the instructions pages. I wanted a system that would be very easy to edit, reuse, and apply to other elements of the app (such as about screens etc) so created generic InAppDoc and InAppDocViewer classes. An InAppDoc is created from an XML document which defines various elements such as pages and paragraphs of text but can also have custom elements that can be rendered by a specific game (such as individual sprites or sprite groups). It should be straightforward to extend this system to cope with localisations or even to add custom tags for more complex page layouts etc. An example of the XML used to create the series of test pages on the right is here. I need to fix those flashing arrows!

Dev Time: 7 days
Total Dev Time: approx 91.5 days

previous|next

mockup_3x
The Tweaked Main Menu With Added ‘Bounce’

mockup_3x
Some Placeholder Instructions Pages To Test The InAppDoc classes

Jetboard Joust Devlog #50 – Sound Thinking!

Woohoo – it’s my 50th DevLog!! Sadly no-one is throwing me a party (or any money).

Just been finishing off the final pieces of missing audio for the alpha version. As before I’ve done everything using the DSI Tempest and a couple of fx processors – namely an ancient Boss RV-1000 reverb and, this time, a Pigtronix Echolution 2 on delay duties.

The only one that caused an issues was the ‘combo’ effect – I wanted an arpeggio-style sequence that played for longer depending on how much of a combo was awarded. Pretty easy to build the audio from a series of separate notes and step forward each time another enemy is destroyed in the combo ‘chain’, harder to stop several notes from all playing at once if several enemies are destroyed simultaneously (ie with a single shotgun blast).

So I built a ‘SequenceAudioPlayer’ class that steps forward in the sequence each time Play() is called but that queues notes to be played after a set interval if Play() is called too quickly. This way I get a nice ascending arp even if multiple enemies are dispatched at once.

You can hear a bunch of the new sounds (as well as some of the old ones) in these short gameplay vids.

Dev Time: 1.5 days
Total Dev Time: approx 84.5 days

previous | next

Jetboard Joust Devlog #49 – Gun Control

This week I’ve added a couple of extra jetboarding enemies and a new weapon – and I think I have enough content now for a playable demo! Just need to to sort the main menu out (groan) add a few more sound fx and (probably) background music. Here’s a bit more about the new stuff that’s been added…

The Gatling Gun
In operation this is really like a rapid-fire pistol, therefore it was pretty easy to subclass the existing ‘pistol’ weapon and change a few parameters to get it working. The hardest thing was getting the recoil to feel right – I wanted enough recoil for it to feel slightly ‘out of control’ and unwieldy but (obviously) not enough to be unplayable. I’ve given it a slightly lower damage-per-bullet than the pistol but this is more than made up for by the rapid firing.

Improved Shotgun Blast
One thing I realised whilst playtesting was that the existing shotgun blast was just nuking everything within its range rather than taking account of the fact that some enemies would shield others from the blast. Fixing this accurately seemed like it would be a mathematical nightmare (I’m not too strong on geometry) but I managed to implement a slightly ‘fuzzy’ solution which I think will be good enough. What I do is order all the enemies that intersect the blast region by their distance from the gun barrel. I then iterate through them in order creating a vertical ‘blocked zone’ for each one based on the combined height of the previous enemies in the list. If more than 50% of the current enemy intersects this ‘blocked zone’ I assume it has been shielded from the blast.

The Assassin
This type of enemy is pretty similar to the omnipresent ‘minion’ only they don’t try and abduct babies, they just go all out for attacking the player. This enemy uses the ‘skullhead’ sprite that I’d already designed and required no additional AI work (just adjusting existing parameters) so it was really easy to get up and running, unlike the next one…

The Bodyguard
Bodyguards have a pretty specific AI in that their main aim in life is to protect other enemies that are in the process of baby-snatching. I thought this would be pretty simple to get working but it was a lot harder than I thought to get something that looked decent. This is the basic framework of rules I ended up with…

– Bodyguards seek out the closest baby-snatcher to protect
– Once a bodyguard gets close enough to protect someone they’ll never desert them
– Only two bodyguards can protect a baby-snatcher at one time
– If there’s more than one bodyguard protecting a baby-snatcher they’ll stand guard on opposite sides
– Bodyguards never stray to far horizontally from the baby-snatcher they;re protecting but they will move vertically to attack the player

…there’s a bit more to it than that but these are the key AI decisions that are made. Bodyguards have a lot of health but move relatively slowly and I’ve tried to design the sprite to reflect this, hence they look rather ‘chunky’ compared to the other jetboarding enemies.

I haven’t fully playtested all this yet but will be doing so over the next few days as I add the outstanding audio fx.

Dev Time: 3 days
Total Dev Time: approx 83 days

previous | next


Whoops – Too Much Recoil On The Gatling Gun

mockup_3x
The Final Gatling Gun In Action

mockup_3x
The Magic Shotgun – Enemies Should Shield Each Other!

mockup_3x
Fixing The Magic Shotgun

mockup_3x
Trying To get The Bodyguard Looking A Bit More ‘Hench’!

mockup_3x
Bodyguards Doing What They’re Paid To Do

mockup_3x
Assassin Enemies Wielding Gatling Guns – Beware!

Jetboard Joust Devlog #48 –Game (Not) Over

Since the last post I’ve mainly been tweaking – playing the game over and over again, adjusting the various difficulty parameters and noting down and fixing various bugs that crop up. My aim at this stage is to get to the point where I have an ‘alpha’ version that can exist as a playable demo, albeit with a very limited set of weaponry and enemies. I’m getting there!

So, in no particular order, heres some of the stuff I’ve been working on…

The Dawdle Police
I’ve made the ‘bastard‘ enemies operate similar to the ‘baiters’ in Defender in that they only appear once the player has spent a certain amount of time trying to complete a level and (unlike other enemies) don’t need to be destroyed in order to complete a level. More bastards appear the longer the player hangs around adding even more of a sense of urgency to the proceedings.

The Pyramid
Some of the more observant readers may be wondering why there are two warp gates that appear at the end of each level. Well, in a nod to ZX Spectrum ‘classic’ The Pyramid, the gate you choose affects the level you’ll appear in next, so rather than a linear arrangement of levels we have a pyramid-type structure with each row defining the difficulty level and each column a different combination of enemies, terrain and weaponry. I will probably add special collectables at certain locations to add an exploratory element to the game. I’ve always had this functionality planned from the start but have only just implemented it properly.

Mutant Frenzy
In another nod to Defender, now if you’re careless enough to let all your babies get abducted a ‘mutant frenzy’ is unleashed! Every jetboard-riding enemy mutates and every remaining attack wave is released. Basically it’s total chaos and you probably won’t last long after this happens!

Score Combos
Killing enemies in quick succession now ups a bonus multiplier which multiplies both the score and cash awarded. It’s a good way to earn extra cash and I like the way it adds to the ‘pinball’ feel of racking up large amounts of points.

Extra Lives
Extra life pickups now appear when a certain score threshold is reached. You’re going to need them!

Game Over
I’ve added a fixed number of lives per game and the ‘game over’ message. Spent quite a while tweaking the ‘game over’ effect!

Jetsuit and Jetboard Upgrades
I’ve now fully implemented the code that makes your jetboard and jetsuit upgradeable items. The upgrade state of all equipment is now persistent across games.

Plus a myriad of other small improvements and bugfixes.

I may now add one extra weapon type (machine gun) and possibly a couple of extra enemy types before moving on to add a few elements of missing game audio, background music, and then a first implementation of overall menu UI.

Dev Time: 5.5 days
Total Dev Time: approx 80 days

previous | next


Kill Enemies In Quick Succession For A Bonus Multiplier

mockup_3x
You’re Gonna Need An Extra Life Or Two…

mockup_3x
Must… Try… Harder… Next… Time…

mockup_3x
Your Jetsuit And Jetboard Are Now Fully Upgradeable

Jetboard Joust Devlog #47 –Having Difficulty With Difficulty

With all the core gameplay elements pretty much in place it’s time to get back to some serious gameplay testing and start thinking in more detail about how I manage the difficulty curve within the game.

I’d already put quite a bit of thought into this as discussed here, but, as is so often the case, Jetboard Joust has grown in complexity fairly significantly since I posted that and my ‘procedural difficulty’ code needed to be reworked in a major way.

I’m still starting from a similar standpoint in that I allocate a difficulty value for each level and then create random waves of enemies that total that difficulty score. Now, however, I have different RPG-style ‘character-levels’ of enemies and weaponry to consider.

Firstly what I do is allocate a series of character-level ‘stats’ to each enemy and weapon type. I set a minimum and maximum value for each stat and the values in between are calculated automatically. Some values (e.g. health, weapon range) are consistent across all enemies and weapons but not all. Every weapon and enemy has a ‘difficulty’ stat.

I then create an EnemyDefinition for each enemy/weapon combo. This is a lot of definitions as I have to have create a separate definition for every combination of each character-level of enemy and weapon.

When a level is created my first approach was to split the total difficulty score into a set of six ‘batches’ of enemies that are released at set time intervals. The enemies that make up each batch were chosen at random from the EnemyDefinition collection. If a batch of enemies is destroyed the next one is released immediately.

This worked pretty well but the combination of enemies was too random and in order to get a more playable selection I needed to implement a few restrictions…

1. Every enemy and weapon type have an ‘intro level’ so that they don’t appear until a certain level of the game has been reached.

2. Every enemy and weapon type have a’level up rate’ that affects the way their ‘character-level’ progresses throughout the game – so, for instance, an enemy with a ‘level-up rate’ of 2 and an ‘intro level’ of zero could only appear at character-level 1 for the first two levels of the game, then at character-level 1 or 2 for game levels 3 and 4 and so on.

3. Only certain enemies will try and abduct the alien babies(!) – as this is fundamental to the way the game plays I needed to ensure a certain amount of ‘baby-chasing’ enemies per batch. I know that sounds a bit dodgy!

After implementing these restrictions the enemy selection was much better but I realised the process was still flawed. As I was choosing enemies from the set of EnemyDefinitions at random (albeit with the above restrictions) the selection was skewed towards certain types of enemies. There would always be many more valid definitions for lower ‘intro-level’ enemies (especially as we have a definition for every character-level and weapon combo) resulting in far too many of certain enemy types in the game.

To solve this I needed to create a structure to store the valid EnemyDefinitions that was not simply a flat list – so I created the wonderfully-named EnemyDefinitionBucket class.

A EnemyDefinitionBucket contains a horrible-looking data structure that’s defined like this…

SortedDictionary<EnemyTypes, SortedDictionary<WeaponTypes, List>> dictionary;

..so first we have a collection of every valid EnemyType in the bucket, then for each EnemyType a collection of each valid WeaponType, and finally for each EnemyType/WeaponType combination a list of each valid character-level of enemy and weapon that fits that combination.

Now when I choose a random EnemyDefinition I first select a random EnemyType, then a random WeaponType, and finally a valid EnemyDefinition that matches that combination. This ensures that all enemies and weapons appear on equal footing.

Only that wasn’t quite good enough! In practice the enemy/weapon selection needs to be skewed towards those that have an ‘intro level’ nearest the current game level. Hence the reason I have used SortedDictionary rather than a standard Dictionary – this way the enemy and weapon type ‘keys’ can be indexed in order of ‘intro level’ and I can implement a sine-based distribution curve that favours the ‘higher’ items when choosing at random. The EnemyDefinitions are also stored in a sorted List and selected in a similar way.

Finally I think that’s done it, now on to tweaking the various ‘character stats’ which is another rabbit-hole.

And, sorry, this post doesn’t contain much to look at so I’ve just included some random gameplay footage – finally found an app Capto that grabs at 60fps!

Dev Time: 3 days
Total Dev Time: approx 74.5 days

previous|next

mockup_3x
The Current State Of Play

Jetboard Joust Devlog #46 –You Can’t Take It With You

Yeah, I know, been a bit quiet round here. Had a bit of time off!

Been working on some more ‘polish’, implementing stuff I’d been putting off for a while and getting various aspects of the gameplay to work together. Here’s what’s been on my ‘to do’ list these past few days…

Add More Cash
I only had one denomination of coinage which clearly wasn’t going to be enough to cover all the cash rewards in the game, not without spawning a ludicrous amount of pickups anyway, so I’ve designed and added a few more. Now there’s five different types of coin 1, 5, 10, 50 and 100. I may have to add a 500 later on.

Bloodstains
One of my favourite mechanics in the ‘Souls’ games is the way that, when you die, you lose all your ‘souls’ (the game’s currency) and can only retrieve them by returning to the place you last died and touching your bloodstain. It can be incredibly annoying losing all the ‘cash’ you’ve earned but it really makes dying something to be avoided (unlike in many modern games where dying is practically meaningless) and adds an extra tension to the next life too. I’ve implemented a similar mechanic whereby you lose all your cash on death and have to return to your abandoned jetsuit in order to retrieve it.

Weapon Unlocks
I’ve now properly implemented the feature whereby picking up an enemy’s jetboard unlocks the weapon they were carrying for your own use. A weapon crate will automatically spawn when this happens giving the user a chance to pick up the weapon they have just unlocked.

Upgrade Equipment
I’ve properly implemented this as part of the gameplay cycle so you are given a chance to upgrade equipment at the end of each level. This took longer than it should! Also added the jetsuit itself as an upgradeable item.

Redo Jetboard Particle FX
I was never that happy with the vertical thrust effect on the jetboard so I’ve redone this giving it a more ‘anti-gravity’ quality. I’ve also tweaked and re-aligned the particle fx for the horizontal thrust.

That’s it. I am getting there, slowly. The next task is to revisit the ‘difficulty’ algorithms for level creation to make them take account of different weapon and enemy levels.

Dev Time: 4.5 days
Total Dev Time: approx 71.5 days

previous


New Denominations Of Coinage

mockup_3x
The Infamous ‘Bloodstain’ Mechanic


Capturing An Enemy’s Weapon


Picking Up An Unlocked Weapon

Jetboard Joust Devlog #45 – Upgrades Are Available

Ah, UI work! Truly the most enjoyable part of building a game. I love building user-interfaces, it’s so much more fun that all that irritating ‘gameplay’ stuff. How I wish I could churn out menus and buttons and fiddle with bitmap fonts all day!

Not.

Building a decent user-interface is often a fiddly and mind-numbing task, yet it’s an absolutely essential part of the overall gameplay experience so cannot be skimped on. Fortunately the only menu-driven part of Jetboard Joust is the ‘weapon upgrade’ stage so I don’t have too much to worry about, but I need to get it right nevertheless.

Seeing as I have just finished the first alternative weapon I decided to bite the bullet (no pun intended) and just get on with the weapon upgrade screens. As expected it was a fairly fiddly and time-consuming task.

So, first step – design the UI. This part wasn’t so bad, I knew what info I had to get across so just went for a layout that was as clear and straightforward as possible whilst retaining a degree of visual interest. I wanted to keep consistent with the game HUD as well so in that sense a large part of the ‘look and feel’ was already defined. It took a few hours to get something I was happy with.

Only problem was it became apparent that I needed a second, larger bitmap font in order to bring some variation to the design. I went for one in the style of the numbers in the HUD which seemed to work well but, as with all bitmap fonts, it took a lot of fiddling around to get it working correctly.

I also thought I needed larger icons for the upgradeable items so had to design an icon for the pistol and shotgun. At the moment I’ve set this at 32*32 though am wondering whether I might need to accommodate different sizes.

Next step – build the design in code. I decided to do all the drawing in code so that it would be easy to expand the text boxes etc if I needed to rejig the design. Again, a pretty tedious and time-consuming process. It paid off though as there were a couple of instances where I needed to change things in the layout (due to underestimating the space I’d need for text) and this was simply a matter of changing the value of a couple of variables rather than redrawing everything in photoshop. The UI is drawn in three separate layers, the background ‘connectors’, the boxes and lastly the text and icons.

I then needed to get the text read ‘live’ from actual data. As it may not just be weapons that are upgraded I defined an IUpgradeable interface that specifies
the functionality an object must implement to be ‘upgradeable’. Maximum and minimum values are set for the various stats and upgrade costs and the values for each ‘level’ calculated on the fly. Spent quite a while on this and implementing it in the two weapons I’ve design so far.

This all worked fine but I couldn’t help feeling that the UI just felt rather ‘dull’. I needed something to give it a bit more life so decided to try and implement a kind of ‘radio static’ type effect along the lines of the interference effect you get on the scanner when the player takes damage. The scanner interference shader was the obvious place to start and by using this, and an awful lot of tweaking, I was able to get an effect I was happy with. I didn’t end up changing the shader code at all, just messing with various parameters. Only the layer with the text and icons is drawn using this shader.

Last task – make it work! I’ve tried to make the process as clear as possible for the user and give visual feedback where necessary – I’ll also add auditory feedback at a later stage. You can see I’ve ‘greyed out’ the upgrade cost and button if the user doesn’t have enough cash and show a confirmation message if the user does purchase an upgrade. the process is fairly simple so hopefully I shouldn’t need much more than that but I’d be interested in any feedback…

Dev Time: 5 days (told you this was time-consuming)
Total Dev Time: approx 67 days

previous | next

mockup_3x
First Mockup Of The UI

mockup_3x
The New Bitmap Font

mockup_3x
New Weapon Icons

mockup_3x
The Final Working UI With ‘Interference’ Shader