Javaプログラミング基礎 冬休みの宿題

解答は

に提出しなさい。クラスファイル (〜.class) は提出しなくてよい。 提出には gFTP 等の ftp ソフトを用いること。

単位取得最低限のためには、問題1の提出が必須。 問題2の提出は任意 (中上級者向け課題) 。

プログラムの一部または全部をコピーしたものが提出された場合、 不正行為として扱う。

冬休みはグラフィックスを勉強しよう!

グラフィックスにおける座標の扱い

Javaをはじめとする多くのグラフィックス環境では、 表示画面を細かい点の集まりとして扱います。この点のことを画素といいます。 そして、この点が画面上のどの位置かを示すために、 左上を原点 (0,0) とした座標を用います。 例えば、幅 640 、高さ 480 の大きさのウィンドウの座標は 下の絵のようになります。

数学のグラフの y 座標を上下逆にしたものだと考えることができます。

ウィンドウ上に位置を指定して図形や文字を書く場合や、 ウィンドウ上の範囲など指定する場合、 すべてこの「座標」を使います。

図形を描く準備

さっそく、ウィンドウを開くプログラムを作りましょう。 まだ、ウィンドウを開くだけで何も中身が表示されませんが、 後の例題で様々な図形や文字の表示方法を学ぶことになります。

import java.awt.*;
import javax.swing.*;

public class BasicPanel {
    public static void main(String[] args) {
	JFrame frame = new JFrame();

	frame.setTitle("Basic Panel");
	frame.setSize(640, 480);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();
        panel.setBackground(Color.white);
	frame.getContentPane().add(panel);

	frame.setVisible(true);
    }
}

このプログラムを実行してみましょう。 中身のないウィンドウが表示されましたね。

プログラムを上から順に見ていきましょう。

import java.awt.*;
import javax.swing.*;

上の部分は、Javaのクラスライブラリの中から「java.awt」と 「javax.swing」というパッケージにあるクラスを、 このプログラム内でデフォルトで使うことができるようにするための記述です。

	JFrame frame = new JFrame();

クラス JFrame は、ウィンドウの「枠」を表示させるためのクラスです。 JFrame のオブジェクトを生成し、 所定のメソッドを実行することによって、 画面にウィンドウを表示することができます。

	frame.setTitle("Basic Window");
	frame.setSize(640, 480);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

上のプログラムは、JFrame オブジェクト frame に所定の値を設定し、 ウィンドウを表示するためのメソッドの実行です。 上から順番に、ウィンドウのタイトルを設定し、 大きさを設定し、 ウィンドウを閉じたときにプログラム自体を終了する設定をしています。

        JPanel panel = new JPanel();
        panel.setBackground(Color.white);
	frame.getContentPane().add(panel);

クラス JPanel は、図形を載せる土台になるものです。 ここでは、クラス JPanel のオブジェクトを生成し、 背景色を白 (Color.white) に設定しています。 次に、ウィンドウの中に panel が表示されるように、 frame に panel を加えています。

	frame.setVisible(true);

最後にウィンドウを表示しています。

この例題は、ウィンドウを開いてグラフィックス表示を行うための基本の枠組みとして、 決まり文句と捉えて良いでしょう。

例題: 図形を表示する

上の例題のプログラムをベースに、簡単な図形を書いてみます。 図形を書く方法の一つに、コンテナであるクラス JPanel を継承し、 図形表示機能を付け加える方法があります。 (BasicShapes.java)

import java.awt.*;
import javax.swing.*;

public class BasicShapes {
    public static void main(String[] args) {
	JFrame frame = new JFrame();

	frame.setTitle("Basic Shapes");
	frame.setSize(640, 480);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	MyPanel panel = new MyPanel();
	frame.getContentPane().add(panel);

	frame.setVisible(true);
    }
}

class MyPanel extends JPanel {
    public MyPanel() {
	setBackground(Color.white);
    }

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

	// ウィンドウ内に描画したい図形をここに書く
	g.setColor(Color.red);
	g.fillRect(50,100,100,100);
	g.drawRect(200,100,100,100);

	g.setColor(Color.black);
	g.drawLine(350,100,450,200);

	g.setColor(Color.gray);
	g.fillOval(50,240,100,100);
	g.drawOval(200,240,100,100);

	g.setColor(Color.black);
	g.drawString("Welcome to java graphics.", 10, 20);
    }
}

クラス MyPanel が、元のクラス JPanel を継承して定義したクラスです。 図形を表示するためには、 メソッド paintComponent をオーバライドし、 図形を書くための処理を書き加えます。 メソッド paintComponent を見てみましょう。

	g.setColor(Color.red);
	g.fillRect(50,100,100,100);
	g.drawRect(200,100,100,100);

