Skip to content

Using the Input System

polyrain edited this page Aug 1, 2021 · 1 revision

Introduction

This page provides details on how to use the Input System, and is a follow on page to Input Handling Overview.

Page Overview:

  1. Setting the Game's Input Type
  2. Making Input Handlers
  3. Creating New Input Handlers
  4. Using Action Components
  5. Adding New Input Types
  6. Implementing a New Input Interface

Usage

Setting the Game's Input Type

The input type is set in InputService. This will determine the type of input factory created. The following is an example of setting the input type to KEYBOARD.

private static final InputFactory.InputType inputType = InputFactory.InputType.KEYBOARD;

Making Input Handlers

Input handlers can be created using the Input Factory. The following is an example to create a type-agnostic input handler for the player:

  InputComponent inputComponent = ServiceLocator.getInputService().getInputFactory().createForPlayer();

Creating New Input Handlers

Create a new input handler which extends InputComponent. InputComponent registers itself with the InputService on creation with the default priority of 0. To set a custom priority level, the input handler's constructor should call super({ priority-level }).

This is an example of creating a new input handler for the tree with a priority level to 5.

public class NewInputComponent extends InputComponent {
  public NewInputComponent() {
    super(5);
  }
  ...
}

InputComponent has methods to handle each type of input event (e.g. keyDown, scrolled, etc.) and by default these methods do not process the input. Therefore, input handlers should override all relevant input handling methods.

As an example, let's extend the input handler to handle typing the character a.

public class NewInputComponent extends InputComponent {
  public NewInputComponent() {
    super(5);
  }
  
  @Override
  public boolean keyTyped(char character) {
    if (character == 'a') {
      // handle input here
      return true;
    } 
    return false;
  }
}

Note: For games supporting multiple input types, you will need to create a type-specific input handler for each input type. For example, if your game supports KEYBOARD and TOUCH, you would need to create KeyboardNewInputComponent and TouchNewInputComponent.

To register the new input handlers with the input factories, add an abstract method for creating the input handler to InputFactory.

public abstract InputComponent createForPlayer();

Then implement these methods within the input type-specific factories. Within KeyboardInputFactory :

  @Override
  public InputComponent createForExample() {
    return new KeyboardNewInputComponent();
  }

Within TouchInputFactory :

  @Override
  public InputComponent createForExample() {
     return new TouchNewInputComponent();
  }

Using Action Components

By adding action components to entities, we can ensure that the entity acts consistently regardless of the input type that the game is receiving. We do this by abstracting away the entity's action logic into its own component and having the input component's call this functionality directly or through the events system. This avoids us having to write out the action code within each of the entity's input components, reducing code duplication and the chance of having inconsistent entity behaviour across input types.

Let's compare the code to make the player attack with and without the action component, PlayerAction.

Without the action component

Within KeyboardPlayerInputComponent:

 @Override
  public boolean keyDown(int keycode) {
    switch (keycode) {
      case Keys.SPACE:
        Sound attackSound = ServiceLocator.getResourceService().getAsset("sounds/Impact4.ogg", Sound.class);
        attackSound.play();
        return true;
      default:
        return false;
    }
  }

Within TouchPlayerInputComponent:

@Override
  public boolean touchDown(int screenX, int screenY, int pointer, int button) {
    Sound attackSound = ServiceLocator.getResourceService().getAsset("sounds/Impact4.ogg", Sound.class);
    attackSound.play();
    return true;
  }

With the action component

Within PlayerAction:

  @Override
  public void create() {
    // add 'player attack' event listener
    entity.getEvents().addListener("attack", this::attack);
  }

  void attack() {
    Sound attackSound = ServiceLocator.getResourceService().getAsset("sounds/Impact4.ogg", Sound.class);
    attackSound.play();
  }

Within KeyboardPlayerInputComponent:

 @Override
  public boolean keyDown(int keycode) {
    switch (keycode) {
      case Keys.SPACE:
        // trigger 'player attack' event
        entity.getEvents().trigger("attack");
        return true;
      default:
        return false;
    }
  }

