In Game Camera

Good camera implementations in games are super interesting, often hiding a lot of subtlety from the player in order to present the best view of the action. Don’t believe me? Check out this quick run-down of some of the camera-modes in Super Mario World.

Since cameras are good sport to figure out (and a few of my students have asked recently), I thought I’d quickly talk about what I’ve been doing with mine. It’s probably worth mentioning that this is pretty simple, so this write-up is unlikely to contain any surprises for the seasoned dev. Hopefully it’ll still be useful if you’re just starting to think about this stuff, though.

What Does My Camera Need To Do?

The inspiration for my current project is obviously the early Zelda games, but Link Between Worlds on the 3DS is probably the best thing to compare against, as it’s 3D and on “modern” hardware.

Zelda-type camera implementations have discreet modes depending on what the player is doing / what type of gameplay is in the area. You can roughly list the main ones as:

  • Free Follow: The camera will track ground height and try to move in front of the player’s direction of travel so they have a better view of what’s in front of them.
  • Locked Follow: Similar to free, except ground-height will be ignored. Think of dungeon rooms with multi-height platforms… the camera’s height may or may not be fixed in these locations, depending on gameplay. But most commonly it’s fixed to one height.

Both of these modes can be restricted to an area. IE: the camera movement will be clamped to predetermined bounds. This is most obvious in the Dungeons where you transition between rooms — the camera doesn’t show you the next room until you go through a door — but the same happens in outdoor areas. You’ll often transition (with a forced scroll) from a wooded area to a “village” or some such. I’ll be doing the same thing in my game to cover up the level streaming between outdoor zones.

  • Focus: Player has interacted with an object, and/or a conversation is happening, so the camera moves the focal point away from the centre of the screen to an area above the dialog box. Or, tracks back from its leading position to re-focus on the player.
  • Combat: Similar to Focus, but tracking the player and the object they’ve locked on to, with priority given to the locked-target’s location.
  • D-Pad Override: In the follow modes — and possibly combat, I’d need to check — the player can manually move the camera using the D-Pad, but it will bounce back when they let go. This is also bounds restricted, depending on location.

You can also argue that the transitions in and out of these modes are also discreet states that require handling (and they will, in code), but for simplicity I’m going to skip over those here.

My first pass at writing this camera system was to break down the camera logic into a state machine, roughly along the lines above, and start building functionality. This was a mistake. It lead me down the path of writing each discreet camera mode as its own, “sub-”state machine, which just overly complicated everything.

I quickly threw that away and started again with the simplest state:

Free Follow

The major thing we want here is to “lead” the player — to make sure that the camera gets in front of the player in order to show them more of where they’re heading. This means:

  • The camera needs to be able to move faster than the player (so a tweakable is getting exposed here)
  • The distance the camera can get ahead of the player needs to be defined (another tweakable) and observed.
  • The camera needs to know which direction the player character is traveling.

Note that third point. We don’t want to know what the character in the world is doing, we want to know the player’s intention.

If the camera is attached, in some way, to the rotation of the character in the world then this will probably feel laggy. The character isn’t going to “flip” from one direction to the next, instantly. Instead it’s (most likely) going to rotate over a few frames to look smoother on the eye. This smoothing is acceptable in terms of moving the character, but it’ll make the camera do long, slow arcs, as it follows the rotation. This will probably look wrong, and definitely feel weird.

Getting the player’s intention is as simple as reading the current stick input. This gives us a direction vector, which we can normalise, and then multiply out by any distance we want. The result of this — when added to the player’s current position — is a new, “wanted” position, with no other considerations taken into account.

If you imagine a circle of such wanted positions — one for each direction the player could be heading in, centered around the player — then this ring represents the limits of where the camera can move when it’s leading the player:

The Outer Ring

To move the camera from its current position to our new “wanted” position, we need to work out the trajectory to move along. We’re not going to jump to the outer ring in one go, but rather in a series of small steps.

We can do this by subtracting the wanted position from the camera’s current position, which gives us a directional vector between the two locations.

To move along this direction vector, we normalise it, multiply it by: (camera’s movement speed * DeltaTime) and then add the result to the camera’s current location. This gives us an intended “final position”.

A Slow Homing Missle

Yup, we’ve made a really slow homing missile. Simples.

Now we have the “final position” all that’s left is the conditional stuff:

If the final position is too far away from the player (outside the ring we identified in the first step) then we need to limit it. One way is to check the Size() of the vector between the intended final position and the character, and use GetClampedToSize() to find the position on the outer ring, if it’s too long.

If we’re going to track the ground height, then we need to make a few choices. We could use a fixed height between the camera and the player character, but this feels a bit off to me when the character can jump.

