Simplistic Cover in Unreal Engine (1): The Cover Slide and Cover Point Selection

Previous Post:
Simplistic Cover in Unreal Engine (0): Introduction - Memories of Melon Pan

When we think about cover systems, there's really only one way to activate it as far as the player goes - press the dang button - but when you program the activation, you gotta get your player to muzzle right up to cover if he's not already there. You can make this a little transparent and only allow cover activation when the player's already toe to toe with a surface, but if you wanna give the player a hand, you'll have to take over movement for a second and move the player right up to cover for him. I did things the second way (i.e. the Gears of War way), but it's obviously the more difficult way.

There are two parts to this, which ironically enough, can be further chopped up into other little problems you'll encounter along the way.

  • Selecting the cover surface
  • Calculating the point at which to take cover

Since you're automatically moving the player to some sort of cover, the first thing you need to do is find out what cover to move the player towards.

We can search for cover around the player by attaching a bounding volume to him. We're not really caring about up or down in this case - the player can't take cover while he's jumping, and we're not going to go so far as to make him jump up to cover. This means that our bounding volume is gonna be flat. Ideally, it would be a cylinder, but for some reason, Unreal Engine doesn't have a bounding cylinder. So I used a box instead.

(Header file)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
UBoxComponent* CoverSearchVolume;


(Constructor)
CoverSearchVolume = CreateDefaultSubobject(TEXT("Cover Search Volume"));
CoverSearchVolume->SetBoxExtent(FVector(450.0f, 450.0f, 75.0f));
CoverSearchVolume->AttachTo(RootComponent);

That sets up the bounding box to be centered on the player's character (or specifically, the root component, which is an Unreal Engine specific thing). It actually has a bit of vertical leeway, stretching a little above and below the center of the character, to catch walls that might not be as tall as the player. To find cover around the player, all we have to do is search within this box. Unreal Engine makes this easy.

this->CoverSearchVolume->GetOverlappingActors(OverlappingActors, AStaticMeshActor::StaticClass());


Where:

  • OverlappingActors is the output, an array of all actors that overlap the search volume, and
  • AStaticMeshActor::StaticClass() is an inclusive class filter, so that the function only considers actors of this class.

We're going to assume that any static mesh in the game world can be used as cover - that's the AStaticMeshActor class - and we're going to search the box for everything inside it of that class, dumping all the hits into OverlappingActors.

You might have to strain your eyes a bit to see that wireframe box, but if we hit the cover button right now, that pipe and that crate will be inside our search volume (read: wireframe box). The wooden ramp you see in the corner is outside the box - while it can be used as cover, since it's not inside our search box, we know it's pretty far away. And if it's too far away, we don't want to slide across the map to it.

So great! We have a bunch of things that can be used as cover surfaces! So which one do we use?

... Yet another question we gotta make up an answer to. Meh, it happens.

The easiest thing you can do is calculate the distance to each piece of cover, then choose the closest one. Again, Unreal Engine (being awesome) can turn this into a single function call... that admittedly calls a slew of other functions I'm sure, but that's all transparent to us.

float Distance = Object->ActorGetDistanceToCollision(CoverSearchPosition, ECC_WorldStatic, ClosestPoint);


For the variables:

  • CoverSearchPosition, the position where the cover search is started from,
  • ECC_WorldStatic, the collision channel used in this collision test, and
  • ClosestPoint, the output, the closest point on the surface to Position.

For CoverSearchPosition, we could use the center of the player character, and that's mostly okay. However, when we do any line trace from our player to cover, we'll actually want to use a point somewhat below the center of the player, so the traces don't soar over any low walls that could also be crouched behind.

I ended up using somewhere around knee-level, though it was actually a point relative to the height of the character's bounding volume at 25% of the way up. Keep in mind you can change this to suit your needs, but that's the general idea.

As for ECC_WorldStatic... I could explain it, but that opens up a big can of worms that'd fill up a few posts. Suffice to say that it's like a tag that you can apply to objects that are part of the level you're playing in, and ActorGetDistanceToCollision takes this tag and only searches for collisions with objects that respond to this tag.

All of you itching for a better explanation, set aside some time and go through the Unreal Engine tutorial vids instead - the explanations there are made of silicon gold. You'll be looking for videos about collisions and collision channels. You can also look at their documentation.

Link: https://docs.unrealengine.com/latest/INT/Engine/Physics/Collision/index.html

But all that said... in its most basic form, you can just iterate through all of the static meshes that overlap with your search volume, and keep a running variable of the closest thing in there. You can leave out any hits that are some distance away from the player, effectively making your search area like a cylinder. When you're done, you can return ClosestPoint - that's the point to automatically move to, which is what we wanted to find.

Formally, what we're doing is scoring each piece of cover, but we're only using distance to score cover surfaces so our scoring function is really simple. If you want to make things a little more complex, you can do something like multiplying distance by some directional bias so that the cover button favors cover in front of the player.

Sound simple? It is... for now. Things're gonna get more complex, but for now, we can leave it at this.

Next Post:
Simplistic Cover in Unreal Engine (2): Character States and Input Handling - Memories of Melon Pan