オブジェクト指向とかデザインパターンとか開発プロセスとかツールとか

satoshi's ソフトウェア開発

js






当サイトはアフィリエイト広告を利用してます。

Java UML オブジェクト指向

JavaでRPGを作る|勇者をフィールド上で動かそう

投稿日:


今回は、JavaのGUI機能を使って、勇者をフィールド上に表示し、キーボードで動かせるようにします。

RPGを作るなら、やはり画面があった方が楽しくなります。

これまでの記事では、Chara、Hero、Slime などのクラスを作りながら、オブジェクト指向や継承について考えてきました。

今回は、JavaでGUIの画面を作り、最終的には、草原のマス目の上に勇者を表示し、キーボードの矢印キーで動かせるようにします。

今回作るもの

今回作る画面は、シンプルなRPGのフィールドです。

画面上に草原の画像を並べ、その中の1マスに勇者の画像を表示します。

そして、矢印キーを押すと、勇者の位置が変わります。

たとえば、右キーを押すと勇者が右に移動します。
下キーを押すと勇者が下に移動します。

本格的なRPGには、マップ、敵、アイテム、戦闘画面など、いろいろな要素があります。

しかし、最初から全部を作ろうとすると大変です。

まずは、

  • 画面を表示する
  • フィールドを表示する
  • 勇者を表示する
  • キーボードで勇者を動かす

ところまで作ります。

Swingで使う主なクラス

Javaで画面を持つアプリケーションを作るには、GUIの仕組みが必要です。

GUIとは、ボタンやウィンドウ、画像などを使って操作できる画面のことです。

Javaには、GUIを作るためのライブラリとして Swing が用意されています。

Swingを使うと、ウィンドウを表示したり、画像を表示したり、ボタンやラベルを配置したりできます。

今回使う主なSwingのクラスは、次の通りです。

クラス 役割
JFrame ウィンドウを表示する
JLabel 文字や画像を表示する
ImageIcon 画像ファイルを読み込む
GridBagLayout 部品をマス目のように配置する
KeyListener キーボード入力を受け取る

Swingにはたくさんのクラスがありますが、今回は最小限のクラスだけ使うことにします。

RPGを作るという目的で考えると、

  • JFrame はゲーム画面
  • JLabel は勇者や草原の画像を表示する部品
  • GridBagLayout はフィールドをマス目に並べる仕組み
  • KeyListener は勇者を動かすための入力処理

と考えると分かりやすいです。

MVCアーキテクチャ

GUIアプリケーションを作る時に大切なのが「MVCアーキテクチャ」です。

MはModel、VはView、CはControllerです。

それぞれ、クラスの役割分担をさせましょうという考え方です。

これまでに作成した Hero や Slime は Model の役割を受け持ちます。

Model だけではアプリケーションは動きません。

Viewは、画面上に表示するためのオブジェクトです。Heroを画面上でどのように表示するのかを決める役割を持ちます。

例えば、Windowsのエクスプローラーでファイルを見る時に、ファイルやフォルダはModelです。

そして、メニューからファイルの表示方法を大きいアイコンや小さいアイコン、詳細などに変更できますよね。

これがファイルやフォルダのViewです。Viewのクラスを切り替えることで、表示方法を変更しているのです。

メニューにある「大きいアイコン」や「小さいアイコン」などがControllerです。

その考え方を前提にして、GUIアプリケーションを作っていきます。

まずは起動用のGuiAppクラスを作る

まず、GUIアプリケーションを起動するための GuiApp クラスを作ります。

import javax.swing.SwingUtilities;

public class GuiApp {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            RpgFrame frame = new RpgFrame();
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            frame.requestFocusInWindow();
        });
    }
}

GuiApp の役割は、RPGの画面を作って表示することです。

RpgFrame frame = new RpgFrame();

ここで、RPG用の画面である RpgFrame を作っています。
このクラスは、RpgFrameを表示する役割を持つControllerにあたります。

frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

pack()は、画面内の配置を自動調整してくれるメソッドです。
setLocationRelativeTo()は、画面の位置を指定するメソッドです。nullを引数に指定すると、ディスプレイの中央に画面を表示してくれます。
setVisivle(true)で画面を表示します。

Swingでは、画面の表示や更新を専用のスレッドで行うため、SwingUtilities.invokeLater() の中で画面を作るようにしています。

最初は細かい仕組みまで理解できなくても大丈夫です。

ここでは、Swingの画面表示は SwingUtilities.invokeLater() の中で行うお約束になっているのでそうしているだけです。

RPG用の画面RpgFrameクラスを作る

次に、RPG用の画面を表す RpgFrame クラスを作ります。画面を表示する機能を持つ、JFrameクラスを継承しています。
このクラスは、RPGのGUIに当たるViewの役割を持ちます。

import javax.swing.JFrame; 

public class RpgFrame extends JFrame {
    public RpgFrame() {
        super("RPG");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    } 
}

ただの JFrame ではなく、RPG用に使いやすくした画面として RpgFrame クラスを作っています。

このように、Javaが用意しているクラスを継承して、自分の目的に合ったクラスを作ることもできます。

