Cover location detection

22 Mar

I’ve been working on top-down angled action idea. For this indie project, I wanted to find safe cover locations for enemies to hide (crouch) behind and shoot from.

On my first attempt at a solution, I tried using the unreal EQS system, then my own implementation (picking points, raycasting to see if they hit an object at a crouching level, and raycast to see the point was clear at gunfire level.

This produced ‘okay’ results. But it presented a few problems. I’d have to implement additional checks for enemy sizes. Implement additional traces to checks if a spot was reserved/occupied. It also would miss spots (as shown by the red spheres) depending on how the checkpoints lined up. I’m also testing a lot of locations.

This solution just had too many issues, so I decided to take a different approach.

My second solution, add a simple box collider to valid cover spots, on a cover only trace channel. To accomplish this, I extended the UBoxComponent class creating UAreaCoverMarker and implementing a GetCoverLocations function. This function takes two parameters, the player’s location, requesting enemies width and returns a TArray of structs that contain each cover points FNavLocation and their ForwardVector (vector pointing towards the cover object.)

To discover the best hiding side, I calculate the colliders top for vertices, and sorted them by distance from the player.

A = Furthest vert from player
B = 2nd Furthest from player
C = 3rd Furthest from player
D = Closest to player

AB will always be the furthest side from the player. The direction of A-C will point away from the BoxCollider and towards the area, we want to test for cover space.

Step 1: Taking the enemies width I divided the length of AB, to calculate how many hiding slots could potentially exist on this line.

Step 2: Using the direction A-C I located the starting and ending test points.

AB

StartPoint = Move A in the direction of A-C by 1/2 the enemies requested width.
EndPoint = Move B in the direction of A-C by 1/2 the enemies requested width.

 

 

 

Step 3: Move the StartPoint 1/2 the distance of enemy width towards B. (this is the center of our first test.

Step 4: Check if this spot has a collider on the ReservedArea channel below it towards the ground. If not proceed.

Step 5: Check if we can cast this point onto navmesh (setup a query tolerance.) if we can the point is valid!

Step 6: advance this test point by the enemies width towards the EndPoint, repeat for the number of hiding slots calculated at Step 1.

As you can see below, this doesn’t exactly work great for large rectangles or cubes. Depending on the player’s location, sometimes enemies should hide behind AC too!

 

Hmm, isn’t the other side safe too.

 

To fix this, let’s turn to the late great Heron and find the side AC.  If the Triangle PlayerAC has a greater area than PlayerAB, include AC in the solution.

Step 7: Repeat Step 2 – 6 for the line AC, if the area of triangle PAC > area PAB

[PAC (green triangle) > PAB (red triangle)]

 

 

 

 

 

PAC area < PAB area

 

 

 

 

Custom latent – delayed Blueprint function in C++

30 Nov

I wanted to make two functions with delays to assist BP scripting of logic. One for dialog and one for item interaction. The one I’ll cover here handles dialog.

My goal: Make a function that accepts dialog parameters, waits for a user response and returns the player’s selection. (Additionally, make a breakout which flows execution based on the player’s selection.)

I couldn’t find any help online when writing this function. So I crept through engine code. Unreal has a LatentActionManager held by UWorld. You can get its reference from GetWorld->GetLatentActionManager()

Using this I made a c++ class derived from FPendingLatentAction and put our own logic in it.

 

 

 

Here is the BP functions header:

Here is the function’s definition:

What happens above, we grab the LatentActionManager and make sure it currently isn’t running a task for this object and pass it our custom latent task, along with our custom latent tasks construction params.  InteractionInfo is a custom struct with the players choices, and PlayerSelection is an enum of the possible responses the player can have.

Let’s look at my custom FPendingLatentAction derived class:

Constructor:

I create a callback (I send a self reference to the actor who called this function.) Once the player has selected input, I push a response to this delayed action.

One could also poll from the UpdateOperation. The tick/update function notes a value has been set and completes the task.

If you want to break out the response use the ExpandEnumAsExecs UFunction specifier:

 

 

Grid based Broad-Phase Collision detection

4 May

This method is best suited when uniformly or very similarly sized objects are being tracked. If objects do not differ much in size each cell should be the size of the largest object. This makes it so only the center of each object needs to be tracked.

 

Example below:

PSizeOverlap from shapes in non-adjacent cells is impossible if the cells are as large or larger than every shape tracked. If shapes end up being larger however overlap is possible and we need to track the shape in every grid cell it exists.

 

If the cells are smaller that the largest object overlap will exist as demonstrated below.

If thisTooLarge happens we need to track more than just an objects center (covered below.) There are a few ways to solve this, increase the grid size, use another detection system or track more than just an objects center.  Increasing the grid size can have unexpected consequences.

 

If the cells are too large, there will be an excess of results in each cell eliminating efficiency and increasing computation to or towards On^2. As the picture to the left demonstrates. TooSmallEvery object in each cell will have to be checked against each other for overlap.

Spatial grid tracking can be accomplished using a hash table or a 2/3d array.  A 2d array is fairly straight forward.

The next post will cover creating a hash table, generating a cells key (from coordinates), finding neighbors and detecting overlap.  Followed by a post on creating bounding shapes to cover objects that are larger than a grids cell size.