Skip to content

Animation Controller

The AnimationController manages which spritesheet animation is displayed for an entity. It determines the current animation based on entity state and handles animation playback.

How Animation Works

  1. Entity has a spritePrefix (e.g., "gurknukem")
  2. Spritesheets follow naming convention: {prefix}-{state}-{direction}.png
  3. AnimationController selects appropriate spritesheet based on entity state
  4. RenderEngine displays the current animation frame

CreatureAnimationController

For Creature entities, LITIENGINE provides CreatureAnimationController with built-in animation rules:

Spritesheet Naming Convention

{spritePrefix}-{state}-{direction}.{extension}

Examples:

  • gurknukem-idle-left.png - Idle animation, facing left
  • gurknukem-walk-left.png - Walking animation, facing left
  • gurknukem-dead.png - Death animation (no direction)

Default States

  • idle - Entity is standing still
  • walk - Entity is moving
  • dead - Entity has died

Automatic Flipping

By default, horizontal flip fallback is enabled. You only need sprites for left OR right direction:

// Only need gurknukem-walk-left.png
// gurknukem-walk-right.png is auto-generated by flipping

Custom Animation Controller

Create custom controllers for complex animation logic:

public class PlayerAnimationController extends CreatureAnimationController<Player> {

  public PlayerAnimationController(Player entity) {
    super(entity);

    // Add custom animation rule
    this.addRule(this::shouldAttack, this::getAttackAnimation);
  }

  private boolean shouldAttack() {
    return getEntity().isAttacking();
  }

  private String getAttackAnimation() {
    String dir = getEntity().getFacingDirection().name().toLowerCase();
    return "attack-" + dir;
  }
}

Animation Rules

Add rules to determine when specific animations play:

// Add an animation rule: (Predicate, Function, int priority) animationController.addRule( e -> e.isDead(), e -> "dead", 10 // High priority );

// Default priority (0) animationController.addRule( e -> e.hasTag("stunned"), e -> "stunned" );

Playing Animations

// Play specific animation
entity.getAnimationController().play("custom-animation");

// Play with loop
entity.getAnimationController().play("walk", true);

// Check if animation is playing
boolean isPlaying = entity.getAnimationController().isPlaying("attack");

Animation Events

Listen for animation keyframes:

entity.getAnimationController().onKeyFrameChanged((anim, frame) -> {
  // Handle keyframe change
  if (frame == 3) {
    playSound("footstep.wav");
  }
});

Accessing Current Animation

Animation current = entity.getAnimationController().getCurrentAnimation();

if (current != null) {
  String name = current.getName();
  int frame = entity.getAnimationController().getCurrentKeyFrameIndex();
  int totalFrames = current.getTotalFrames();
}

Spritesheet Requirements

For proper animation:

  1. Frame dimensions must be set correctly in utiLITI
  2. Frame duration controls animation speed (milliseconds per frame)
  3. Loop setting determines if animation repeats

In utiLITI

  1. Select spritesheet in Resources panel
  2. Edit sprite dimensions (width x height per frame)
  3. Set frame duration
  4. Enable/disable loop

Registering Animations

// Animations are auto-registered from spritesheets
// Or manually register:
Spritesheet sheet = Resources.spritesheets().get("player-idle");
Animation anim = new Animation(sheet, true, 100); // loop=true, duration=100ms
entity.getAnimationController().add(anim);

Image Effects Pipeline

You can attach real-time post-processing effects directly to an entity's AnimationController via add(ImageEffect effect). Active effects transform the sprite before rendering:

// Flash white on hit for 100ms
entity.animations().add(new OverlayPixelsImageEffect(100, Color.WHITE));

// Apply 50% transparency
entity.animations().add(new TransparencyImageEffect(2000, 0.5f));

For full details on built-in effects, contact shadows, and custom per-pixel filters, see the Image & Render Effects guide.

See Also