前の記事では、Hero や Slime が Chara を継承しました。

今回は、RpgFrame が JFrame を継承しています。

どちらも、

既存のクラスの機能を受け継いで、新しいクラスを作る

という点では同じです。

勇者を表示するHeroLabelクラスを作る

次に、勇者の画像を表示するためのクラスを作ります。

HeroLabelは、HeroというModelに対応するViewクラスです。

画像を表示するには、JLabel と ImageIcon を使います。

ここでは、勇者の画像ファイルとして hero.png を用意しているものとします。

import java.awt.Dimension; 
import java.awt.Image; 
import javax.swing.ImageIcon; 
import javax.swing.JLabel; 

public class HeroLabel extends JLabel {
    public HeroLabel() {
       ImageIcon icon = new ImageIcon("hero.png");
       Image image = icon.getImage().getScaledInstance( RpgFrame.ICON_WIDTH, RpgFrame.ICON_HEIGHT, Image.SCALE_SMOOTH);
       setIcon(new ImageIcon(image));
       setPreferredSize(new Dimension( RpgFrame.ICON_WIDTH, RpgFrame.ICON_HEIGHT));
    }
}

HeroLabel は JLabel を継承しています。

JLabel は、文字や画像を表示するためのJavaの標準部品です。

つまり、HeroLabel は、勇者の画像を表示するためのラベルです。

画像ファイルは、次の部分で読み込んでいます。

ImageIcon icon = new ImageIcon("hero.png");

画像サイズをフィールドのマス目に合わせるために、getScaledInstance() で画像を拡大・縮小しています。

Image image = icon.getImage().getScaledInstance( RpgFrame.ICON_WIDTH, RpgFrame.ICON_HEIGHT, Image.SCALE_SMOOTH);

このように、勇者の表示に関する処理を HeroLabel にまとめておくと、コードが読みやすくなります。

草原を表示するFieldLabelクラスを作る

次に、草原を表示するための FieldLabel クラスを作ります。

ここでは、草原の画像ファイルとして grass.png を用意しているものとします。

import java.awt.Dimension; 
import java.awt.Image; 
import javax.swing.ImageIcon; 
import javax.swing.JLabel; 

public class FieldLabel extends JLabel {
    public FieldLabel() {
       ImageIcon icon = new ImageIcon("grass.png");
       Image image = icon.getImage().getScaledInstance( RpgFrame.ICON_WIDTH, RpgFrame.ICON_HEIGHT, Image.SCALE_SMOOTH);
       setIcon(new ImageIcon(image));
       setPreferredSize(new Dimension( RpgFrame.ICON_WIDTH, RpgFrame.ICON_HEIGHT));
    }
}

FieldLabel も JLabel を継承しています。

HeroLabel は勇者を表示するためのラベルです。

FieldLabel は草原を表示するためのラベルです。

どちらも画像を表示する部品ですが、役割が違います。

このように、画面に表示するものごとにクラスを分けると、コードの役割が分かりやすくなります。

RpgFrameにフィールドの大きさと勇者の位置を持たせる

次に、RpgFrame にフィールドの大きさと勇者の位置、アイコンのサイズを定数で持たせます。

public class RpgFrame extends JFrame {
    public static final int ICON_WIDTH = 64;  // アイコン幅
    public static final int ICON_HEIGHT = 64; // アイコン高さ
    public static final int FIELD_WIDTH = 10; // フィールド幅
    public static final int FIELD_HEIGHT = 8; // フィールド高さ
    private int heroX = 0; // ヒーローのX座標
    private int heroY = 0; // ヒーローのY座標

    public RpgFrame() {
       super("RPG");
       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    } 
}

GridBagLayoutでフィールドをマス目のように並べる

次に、草原と勇者を画面に並べます。

フィールドをマス目のように並べるために、GridBagLayout を使います。

RpgFrame を次のように書き換えます。

import java.awt.Component; 
import java.awt.Container; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import javax.swing.JFrame; 

public class RpgFrame extends JFrame {
    public static final int ICON_WIDTH = 64;  // アイコン幅
    public static final int ICON_HEIGHT = 64; // アイコン高さ
    public static final int FIELD_WIDTH = 10; // フィールド幅
    public static final int FIELD_HEIGHT = 8; // フィールド高さ
    private int heroX = 0; // ヒーローのX座標
    private int heroY = 0; // ヒーローのY座標

    public RpgFrame() {
       super("RPG");
       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

       Container pane = getContentPane(); 
       pane.setLayout(new GridBagLayout());

       drawField();
    } 

    private void drawField() {
        Container pane = getContentPane();
        pane.removeAll();
        for (int y = 0; y < FIELD_HEIGHT; y++) {
           for (int x = 0; x < FIELD_WIDTH; x++) {
               if (x == heroX && y == heroY) {
                   add(pane, new HeroLabel(), x, y, 1, 1);
               } else {
                   add(pane, new FieldLabel(), x, y, 1, 1);
               }
           }
        }
        pane.revalidate();
        pane.repaint();
    }