上のプログラムは、まず描画色を赤に設定し、 中身の塗りつぶされた四角形を描き、 枠だけの四角形を描いています。

	g.setColor(Color.white);
	g.drawLine(350,100,450,200);

	g.setColor(Color.gray);
	g.fillOval(50,240,100,100);
	g.drawOval(200,240,100,100);

	g.setColor(Color.black);
	g.drawString("Welcome to java graphics.", 10, 20);

それぞれ、drawLine は、線を描くためのメソッド、 fillOval は、塗りつぶされた円を描くためのメソッド、 drawOval は、円を描くためのメソッド、 drawString は、文字列を描くためのメソッドです。

各メソッドの使いかたを簡単にまとめます。 詳しくは Java SE 6 APIドキュメント の クラス Graphics の説明を参照してください。

setColor(color)
描画色を引数で指定された色に変更します。 指定可能な色には主に以下のようなものがあります。
drawLine(x1, y1, x2, y2)
座標 (x1, y1) と (x2, y2) を結ぶ線を描きます。
drawOval(x, y, width, height)
座標 (x, y) を左上とする、高さ width、幅 height の円 (楕円) を描きます。
drawRect(x, y, width, height)
座標 (x, y) を左上とする、高さ width、幅 height の四角形を描きます。
drawString(string, x, y)
座標 (x, y) に文字 string を描きます。
fillOval(x, y, width, height)
座標 (x, y) を左上とする、高さ width、幅 height の塗りつぶされた円 (楕円) を描きます。
fillRect(x, y, width, height)
座標 (x, y) を左上とする、高さ width、幅 height の塗りつぶされた四角形を描きます。

アニメーション

アニメーションを行う基本的な考え方は、 1コマ1コマ絵を書き換えることです。 プログラムの中にある仕掛けをすると、 少しずつ異なった図形を繰り返し画面を書き換えながら表示することができます。

ボールが枠内を跳ね返るアニメーションをグラフィック表示してみましょう。 ファイル名はMovingBallAnimation.java。 このプログラムはアニメーション表示を続けるので、プログラムの実行を中断するには Control + C (Controlキーを押しながらC) を押すこと。

プログラムの大半はグラフィックス表示に必要な決まり文句として詳しい説明は省きます。 ここでは、アニメーション表示に必要な要素だけを取り上げます。

import java.awt.*;
import javax.swing.*;

public class MovingBallAnimation {
    public static void main(String[] args) {
	JFrame frame = new JFrame();

	frame.setTitle("Ball Animation");
	frame.setSize(640, 480);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	MyPanel panel = new MyPanel();
	frame.getContentPane().add(panel);

	frame.setVisible(true);
    }
}

class MyPanel extends JPanel implements Runnable {
    // ボールオブジェクトの変数宣言
    Ball ball;

    // このコンストラクタにはアニメーションの開始前の準備を書く
    public MyPanel() { 
        // ボールオブジェクトの生成
        // (初期位置, 初期速度, 移動範囲の情報を与えて初期化)
	ball = new Ball(100,100,2,1,0,0,610,430);

	setBackground(Color.white);
	Thread refresh = new Thread(this);
	refresh.start();
    }

    // このメソッドにはアニメーションの1コマごとの描画処理を書く
    public void paintComponent(Graphics g) { 
        // アニメーション1コマ分の移動処理
	ball.forward();
        int x = ball.getX();
        int y = ball.getY();

	// 画面描画の準備 (おなじない)
	super.paintComponent(g);

        // 1つのボールの描画
	g.setColor(Color.red);           // ボールの描画色をred=赤に設定
	g.fillOval(x, y, 20, 20);      // x, yの位置に円を描く 20はボールの半径
    }

    // このメソッドは10ミリ秒ごとに画面を書き換えるおまじない
    public void run() { 
	while(true) {
	    repaint();
	    try {
		Thread.sleep(10);
	    }
	    catch(Exception e) {
	    }
	}
    }
}

class Ball {
    private int x;
    private int y;
    private int vx;
    private int vy;
    private int left;
    private int right;
    private int top;
    private int bottom;

    public Ball(int x, int y, int vx, int vy, int l, int t, int r, int b) {
	this.x = x;
	this.y = y;
	this.vx = vx;
	this.vy = vy;
	right = r;
	left = l;
	top = t;
	bottom = b;
    }

    public void forward() {
	if (x + vx < left || x + vx > right) 
	    vx = -vx;
	if (y + vy < top || y + vy > bottom) 
	    vy = -vy;
	x = x + vx;
	y = y + vy;
    }

    public int getX() {
	return x;
    }

    public void setX(int x) {
	this.x = x;
    }