We could just raycast from the camera’s position, down to the ground, and then fix our height based off that, but then we’re going to get lots of weird issues when the camera “falls off” a cliff that the player is looking out over.

So instead I opted to raycast down from the player character — ignoring whether they are jumping, or not — and finding the height of the ground directly underneath. The new camera height is just this, plus a fixed amount, but this may cause issues if there are areas in the world with very sharp inclines. In my case this isn’t a problem, I’m making the world and I’ll avoid doing this, but as a belts and braces thing I lerp between changes in height so it’ll always be smooth.

The last thing to think about is how to handle when the player has moved the left stick on the gamepad — giving us a new wanted position — but the player character is unable to move in the world, or the character is moving very slowly. As it stands the camera will continue to move toward its outer limits at a fixed speed, which (in my game) looks wrong.

The way I’ve opted to fix this is in the step where I calculate the trajectory between the camera’s current position and the new wanted position. Rather than multiplying this trajectory by: (camera’s movement speed * DeltaTime) I also take into account how much they’ve moved the stick on the gamepad, and how quickly the character is moving in the world. This looks a bit more like: (camera movement speed * (Stick Input * Speed of character in the world)) * DeltaTime

’ve not mentioned the bounds checking in any of this, as it’s simply a Clamp(). How you get the bounds into the camera system is down to you. I have an area controller which contains this data, and I grab it via the game mode.

There are other niceties we can think about that are down to personal taste:

  • Maybe the camera will lerp back from a leading position to centre on the player character after a period of inactivity?
  • Maybe there’s a small dead zone around the player, so not all movements force the camera to move?
  • Maybe the camera moves faster when it’s behind the player, than it does when it’s in front? A sorta “catch-up” mode.
  • Maybe the camera can take into account hot-spots in the world? Other NPCs, or parts of the environment could “attract” the camera’s wanted position, as it leads the player, to indicate clues or areas that might be worthy of attention.

There’s plenty of ways we can plus this…

Locked Follow

As you can guess, this isn’t really a special mode, it’s just deciding if we’re going to track ground height at any given point. We can do this with trigger areas in the world, or even at the game mode level. In my case I ended up making a sub-class of my camera specifically for dungeons, but mainly because I wanted to split out how I transition between rooms. On reflection, there was absolutely no need to do this.

Focus

In my game I remove control from the player when they’re in a conversation, so the main focus mode is simply a lerp to a focal point that’s either determined by the object that’s being interacted with, or the mid-point between the character and the NPC they are conversing with.

To make this look nicer, I use easing curves on the lerp.

When returning to follow (locked or free), I’ve not had to do anything. It actually looks better to have the camera move from where it is, rather than refocus on the player. I might change this in the future, though.

Combat

I’m not finished with combat at the minute, but the way I intend to do this is by modifying the camera’s wanted position to either be a point between the character and NPC, or on a circle around the NPC. Basically, I want the camera to focus on the thing the player is attacking, not the player’s character in the world, so biasing toward the NPC seems the way to go here. I’ll update this post once I’ve written it.

D-Pad Override

This is pretty simple. Any input from the D-Pad creates an additional direction vector which I add, as a final stage, to what we’ve calculated as the camera’s “final” follow position. Because the D-Pad is digital, I ramp the length of this vector up as the button is pressed, and down when it’s released, to smooth the additional movement. Just be careful to ramp the vertical and horizontal directions separately, to avoid weird results.

By clamping the length of D-Pad’s direction vector (which is inherent in how I ramp it up and down over time), nothing breaks by just adding it. Height is unaffected, but there could be a case where the camera clips against a cliff edge when pushed too far. Fortunately, my world isn’t tall enough for this to be a problem and I only let the player manually move the camera short distances.

This override is ignored in focus modes, as it’s not useful.

Bear in mind, this sort of follow cam may not be for you. I’m specifically emulating a Zelda-type implementation with the express desire to lead the player and frame important things in the best way, without manual control.

UE4 has a camera boom system which you can easily point straight down to avoid having to do any of this work, or even attach it to something that follows the character in the world, but in my opinion, it’ll not have some of the niceties that the player will feel even if they’re not aware of them.

The real magic is in that list of things we could do to plus this system (some of which are above), and how we transition between the camera’s discreet modes in a smooth and seamless way.

Which I’m leaving down to you :D

Devlog: November

Apologies for the long delay in posting. I’m ashamed to say that I’ve still not bought a coffee machine, but it’s not stopped me from having a fairly productive few weeks.

Lumo

Yes, it’s finally out on the Switch and just in time for Xmas! The port was done by Spiral House Ltd and I’m very pleased with the result. After all the hassles JAW had with Unity on Vita, I was worried that the same problems might plague Unity on the Switch, but fortunately that doesn’t appear to be the case.