    private static void add(
        Container pane,
        Component component,
        int x,
        int y,
        int width,
        int height) {
            GridBagConstraints constraints = new GridBagConstraints();
            constraints.fill = GridBagConstraints.BOTH;
            constraints.gridx = x;
            constraints.gridy = y;
            constraints.gridwidth = width;
            constraints.gridheight = height;
            pane.add(component, constraints);
        }
    }
}

少し長くなりましたが、やっていることはシンプルです。

drawField() メソッドで、フィールド全体を描画しています。

描画しようとしているマス目が勇者の位置なら HeroLabel を表示し、それ以外はFieldLabelを表示します。

最後の add() メソッドは、GridBagLayoutのマス目のどこに表示するのかを指定する機能をまとめています。

キーボード入力を受け取る

次に、矢印キーで勇者を動かせるようにします。

キーボード入力を受け取るために、KeyListener を使います。

RpgFrame を KeyListener に対応させます。

KeyListenerは、Javaが用意しているキー入力を受け取るためのインターフェースです。

RpgFrameクラスを宣言している部分に、implements KeyListener を追加することで、RpgFrameがキー入力を受け取れるようになります。

キー入力を受け取る役割は、Controllerとして振る舞うので、
RpgFrameというViewのクラスにControllerの機能を追加することになりますが、ここはちょっと手抜きしておきます。

public class RpgFrame extends JFrame implements KeyListener {

KeyListener を使うには、次の3つのメソッドをRpgFrameに追加する必要があります。

@Override
public void keyTyped(KeyEvent e) {
}

@Override
public void keyPressed(KeyEvent e) {
}

@Override
public void keyReleased(KeyEvent e) {
}

今回は、キーが押されたときに勇者を動かしたいので、keyPressed() に処理を書きます。
以下のコードをRpgFrameの最後の } の手前に追加します。

何かキーが押されたら、勇者の座標を移動し、画面を再描画しています。

    @Override public void keyTyped(KeyEvent e) {
    }

    @Override public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            if (heroX > 0) {
                heroX--;
            }
        }
        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            if (heroX < FIELD_WIDTH - 1) {
                heroX++;
            }
        }
        if (e.getKeyCode() == KeyEvent.VK_UP) {
            if (heroY > 0) {
            heroY--;
            }
        }
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            if (heroY < FIELD_HEIGHT - 1) {
                heroY++;
            }
        }
        drawField();
        System.out.println("x=" + heroX + ", y=" + heroY);
    }

    @Override public void keyReleased(KeyEvent e) {
    }

今回作ったクラスの役割

今回作ったクラスの役割を整理すると、次のようになります。

クラス 役割
GuiApp アプリケーションを起動する
RpgFrame RPGの画面を作り、勇者の位置を管理する
HeroLabel 勇者の画像を表示する
FieldLabel 草原の画像を表示する

このようにクラスごとに役割を分けると、プログラムが読みやすくなります。

クラス図で整理する

今回作ったクラスを、簡単なクラス図で整理してみます。

+----------------+ 
| GuiApp         | 
+----------------+ 
|                | 
+----------------+ 
| main(args)     | 
+----------------+ 

+----------------+ 
| JFrame         | 
+----------------+ 
        △
        |
+----------------+ 
| RpgFrame       | 
+----------------+ 
| ICON_WIDTH     | 
| ICON_HEIGHT    | 
| FIELD_WIDTH    | 
| FIELD_HEIGHT   | 
| heroX          | 
| heroY          | 
+----------------+
| drawField()    | 
| keyPressed(e)  | 
| keyTyped(e)    | 
| keyReleased(e) | 
+----------------+ 

+----------------+ 
| JLabel         | 
+----------------+ 
        △
        |
        +--------------------+
        |                    |
+----------------+   +----------------+
| HeroLabel      |   | FieldLabel     |
+----------------+   +----------------+
|                |   |                |
+----------------+   +----------------+
|                |   |                |
+----------------+   +----------------+

まとめ:勇者が動くとRPGらしくなる

今回は、JavaでRPGの画面を作り、勇者をフィールド上で動かしてみました。

まず、GuiApp でアプリケーションを起動しました。

次に、RpgFrame でRPG用の画面を作りました。

勇者の画像は HeroLabel、草原の画像は FieldLabel としてクラスを分けました。

そして、GridBagLayout を使って草原をマス目のように並べ、その中に勇者を表示しました。

最後に、KeyListener を使って矢印キーの入力を受け取り、勇者の座標を変更して画面を描き直しました。

今回の段階では、まだ敵もアイテムもありません。

しかし、勇者をフィールド上で動かせるようになると、次に作りたいものが見えてきます。

次回は、勇者が移動したときに敵と遭遇する処理を追加して、戦闘画面を表示してみます。







-Java, UML, オブジェクト指向
-, , , , ,

This website stores cookies on your computer. These cookies are used to provide a more personalized experience and to track your whereabouts around our website in compliance with the European General Data Protection Regulation. If you decide to to opt-out of any future tracking, a cookie will be setup in your browser to remember this choice for one year.

Accept or Deny

Copyright© satoshi's ソフトウェア開発 , 2026 All Rights Reserved Powered by STINGER.