RTS Demo – 03 – Scouting
The purpose of this document is to provide explanations and descriptions for how scouting was handled in the Apex Utility AI RTS (Real-Time Strategy) Demo project. It is a part of a series of use-cases showcased through the demo project. If you haven’t already, we recommend reading the High Level Overview Document first. The scene associated with this document is called “03_Scouting”.
In the RTS demo, worker units are utilized as scouts due to their superior scan range and movement speed, compared to other unit types. Additionally, they are cheap to spawn so losing one is of little consequence. The purpose of scouting is to identify the location of resources, enemy units and especially enemy structures. Thus, Controllers will issue a scout order to one of their units as long as they do not know the location of at least one of the enemy’s structures.
The Controller issues a scout order to one of its units as long as it does not have an observation of an enemy structure, and it does not already have a scout active. Thus, only one worker unit is scouting at any point in time. If the scout dies, a new order can be issued. The scout order itself has no special data associated, it just changes the behaviour of the AI. In the current implementation of the RTS Demo, a worker unit never stops scouting (until its death or game ends) after being given a scout order.
Map Grid Timestamps
In order to facilitate intelligent scouting, where a scout would predominantly search in areas that it or its allies have not already been in recently, timestamps on the map grid cells are utilized. Each map grid cell has a timestamp which is set by units every time they perform scanning. They update the timestamp on the map grid cell that is nearest to them. This facilitates scoring positions in cells with the oldest timestamp (or none at all) higher than those with recent timestamps. Thus, the scouting behaviour is ‘biased’ towards unseen cells or those that were seen a while ago. All units update the map grid cell timestamps, not just scouts, but scouts are the only ones in the current implementation utilizing the timestamps as part of their AI. An extremely simple AI action is used to update the timestamp, as can be seen here:
var c = (UnitContext)context;
var cell = c.unit.controller.mapGrid.GetNearestCell(c.unit.position);
if (cell == null)
cell.lastSeen = Time.timeSinceLevelLoad;
The actual scouting behaviour is implemented through position scoring. Units score all their sampled positions based on a range of scorers, see [link: 01 Scanning] for more about how they sample positions. The most important scorer is the map grid cell timestamp scorer. Older timestamps (lower values) should score higher than new ones, to ensure that scouts seek towards unexplored map grid cells. Additionally, the Controller attempts to predict the location of the enemy base, and scouts are biased towards that predicted location in order to attempt to arrive at the enemy’s base as soon as possible. Apart from that, scouts loosely attempt to avoid enemies, although this is not enforced. In fact, their fleeing mechanic is disabled while scouting, since in some situations it kept scouts from ever discovering the enemy’s structures, due to enemy units standing in the way. The position scoring combination of scorers can be seen in figure 1.
Figure 1 – This screenshot shows the composition of scorers for scout movement, which is a Move To Best Position action, scoring all sampled positions and moving to the highest scoring one.
Visualization and Debugging
In order to debug scouting behaviour, visualization is a crucial tool. The timestamps on each map grid cell can be visualized through GUI-writing it to the screen for each map cell. This option is available on the Controller’s visualizer, and requires that the Controller is the sole selected game object. Two numbers are written for each map grid cell – its calculated threat score (not relevant for this topic) and its timestamp, which is the bottom number. As the scout moves through the map, the timestamps are updated, prompting the scout to move towards unexplored areas.
The position scoring is also visualized through gizmo drawing and GUI writing. An ActionWithOptionsVisualizerComponent was implemented to facilitate the position score visualization. It shows the scores for each individual sampled position through a color coded gizmo sphere, as well as the actual score in writing. Additionally, the scout’s move target, which is actually just the highest scoring position, is also visualized through gizmo line and wire sphere drawing, as can be seen in figure 2.
Figure 2 – This screenshot shows a scout’s position scoring. All the sampled positions around the unit are scored, and the scores are visualized through color coding and the numerical values. Additionally, the scout’s current move target, the highest scoring position, is shown in blue.