To all intents and purposes Lumo on the Switch is identical to the PS4 version, just capped at 30 fps. It’s also an unintentionally perfect game for the Switch, as the room based style of play means you can pick it up for 10 minutes, run about and then put it down. Obviously I’m a massive fan-boy for the Switch, but I really did enjoy going back to the game and playing it on the go. It feels perfect.

There are physical copies in the EU and US, but not a lot of them so hit up Amazon if you’re after one. They’re going to disappear quickly.

Fortunately I’ve not had to do much PR for this release, but I did do an AMA on r/NintendoSwitch. Many thanks to Tim and the other mods there for inviting me on. :)

Little Breton

With Psyance parked until I decide how to get some art done, I’ve moved on to the other idea that’s been bubbling away in the back of my head. In some respects this is a bit of a spiritual sequel to Lumo, as the big problem I kept facing when making that game was to stop myself from turning it into a massive Zelda dungeon. Head over Heels and co. largely kept their puzzles restricted to single rooms, and so I wanted Lumo to stick to that template, which meant not doing certain things.

So sod it, it’s time to make some Zelda dungeons :)

Little Breton

I’ll post more info on this after Xmas. For now I’m busy making modular art pieces so I can start sketching out a world to run around in.

Devlog: Upheaval

I’ve lost count of the number of times I’ve moved house. Enough that I should be a bit of an expert at it, or at least, not underestimate how long it takes to sort everything out once I’ve “unpacked”. As it was, the first three weeks of October were a right-off, but bar buying a new coffee machine I’m basically good to go, I‘m back on the ‘net, and I’ve just come back from a much more chilled few days in the UK to celebrate my birthday.

Go go go!

Unfortunately, a few more things changed than just my location. It’s been a weird month.

Next Game

I’ve not written about this in detail for a while, mainly because progress has been so stop-start it’s been hard to wrap-up the work into an interesting post.

Where am I? I think from the code point of view I’ve got pretty much everything in place for a networked, multi-player game. Steam integration is working, you can join / leave hosted games through friend invites, browsing for public hosted games works, the co-op and TDM modes work, environmental stuff like trigger operated doors and moving platforms are in, pick-ups, weapon types, ammo, projectiles, area explosions, AI spawning, death— everything you’d expect from a game of Quake — is in and working. But it’s hit a wall.

I’ve lost some collaborators (totally understandably, and I’m amazingly grateful for their help to-date) and I’ve not got the money to sub-contract out in order to hit the quality bar that I want. So I’m left with a choice: rollback on the art/audio/VFX-sides, and maybe go for something like Strafe or Gibhard, or, come back to this project if-and-when I have the cash to bring on a couple of people for a few months.

I probably could hit a nice retro-feel with some low poly characters and pixely textures, but I think Strafe’s got that covered now and I don’t want to re-tread that ground. Going even more coarsely grained, almost Doom2-like, is an option, but again, feels like a step back from what I have in my head. So I’m going to park this.

If I’m honest, every step in this project has been just a smidge harder than it should have been, not least because of some seismic shifts in my personal-life that have royally fucked my ability to focus, but I’m inclined to listen to the universe and accept that this “isn’t the one”. Meh.

I still want to make a really fucking fast, twitchy FPS, so I’m not ready to let this go completely. Which means maintaining it with each UE4 upgrade and biding my time…

Neutrino

Man, I’m so fucking glad that I decided to roll with a little side-project, post Lumo. Being able to work on something when surrounded by boxes, on a train, bus, or stuck in an airport, has been a life-saver. I’d be much more stressed if I was looking back on October as being entirely unproductive.

The fruits of this half-arsed labour?

GunEd

Behold, my nearly finished Gun Editor.

Yup, dead simple, probably only a day’s work, but I’ve also gone through and cleaned up a few hanging TODO items, and added viewport scaling to the mouse wheel for the editor modes.

I need to add the bullet definitions to this, then do a particle emitter editor, at which point I literally have no more excuses. I need to be making a game with this. But I’m not going to go back to it until Xmas.

I have a cunning plan…

Devlog: Curves

I’m in the process of packing up my life for the move back to Tampere, so this will be the last catch-up blog until I’m setup again. Meh.

Neutrino

I’ve done a fair bit more work on this, due to travel, downtime waiting for estate agents and the start of the new Semester at University. The “big” new item is the curve editor.

I want the enemies to have pretty varied attack patterns, and although a lot of this could be done with the judicious use of Sine, you can’t beat having a decent spline to hand.

There’re a few ways you can do this, but I’ve opted for chaining cubic Bézier curves together, as the maths is about my level.

Maths.... Ugh

Or, in code:

glm::vec2 CalculateBezierPoint(const float t, const glm::vec2* p0, const glm::vec2* p1, const glm::vec2* p2, const glm::vec2* p3) 
{
    float fT = clamp(t, 0.0f, 1.0f);
    float u = 1  fT;
    float tt = fT*fT;
    float uu = u*u;
    float uuu = uu * u;
    float ttt = tt * fT;
    glm::vec2 p = glm::vec2(uuu * *p0);
    p += 3 * uu * t * *p1;
    p += 3 * u * tt * *p2;
    p += ttt * *p3;
    return p;
}

ImGui has been a lifesaver, again, as it took very little time to put together enough UI to load, save and edit curves. There’s no real limit to the number of curves I can chain together, but in practice, two or three is more than enough. It’s simple, but it works.

Fancy Gif

The other nice addition to Neutrino, as you can see above, is embedded .GIF output. Totally stole this idea from Philip Bak, but it makes perfect sense; in a world where you need to fire out screenshots on social media as often as possible, what could be better than pressing a button and getting an animated .GIF, resized for Twitter?

We both opted to use this single header lib from Charlie Tangora. 3 function calls and Bob’s your mother’s brother. Perfect.

Other stuff that’s been done:

  • Simple timer based callbacks, so single entities don’t need to constantly poll their state, they can just ask to do something in the future.
  • Start of a level sequencer… This is based off the camera position, and will be responsible for doing all the spawning and in-game events. I’ll probably do a ImGui editor for this and talk about it in a future post
  • Roughly sketched out how enemy entities will work. I’m trying to do existence based processing using a table layout — I’m high on Data Oriented Design kool-aid — so I’ll talk about this in a future post, once I’ve worked out how I’m doing it.
  • Ran the codebase through Valgrind and removed a bunch of silly little memory leaks. One problem with the stop/start nature of the way I work on this is that, sometimes, if I have to stop in the middle of something, I forget to go back and clean-up after myself. I’ve got into the habit of just checking periodically in-case I’ve done something stupid. Normally I have ;D

Anyway, I better get back to packing boxes ;D

Devlog: Grab bag catch-up post...

I’m waaaay overdue a post.

A lot has been going on, on the dev-side, and in my personal life. I’ve had a bit of a holiday, I’ve been to the UK and back, and I’m at the start of trying to move away from Helsinki, and back to Tampere. My plans of a heads down summer of work didn’t really happen. Oh well :D

Neutrino

Due to the travel, hanging about in airports and nights in hotels, Neutrino has had a fair bit of attention. Effectively the engine side of it is complete.

Box2D has been integrated. I’m not a massive fan of Box2D, but it’s simple enough to use and reasonably fast for a small number of objects. I’ve gone the whole hog this time and written the debug renderer for the physics world! Seeing what’s happening under the hood really does make a difference.

Neutrino is still single threaded, and still, effectively, vsync locked. I’m going to do a bit of work at some point to move Box2D over to another thread and fix the fucking timestep, at which point it’d be interesting to see it run on a GSync screen. But anyway…

Debug Rendering

Still not much to look at, but it’s getting there. Next step is to make a particle emitter editor, bullet trajectory “pattern” editor and a spline editor for the attack waves. Then I think I can make some levels…

Next Game

A month or so ago I decided that I had a lot of bits, but I didn’t have a game. I really should start putting this all together.

Steam’s a good forcing function for this: If I’m going to make an online game then I really need to have it running through Steam and then get some other people to play it! To do that, I’ll need working UI and a proper flow in-and-out of the various game modes.

UI in UE4 is pretty lovely. UMG has its quirks, but it works, looks good, and compared to the complete abomination in Unity, is a joy to put together. And — shock — I really like Blueprints for chaining UI together. Delay node ftw!

That being said, there’s still a lot of UI screens to put together — once you throw in game-modes, with options, lobby screens, error handling etc. — but I have a working screen for each part of this (with no real thought applied to the styling atm) and I’m able to enter and exit a game.

Online Session handling (like pretty much everything else in UE4) is piss poorly documented. There’s a video showing you how to do it in Blueprints, and a working example in Shooter Game that does it in C++. Because it’s so intrinsically tied to the UI, for the first pass I opted to start a simple blueprint session and see if I can connect to it via Steam. This took the better part of a day, but it does actually work; I can see hosted games over Steam, pull up some small bits of information about them, and I can join and leave a session. Wow!

The problem is, the blueprint stuff doesn’t really have any idea about any of the custom parameters that define a hosted session, so I’m going to have to go back under the hood and do this properly in C++. Then I can filter against game mode, see the name of the server, handle friend invites and the like. But for now, just for the purposes of development, I have a “game” that I can upload to Steam, send to friends, and we can run about and shoot each.

Time to grey box some levels to run around in.