    public int getY() {
	return y;
    }

    public void setY(int y) {
	this.y = y;
    }
}

プログラムの末尾にあるクラス Ball は、座標情報を備えたボールを表すクラスです。 これは、ボールに関する座標計算を行うだけでグラフィックス表示固有の処理はないので、 これまでの知識で理解できるはずです。

クラス MyPanel は、アニメーション表示をするウィンドウの役割をするクラスです。 このクラスにアニメーション表示の対象とするオブジェクトの宣言や、 実際に画面を書き換えるメソッドを書きます。 アニメーション表示に実質的に関係する部分はプログラム中に下線で示してあります。

クラス MyPanel の属性 ball は、アニメーションを行うボールを表すオブジェクトです。 このオブジェクトを使って実際のボールの座標情報を格納したり、 1コマあたりに進む計算を行うことにします。

クラス MyPanel のコンストラクタには、アニメーション表示を行う準備作業を書きます。 具体的には、ボールのオブジェクトを new を使って生成し、座標情報などを初期化します。 また、その他の準備 (おまじない) もあります。

クラス MyPanel のメソッド paintComponent には、 アニメーションの1コマごとの描画処理を書きます。 特別にこのメソッドは10ミリ秒ごとに自動的に繰り返して呼び出され、 アニメーション1コマ分の画面の書き換えを行います。 このプログラムでは、ballオブジェクトの座標計算を行い、 所定の座標に円を描くことでボールを表現しています。

少し乱暴ですが、あとは見様見真似で実験してみましょう。 その他の部分は決まり文句としておきます。 詳しいことを知りたい場合は、 Java SE 6 APIドキュメント を参照してください。

問題1 (Basic〜Standard)

例題のボールのアニメーションを拡張し、 2つのボールが移動するアニメーションを考える。 ボールとボールの間に線を描くようにすると ラインアートのようなアニメーションが可能である。

ソースは以下のとおり。 (LineArt.java)

import java.awt.*;
import javax.swing.*;

class LineArt {
    public static void main(String[] args) {
	JFrame frame = new JFrame();

	frame.setTitle("Line Art");
	frame.setSize(640, 480);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	MyPanel panel = new MyPanel();
	frame.getContentPane().add(panel);

	frame.setVisible(true);
    }
}

class MyPanel extends JPanel implements Runnable {
    private Ball ball1;
    private Ball ball2;

    public MyPanel() {
	setBackground(Color.white);

	ball1 = new Ball(100,100,10,5,0,0,630,450);
	ball2 = new Ball(200,100,5,10,0,0,630,450);

	Thread refresh = new Thread(this);
	refresh.start();
    }

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

	ball1.forward();
	ball2.forward();
	g.setColor(Color.red);
	g.drawLine(ball1.getX(), ball1.getY(), ball2.getX(), ball2.getY());
    }

    public void run() {
	while(true) {
	    repaint();
	    try {
		Thread.sleep(20);
	    }
	    catch(Exception e) {
	    }
	}
    }
}

class Ball {
    private int x;
    private int y;
    private int vx;
    private int vy;
    private int left;
    private int right;
    private int top;
    private int bottom;

    public Ball(int x, int y, int vx, int vy, int l, int t, int r, int b) {
	this.x = x;
	this.y = y;
	this.vx = vx;
	this.vy = vy;
	right = r;
	left = l;
	top = t;
	bottom = b;
    }

    public void forward() {
	x = x + vx;
	y = y + vy;

	if (x < left || x > right) {
	    vx = -vx;
	}
	if (y < top || y > bottom) {
	    vy = -vy;
	}
    }

    public int getX() {
	return x;
    }

    public void setX(int x) {
	this.x = x;
    }

    public int getY() {
	return y;
    }

    public void setY(int y) {
	this.y = y;
    }
}

このプログラムを参考に独自の工夫をし、 アニメーションを行うプログラムを作成しなさい。 例えば、次のようなアニメーションが考えられる。

サンプルの実行方法:

$ java -jar ファイル名.jar

mainメソッドを書くクラスは BallArt とし、 ファイル名は BallArt.java とする。

問題2 (Advanced)

参考資料 を学んだ上で、 マウスやキーボード入力でアニメーション表示に面白い作用を与えるような、 対話的アニメーション作品を作りなさい。

独自のアイディアに基づく作品とすること。 例えば、以下のようなサンプルが考えられる。

ソースコードのファイル名は InteractiveArt.java とする。 画像データなどがあればそれらを含めて、 構成するすべてのファイルを提出しなさい。 なお、ソースコードを複数のファイルに分割して開発する場合 (Java のプログラムはクラスごとに独立したファイルに分割できる)、 すべてのソースコードのファイルを提出すること。