Projectile Improvements and Fixes for Sprint 3 - UQdeco2800/2022-studio-2 GitHub Wiki

Projectile Improvements

Before the following improvements were made, projectiles had larger hitboxes than the actual projectile and these larger hitboxes would knock the player back. Also the projectiles would follow the player around instead of being shot at the player. Also these projectiles did not disappear on collision. Therefore, the projectiles could all clump up together and trap and hurt the player with their oversized hitboxes. This therefore, created a bad experience for the player.

Improvement 1: Stop the projectile from knocking back the player

Original Code:

Entity projectile = new Entity()
                .addComponent(new PhysicsComponent())
                .addComponent(new PhysicsMovementComponent())
                .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE))
                .addComponent(new HitboxComponent().setLayer(PhysicsLayer.OBSTACLE))
                .addComponent(new TouchAttackComponent(PhysicsLayer.PLAYER, 1.5f)) //7.5
                .addComponent(aiComponent);

Improved Code:

Entity projectile = new Entity()
                .addComponent(new PhysicsComponent())
                .addComponent(new ColliderComponent().setLayer(PhysicsLayer.NONE))
                .addComponent(new HitboxComponent().setLayer(PhysicsLayer.PLAYER))
                .addComponent(new TouchAttackComponent(PhysicsLayer.PLAYER, 0f))
                .addComponent(new CombatStatsComponent(1, 1, 0, 0))
                .addComponent(enemyProjectileComponent);

In order to fix the projectile hit boxes moving the player back, the Collider Component had to be changed from the OBSTACLE layer to the NONE layer. This ensured the projectiles would not stop and actually move the player back. Also, the Hitbox Component's layer was also changed from OBSTACLE to PLAYER. This ensured that the projectile deals damage to the player.

Improvement 2: Fixing aiming issues with projectiles

Before this fix, the projectiles would keep following the player. This lead to an issue with whole clumps of projectiles following around the player. In order to fix this, the aiming was changed so instead of following the player, the projectiles would shoot at the players position and then keep going. This required a big restructure of how enemy projectiles worked.

The projectile's shoot task was removed, and instead an Enemy Projectile Component class was created. This class is used for moving the projectile and took inspiration from the Player Skill Projectile Component class. In that class there is the update method which keeps the projectile moving in a certain direction at a certain speed and the setProjectileDirection method which is used to set the direction for the projectile to go.

When creating the projectiles in the ProjectileFactory class, the direction for the projectile was set via the code below:

enemyProjectileComponent.setProjectileDirection(new Vector2(
                target.getPosition().x - ownerEntity.getPosition().x,
                target.getPosition().y - ownerEntity.getPosition().y
                ));

By finding the difference between the target position (position of the player) and the owner position (the entity who throws the projectile), this gives the direction for the projectile to go towards.

Improvement 3: Fix Projectile Hit Box and Collision Box Sizing

The collision and hit box alignments and sizing were both incorrect before this fix. The projectile would hit the player without touching them, and this made the projectiles unfair and a bad experience for users. Therefore, this had to be fixed. The original code for this hit box and collision box sizing and alignment is as below:

PhysicsUtils.setScaledCollider(projectile, 0.5f, 0.5f);

After further investigation, it was revealed that this setScaledCollider method automatically aligns the collider box to the centre of the X axis and bottom of the Y axis of the sprite box. Since the enemy projectiles were both aligned in or close to the centre of their respective sprite images, this did not work. Therefore this was changed to the following code put in the createPoopsSludge and createDiscus method respectively:

poops.getComponent(ColliderComponent.class).setAsBox(new Vector2(0.17f, 0.09f), new Vector2(0.48f, 0.47f));
        poops.getComponent(HitboxComponent.class).setAsBox(new Vector2(0.17f, 0.09f), new Vector2(0.48f, 0.47f));
discus.getComponent(ColliderComponent.class).setAsBoxAligned(new Vector2(0.28f, 0.125f),
                PhysicsComponent.AlignX.CENTER, PhysicsComponent.AlignY.CENTER);
        discus.getComponent(HitboxComponent.class).setAsBoxAligned(new Vector2(0.28f, 0.125f),
                PhysicsComponent.AlignX.CENTER, PhysicsComponent.AlignY.CENTER);

The setAsBox method allows us to set a collider or hitbox component to a certain size and certain alignment. It was used in the createPoopsSludge method to specify the size and alignment of the component as the poop sludge was close to but not entirely centred in its sprite image. The setAsBoxAligned was used in the createDiscus method as the discus was exactly in the centre of its sprite image. Therefore, it could easily be aligned to the CENTER without having to calculate its alignment.

Improvement 4: Making projectiles disappear when they hit player

Before this fix, the projectiles wouldn't disappear after hitting the player so they just built up into a large wall of projectiles that kept continuously dealing damage to the player. This did not make projectiles a good experience for the player. Therefore, this had to be fixed.

In order to fix this, we added a method to remove projectiles into the Enemy Projectile Component as shown here:

public void removeProjectile(Fixture me, Fixture other) {
        Fixture f = ServiceLocator.getGameArea().getPlayer().getComponent(HitboxComponent.class).getFixture();
        if (other == f) {
            Entity entityOfComponent = getEntity();
            ForestGameArea.removeProjectileOnMap(entityOfComponent);
        }
    }

This method first checks if the projectile is hitting the player. If it is, it then calls removeProjectileOnMap. This method is another method we created to fix this problem. The code for it can be seen below. removeProjectile sends the projectile to remove to this method, and then this method disables the projectile before disposing of it.

public static void removeProjectileOnMap(Entity entityToRemove) {
    entityToRemove.setEnabled(false);
    Gdx.app.postRunnable(() -> entityToRemove.dispose());
  }

Then we needed to add a listener so this event can be triggered when the projectile hits the player. Therefore we added the following line into the Enemy Projectile Component:

entity.getEvents().addListener("collisionStart", this::removeProjectile);

By adding this code, we were able to make it so that the enemy projectiles disappear on contact with the player.

Improvement 5: Adjust enemies' attacking and speed value to a reasonable state

Also, the bug is fixed related to the speed of the enemies where it glitched when it moves too fast. Now the speed is improved so that it visually is faster than before

Melee Gym Enemy:

public class GymBroConfig extends BaseEntityConfig {
    public float speed = 2f;
    public int baseAttack = 5;
    public int health = 70;
    public Entity weapon = WeaponFactory.createDumbbell();
}

Range enemy:

public class PoopsConfig extends BaseEntityConfig {
    public int health = 60;
    public float speed = 1f;
}

Level 1 boss Heracles:

public class HeraclesConfig extends BaseEntityConfig {
    public int health = 150;
    public int baseAttack = 10;
    public float speed = 2f;
}

Improvement 6: Removing the K press for disposing of all enemies

The K key press is initially created to provide a testing process while the player cannot attack the enemies. However, as the game progress, this feature is not needed. So, it will be removed from this Sprint.