30 Mar 2014
March 2014 - FreeRunner Tech Demo
unitytopdownfreerunningdemopostmortemdigitalresearch

Well the theme this month is Neon, and I really did use that as a starting point in the brainstorming, but while the end result does involve darkness and lights, there's no neon to be found. Oh well.

What I ended up playing with is the idea of a runner in a locked down city neighborhood, where curfew is partially enforced by shutting down all the residential power after dark. You know how to get the lights back on, and that will be your act of resistance - run through the night, with agility as your only advantage, and turn on the power all over the neighborhood. Light up the night. Maybe the people will be encouraged to resist by your example, maybe not, but that's the shot you're taking.

That hasn't entirely materialized, but I'm still happy to count this as the March project, because the most interesting part to me, and the part that was new to me, was the free running aspect. So what I have here is the FreeRunner Tech Demo.

FreeRunner

To be specific, what I wanted to try to implement was the kind of system many action-adventure games have these days, where the player doesn't have to manually bunny-hop around over every ground level change. Instead, their character animates appropriately to represent the character vaulting, jumping, climbing, and so forth as needed.

I have no idea how this is typically done. I did zero research. I wanted to just dive in with my existing knowledge and see if I could get it working on my own. I am extremely happy that, overall, it's working pretty well. For what it's worth, here's the process I used.

0. Determine Levels
The height levels that will be treated differently needed to be determined first. I decided I would initially try this with three levels - step at mid-shin and below, vault at up to around waist height, and climb up to just above character height. At step level, the character should just smoothly adjust height and continue running uninterrupted. At vault level, the character should animate through a short animation representing them jumping at speed and pulling their legs up to adjust height, and climb height should trigger an automatic overhead grab-and-climb animation and move the character to the new level.
1. Check for Obstruction
My basic overhead character controller was already constantly monitoring for blocking obstacles directly ahead (checkDistance * delta * player.currentMoveDirection). The first change there was that multiple checks are done now at different heights, to make sure obstacles at different levels are all detected.
2. Check Obstruction Height
If an obstruction is found, project forward one character collider radius to find a target landing position, then move up two character heights and fire a ray straight down. That will give the exact height the top of the obstruction in front of the character. This is also the point where some level design restrictions are introduced - there are some assumptions being made here, like how deep a ledge has to be before it can be auto-moved to, whether or not colliders will overlap, whether all colliders are created equal, etc. but I figured the best plan was to get the basics working with very straightforward, basic level geometry, then address any complications later.
3. Test for Character Space
Now that you have a destination candidate, use that point as the base of a collision test that provides enough space for the character to land. For me, using Unity, that meant finding the dimensions of a capsule that could contain the player and using a Physics.CheckCapsule to find any potential overlaps.
4. Anticipate Collision Timing
Based on the distance to the target and the character's speed, you can easily estimate the character's time to collision under current conditions. This allows the automated movements to be triggered at specific times, e.g. a climb animation should only trigger just before the player reaches the obstruction, whereas a running vault animation should play much earlier to allow the animation to begin and the character to be moved upward far enough to avoid the collision and keep the character moving at full speed.
5. Animate and Relocate
Assuming all of the above checks out, you now have a target position for an automatic movement. In a project with more than one developer, one month of free time and no budget, this would probably involve custom root transform animations to perform the requested movement and animation. For me, this means running a quick and dirty coroutine that only executes for a few physics frames, during which the player's input is ignored, and an animation is triggered while the character's position is quickly interpolated through two or three points, depending on the move requested.
  • For a step, the run animation continues uninterrupted while the character's height is quickly lerped to the new target height. Properly calculated, this moves the character to the target height before collision and keeps them moving at a full run.
  • For a vault, a similar process is used, but with a hurdler-style animation playing and a longer lead-in time. If a player runs straight into a vault obstacle, they will retain most or all of their speed. If they start too close, they will lose some speed, but that seemed like a happy coincidence since it also makes sense that a person not yet moving at full speed would not be able to vault over an obstacle as quickly.
  • For a climb, the character is moved through two positions while a climbing animation plays. The first movement represents the bulk of the vertical movement, while the second moves the character up and forward to the landing position.
Special Cases
With the above cases working, a few special edge conditions can be sorted out. For instance, step and vault actions require ground, while a climb animation starts from the arms. If the step and vault actions are disabled while airborne but the climb action remains available, this allows the same logic to act as a jump-and-climb check, allowing the player to jump up and grab a ledge, and as a ledge grab for jumps that come up short.

With all of that working, I got as far as implementing and testing trigger zones for automatic jumps when running off of a ledge before realizing I was wasting my time. By just adding an altitude check during grounding checks and storing the previous frame's grounding state, I could have the character automatically jump when running off of any ledge by testing for (!grounded && wasGrounded && altitude > autoJumpTreshold), Conversely, I could test for situations where the player becomes ungrounded at a very small altitude, such as when running down stairs, and immediately snap the character down to ground level. This prevents the character from quickly toggling between grounded and ungrounded when running down stairs, which could cause any number of side effects depending on how you're handling and reacting to grounded state.

So in the end, I have a simple character controller that sticks to the ground on small drops, automatically jumps when running off a ledge of sufficient height, runs up small steps without a hitch, vaults over waist-height obstacles, climbs up to platforms, grabs ledges and climbs up when missing a jump, all without the player doing anything but moving in whichever direction. With the generic animations and approximations it definitely wouldn't work for anything with a closer view of the character, but for a low-poly overhead view like this it's pretty smooth, so I'm calling that a win regardless of whatever else got done.

The City

The other major project here was the neighborhood itself. This is a huge environment compared to anything I've built before, and while much of it is a variety of general-purpose assets reused dozens (or hundreds) of times, it was a major undertaking to get it all laid out, with traffic signals and street light poles and hydrants and whatnot. I believe it came out to something like ~132,000 square meters, or ~158,000 square yards, or around 32 acres.


The only interesting part there is I assembled a set of building components (base, layers, roof, optional components), then used a script that constructed the building dynamically from those components to a desired height. With that script executing in editor, that gave me buildings where I could slide a slider in the properties and have the building rebuild itself to any height in realtime. This was handy when populating the city, and would really come in handy in a larger project, since the same logic without modifiation could be used to construct arbitrary buildings from different sets of prototype components.

I started with an idea that would largely involve running across rooftops, and ended up with very little rooftop action. However, the cars and trucks of the neighborhood provide opportunities to test out the different movement options (car hoods and trunks are vault height from the ground, roofs are climb height from the ground or step height from the hood/trunk) and there's a set of specially-constructed stairs and stair-step obstructions in the park area for testing - the stairs in order from smallest to largest are step, vault, climb, jump-to-climb, and impassible.

Credits

This project, as with nearly everything I do, contains the auditory forgings of Ben Freund.

The Verdict

I may not have gotten the objectives and NPCs implemented as I'd planned to, but I'm still happy with this one. Having a player controller that smoothly runs, vaults, climbs, autojumps, and all the rest provides a very satisfying experience, and that's going to be useful stuff to know how to implement in the future. Although maybe now I should look into how other people accomplish things like this...

Note: the following builds are available for testing purposes but I don't have the ability to test them. These are Unity builds so I don't anticipate any serious issues, but I can't vouch for correctness or performance of these builds.