グラフィックへ戻る | サンプル集目次へ戻る

実行結果
BallReflectionサンプルソース
Swingにおける、アニメーションの基本パターンを紹介。 ウィンドウの枠の中をボールが跳ねながら移動します。
Ball.java
BallReflection.java
DrawPanel.java
/**
 * 愛のJava256本ノック for Java 5.0
 * Javaサンプルソース ver0.2C "BallReflection"
 * Ball.java 「ウィンドウの内側を反射するボール」
 *
 * 2005/09/23 制作:安永ノリカズ
 *
 * 【コンパイル&実行方法】
 *     >javac *.java
 *     >java BallReflection
 * 【キーワード】
 *     乱数(random), 楕円(oval)の描画, 描画色の指定, 入射角と反射角
 *     
 * 【試してみよう】
 *     色を指定できるようにする。
 *     移動スピードも指定できるようにする。
 */
import java.awt.Color;
import java.awt.Graphics;
import java.lang.Math;

public class Ball {
    int I00;
    int I01;
    int I02;
    int I03;
    int I04;

    Ball(int A00) {
        this.I02 = A00;

        I00 = (int)(Math.random() * (DrawPanel.C00 - I02));
        I01 = (int)(Math.random() * (DrawPanel.C01 - I02));

        // 進行方向はランダム
        if (Math.random() > 0.5) {
            I03 = 1;
        } else {
            I03 = -1;
        }
        if (Math.random() > 0.5) {
            I04 = 1;
        } else {
            I04 = -1;
        }
    }

    void M00() {
        I00 += I03;
        I01 += I04;

        // 壁に衝突すれば反射
        if (I00 >= (DrawPanel.C00 - I02) || I00 <= 0) {
            I03 = -I03;
        }
        if (I01 >= (DrawPanel.C01 - I02) || I01 <= 0) {
            I04 = -I04;
        }
    }

    void M01(Graphics A00) {
        A00.setColor(Color.orange);
        A00.fillOval(I00, I01, I02, I02);
    }
}

/* ■ クラスの外でちょっと一言 ■
まず、一番下のfillOvalメソッドの引数から、いくつかのインスタンス変数名を
特定すると、全体の処理が見えてくるのではと思います。ボールの座標は円の中
心ではなく、円に外接する四角形の左上角になってますが、これはfillOvalメ
ソッドの引数の指定方法とあわせたからです。

このクラスには、イベントやウィンドウシステムの記述がなく、単純にボール関
する処理のみになってるのがミソですね。
 */
↑ 先頭へ戻る
/**
 * 愛のJava256本ノック for Java 5.0
 * Javaサンプルソース ver0.2C "BallReflection"
 * BallReflection.java 「ウィンドウの内側を反射するボール」
 *
 * 2005/09/23 制作:安永ノリカズ
 *
 * 【コンパイル&実行方法】
 *     >javac *.java
 *     >java BallReflection
 * 【キーワード】
 *     画面のリフレッシュレート(refresh rate:垂直走査周波数)
 * 【試してみよう】
 *     タイマーの発生間隔を変えて、アニメーションの見え方を比べる。
 *     ウィンドウのサイズを変更可能にし、フレーム内で跳ね返るようにする。
 */
import java.awt.Color;
import java.lang.String;
import javax.swing.JFrame;
import javax.swing.Timer;

public class BallReflection extends JFrame {
    final static int C00 = 30;

    public BallReflection() {
        DrawPanel L00 = new DrawPanel();
        add(L00);

        new Timer(C00, L00).start();
    }

    public static void main(String[] A00) {
        JFrame L00 = new BallReflection();
        L00.setTitle("反射するボール");
        L00.setDefaultCloseOperation(EXIT_ON_CLOSE);
        L00.setBackground(Color.white);
        L00.setResizable(false);
        L00.pack();
        L00.setVisible(true);
    }
}

