アニメーション

43
第5章
図形を動かしてみよう!!
¶
³
重要
• Timer クラス
– Timer(int delay, ActionListener listener)
– 一定間隔 (delay ms) でアクションイベントを発生
– ActionListener インタフェースは,アクションイベントを受信し,actionPerformed メソッ
ド呼び出す
– actionPerformed メソッドに一定間隔で実行する処理を書く
• 乱数
– java.lang.Math クラス
µ
´
– random メソッド 0 ∼ 1 の乱数を発生
5.1 ボールを動かしてみよう!!
例題 5.1.1 ボールが左から右に等速直線運動a するアニメーションプログラムを作成せよ.
a
2
等速直線運動とは,一定速度 v で,ある空間内で一定方向に物体が移動する運動である.いま,物体の時刻 t における位置を
x(t),速度を vx とすると,等速直線運動は,x(t) = x(0) + vx t で表される.
swing の Timer クラスは一定時間間隔で指定されたメソッドを呼び出すことでアニメーション処理を行う.
Timer(int delay, ActionListener listener)
第一引数がそのメソッドを呼び出す時間間隔で,ms(millisecond; 1/1000second) で指定する.第二引数が呼び出す
メソッドそのものになる.Timer から呼び出されるメソッドは,java.awt.event.ActionListener というのもので,アク
ションイベントを受け取るためのリスナインタフェースである.この ActionListener インタフェースは,アクショ
ンイベントが発生するとオブジェクトの actionPerformed メソッドが呼び出される.この actionPerformed メソッ
ド中で,ある時間間隔で実行すべき処理を書く.プログラム 5.1.1 では,actionPerformed メソッドが呼び出される
度にボールの x 座標値 x に vx を加算する処理*1 をし,repaint メソッドを呼び出している.つまり,paint メソッ
ドメソッドが呼び出され,一定間隔でボールが再描画される処理を行っている.15 行目から 22 行目では,Timer の
オブジェクトを作成し,22 行目の start メソッドメソッドでタイマを開始,つまりアニメーションを開始している.
*1
時刻 t と t + 1 におけるボールの位置は,それぞれ x(t) = v(0) + vx t, x(t + 1) = v(0) + vx (t + 1) で表される.つまり,時刻 t と t + 1 に
おけるボールの位置の差 x(t + 1) − x(t) は,(v(0) + vx (t + 1)) − (v(0) + vx t) = vx となる.このプログラムでは,Timer で設定した一
定間隔を単位時間としているので,1 単位時間経過後のボールの座標は,前の位置に vx を加えた位置となる.
第 5 章 図形を動かしてみよう!!
44
メソッド,クラス,インタフェースに関しては次章以降で詳しく述べる.ここでは,アニメーションを作成する場合,
actionPerformed メソッドで一定間隔で行うべき処理を書き,paint メソッドで描画処理を書くことで簡単なアニ
メーションが実行できることを理解して欲しい.
25 行目から 28 行目の paint メソッドでは,再描画の処理を書いている.JApplet は swing のアップレットなので,
ダブルバッファリングをサポートしているが,再描画時に背景色で塗りつぶさないので,明示的に clearRect メソッ
ドで塗りつぶしている.
プログラム 5.1.1 水平方向に等速直線運動するボール (BallMove1.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.*;
5
public class BallMove1 extends JApplet {
Dimension d;
int x, vx;
10
public void init(){
d = getSize(); x = 0; vx = 1;
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (x < d.width) x += vx;
15
else x = 0;
repaint();
}
}
}).start();
20
public void paint(Graphics g){
g.clearRect(0, 0, d.width, d.height);
g.drawArc(x, 10, 10, 10, 0, 360);
}
25
}
2
例題 5.1.2 プログラム 5.1.1 の速度を倍にしたアニメーションプログラムを作成せよ.
2
例題 5.1.3 ボールが上から下に等速直線運動するアニメーションプログラムを作成せよ.
2
例題 5.1.4 ボールが右から左に等速直線運動するアニメーションプログラムを作成せよ.
2
例題 5.1.5 ボールが平面上を等速直線運動するアニメーションプログラムを作成せよ.
2
2 次元平面上の運動は,2 つの直交する座標軸の成分に分けて考えることができる.いま図 5.1 のように時刻 t から
時刻 t + 1 のように (12, 5) の方向に等速直線運動しているとする.このときの速度は v = 13 である.この運動を x, y
軸方向にそれぞれ分解すると,x 軸方向には速度 vx = 12 の等速直線運動,y 軸方向には速度 vy = 5 の等速直線運動
をしているといえる.したがって,x 軸方向,y 軸方向にそれぞれ vx = 12, vy = 5 の等速直線運動するようにプログ
ラムを書くことで,(12, 5) の方向に速度 v = 13 の等速直線運動をするアニメーションを作成することができる.
5.2 ボールをランダムに動かしてみよう!!
45
プログラム 5.1.2 平面上を等速直線運動するボール (BallMove5.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.*;
5
public class BallMove5 extends JApplet {
Dimension d;
int x, y, vx, vy, w = 10;
10
public void init(){
d = getSize();
x = y = 0;
vx = 1; vy = 2;
15
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (x < d.width && y < d.height){
x += vx;
y += vy;
20
}
else{
x = y = 0;
}
25
repaint();
}
}
}).start();
30
public void paint(Graphics g){
g.clearRect(0, 0, d.width, d.height);
g.drawArc(x, y, w, w, 0, 360);
}
}
35
2
5.2 ボールをランダムに動かしてみよう!!
例題 5.2.1 ボールが描画領域のランダムな位置から等速直線運動するアニメーションプログラムを作成せ
よ.
2
乱数の発生には,Math.random メソッドを用いる.Math.rondom メソッドは,0 ≤ x < 1 の double の乱数を発生
させる.描画範囲は幅が d.width,高さが d.height で,ボールの大きさは直径 w であるので,(0, 0) から (d.width
- w, d.height - w) の範囲でボールの位置をランダムに決定すれば良い.したがって,x 座標を (d.width - w) *
Math.random(),y 座標を (d.height - w) * Math.random() で求める.ここで,算術演算においては,2 つの被
演算子のうちの精度の高い型に暗黙に変換され計算される.そのため,(d.width - w) * Math.random() という演
算は,(d.width - w) で整数 − 整数の計算をして整数が得られ,その後,整数と Math.random() で生成した double
の掛け算が行われ,double の値が得られる.一方,drawArc メソッドには,描画する円弧の外接矩形の左上の座標を整
第 5 章 図形を動かしてみよう!!
46
数で与える必要があるため,double の値を明示的に整数に型変換する必要がある.その明示的な型変換を行うために
キャスト演算子が用意されている.キャストは,
「(型) 文」で行うことができる.13 行目の x = (int)((d.width -
w) * Math.random()); は,(d.width - w) * Math.random() で得られた double の値を int の値に変換している.
プログラム 5.2.1 ランダムな位置から等速直線運動するボール (BallMove6.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.*;
5
public class BallMove6 extends JApplet {
Dimension d;
int x, y, vx, vy, w = 10;
10
public void init(){
d = getSize();
x = (int)((d.width − w) * Math.random());
y = (int)((d.height − w) * Math.random());
vx = 1; vy = 2;
15
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (x < d.width && y < d.height){
x += vx;
y += vy;
20
}
else{
}
x = (int)((d.width − w) * Math.random());
y = (int)((d.height − w) * Math.random());
25
repaint();
}
}
}).start();
30
public void paint(Graphics g){
g.clearRect(0, 0, d.width, d.height);
g.drawArc(x, y, w, w, 0, 360);
}
35
}
2
例題 5.2.2 ボールが描画領域のランダムな位置からランダムなスピードで等速直線運動するアニメーショ
ンプログラムを作成せよ.ただし,x, y 方向の速度は正に制限する.
2
例題 5.2.3 ボールが描画領域のランダムな位置からランダムなスピードで等速直線運動するアニメーショ
ンプログラムを作成せよ.
2
5.3 枠の中でボールを自由に運動させてみよう!!
47
5.3 枠の中でボールを自由に運動させてみよう!!
例題 5.3.1 ボールが描画範囲内を壁に反射しながら左右に等速直線運動するアニメーションプログラムを
2
作成せよ.
プログラム 5.3.1 水平方向に反射しながら等速直線運動するボール (BallMove9.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.*;
5
public class BallMove9 extends JApplet {
Dimension d;
int x, w = 10, vx;
10
public void init(){
d = getSize();
x = 10;
vx = 1;
15
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (x <= 0 | | x + w >= d.width − 1) vx *= −1;
x += vx;
repaint();
}
20
}
}).start();
public void paint(Graphics g){
g.clearRect(0, 0, d.width, d.height);
}
}
25
g.drawArc(x, d.height − w − 1, w, w, 0, 360);
2
ボールの動きは,左から右に等速直線運動を行い,右の壁に衝突して運動方向を逆転させ,右から左に等速直線運動
を行う.どうように,右から左に等速直線運動を行っているボールは,左の壁に衝突すると運動方向を逆転させ左から
右に等速直線運動を行うようになる.この壁に衝突して運動の方向を反転させる反射運動を実現するために,18 行目
で,ボールの外接矩形の左上の x 座標が,0 以下か,d.width − 1 − w 以上になった場合に速度を反転,つまり,速度
の符号を反転させる処理を行っている*2 .
例題 5.3.2 ボールが描画領域内で壁に反射しながら 2 次元平面内で等速直線運動するプログラムを作成せ
*2
よ.
2
例題 5.3.3 ボールが描画領域内で飛び跳ねるプログラムを作成せよ.
2
ここでは,処理を簡略化するために正確な衝突判定は行っていない.
第 5 章 図形を動かしてみよう!!
48
プログラム 5.3.2 飛び跳ねるボール (BallMove11.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.*;
import java.lang.Math;
5
public class BallMove11 extends JApplet {
Dimension d;
int r = 10;
10
double x, y, vx, vy, g, ratio;
public void init(){
d = getSize();
x = (d.width − r) * Math.random();
y = d.height / 2;
15
vx = 10 * (Math.random() − .5);
vy = 1 * Math.random() + 1;
g = .25;
ratio = .95;
20
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (x <= 0 | | x + r > d.width) vx *= −1;
x += vx;
25
if (y + r > d.height) vy *= −1;
y += vy;
if (y < d.width / 2 && vy < 0) vy *= ratio;
vy += g;
repaint();
30
}
}
}).start();
public void paint(Graphics g){
35
g.clearRect(0, 0, d.width, d.height);
g.drawArc((int)x, (int)y, r, r, 0, 360);
}
}
2
5.4 練習問題
問題 5.1 描画範囲 (300, 400) とし,y = 50 の位置に右方向に 1 の速度で動く大きさ (30, 10) の矩形を描画するプロ
グラムを作成せよ.ただし,右側に矩形が消えたら,左から新たな矩形が表示されるようにせよ.
問題 5.2 描画範囲 (300, 400) とし,y = 50 の位置に水平方向に 1 の速度で,壁に反射しながら動く大きさ (30, 10)
の矩形を描画するプログラムを作成せよ.
問題 5.3 描画範囲 (300, 400) とし,y = 50 と y = 80 の位置にそれぞれ右方向に 1 の速度で動く大きさ (30, 10) の矩
形,左方向に 2 の速度で動く (10, 10) の矩形を描画するプログラムを作成せよ.ただし,矩形が消えたら,他方から新
5.4 練習問題
49
たな矩形が表示されるようにせよ.
問題 5.4 3 個のボールが描画領域内で壁に反射しながら 2 次元平面内で等速直線運動するプログラムを作成せよ.た
だし,個々のボールの初期位置,速度は乱数で発生させ,ボールどうしの衝突は考慮しなくても良い.
図 5.1
2 次元平面上の運動