Within TouchPlayerInputComponent:

@Override
  public boolean touchDown(int screenX, int screenY, int pointer, int button) {
    // trigger 'player attack' event
    entity.getEvents().trigger("attack");
    return true;
  }

You can see that the code is cleaner and less prone to errors as if the player's actions change, we only need to update it in one place.

Adding New Input Types

InputComponent currently implements the libgdx InputProcessor and GestureDetector.GestureListener interfaces. This means that it can support any input handlers that use only keyboard, mouse and touch gesture input. If the input type you are looking to create is not supported by these two interfaces, first follow the steps in "Implementing a New Input Interface" before coming back to this section.

For the purpose of explaining how to add a new input type, we will look at how TOUCH was added into the game.

Within InputFactory: Add the new input type to InputType.

  public enum InputType {
    KEYBOARD,
    TOUCH
  }

Add the option to create a Touch InputFactory in createFromInputType().

  public static InputFactory createFromInputType(InputType inputType) {
    ...

    if (inputType == InputType.KEYBOARD) {
      return new KeyboardInputFactory();
    } else if (inputType == InputType.TOUCH) {
      return new TouchInputFactory();
    }
    ...
  }

Create a touch input factory which returns touch-specific input components. It should have methods for all the entity's within the game with custom input components, i.e. for the starting game this means the player and debug terminal.

Within TouchInputFactory :

  @Override
  public InputComponent createForPlayer() {
    return new TouchPlayerInputComponent();
  }

  @Override
  public InputComponent createForTerminal() {
    return new TouchTerminalInputComponent();
  }

Create touch-specific input components for all the entity's within the game with custom input components. As an example we will look at how the KEYBOARD input component KeyboardPlayerInputComponent handles the player attack differently to the TOUCH input component TouchPlayerInputComponent.

For KEYBOARD, within KeyboardPlayerInputComponent the player attack is triggered by pressing space:

 @Override
  public boolean keyDown(int keycode) {
    switch (keycode) {
      ...
      case Keys.SPACE:
        // player attack
        entity.getEvents().trigger("attack");
        return true;
      ...
    }
  }

For TOUCH, within TouchPlayerInputComponent the player attack is triggered by touching down on the game (with a mouse or via a touch device):

  @Override
  public boolean touchDown(int screenX, int screenY, int pointer, int button) {
    // player attack
    entity.getEvents().trigger("attack");
    return true;
  }

Implementing a New Input Interface

For the purpose of explaining how to support a new input interface, we will pretend that the game does not yet support Touch Gesture input and will thus will go through the steps of adding it.

Set InputComponent and InputService to implement the new input interface.

