• 検索結果がありません。

アニメーションプログラムの完成

ドキュメント内 progj-text.dvi (ページ 44-47)

第 4 章 Java によるアニメーション表現 41

4.3 アニメーションプログラムの完成

実習

sample06.java

sample07.java

の大きな違いは,

implements

の後

Runnable

という名前(正確にはインタフェースと呼びます)が追加

されたことと,

Thread

クラスの変数の扱い,特に

sleep()

メソッドの 周辺でした。しかし,せっかくのアニメーションもギクシャクとした動 きで何か不満に思えませんか?そこで,プログラム例を元に,より滑ら かな動きを表現するための定石について学んでいきます。

4.3.1

ダブルバッファ

滑らかな動きを妨げるひとつの要因は,

paint()

メソッドの呼ばれ方にあります。何も 手を打っていないと,このメソッドは,

repaint()

から間接的に呼ばれる

update()

内か ら,画面を背景色でクリアした後に呼ばれるようになっています。通常は,この動作で不 都合はないし,むしろ,いちいち画面を消すことを考えなくてすむので好ましい設計とも いえます。ところが,アニメーションのようにできるだけすばやく絵を書き換えようとす ると,この消去の動きも目に止まるようになるので,結果としてチラチラして見にくくな るわけです。

sample05.java

で気付かれた人もいるかと思いますが,これを避けるためには,標準

で用意されている

update()

メソッドの代わりに自前のメソッドを準備して消去動作を迂 回して直接

paint()

を呼ぶようにします。このように,標準の機能を書き換えてしまうこ とをオーバーライトと呼んでいます。

ただ,これだけだと前の画面が残ってしまうことになって,自前で消す方法を考えなく てはいけません。ひとつの手は,前の画面と新しい画面で異なる部分だけを描き直すとい うものです。これは,前の画面の状態をどこかで保持しておく必要もあり,なかなか面倒 です。そこで,前の画面の状態にかかわらず,できるだけ高速に画面を切替える手法とし てダブルバッファと呼ばれる手法がよく利用されます。

簡単に言えば,表示用の画面と(計算を伴うために時間のかかる描画のための)作業用 の画面を2つ用意し,作業用の画面での描画が終った段階で,表示用の画面に(個別に図 形を描画するよりも一般に高速な)メモリ転送を使って描くというものです。

4.3.2 sample07.java

の改変

それでは,具体的にどのような改変をするか,以下に実際の例をあげてみました。元 のプログラムと比較するために,

sample07.java

を開いたら「名前を付けて保存」で

sample07a.java

と変えてから異なる部分に注意して修正してみてください。

ダブルバッファを使用したアニメーション(

sample07a.java

) 1 // 円軌道を運動するボールの軌跡

4.3.

アニメーションプログラムの完成

45

2 import java.applet.* ;

3 import java.awt.* ; 4

5 public class sample07a extends Applet implements Runnable {

6 int t = 0 ; // 角度 (deg.)

7 double hankei = 150 ; // 軌道の半径 8 Thread th = null ;

9 Image backImg = null ; 10 Graphics backG = null ; 11

12 public void start() {

13 backImg = createImage(400,400) ; 14 backG = backImg.getGraphics() ; 15 paintBack() ;

16 repaint() ;

17 th = new Thread(this) ; 18 th.start() ;

19 } 20

21 public void run() { 22 while( th != null ) { 23 paintBack() ; 24 repaint() ; 25 t = (t+5)%360 ;

26 try {

27 th.sleep(10) ; // 10ms = 0.01秒停止

28 }

29 catch (InterruptedException e) {

30 }

31 }

32 } 33

34 public void paintBack() { 35 double a ;

36 int x, y ;

37 if( backG != null ) {

38 backG.setColor(Color.white) ; 39 backG.fillRect(10,10,380,380) ; 40 a = (double)t*Math.PI/180.0 ; 41 x = (int)( hankei * Math.cos(a) ) ; 42 y = (int)( hankei * Math.sin(a) ) ; 43 backG.setColor(Color.blue) ;

44 backG.fillOval( x+200-10, -y+200-10, 21, 21 ) ;

45 }

46 } 47

48 public void update(Graphics g) { 49 paint(g) ;

50 }

51 public void paint(Graphics g) {

52 if( backImg != null ) {

53 g.drawImage(backImg,0,0,this) ;

54 }

55 } 56 }

GUI部品による制御

余力のある方は,

GUI

部品を追加して,動きをとめたり再開するためのオプションを用 意してみましょう。

boolean

型の変数(たとえば

isRunning

)を共通変数として準備し,ボタンのイベント に対応して,リスナ内で

isRunning

true

または

false

を代入するようにしておきます。

run()

にも手を加えて,角度を更新している部分を

isRunning

true

の時だけ実行す るように改造します。

また,ボタンのラベルは,実行/停止の状態に応じて,

bt.setLabel("実行") ; bt.repaint() ;

などとすることで切替えが可能ですので,できるならボタンはひとつですますのがスマー トです。

これもできた方は,回転方向を変えるためのボタンを追加するなどいろいろ試してみま しょう。

ここでちょっと休憩

47

ドキュメント内 progj-text.dvi (ページ 44-47)