Pathfinding Bugfix: Modifications to TextureScaler - UQdeco2800/2022-studio-3 GitHub Wiki

Introduction

Given the design stipulation of polishing gameplay, it was deemed necessary to revise how the positions and sizes of entities were stored, in order to allow unit pathfinding to function as intended. It was determined that pathfinding was primarily impeded by the internal storage of Entity data, as previously the position and size were determined by its position and scale.

Issue

The existing means of storing an entity's data was deemed insufficient, as the only data available before our solution was the position of the bottom left corner of the entity (I.e. Entity.getPosition()) and the size of it's texture orthogonally (Entity.getScale()).

These points are illustrated by the following figure, which shows the position of the entity (red dot) and the scale of it (indicated by yellow arrows)

Evidently, these points alone give no information as to the true tile position the entity is on, nor do they provide an accurate insight into where its hitbox is created. Having Entities strictly characterized by these points led to a map that was largely untraversable for allied units, as each building entity would block off a large swathe of the map.

This also was causing Entities to be incorrectly rendered by the minimap, as it could only approximate the location and size of large entities (as shown in the figure below)

Solution

The solution was already partially accessible through the previously created TextureScaler component. This component allows for precise placing of large isometric entities, by aligning the edge of their texture to a tile, as well as isometric side length scaling. In this sprint, TextureScaler was adapted to store the correct tile position of a placed entity, as well as its side length after scaling. A notable challenge of this adaptation was the fact that an Entity is only scaled in one direction (i.e. width or height), which meant that while one side length of the entity would be a precise tile length, the other would be variable. This was solved by storing another point of the entity's texture, that corresponds to its maximum y (height) value and reverse-engineering the scaling algorithm to determine how many tiles long it would be after the scale was applied.

The result of this is visualised in the game image taken below, which features debug code drawing the TextureScaler's stored values for position, width and height as lines across each entity with a TextureScaler component:

Clearly, the TextureScaler can precisely identify the position of the entity, as well as its dimensions. It is noted that some dimensions (for example the Farm's in the above image) are slightly larger than expected. This is intentional, as an image that has one side length scaled to a precise integer tile length likely leaves the other side to be fractions of tiles long. In this implementation, to be on the safe side, any side length that is a fraction of a tile long is rounded up, such that even a side length of 6.1 tiles will be considered 7 tiles. This decision was made to reduce collisions, in the case where the pathfinding algorithm could send a unit directly past the edge of a building.

These changes can be easily visualised in the minimap, which now can correctly identify the position and size of buildings:

Additional changes to TextureScaler

Alongside the upgrades mentioned above, TextureScaler also had the following functionality added:

  • It is now possible to pass in an offset while precisely spawning an entity, to position it within the tile itself. (This was used when positioning the connector walls between wall pillars on the outskirts of the city)
  • Either side (Width or Height) of an entity may now be scaled through a boolean in the setPreciseScale() function. This was still possible previously, but required a specific choice of input points when creating the TextureScaler
  • It is now possible to scale any Entity with a TextureScaler, by passing in a provided reference texture in the constructor. Previously the Entity had to have a TextureRenderComponent, which excluded Entities such as the forager from using it

Modifications to MapService

When entity positions are calculated, the TextureScaler component for that entity is retrieved, and from that component their position, height and width. The MapService uses these now in getAllOccupiedPositions(MapComponent) instead of Entity.scale and Entity.getPosition(), so now the actual positions of the textures on the map are stored correctly. As all entities added to MapService now require a TextureScaler to be tracked correctly, these were added by Team 6 to essential static entities in the game.

It was decided not to track units and enemies in MapService, as MapService path finding was implemented statically by unit path finding, so tracking moving entities would be both redundant and increase computational complexity. Another reason it was decided not to was because it was believed it would clutter the mini map. The ability of the mini map to track dynamically changing entities can still be seen during flooding events.