Skip to content

Combat Animal Animations

Paul edited this page Oct 17, 2024 · 6 revisions

Overview

The combat animation system in our game currently supports two animation states: IDLE and MOVE. These animations are controlled by the CombatAnimationController component and are used for both player and enemy entities in the Combat Screen.

  • IDLE animations are automatically started when entities are added to the Combat Screen.
  • MOVE animations are triggered when the player interacts with Move buttons (Attack, Guard, Sleep, or Items). MOVE animations are replaced by IDLE animations when a combat turn is being processed by the UI.

An example of IDLE and MOVE animations for the Enemy Frog can be seen in this video demonstration.

Implementation

CombatAnimationController class

This class is responsible for switching between combat animations and controlling the direction entities are facing. It listens to events and triggers the appropriate animation.

public class CombatAnimationController extends Component {
    AnimationRenderComponent animator;

    @Override
    public void create() {
        super.create();
        animator = this.entity.getComponent(AnimationRenderComponent.class);
        entity.getEvents().addListener("idleLeft", this::animateIdleLeft);
        entity.getEvents().addListener("idleRight", this::animateIdleRight);
        entity.getEvents().addListener("moveLeft", this::animateMoveLeft);
        entity.getEvents().addListener("moveRight", this::animateMoveRight);
    }

    private void animateIdleLeft() {
        if (this.entity.getEnemyType() != Entity.EnemyType.BEE) {
            animator.setFlipX(true);
        }
        animator.startAnimation("combat_idle");
    }

    private void animateIdleRight() {
        animator.setFlipX(false);
        animator.startAnimation("combat_idle");
    }

    private void animateMoveLeft() {
        if (this.entity.getEnemyType() != Entity.EnemyType.BEE) {
            animator.setFlipX(true);
        }
        animator.startAnimation("combat_move");
    }

    private void animateMoveRight() {
        animator.setFlipX(false);
        animator.startAnimation("combat_move");
    }
}

CombatAnimalFactory class

This class is responsible for creating entity instances for various animals in the Combat Screen. It provides a base method createCombatBaseEnemy() which is used by specific entity creation methods.

public class CombatAnimalFactory {
    private static final NPCConfigs configs =
            FileLoader.readClass(NPCConfigs.class, "configs/enemyNPCs.json");

    public static Entity createCombatBaseEnemy(BaseEnemyEntityConfig config, Entity.EnemyType type) {
        Entity entity = new Entity()
                .addComponent(new PhysicsComponent())
                .addComponent(new ColliderComponent());

        PhysicsUtils.setScaledCollider(entity, 0.9f, 0.4f);
        
        entity.setEnemyType(type);
        
        TextureAtlas textureAtlas = ServiceLocator.getResourceService().getAsset(config.getSpritePath(), TextureAtlas.class);
        AnimationRenderComponent animator = new AnimationRenderComponent(textureAtlas);
        
        animator.addAnimation("combat_idle", 0.8f, Animation.PlayMode.LOOP);
        animator.addAnimation("combat_move", 0.2f, Animation.PlayMode.LOOP);
        
        entity.addComponent(animator)
              .addComponent(new CombatAnimationController());
        
        return entity;
    }

    // Example of a specific enemy creation method
    public static Entity createChickenCombatEnemy() {
        BaseEnemyEntityConfig config = configs.chicken;
        Entity chickenEnemy = createCombatBaseEnemy(config, Entity.EnemyType.CHICKEN);
        chickenEnemy.scaleHeight(110.0f);
        return chickenEnemy;
    }

    // Other enemy creation methods...
}

CombatArea class

Changes in animations (between IDLE and MOVE) are triggered from this class. Specifically, the functions startEnemyAnimation(CombatAnimation animation) and startPlayerAnimation(CombatAnimation animation) are called from CombatButtonDisplay.java.

    public void startEnemyAnimation(CombatAnimation animation) {
        switch (animation) {
            case IDLE:
                enemyDisplay.getEvents().trigger("idleLeft");
                break;
            case MOVE:
                enemyDisplay.getEvents().trigger("moveLeft");
                break;
        }
    }

