java-beginner.com ブログ

プログラミングを学習するブログ(Javaをメインに)

キーイベントの練習

投稿日:

最終更新日:2018年01月31日

アイキャッチ

こんにちは。「Javaを復習する初心者」です。

GUIプログラミングでゲームを作る場合、キー操作を取得したい場合があると思います。今回は押されたキーの取得についてプログラミングしてみました。

左右キー押下の取得

押下されたキーを取得するためには、addKeyListener()メソッドを使います。このメソッドはComponentクラスで定義されています。引数はKeyListenerインターフェースです。なので、その実装クラスを指定する必要があります。ここではKeyAdapterクラスの拡張クラスを使います。このクラスはKeyListenerインターフェースを実装しています。処理内容自体は空です。目的に応じてオーバーライドします。

以下プログラムでは、フレームを表示して、左右キーが応されたらメッセージを描画するという動作をします。

ソース

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;

public class MyJFrame extends JFrame {

	private final static int FRAME_LENGTH = 300;

	private int keyCode = 0;

	public static void main(String[] args) {
		new MyJFrame();
	}

	public MyJFrame() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// リスナー
		MyKeyAdapter myKeyAdapter = new MyKeyAdapter();
		addKeyListener(myKeyAdapter);

		setSize(FRAME_LENGTH, FRAME_LENGTH);
		setVisible(true);
	}

	public void paint(Graphics g) {
		// 画面を塗りつぶす
		g.setColor(Color.white);
		g.fillRect(0, 0, FRAME_LENGTH, FRAME_LENGTH);

		// キー
		String keyName = null;
		switch (keyCode) {
		case KeyEvent.VK_LEFT:
			keyName = "左";
			break;
		case KeyEvent.VK_RIGHT:
			keyName = "右";
			break;
		}

		if (keyName != null) {
			g.setColor(Color.black);
		    g.drawString(keyName + "が押されました。", 50, 100);
		}
	}

	private class MyKeyAdapter extends KeyAdapter {

		@Override
		public void keyPressed(KeyEvent e) {
			keyCode = e.getKeyCode();
		    repaint();
		}

	}

}

キーが押下されるとkeyPressed()メソッドが呼び出されます。仮引数のKeyEventクラスは押下されたキーの情報を持っているようで、このクラスのgetKeyCode()メソッドで取得することができます。KeyEventにはキーに対応した定数が定義されています。これを使って、どのキーが押下されたかを判断しています。

今回は以下のキーのみ押下の判定しています。

キー

対応する定数

左方向キー

KeyEvent.VK_LEFT

右方向キー

KeyEvent.VK_RIGHT

定数はint型で定義されています。なのでswitch文を使って分岐処理をして、画面に表示するメッセージを切り替えています。

四角を左右に動かす

フレーム内の中央付近に四角を表示し、左右キーで位置を移動するというプログラムをしてみました。

方法は様々だと思いますが、ここではRectangleクラスを使って四角を表示することにしました。キーイベントでその四角インスタンスの位置を変更するという考え方で作りました。

ソース

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;

public class MyJFrame extends JFrame {

	private final static int FRAME_LENGTH = 300;

	private final static int LENGTH_OF_PLAYER = 10;

	private Rectangle player = new Rectangle(FRAME_LENGTH / 2, FRAME_LENGTH /2,
			LENGTH_OF_PLAYER, LENGTH_OF_PLAYER);

	public static void main(String[] args) {
		new MyJFrame();
	}

	public MyJFrame() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// リスナー
		MyKeyAdapter myKeyAdapter = new MyKeyAdapter();
		addKeyListener(myKeyAdapter);

		setSize(FRAME_LENGTH, FRAME_LENGTH);
		setVisible(true);
	}

	public void paint(Graphics g) {
	    g.drawImage(getScreen(), 0, 0, this);
	}

	private Image getScreen() {
		Image screen = createImage(FRAME_LENGTH, FRAME_LENGTH);
	    Graphics2D g = (Graphics2D)screen.getGraphics();

	    // ターゲット描画
		g.setColor(Color.black);
		g.draw(player);

		return screen;
	}

	private class MyKeyAdapter extends KeyAdapter {

		@Override
		public void keyPressed(KeyEvent e) {
			switch (e.getKeyCode()) {
			case KeyEvent.VK_LEFT:
				player.setLocation((int)player.getX() - 10, (int)player.getY());
				break;
			case KeyEvent.VK_RIGHT:
				player.setLocation((int)player.getX() + 10, (int)player.getY());
				break;
			}
		    repaint();
		}

	}

}