public abstract class InputComponent extends Component
    implements InputProcessor, GestureDetector.GestureListener {

public class InputService implements InputProcessor, GestureDetector.GestureListener {

Add methods to InputComponent which return false for each new input methods. By default InputComponent will not handle any input, as these methods will be overridden by the actual input handlers.

  @Override
  public boolean fling(float velocityX, float velocityY, int button) {
    return false;
  }

  @Override
  public boolean longPress(float x, float y) {
    return false;
  }
  ...

Add methods to InputService which iterate through the registered input handlers for each of the new input methods.

  @Override
  public boolean fling(float velocityX, float velocityY, int button) {
    for (InputComponent inputHandler : inputHandlers) {
      if (inputHandler.fling(velocityX, velocityY, button)) {
        return true;
      }
    }
    return false;
  }

  @Override
  public boolean longPress(float x, float y) {
    for (InputComponent inputHandler : inputHandlers) {
      if (inputHandler.longPress(x, y)) {
        return true;
      }
    }
    return false;
  }
  ...

Gameplay

Home

Main Character

πŸ‘Ύ Obstacle/Enemy

Food & Water

Pickable Items

Game achievements

Game Design

Emotional Goals

Game Story

Influences

Style

Pixel Grid Resolution

Camera Angle and The Player's Perspective

Features Design

Achievements Screen

Achievements Trophies and Cards

Music Selection GUI

πŸ‘Ύ Obstacle/Enemy

 Monster Manual
 Obstacles/Enemies
  - Alien Plants
  - Variation thorns
  - Falling Meteorites
  - FaceHugger
  - AlienMonkey
 Spaceship & Map Entry
 Particle effect

Buffs

Debuffs

Buffs and Debuffs manual

Game Instruction

[code for debuff animations](code for debuff animations)

Infinite loop game system

Main Menu Screen

New Setting Screen

Hunger and Thirst

Goals and Objectives

HUD User Interface

Inventory System

Item Bar System

Scoring System

Props store

BGM design

Sound Effect design

Main game interface

Invisible ceiling

New terrain sprint 4

New game over screen sprint 4

Code Guidelines

Main Character Movement, Interactions and Animations - Code Guidelines

Item Pickup

ItemBar & Recycle system

Main Menu Button Code

Game Instructions Code

πŸ‘Ύ Obstacle/Enemy

 Obstacle/Enemy
 Monster Manual
 Spaceship Boss
 Particle effects
 Other Related Code
 UML & Sequence diagram of enemies/obstacles

Scoring System Implementation Explanation

Music Implementation

Buff and Debuff Implementation

Score History Display

code improvement explanation

Infinite generating terrains Implementation Explanation

Game Over Screen and functions explaination

Buffer timer before game start

Scrolling background

Multiple Maps

Invisible ceiling

Rocks and woods layout optimization

Magma and nails code implementation

Background Music Selection

Chooser GUI Implementation

Chooser GUI Logic Persistence

Guide: Adding Background music for a particular screen

Achievements Ecosystem - Code Guidelines

Achievements System

Achievements Screen

Adding Achievements (Guide)

Game Records

DateTimeUtils

History Scoreboard - Score Details

Listening for important events in the Achievements ecosystem

Food and Water System

Food System Water System

Hunger and Thirst icon code guidelines

Asset Creation

In Game Background Music

User Testing

Hunger and Thirst User Testing

Main Character Testing

Buffs and Debuffs Testing

Buff and Debuff Manual User Testing

Game Instruction User Testing

The Main Menu User Test

The New Button User Test in Setting Page

The Main Menu Buttons User Testing

Hunger and Thirst User Test

Infinite loop game and Terrain Testing

Item Bar System Testing

Randomised Item Drops

Recycle System Testing

Scoring System Testing

Music User test

https://github.com/UQdeco2800/2021-ext-studio-2.wiki.git

πŸ‘Ύ Obstacle/Enemy

 Obstacle testing
  - Alien Plants & Variation Thorns
  - Falling Meteorites
 Enemy testing
  - Alien Monkeys & Facehugger
  - Spaceship Boss
 Monster Manual
 Particle-effect
 Player attack testing
  - Player Attack

Inventory system UI layout

Props store user testing

Achievements User Testing

Sprint 1

Sprint 2

Sprint 3

Sprint 4

Items testing

Player Status testing

Changeable background & Buffer time testing

Main game interface test

Invisible ceiling test

Game over screen test sprint 4

New terrain textures on bonus map test sprint 4

Buying Props User Testing

Testing

Hunger and Thirst Testing

Main Character Player

Achievements System, Game Records and Unlockable Chapters

DateTimeUtils Testing

Scoring System Testing Plan

Distance Display Testing Plan

Musics Implementation Testing plan

History Board Testing plan

Rocks and woods testing plan

Sprint 4 terrain tests

Items

Item Bar System Testing Plan

Recycle System Testing Plan

Game Engine

Game Engine Help

Getting Started

Entities and Components

Service Locator

Loading Resources

Logging

Unit Testing

Debug Terminal

Input Handling

UI

Animations

Audio

AI

Physics

Game Screens and Areas

Terrain

Concurrency & Threading

Settings

Troubleshooting

MacOS Setup Guide

Clone this wiki locally