/* ■ クラスの外でちょっと一言 ■
グラフィックを定期的に描画する、つまりアニメーションさせるには、一定間隔
でイベントを発生させる仕組みが必要です。このプログラムでは
javax.swing.Timerを使って、約0.03秒ごとに(毎秒約33回)、
java.awt.event.ActionEventを発生させています。

ゲームでは、毎秒30回画面を書き換える「秒30フレーム」が平均的ですね。書き
換え回数を増やすほど、アニメーションがスムーズになるわけですが、秒60フ
レーム以上は、いくら増やしても同じに見えます。人間の目の限界ってのもあり
ますが、一般的なパソコンのディスプレイは、毎秒60〜85回くらいで画面を更新
してるんで、それ以上やっても無駄なわけです。むしろ、増やしすぎると描画に
処理時間を取られて「重く」なるので、気をつけてください。

逆に減らす場合は、動きの少ないものなら、秒15フレームでもそれなりに見えて
くれます。この辺はバランスの問題ですので、実際に試して感触をつかんでくだ
さい。
 */
↑ 先頭へ戻る
/**
 * 愛のJava256本ノック for Java 5.0
 * Javaサンプルソース ver0.2C "BallReflection"
 * DrawPanel.java 「ウィンドウの内側を反射するボール」
 *
 * 2005/09/23 制作:安永ノリカズ
 *
 * 【コンパイル&実行方法】
 *     >javac *.java
 *     >java BallReflection
 * 【キーワード】
 *     イベント駆動(event-driven)プログラム, コンポーネントの再描画(repaint), 
 *     コールバック(callback)メソッド,
 *     イベントディスパッチスレッド(event dispatch thread), 
 * 【試してみよう】
 *     ボールの数を増やして配列で管理する。
 */
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JPanel;
import static java.awt.RenderingHints.KEY_ANTIALIASING;
import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;

public class DrawPanel extends JPanel implements ActionListener {
    static final int C00 = 300;
    static final int C01 = 200;

    Ball I00 = new Ball(50);
    
    public DrawPanel() {
        setBackground(Color.white);
        setPreferredSize(new Dimension(C00, C01));
    }

    public void actionPerformed(ActionEvent A00) {
        I00.M00();
        repaint();
    }

    public void paintComponent(Graphics A00) {
        super.paintComponent(A00);

        Graphics2D L00 = (Graphics2D)A00;
        L00.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
        I00.M01(L00);
    }
}

/* ■ クラスの外でちょっと一言 ■
このクラスのメソッドは、全て、初めからちゃんとした名前が付けてあります。
これは(コンストラクターを除いて)、プログラマーが直接呼び出すのではな
く、Swing側に呼び出してもらうコールバックメソッドだからです

処理の流れを理解するには、どのタイミングでメソッドが呼び出されるかを知る
のがポイントで、特に、TimerとactionPerformed()の関係、及び、repaint()と
paintComponent()の関係は重要です。

BallReflectionクラスで生成したTimerにより、定期的にActionイベントが発生
し、そのつど、actionPerformedが呼ばれます。ここに描画メソッドを書けば、
アニメーションが実現されるわけですが、実際は、ボールの座標を更新するのみ
で、描画は行わず、repaint()でパネルの「再描画要求」を出すにとどまってま
す。この「再描画要求」が出された後、paintComponent()が呼び出され、実際に
画面にボールが描画されるという仕組みになってます。

このように、GUIプログラミングでは、直接メソッドを呼び出すのではなく、イ
ベントという「仕組み」を利用してメソッドを呼び出します。これをプログラ
マーが直接やった場合には、必要なときに画面の描画がなされずに表示が崩れた
り、ウィンドウを操作しようにも反応しないなどの問題が起こる可能性がありま
す。コールバックメソッドでは、もたもたせずに、素早くシステムに処理を返す
のが、応答の素早いGUI構築のカギになります。
 */
↑ 先頭へ戻る

安永ノリカズのゲーム制作&Javaサンプル集 / Java初心者用サンプル集『愛のJava256本ノック』