Precise Isometric Entity Scaling and Placement - UQdeco2800/2022-studio-3 GitHub Wiki

Introduction

Given the nature of the issues covered by Team 6 in Sprint 3, it was essential that buildings / walls could be placed precisely on the tile desired, as walls needed to align to the edges of the city, and buildings are allocated a set tile location to be placed - if the buildings are slightly off from their allocated position, they risk clipping through walls on the edges of the city.

Engine Issues

Placement

In the previous iteration of the game engine, it was possible to place an entity approximately about a point using the spawnEntityAt() function in GameArea. When called, this would place an entity such that its texture hitbox was aligned in some way with the tile's point. As an example to better explain this, consider the following image:

This entity is placed at the world position denoted by the Red Dot in the centre of the image (The CentreX and CentreY arguments were true). The Entity's "World" position (entity.getPosition()), is located at the purple dot. The issue is that the engine only knows the corner position of the entity, the dimensions of the image and the final position to place it at, so this is the best guess it can make as to where to place it. This issue is more distinguishable in scaled entities, as 1 by 1 entities will look approximately correct on their tiles.

Placement solution - TextureScaler.java component: setSpawnPosition() function

Consider once again the image above, the Yellow dot corresponds to the location on the texture where the image starts (i.e. the position which should intersect with the Red dot, or spawn tile location). In order to place entities correctly relative to their texture, the component TextureScaler.java was developed. This component attaches to an Entity that has a TextureRenderComponent added, and accepts two arguments, the Vector2 (x,y) pixel representation of the position to align to the desired tile, and the Vector2 (x,y) pixel representation of the rightmost point of the image. It uses these two values to offset the entity's position such that the texture is aligned to the tile. The result is shown below, using debug code produced for the sprint:

The red box represents where it would have initially been placed, the blue box represents the edge of the tile, which now has the edge of the Entity's texture aligned to it, the yellow box represents the point relative to the red box where the Entity's texture's edge began, and the black box is the final hitbox of the entity after placing.

Scaling

Entities in the game engine previously could only be scaled orthogonally (think dragging the edge of an image to make it larger). This means that when setting a scale for an entity, say (5 x 5), it would have a 5 x 5 texture hitbox in game (in world dimensions), but the length and width isometrically of the texture would not be 5 tiles.

This fact is demonstrated by the following debug code output:

In this example:

  • Two Gate entities are each scaled to 5 x 5 tiles (as evidenced by the black rectangle - the square texture hitbox surrounding them)
  • They are placed 6 tiles apart and barely touch (which already is evidencing an issue, as they should be precisely touching 5 tiles apart if scaling worked as intended)
  • Purple lines are drawn to indicate 5 tiles of distance in the x direction

Upon consideration of the purple lines relative to the texture, it is clear that the textures are more than 5 tiles wide, which is problematic for precise alignment challenges, such as placing a number of city wall entities adjacently.

Scaling solution

To solve this, the setPreciseScale() function was added to TextureScaler.java component which, when created with points corresponding to the left and right points of a texture, will scale an entity to the desired length. This replaces both the scaleWidth() and scaleHeight() functions that previously existed to scale entities/textures orthogonally.

The TextureScaler.java component

Add this component to an Entity with a TextureRenderComponent, and it will scale it properly when provided with the correct inputs To create a TextureScaler component:

  1. Define the pixel points of an entity, such that they correspond to the diagram below:
        Vector2 left = new Vector2(35f, 194f); //Bottom leftmost edge in pixels
        Vector2 maxX = new Vector2(165f, 245f); //Pixel position of largest x-axis value
        Vector2 maxY = new Vector2(170f, 148f); //Pixel position of largest y-axis value
  1. Add the component
Entity myEntity.addComponent(new TextureScaler(left, maxX, maxY));

NOTE that after a recent change, it is possible to add a TextureScaler to an Entity without a TextureRenderComponent using the following. This is helpful for scaling/placing entities that have an AnimationRenderComoponent instead of a TRC. On creation, pass the texture corresponding to frame one of their idle animation to the TextureScaler's constructor.

Texture entityBaseTexture = ServiceLocator.getResourceService().getAsset("images/myAsset.png", Texture.class); //Store base texture of entity
Entity myEntity.addComponent(new TextureScaler(left, maxX, maxY, entityBaseTexture));

Choosing how to define the left, maxX and maxY points is explained below

Precisely placing an Entity with a TextureRenderComponent

  1. Set the left point of the entity to the bottom left point of the texture in pixels

To do this, enter MSPaint and hover over the corner edge, and note down the x,y pixels of it

  1. Do the same with the maxX and maxY points, as dictated in the figure above

Note that the left point is the Vector2 pixel coordinates that you want to align with the bottom left of the tile

  1. Attach the TextureScaler component to the entity in its associated factory
  2. Set the spawn point of the entity using the texture scale on creation in the AtlantisGameArea
public void spawnMyCoolEntity() {
Entity myEntity = myEntityFactory.createMyEntity();
GridPoint2 spawn = new Gridpoint2(0,0); //Tile spawn point of entity
myEntity.getComponent(TextureScaler.class).setSpawnPoint(spawn, terrain); //terrain is the TerrainComponent of AtlantisGameArea
spawnEntity(myEntity); //Call game Area function to create it, as it has already been placed
}

Scaling an Entity with a TextureScaler

  1. As above, add a TextureScaler to your entity, with the corresponding Left, maxX and maxY pixel points
  2. In the spawn entity function within your Factory, after the TextureScaler is added to the entity, use the following command
float scale = 5f; //This will set the width OR height of the entity to 5 TILES long, depending on the boolean scaleWidth
boolean scaleWidth = true; //Set this false if you wish to scale its height to a precise scale
myEntity.getComponent(TextureScaler.class).setPreciseScale(scale, scaleWidth);