Blackboard Study: Space Battles

In order to gain more expertise with the AI systems in Unreal Engine I decided to create a project that would lean on the behavior trees of the controllers with as little human input as possible, I ended up at an auto battler.

What I created

I built a simulator where two sides of space ships fight against each other without the player/viewer controlling any of their actions.

The space ship theming was because I wanted to focus on the blackboards and behavior trees first and foremost without having to hook everything up to animations or humanoid characters. Additionally I felt that it would be easier to make modular content to fill out the simulator with by swapping the turrets around the ship’s chassis.

How does it work?

Every ship is controlled by an AIController that follows behaviors and parameters that are defined in each ship.

Every turret mounted on each ship is independently piloted by its own AIController that determines what it should be shooting at and when it should be firing.


What I did

Ships - Overview

Ships are the pawns flying in space while their mounted turrets try to destroy each other.

  • Ships possess an enum, [Light, Medium, Heavy] that is used to categorize it for turrets’ decision making.

  • Another enum determines the behavior of the ship’s chassis, Ship Behavior, and how it interacts with enemy ships.

    • Aggressive: moves within range of all of the mounted turrets.

    • Balanced: determines the range where at least half of its turrets can get a valid target and maintains it.

    • Safe: the ship maintains distance to the nearest enemy so that only its longest range turrets are firing.

  • A struct, Ship Attributes, holds all the information that the ship uses for navigation and combat alongside other values.

Ship Attributes

Ships - Brain

BP_Ship’s editor viewport

The AIController or brain behind each ship follows a fairly simple sequence of events to determine what it needs to be doing.

  1. Use a custom Behavior Tree Task, BTTask_FindTarget, to determine what the nearest enemy ship is and store it as a variable in the blackboard for later use.

  2. Rotate towards that target ship using another custom task.

    1. Rather than using one of the default tasks, rotate to face entry, I created my own because I did not want my ships being able to instantly change their direction. This task causes them to use their rotation rate so that it’s a gradual facing.

  3. Updates the current speed of the ship.

    1. Ships use a dot product to determine how close they are to “looking” at their target and while they aren’t within a determined range instead of using their full speed they make use of their slower lateral speed.

    2. This was to emulate the feeling of a space ship having to correct its facing before accelerating to its maximum speed.

  4. Finally they try to move to their desired distance based on their behavior in the ship’s ShipAttributes struct.

ShipBrain behavior tree

Turrets - Overview

BTTask_MoveToDesiredRange


Turrets are purely combat oriented and sport a more narrow set of parameters than the ships but a wider breath of decision making.

  • Every turret is independently controlled from the ship it is attached to by its own AIController.

  • Turrets are attached to ships through a BP_TurretMount that possess a child BP_Turret actor to allow for quick and easy customization of a ship’s chassis.

  • Turrets possess their own attribute struct which stores variables like what projectiles they’re shooting, their maximum range, or their rate of fire.

Turrets - Brain

Turret decision making is predicated on two main factors, visibility and priority. As mentioned earlier, ships possess a ShipType enum that is used for determining how interested a turret is shooting at a potential target. This is so that turrets are shooting at the “right” target since player control is absent during combat. A turret with a slow rate of high firepower would have a priority list that had it focusing on the largest targets available as opposed to shooting at smaller strike craft and vice versa.

  1. First turrets clear out their previous blackboard data.

    1. This was because there was any issue with some turrets attempting to retain line of sight to a target that had been destroyed.

  2. Next the turret determines the visibility to all enemy ships it possess and storing the obscured ships within an array for future use.

  3. Then the turret determines what its final target is, excluding ships that it can’t see or ones that are outside of its stated range.

    1. I created a task and used it three times instead of a single task that cycled through all three priorities because I felt it would be easier to understand as well as easier to move on from this portion of the behavior tree after it has successfully found a target due to the selector node.

  4. To make sure that it doesn’t shoot at nothing it uses a condition to ensure that its target does exist before moving on to the last steps.

  5. Unlike the ship brains, I wanted to use the default rotate to face blackboard entry task but that could not adjust their pitch so I had to make my own task.

  6. Finally, after all these steps the turret tries to fire its gun with an internal cooldown period preventing too many shots.

TurretBrain behavior tree


Home

BTTask_FindAppropriateTarget

Study: Level Design