So we meet again... school's back in full swing, but that hasn't stopped me from getting things done! Though I have to say, this past week was quite a frustrating one at times. Dealing with animations is not a strength of mine, and while I know some things will have to change when I get custom animation work done, I really wanted a complete and working basis for the animation logic in here before moving on to the next thing.
Anyway, you can check out some of what I got done in the video below, though it's by no means exhaustive:
Peeking Around Cover
May as well start with the main focus of the past week, eh? I took some pretty major detours, which I'll briefly discuss further down, but it was all for the purpose of getting peeking around cover really solid logically so I'm not having to adjust a bunch of things once it comes time to get assets commissioned. Basically, I just want to make sure I have lots of control over things like angles the player can peek at, the range of the camera pitch/yaw in cover, etc.
So, let's talk about how it's actually implemented. For starters, the foundation for this feature is actually the "CoverEdgeBox" class I made. This class is the basis upon which the three main cover edge actions derive most of their logic: peeking around cover, swatting from cover to cover, and moving out of cover.
Here's a screenshot of the actors; they're the orange boxes with blue arrows in them:
The boxes themselves don't look particularly special... not that you'll ever see them in game anyway!... but they contain all of the necessary information for the three features I mentioned above to work. This includes things like...
- bEdgeRight - A boolean set to "true" if the box is a right edge (as opposed to a left one).
- bLowCover, bCanAimOver, bVaultCover - Three booleans that derive from the regular CoverBoxes which dictate whether it is low/high cover, whether the player can aim over it, and whether the player can vault over it respectively.
- SwatLocationComponent - A reference to the arrow component of another CoverEdgeBox, which will be used when calculating where the player should move to during a Swat.
...among other things. And since they derive from the original CoverBox class, all I had to do to get the information out of them was extend the "trace for cover" function I was already using; I talked about this in an earlier DevLog, but in brief, I check whether the trace hit a CoverBox (which will be true whether it's a CoverBox OR a CoverEdgeBox), and if so, we move to its location component, set some basic variables, etc. Now what I'm doing is casting again to see if it was also a CoverEdgeBox, at which point I set those extra variables on the pawn to do things like prevent them moving to the right if it's an "edge right", for example.
As for the peeking itself, it's actually just an animation with some root motion along the X-axis. I can set the distance the player peeks out of cover using a variable attached to that root motion amount. From there, I just need to tweak things like the range of angular motion and pitch max/mins to get it feeling natural and prevent awkward situations like clipping the gun way into the wall.
The trickier part on the logic side of things was actually carefully going through all the checks to make sure the player can do certain actions only when pressing in certain directions. You'd be surprised how involved it is! The player could be pressing any direction while their camera is rotated somewhere in a 360 degree space (we're really just concerned with the Yaw here, of course), so I have to make sure that actions A) don't overlap and B) feel natural based on where the camera is pointing and the direction they are inputting.
Not even taking into account the swatting and moving from cover features, just peeking around cover, I had to consider things like...
- What angles can the player camera be at for them to peek around cover if they are crouched idle at the edge?
- What angles can the player be at to go from standing to peeking if aiming and moving towards cover edge? If that angle is pointed INWARDS, towards cover, we need to rotate the player so they're not pointing their gun in the wall when transitioning to a peeking state!
- Is the peeking animation lined up with the reticle? If my crosshair is over another player while peeking, does it look like I'm aiming at them on their screen?
- How far can the player turn in the yaw direction while peeking? How far can they look up and down?
- Do we automatically set the player to a standing state when they move their gun towards the wall? Do we just stop them against the wall?
...And lots of other little details that I don't think are worth recording here. But yes, I asked all of the questions I could think of and answered all of them. As can be seen in the video at the top of the article, the player can transition from standing to peeking by moving towards the wall, and then they'll be in a "peeking state". To get back into standing, they can simply press the left stick up or towards the wall, and to get back into peeking, press towards the edge of cover. Very simple once it's all said and done!
I mulled for a while over whether to have the player automatically pull their gun back up into a standing position when it gets too close to cover (this is how it works in Gears 5, for example), but I opted not to do that in the end. I dislike having my controls automatically do anything in shooters, so I thought it'd be better if the player explicitly chose when to peek/stand by pressing in the appropriate direction.
This is not an optional feature in cover-based shooters, in my opinion. If you don't have left-handed animations, the whole experience of being in cover just feels off; even more so when you get to the left edge of cover and you're trying to do a peek animation while the character is holding the gun in her right hand. Super awkward!
That said, I did finally get this figured out, which I was extremely happy about. I was never able to get this far on my old project, so it's a good feeling to be plowing through new territory. But I have to say... this whole ordeal took WAY. TOO. LONG. Seriously too long. I'd be completely bald if my imaginary hair-tearing had actually manifested itself in the real world.
The thing is, there's only one way to do this properly, and that's to mirror your weapon animations. There's no way around it. You can call it "artistic choice" and whatever other nonsense excuses you want (like I did for a while) to try to feel better about it, but if your left-handed animations don't mirror your right-handed ones, it will feel weird. I can attest to this, because for three full days, I basically did nothing but try to use my existing prototype animations to fenangle some left-handed versions and creatively adjust the camera to "make it work." It doesn't work. Just give up hope. MIRROR THEM!!!
As for how to actually do that, well there are many tutorials for various 3D programs online, but I personally went the quick route and bought/compiled the source code for a plugin called "RMAMirrorAnimation". The plugin isn't perfect; the AnimBP node to do realtime mirroring is particularly buggy, but after stripping out the parts I didn't need it was basically a life savor. All the mirrored animation in the video above is a result of the plugin's work, so big ups to the developer.
Bug Fixes and Optimizations
I put in a lot of work getting bug fixes and optimizations ironed out this past week as well. I just wanted things to feel like they were really working before moving into the next couple of cover edge features! I'll summarize some of the changes here:
- Sliding to cover would sometimes not work at certain angles (especially on gamepad) because the animation canceling was activated right after the slide started. This would sometimes inadvertently cancel the slide before it even appeared to start. Fixed this by setting a short timer (~0.05 seconds) at the beginning of the SlideToCover function which turns on a bool that then allows the player to cancel the animation.
- Lots of tweaks to make sure the left-handed animations blended in well. If the player moves off of cover while aiming left-handed, for example, they will stay left-handed until releasing their aim and re-aiming.
- One of the most expensive things you can do when it comes to animation in UE4 is make calls to the virtual machine in the AnimBP. Where possible, you want your animations to be on "Fast Path", which means not doing arithmetic or boolean calculations in the event graph. I made some adjustment in my AnimBP to account for this; for example, I used to have some "Blend Pose by Bool" nodes that check to see if the player is moving faster than a certain velocity. I replaced these with a single boolean that is set elsewhere.
- Reworked my Left-Hand IK system to work with the new left-handed animations (in other words, Right-Hand IK!) and optimized those checks to use Fast Path as well.
- Some weirdness when doing vault cancelling and aiming right afterwards; you could occasionally end up aiming away from cover while still being in cover, which would mess up the player's rotation against the cover wall. Fixed this by making sure to do a check on the player's yaw when aiming right after a slide to see if they're really pointed towards cover or not.
Apart from these, there are of course 10's of other little things that would always come up in the natural process of testing the gameplay as well. Basically, my method was to iron out everything until I could run around and have a jolly good time in my prototype map with nothing weird happening for a solid 5 minutes. Seems like a good litmus test to me for the time being, don't you think? :P
I'm pleased with the progress I'm making on the game so far. It feels like every week I have brand new stuff I've never done before figured out, so it's an exciting period of development. The next week or two will be devoted to getting the other two cover edge features, moving from cover and swatting from cover to cover, figured out.
After that, the base cover movement will ALL be done! Excellent! Let's get to it!... but maybe tomorrow, because it's 2:30 in the morning here x_x