I’ve run into a technical hitch that can’t be solved until I get to a computer with 3DSMax, so I figured i’d scratch the itch for now by writing a post about the Wallboost mechanic I’ve implemented.
I knew from early on that i didn’t want to implement horizontal wallrunning into Neon Arena – for the size of project it will be, it’s already worryingly unwieldy once vertical wallrunning, grabbing and climbing ledges, and the wallboost below are in. For the amount of work and complexity it would be, it doesn’t open up enough options for the player – I mean, it’s pretty much just a longer jump, right?
What I did want was the ability to jump away from a wall, and a little thought suggested I could probably put together an ability that would kill several birds with one stone. I wanted players to be able to jump off walls they were running up them or hanging from the top, but also wanted the ability to kick away from walls that they were in mid-air besides or jumping towards. I felt this would add a new set of movements for the player – particularly the ability to bounce between two walls to traverse horizontally or vertically without touching the ground: climbing in this way was always tremendous fun in Prince of Persia: why not here?
I’m no expert in Blueprint, (at the time of writing that is, if you are a potential employer in the future then yes now I am an expert, and also you look lovely today) so it was a pleasant surprise how easy this was to implement. In order, the steps of the implementation are:
- (Player presses Q)
- Draw rays outward from the player’s movement direction to find the nearest wall.
- Calculate the angle in the XY plane to apply the force away from this wall.
- Calculate if the player view should turn to face the new direction.
- If it should, do that.
- Apply the force along this angle, with an additional boost upwards in Z.
- Clear any existing conditions (hanging from the wall, playing the Running Up animation, etc etc).
In more detail:
Draw Rays Outward from the Player’s Movement Direction to Find the Nearest Wall
Easy enough. Get the vector for the direction that the player is going in – discard the Z, we don’t care about their ability to bounce off floors or ceilings, and I didn’t want them getting boosted upward by slopes – and raytrace it. Then, rotate it fifteen degrees and do it again, and repeat that until you’ve traced at 15deg intervals from -90 to +90. This of course means that we can’t detect walls that we’re moving away from, which is fine – I can’t see any situations where we’d want this. If the player isn’t moving horizontally – maybe they’re falling straight down, or wallrunning, or clinging to a ledge – then instead go outwards from the direction they’re facing.
We get an array of collisions, then go though it again to find the nearest one (this at least applies consistency, so players can be sure which way they’re going to go) and then get some details of that impact point.
Calculate the Angle in the XY Plane to Apply the Force
This took a little figuring out – as always when working with vectors, I filled a few notebook pages with sketches. When the player is jumping alongside the wall, I want them to keep going forwards, but now away from it: when they’re jumping towards the wall, they should end up moving away but keep their parallel velocity, and when hanging from the wall, they should just jump away from it. The second was the clue to how to resolve this: we need to break the vector into it’s parallel and perpendicular parts. Confusingly this is best done with the wall normal, to which the perpendicular part is parallel, the parallel part is perpendicular, and peter piper picked a peck of pickled vectors.
Sorry, to continue: We discard the perpendicular (to the wall) part: instead, we replace it with the “bounce” force, applied in the direction of the wall normal – this is the force that pushes us away from the wall. We then add this to the parallel component, to keep us moving as fast alongside the wall as before, but now moving away (with no relation to how fast we were moving towards: this seems screwy but remember we’re applying force here, rather than just bouncing off like a tennis ball (I confidently say this now, after spending maybe half an hour trying to figure out why my sketches weren’t sufficiently tennis-like (tennesque?))) Remember for all of this we’re ignoring the Z component completely – we don’t need to add that in until later.
Calculate if the Player View Should Turn to Face This New Direction
One thing that came up as soon as I tried out the boost was that it was weird to jump backwards (and impossible to accurately bounce between walls when one was completely behind you). Equally, though, I felt it would be weird to constantly be switching the players view by small increments – particularly when trying to bounce horizontally, constantly swinging the view back and forth by forty-five degrees or so would make you sick. So, a simple comparison of angles: is the player currently looking (again in the XY only) more than
90 120 degrees away from the direction they’re now moving in? I say simple but actually it proved to be quite a pain: largely because I was still learning what exactly a Rotator was, and how I could build one from a vector. It took a bit of trial and error, but the results work.
If It Should, Do That
Teleporting the player’s view by 180 degrees was bad, so I put a super-simple timeline in to lerp (linearly interpolate (move by a bit each frame)) from one rotation to the next over the course of half a second.
Apply the Force Along This Angle, Plus an Upward Boost in Z
LaunchPlayer did most of this for me. There’s no screenshot for this one since it’s spread out too much, but yeah: take the angle to leave the wall we generated, normalise it, multiply it by the force to push away from the wall, add a Z-component (I chose one slightly less than the force an upward jump exerts – might need to fiddle with it but sounds conceivably right), and plug that into a Launch Player function, which applies the force for me. Note that we override the X and Y components (we’ve already taken the previous X and Y into account) but not the Z.
(That’s something I want to fiddle with, actually. If we override the Z, players bouncing between walls can eternally push themselves upwards so long as the walls aren’t so far apart they’re falling faster than the boost would push them up. If we don’t override the Z, then players get less bonus from each bounce, and end up slowly and torturously reaching a maximum, then falling down. Neither feels satisfactory – I wonder if manually stepping in is what’s needed here. Applying a maximum number of bounces doesn’t sound good to me – how do I communicate that to the player? I also want to allow neat tricks like the horizontal bounces, where that doesn’t take the player out of the level. Perhaps overriding the upward velocity but making each bounce after the first apply less upward force as the character “tires” would work? I’m still thinking about it.)
Clear any existing conditions (hanging from the wall, playing the Running Up animation, etc etc).
Last thing to do, and there’s no screenshot for this one either since it’s much more idiosyncratic: remove the various conditions applying to the player if they’re wall-running, hanging from a ledge, or doing whatever else wierd happened. I also save the time of the boost here, because I was having a bug where, if the player leapt away from the wall at the apex of the run, they would get only a foot or so away before their momentum was cancelled and they dropped like a stone. This took far too long to figure out: in the end, it turned out to be a conflict with the “stop wall-running” code, which would run a few frames after the boost, and included a “Cancel Movement” function. Easy solution: did we boost less than half a second before? Don’t cancel movement, then.
Done! Not too complicated at all, and feels good to play. In a cartoonish game it would come with a ba-boinggg sound: here though it’s easy enough to understand, a sharp shove against the nearest wall. There’s a few bugs:
- It’s not very easy to aim. I’m hardly the worlds greatest at computer games, but even I was finding it difficult to jump along a wall, boost away, and land on a target. I think I need to tweak the precise amounts of force, so it’s a little more intuitive: or look at a more drastic solution, like jumping in the direction the player is looking (might take some getting used to, less physically intuitive) or even identifying the “target” the player is looking at, and directing straight at that. This option would be very useful later on when the player tries to target a drone – hell, I could probably create “target” nodes to put in the environment, to place on small, hard-to-hit ledges, and attach to things like drones and collectables.
- It acts wierd when jumping at corners. Sometimes, if you hit the corner of a wall, the “nearest” ray impact is in fact on the side of the wall, rather than on the face you’re moving towards, thus making you bounce unexpectedly sideways. I dialled up the number of rays the first time I saw this, and now it happens much less – I suspect it’s still mathematically possible, though, so will be keeping an eye out.
Nothing too much of a problem, though. I’m currently working on implementing a roll after landing and a slide, but both are currently stymied by Mixamo’s problems with Root Motion – apparently both Mixamo and Epic have known that Mixamo animations don’t work with Unreal’s root motion for about four years, and neither has fixed it, which is more than a little irritating.
Unless, future employer, you work for Epic or Mixamo. In which case, it’s totally fine, works just great, and I love what you’ve done with your hair.