Rectangle型変数playerにインスタンスを格納して、画面の中央付近に表示しています。keyPressed()メソッド内で位置を左右に動かすという処理をしています。左右に10ずつ動かすようにしてみました。単位はおそらくpxだと思ってますが、JavaDocには記述は見つかりませんでした。

上記プログラムでは「左右キーを押す」と四角が動くという動作です。キーを押し続けるとどうなるかというと、ある程度時間がたったあと、連続的にキーイベントが発生するという動作をしました。

タイマーで押された状態を判定

「キーが押され続けている」というのをタイマーを利用して判定するということもできます。TimerTaskのrun()メソッドでrepaint()メソッドを呼び出す方法です。

ソース

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;

public class MyJFrame extends JFrame {

	private final static int FRAME_LENGTH = 300;

	private final static int LENGTH_OF_PLAYER = 10;

	private Rectangle player = new Rectangle(FRAME_LENGTH / 2, FRAME_LENGTH /2,
			LENGTH_OF_PLAYER, LENGTH_OF_PLAYER);

	private static boolean keyFlgs[] = new boolean[2];
	private static final int INDEX_OF_LEFT_KEY  = 0;
	private static final int INDEX_OF_RIGHT_KEY = 1;


	public static void main(String[] args) {
		new MyJFrame();
	}

	public MyJFrame() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		setSize(FRAME_LENGTH, FRAME_LENGTH);

		// キーリスナー
		MyKeyAdapter myKeyAdapter = new MyKeyAdapter();
		addKeyListener(myKeyAdapter);

		// タイマー
		Timer timer = new java.util.Timer();
		timer.schedule(new MyTimeTask(), 1l, 10l);

		// 表示
		setVisible(true);
	}

	public void paint(Graphics g) {
	    g.drawImage(getScreen(), 0, 0, this);
	}

	private Image getScreen() {
		Image screen = createImage(FRAME_LENGTH, FRAME_LENGTH);
	    Graphics2D g = (Graphics2D)screen.getGraphics();

	    // ターゲット描画
		g.setColor(Color.black);
		g.draw(player);

		return screen;
	}

	private class MyTimeTask extends TimerTask {

		@Override
		public void run() {
			int x = player.x;
			int y = player.y;

			if (keyFlgs[INDEX_OF_LEFT_KEY]) {
				x -= 1;
			}

			if (keyFlgs[INDEX_OF_RIGHT_KEY]) {
				x += 1;
			}

			player.setLocation(x, y);
			repaint();
		}

	}

	private class MyKeyAdapter extends KeyAdapter {

		@Override
		public void keyPressed(KeyEvent e) {
			switch (e.getKeyCode()) {
			case KeyEvent.VK_LEFT:
				keyFlgs[INDEX_OF_LEFT_KEY] = true;
				break;
			case KeyEvent.VK_RIGHT:
				keyFlgs[INDEX_OF_RIGHT_KEY] = true;
				break;
			}
		}

		@Override
		public void keyReleased(KeyEvent e) {
			keyFlgs[INDEX_OF_LEFT_KEY]  = false;
			keyFlgs[INDEX_OF_RIGHT_KEY] = false;
		}

	}
}

上記のプログラムではboolean型配列のメンバ変数keyFlgsにキーの押下状態を保持しています。この押下状態に従って、MyTimeTaskクラスのrun()メソッドではRectangle型のメンバ変数playerの位置を設定し、repaint()メソッドを呼び出しています。

keyFlgsの状態はキーイベントで変化します。MyKeyAdapterクラスで変化のさせ方を定義しています。配列keyFlgsの配列番号と押下したキーを対応させ、押下された場合は対応する配列要素をtrueにしています。キーを押し続けていれば、ずっとその要素はtrueのままなので、タイマーイベントでtrueであることを検知してくれます。