Example of calling the above functions from the CombatButtonDisplay class

    combatArea.startEnemyAnimation(CombatArea.CombatAnimation.IDLE);
    combatArea.startEnemyAnimation(CombatArea.CombatAnimation.MOVE);

UML Diagrams

Class Diagram

classDiagram
    class Entity {
        +addComponent(Component)
        +getComponent(Class)
        +setEnemyType(EnemyType)
    }
    class Component {
        +create()
    }
    class CombatAnimationController {
        -AnimationRenderComponent animator
        +create()
        -animateIdleLeft()
        -animateIdleRight()
        -animateMoveLeft()
        -animateMoveRight()
    }
    class AnimationRenderComponent {
        +setFlipX(boolean)
        +startAnimation(String)
        +addAnimation(String, float, PlayMode)
    }
    class CombatAnimalFactory {
        +createCombatBaseEnemy(BaseEnemyEntityConfig, EnemyType)
        +createChickenCombatEnemy()
        +createMonkeyCombatEnemy()
        +createFrogCombatEnemy()
    }

    Entity "1" *-- "many" Component
    CombatAnimationController --|> Component
    CombatAnimationController --> AnimationRenderComponent
    CombatAnimalFactory ..> Entity : creates
    CombatAnimalFactory ..> CombatAnimationController : adds
Loading

Sequence Diagram

sequenceDiagram
    participant CombatScreen
    participant CombatAnimalFactory
    participant Entity
    participant CombatAnimationController
    participant AnimationRenderComponent

    CombatScreen->>CombatAnimalFactory: createChickenCombatEnemy()
    CombatAnimalFactory->>Entity: new Entity()
    CombatAnimalFactory->>Entity: addComponent(AnimationRenderComponent)
    CombatAnimalFactory->>Entity: addComponent(CombatAnimationController)
    CombatAnimalFactory-->>CombatScreen: return chickenEnemy

    CombatScreen->>Entity: getEvents().trigger("idleLeft")
    Entity->>CombatAnimationController: animateIdleLeft()
    CombatAnimationController->>AnimationRenderComponent: setFlipX(true)
    CombatAnimationController->>AnimationRenderComponent: startAnimation("combat_idle")

    CombatScreen->>Entity: getEvents().trigger("moveRight")
    Entity->>CombatAnimationController: animateMoveRight()
    CombatAnimationController->>AnimationRenderComponent: setFlipX(false)
    CombatAnimationController->>AnimationRenderComponent: startAnimation("combat_move")
Loading

Usage

To use the combat animation system, e.g. add combat animal animations to a new enemy entity:

  1. Define combat_idle and combat_move animations in the entity's texture atlas.
  2. Create enemy entities using the CombatAnimalFactory methods.
  3. Ensure the necessary textures (sprite sheet image file) and texture Atlases are loaded into the CombatArea by CombatAreaConfig.java.
  4. Changes in animations will be triggered centrally from the CombatArea.

Notes

  • The player is placed on the left side of the combat screen and faces right, while enemies face left.
  • The CombatAnimationController handles flipping the entity sprite based on the direction it should face.
  • Special handling is implemented for the Bee enemy type, because it faces a different side in the sprite sheet.
  • Animation speeds and entity scaling can be adjusted in the CombatAnimalFactory for each specific enemy type.

Extending the System

To create new types of animation sequences

  1. Define a new animation key in the relevant entity's texture atlas.
  2. Add the new animation to the AnimationRenderComponent in the CombatAnimalFactory.
  3. Add the new animation key to the enum in CombatArea and the switch case in startPlayerAnimation() and/or startEnemyAnimation() of the CombatArea.
  4. Call the animation start functions with the new combat animation keys from the CombatButtonDisplay.
Clone this wiki locally