Skip to content

Event Delays

pchsa edited this page Oct 3, 2023 · 7 revisions

Please see Event System before using

The EventHandler has been given extended functionality of scheduling events in the future and handling scheduled events. This simplifies code and provides precise timing for features that should not depend on Time System.

Note: Events cannot be saved/loaded. Do not use for functionality that needs to be saved.

Functionality

The EventHandler provides the following additional functionality:

  • scheduleEvent - Similar to trigger but takes in additional delay arg. Takes in delay (in seconds), event name and up to 3 args. Triggers the event after input seconds. Returns a reference to the event.
  • cancelEvent - Cancels given event. Pass in return value of scheduleEvent.
  • cancelAllEvents - Cancels all events. Use cautiously as it will also cancel scheduled events from other components.
  • update - Checks and executes scheduled events based on ServiceLocator.getTimeSource(), this has been added in entity.update(). If you an EventHandler elsewhere, this function will have to be added somewhere in the game loop.

Usage

The code below outlines how functionality is used for HostileAttackPattern.

Listener is added to the component in create()

entity.getEvents().addListener("attack", this::attack);

In attack function, it is used to delay shooting projectile and continue attack loop

public void attack() {
  ... 

  if (nearestEntity == null) { // No entity detected, clear attack loop
    currentAttackEvent = null;
    return;
  }
  ...

  entity.getEvents().trigger("attackStart") // trigger attack animation
  entity.getEvents().scheduleEvent(0.2f, "shoot", position) // shoot projectile after 0.2 sesconds.

  // or for dragonfly hostile, shoots 3 projectiles in succession
  entity.getEvents().scheduleEvent(0.2f, "shoot", position)
  entity.getEvents().scheduleEvent(0.3f, "shoot", position)
  entity.getEvents().scheduleEvent(0.4f, "shoot", position)
  
  // Schedule the next attack event
  currentAttackEvent = entity.getEvents().scheduleEvent(ATTACK_FREQUENCY, "attack")
}

How it works

When an event is scheduled, it is added to the scheduledEvents list in the EventHandler. Every time event handler update(), is called, it will check if each scheduled event can be triggered based on the current game time and the end time of the event. The event is then removed from the list

  public void update() {
    if (timeSource == null) {
      return;
    }

    List<ScheduledEvent> eventsToTrigger = new ArrayList<>(scheduledEvents);
    eventsToTrigger.removeIf(event -> !(timeSource.getTime() >= event.endTime()));

    eventsToTrigger.forEach(this::triggerScheduledEvent);

    // remove in separate loop to avoid concurrent modification error
    scheduledEvents.removeIf(eventsToTrigger::contains);
  }

Reasoning behind Implementation

The implementation of the scheduled events was driven by several key factors:

  • Independent of time system: e.g. Can set hostile to attack every 1.5s instead of converting to ingame time.
  • Improve gameplay features: Introducing delays can add more depth to gameplay, e.g. playing animation to warn player before hostile shoots projectile.
  • Simplifies code: Similar functionality can be achieved by listening to time updates from TimeService. However this would involve keeping track of and incrementing multiple counters.
  • Precise Timing: Allows delays to be precisely configured based on input seconds. e.g. shoot projectile 0.2s after attack animation.
  • Complies with current code: LibGdx already provides its own TaskScheduler. However the functions will trigger even when the TimeService is paused and so cannot be used.
Clone this wiki locally