Friday, October 16, 2015

Box Selecting Multiple Vehicles In Unreal Engine 4

We now have our top-down vehicle moving to a point-and-click destination, pathfinding, cornering and parking.  But one vehicle does not constitute a squad.

I created a hierarchy of structs to setup team compositions:
  • Match
    • Team[]
      • Player
      • Units[]
        • PawnClass
        • Active
        • Debug
        • Health
        • etc...
That struct is passed to a UnitManager which handles assigning the teams start locations and spawning units.

So far, fairly straight-forward.  To box select units with the mouse:
  1. Get the mouse X,Y position in the HUD with GetMousePosition
  2. As the mouse is moved, pass the start and end positions to the HUD
  3. On the HUD's ReceiveDrawHUD event, DrawLines representing the selection square

    Meanwhile...
     
  4. GetHitResultUnderCursorByChannel > BreakHitResult.HitActor cast to a vehicle unit gets a vehicle clicked on.  If the cast fails...
  5. GetHitResultUnderCursorByChannel > BreakHitResult.Location gets the mouse world location
  6. As the mouse is moved, pass the start and end locations to...
Here's where it gets tricky.  Initial instinct is to pass it to BoxOverlapActors.  That yields:


The problem is caused by the isometric camera perspective.  A square drawn on the UI is actually  trapezoidal in world space.  You can't rely on just two points.  You must pass all four UI positions to LineTraceByChannel to get the world position of each corner, which then represent the four planes of the trapezoid:


So how do you find the vehicles inside a polygon without a "PolygonOverlapActors"?  There's a simple trick.  Trace a ray outward from each vehicle and count the number of polygons it intersects using LinePlaneIntersection.  If it intersects two or none, it's outside.  If it only intersects one, it's inside:

I just added lasers to the vehicle weapon list.

We can now control a squad:


Next, we'll get them to be aware of each other and try to avoid colliding.  (And yes, it's almost time for guns!)

Friday, October 2, 2015

Parking Using a MoveTo Behavior For Vehicles In Unreal Engine 4

Buckle up, this one's going to involve some math.  To keep motivated, let's remind ourselves what the goal is:

Image from imdb.com
This is the view from the NPC.  Those would all be mine.

Parking in a specified direction involves two steps: the approach and the landing.

Parking, the Approach

We could just fling our vehicle directly at a destination and let it line itself up when it gets there.  However since there are 360 angles it could arrive at and 360 angles it could be destined for, it may spend quite some time turning around.  So ideally we want it as close as possible to its final rotation when it begins the landing.

To do that, we're going to inject a waypoint into the array of Path Points returned by the NavigationPath object.  We want to calculate a waypoint that is 90 to 180 degrees behind the destination.  If the vehicle is in front of the destination, it'll aim for 90 degrees to the side.  As the vehicle drives behind the destination, it will curve inward to 180 degrees directly behind the destination:

The similarity to aircraft landing is not lost on me.

The distance behind the destination (the yellow dotted line above) is determined by:

maxDistance * SIN( ABS( DeltaAngleFromNextNavToFinalRotation ) * 0.5 )

I chose 1500 for maxDistance, as longer distances makes the vehicle appear to veer way off course and shorter distances didn't give it enough length to line up behind it.

The direction from the destination to insert the waypoint (the orange dotted line above) is:

COS( DeltaAngleFromNextNavToFinalRotation * 0.333 ) * SIGN( DeltaAngleFromNextNavToFinalRotation ) * -180

The result is a rotator from 90 to 180 degrees, to be added to the final rotation.  The numbers work out like this:

AngleRotatorLength
-18090.11
-150115.80.96
-120137.90.86
-90155.90.70
-60169.1 0.5
-30177.2 0.25
00 0
30-177.20.25
60-169.1 0.5
90-155.9 0.70
120-137.90.86
150-115.80.96
180-90.11

Here's the blueprint:


Parking, the Landing

Once the vehicle is likely behind the destination, probably facing nearly the correct direction, and within CloseEnough(), we can start actually parking.

The primary goal is to align the vehicle along the rotation plane that passes through the destination.  That's the hard part.  Then it's a simple matter to forward or reverse to the destination.

We need to calculate a target to steer toward again, but unlike the Approach target, the Landing target will always be in a line along the rotation plane.  The farther to the side the vehicle is, the straighter it will aim directly for the plane.  As it gets closer, it will steer to drive alongside it:

I would have been more interested in trigonometry if the problems were like this instead of the height of trees.

We need three values: how far the vehicle is along the plane, its distance away from the plane and whether the vehicle is facing toward or away from it.

We know how far the vehicle is along the plane (the yellow line above) using:

COS( DeltaAngleFromFinalRotationToVehicle ) * DistanceFromDestinationToVehicle

And its distance away from the plane (the orange dotted line above) with:

SIN( DeltaAngleFromFinalRotationToVehicle ) * DistanceFromDestinationToVehicle

We can tell if the vehicle is aimed toward or away from the plane by:

SIGN( DeltaBetweenFinalRotationAndVehiclesRotation ) *
SIGN( DOTPRODUCT( DestinationRightVector, VehicleRightVector ) )

That results in a 1 or -1 describing whether the vehicle is aiming left or right of the destination and which side of the plane it's on.  That becomes the vehicle's throttle.

Finally, we can calculate an exponentially increasing length away from the vehicle's position along the plane:

ABS( ( 1 - ( CloseEnough / ABS( DistanceFromPlane ) ) ) * DistanceAlongPlane ) * Throttle + DistanceAlongPlane

The blueprints:

You said THREE values?!

Will it fit like a glove?  Probably not.  Could wasteland warriors use it to setup an ambush?  Absolutely:



The logic still isn't too complicated yet:

Next time, we'll spawn multiple vehicles and handle group selection.