Keyboard input can be done for graphical components as well. To do this, we must create a class that implements the KeyListener interface.
This interface contains methods that are called when keys are pressed, released, or "typed". The KeyListener object must be added to a frame using the addKeyListener method. The "typed" event is used for textual input type controls (like text fields).
This example shows how this can be done:
import javax.swing.*;
import javax.imageio.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
import java.util.*;
// a character in a possible game
class Character {
// the speed of the character measured in pixels/second
private static final double SPEED = 100.0;
// the image which is drawn for them
private Image mario;
// current position and speed
private double x, y, dx, dy;
public Character() {
// load the image from a file
try {
mario = ImageIO.read(new File("mario.png"));
} catch (Exception e) {
mario = null;
}
// set default position and speed
x = 100;
y = 100;
dx = 0;
dy = 0;
}
public void draw(Graphics g) {
// draw the image on the screen at current location
g.drawImage(mario, (int) x, (int) y, null);
}
// stop the character from moving
public void stop() {
dx = 0;
dy = 0;
}
// set speed to move in a specific direction
public void left() {
dx = -SPEED;
}
public void up() {
dy = -SPEED;
}
public void right() {
dx = SPEED;
}
public void down() {
dy = SPEED;
}
// update based on the elapsed time
public void update(double dt) {
x += (dx * dt);
y += (dy * dt);
if (y < 0) {
y = 500;
}
if (y > 500) {
y = 0;
}
if (x < 0) {
x = 500;
}
if (x > 500) {
x = 0;
}
}
}
// the GameWorld is the GUI component we put in the window
class GameWorld extends JComponent implements KeyListener {
// store the game object(s) and elapsed time
private Character mario;
private long elapsed;
public GameWorld() {
elapsed = new Date().getTime();
mario = new Character();
}
@Override
public void keyTyped(KeyEvent e) {
// needed to implement the interface, but we don't care
// about "typed" keys
}
@Override
public void keyPressed(KeyEvent e) {
// this is what we care about - what key was pressed?
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
mario.right();
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
mario.left();
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
mario.up();
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
mario.down();
}
}
@Override
public void keyReleased(KeyEvent e) {
// if any key is released, stop the character
mario.stop();
}
@Override
public void paintComponent(Graphics g) {
// draw the character
mario.draw(g);
// now update just like before
long time_now = new Date().getTime();
double seconds = (time_now - elapsed) / 1000.0f;
elapsed = time_now;
mario.update(seconds);
// force an update of the screen
revalidate();
repaint();
}
}
public class InputExample {
public static void main(String args[]) {
// create and set up the window.
JFrame frame = new JFrame("Movement Example!");
// make the program close when the window closes
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// add the GameWorld component
GameWorld g = new GameWorld();
frame.add(g);
frame.addKeyListener(g);
// display the window.
frame.setSize(500, 500);
frame.setVisible(true);
}
}
The key pressed and released functions allow us to respond to different key types.
We can also make graphical programs that respond to mouse input. This is done by implementing the "MouseListener" interface. This includes function for responding to different mouse events:
The program below moves the character to the position the mouse is clicked on:
import javax.swing.*;
import javax.imageio.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
import java.util.*;
// a character in a possible game
class Character {
// the speed of the character measured in pixels/second
private static final double SPEED = 100.0;
// the image which is drawn for them
private Image mario;
// current position and speed
private double x, y, dx, dy;
public Character() {
// load the image from a file
try {
mario = ImageIO.read(new File("mario.png"));
} catch (Exception e) {
mario = null;
}
// set default position and speed
x = 100;
y = 100;
dx = 0;
dy = 0;
}
public void setPosition(int newx, int newy) {
x = newx;
y = newy;
}
public void draw(Graphics g) {
// draw the image on the screen at current location
g.drawImage(mario, (int) x, (int) y, null);
}
// stop the character from moving
public void stop() {
dx = 0;
dy = 0;
}
// set speed to move in a specific direction
public void left() {
dx = -SPEED;
}
public void up() {
dy = -SPEED;
}
public void right() {
dx = SPEED;
}
public void down() {
dy = SPEED;
}
// update based on the elapsed time
public void update(double dt) {
x += (dx * dt);
y += (dy * dt);
if (y < 0) {
y = 500;
}
if (y > 500) {
y = 0;
}
if (x < 0) {
x = 500;
}
if (x > 500) {
x = 0;
}
}
}
// the GameWorld is the GUI component we put in the window
class GameWorld extends JComponent implements MouseListener {
// store the game object(s) and elapsed time
private Character mario;
private long elapsed;
public GameWorld() {
elapsed = new Date().getTime();
mario = new Character();
}
@Override
public void paintComponent(Graphics g) {
// draw the character
mario.draw(g);
// now update just like before
long time_now = new Date().getTime();
double seconds = (time_now - elapsed) / 1000.0f;
elapsed = time_now;
mario.update(seconds);
// force an update of the screen
revalidate();
repaint();
}
@Override
public void mouseClicked(MouseEvent e) {
// this is called when the mouse is clicked and then released
// position Mario at this location
mario.setPosition(e.getX(), e.getY());
System.out.println("(" + e.getX() + ", " + e.getY() + ")");
}
@Override
public void mousePressed(MouseEvent e) {
// this is called when the mouse is first pressed
// e.g. as the first part of a click
}
@Override
public void mouseReleased(MouseEvent e) {
// this is called when the mouse is released
// e.g. as the second part of a click
}
@Override
public void mouseEntered(MouseEvent e) {
// this is called when the mouse enters the window
}
@Override
public void mouseExited(MouseEvent e) {
// this is called when the mouse leaves the window
}
}
public class MouseInput {
public static void main(String args[]) {
// create and set up the window.
JFrame frame = new JFrame("Movement Example!");
// make the program close when the window closes
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// add the GameWorld component
GameWorld g = new GameWorld();
frame.add(g);
// hookup the mouse listener to the game world
g.addMouseListener(g);
// display the window.
frame.setSize(500, 500);
frame.setVisible(true);
}
}
We use the "getX" and "getY" methods to find the position of the mouse when the event occurs.
Mario doesn't look that good moving across the screen standing still. To make him look like he's running, we can create a simple animation by flipping between multiple images.
To make this work, we need to keep track of a few things:
Sometimes a single image is used, but we read from different portions of it. Images with several different images or frames in them are called sprite sheets.
This example shows a simple version of an animated sprite:
import javax.swing.*;
import javax.imageio.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
import java.util.*;
// a character in a possible game
class Character {
// the speed measured in pixels/second
private static final double SPEED = 100.0;
// the current position and speed
private double x, y, dx, dy;
// now we have an array of images!
private Image[] mario;
// the current image we are on
private int current = 0;
// whether we are facing right or not
private boolean right = true;
// whether we are moving or not
private boolean moving = false;
// time between flips in the animation
private static final double FLIP_TIME = 0.125;
// time since last flip
private double timer = 0.0;
public Character() {
// load all the images
try {
mario = new Image[4];
mario[0] = ImageIO.read(new File("r1.png"));
mario[1] = ImageIO.read(new File("r2.png"));
mario[2] = ImageIO.read(new File("l1.png"));
mario[3] = ImageIO.read(new File("l2.png"));
} catch (Exception e) {
mario = null;
}
// set default position and speed
x = 100;
y = 100;
dx = 0;
dy = 0;
}
// draw the character to the screen
public void draw(Graphics g) {
// add two to the index if going left
int add = 0;
if (!right) {
add = 2;
}
// draw mario on the screen
g.drawImage(mario[current + add], (int) x, (int) y, null);
}
// stop mario
public void stop() {
dx = 0;
dy = 0;
moving = false;
}
// left/up/right/down
public void left() {
moving = true;
right = false;
dx = -SPEED;
}
public void up() {
moving = true;
dy = -SPEED;
}
public void right() {
moving = true;
right = true;
dx = SPEED;
}
public void down() {
moving = true;
dy = SPEED;
}
// update him
public void update(double dt) {
// update position first
x += (dx * dt);
y += (dy * dt);
if (y < 0) {
y = 500;
}
if (y > 500) {
y = 0;
}
if (x < 0) {
x = 500;
}
if (x > 500) {
x = 0;
}
// then update the animation
if (moving) {
timer += dt;
// if it has been long enough since the last flip
if (timer > FLIP_TIME) {
// then flip the image
timer = 0;
current = (current + 1) % 2;
}
}
}
}
class GameWorld extends JComponent implements KeyListener {
// store the game object(s) and elapsed time
private Character mario;
private long elapsed;
public GameWorld() {
elapsed = new Date().getTime();
mario = new Character();
}
@Override
public void keyTyped(KeyEvent e) {
// needed to implement the interface, but we don't care
// about "typed" keys
}
@Override
public void keyPressed(KeyEvent e) {
// this is what we care about - what key was pressed?
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
mario.right();
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
mario.left();
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
mario.up();
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
mario.down();
}
}
@Override
public void keyReleased(KeyEvent e) {
// if any key is released, stop the character
mario.stop();
}
@Override
public void paintComponent(Graphics g) {
// draw the character
mario.draw(g);
// now update just like before
long time_now = new Date().getTime();
double seconds = (time_now - elapsed) / 1000.0f;
elapsed = time_now;
mario.update(seconds);
// force an update of the screen
revalidate();
repaint();
}
}
public class Animation {
public static void main(String args[]) {
// create and set up the window.
JFrame frame = new JFrame("Animation Example!");
// make the program close when the window closes
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// add the GameWorld component
GameWorld g = new GameWorld();
frame.add(g);
frame.addKeyListener(g);
// display the window.
frame.setSize(500, 500);
frame.setVisible(true);
}
}
For this to run, you will need the four images:
This can be further improved by increasing the number of frames in the animation.
Copyright © 2024 Ian Finlayson | Licensed under a Creative Commons BY-NC-SA 4.0 License.