↓↓クリックして頂けると励みになります。
【28 | 画像と文字列描画】 << 【ホーム】 >> 【30 | イベント処理】
複数のイメージを生成して、順次表示させることでアニメーションとなります。
アニメーションを行う場合、「repaint()
」で画面描画を制御する必要があります。
Visual Studio Codeで以下のJavaファイル「AwtAnimationTest1.java」を作成してください。
新規作成 【AwtAnimationTest1.java】
import java.awt.*; public class AwtAnimationTest1 extends Canvas implements Runnable { private final int MAX_IMAGE = 7; private final int WIDTH = 500; private final int HIGHT = 400; private Image[] image = new Image[MAX_IMAGE]; private MediaTracker tracker; //ダブルバッファリング(オフスクリーン・バッファ用)変数 private Graphics offg; private Image offImage; private int x = 0; //画像の座標 private int y = 100; private int imageNum = 0; //利用する画像 private int imageCount = 1; //画像の枚数カウント制御 private int moveCount = 20; //移動距離カウント制御 /**コンストラクタ*/ public AwtAnimationTest1() { setSize(WIDTH, HIGHT); setBackground(Color.black); Toolkit tk = Toolkit.getDefaultToolkit(); tracker = new MediaTracker(this); //キャラクターイメージの作成 for(int i=0; i<MAX_IMAGE; i++) { image[i] = tk.getImage("char" + i + ".gif"); tracker.addImage(image[i], i); } //全イメージのダウンロードが終わるまで待つ try { tracker.waitForAll(); } catch (InterruptedException e) { System.err.println("tracker error"); } } /**メインの処理(スレッド処理)*/ public void run() { while (true) { //永久に繰り返し try { Thread.sleep(100); //100ミリ秒(0.1秒)スリープ } catch (InterruptedException ex) { System.err.println(ex); } repaint(); } } /**画像描写が必要な場合の処理*/ public void update (Graphics g) { //キャラクター、障害物の消去(全画面消去) //オフスクリーン・バッファへの描き込み。実際にはまだ見えない offg.setColor(Color.black); offg.fillRect(0, 0, WIDTH, HIGHT); //口を開けるアニメか閉じるアニメかを設定 if (imageNum >= (MAX_IMAGE-1)) { imageCount = -1; } else if (imageNum <= 0) { imageCount = 1; } imageNum+=imageCount; //イメージの移動 if (x+image[0].getWidth(this) >= WIDTH) { moveCount = -10; } else if (x <= 0 ) { moveCount = 10; } x+=moveCount; //オフスクリーン・バッファへの描き込み。実際にはまだ見えない。 offg.drawImage(image[imageNum], x, y, this); //フォントの設定 Font font = new Font("Serif", Font.ITALIC, 48); offg.setFont(font); offg.setColor(Color.yellow); offg.drawString("サンプル画像表示", 10, 200); //オフスクリーン・バッファに描かれたものを画面に表示 g.drawImage(offImage, 0, 0, this); } /**キャンバスで描写の必要のあるときに呼ばれるメソッド*/ public void paint(Graphics g) { //オフスクリーン・バッファの領域がない場合は作成 if (offg == null) { //キャンバスと同じ大きさの仮想画面を生成 offImage = createImage(getSize().width, getSize().height); //offimageから仮想画面描写用のグラフィックスコンテキストを取得 offg = offImage.getGraphics(); } g.drawImage(image[imageNum], x, y, this); //フォントの設定 Font font = new Font("Serif", Font.ITALIC, 48); g.setFont(font); g.setColor(Color.yellow); g.drawString("サンプル画像表示", 10, 200); } /**main()*/ public static void main( String[] args) { AwtAnimationTest1 canvas = new AwtAnimationTest1(); Frame frame = new Frame("Animation1"); frame.add(canvas); frame.pack(); frame.setVisible(true); //スレッドを生成して、ゲーム開始 new Thread(canvas).start(); } }
このプログラムを実行するには画像ファイルが必要です。
char0.gif〜char6.gifをプログラムを保存するのと同じフォルダ(ディレクトリ)に保存してください。
[char0.gif]
[char1.gif]
[char2.gif]
[char3.gif]
[char4.gif]
[char5.gif]
[char6.gif]
アニメーションさせる場合、「止まって見える時間」を作る必要があります。
今回は「スレッド」と呼ばれる機能を利用します。
スレッドはjava.lang.Threadクラス
で定義されており、ひとつのプログラムの中で並列した処理を行える機能のことを言います。
なおスレッドにはいろいろな機能がありますので全部一気に覚えようとはせず、出てきた機能を順番に一つずつ覚えていってください。
スレッドを行うには2通りの方法があります。
まずはRunnableインターフェイスを実装する方法を示します。
Runnableインターフェイスを実装するとrun()メソッド
を実装しますので、runメソッドの処理を記述します。
下の例では実行を一時停止するThreadクラスのsleep()メソッド
を呼び出しています。
スレッドは「Thread(test).start()
」のように、スレッドを実装したクラスを引数としてオブジェクトを生成し、start()メソッド
で、run()メソッド
の処理を実行します。
public class Test implements Runnable { private String str; private int count; /** コンストラクタ */ public Test(String str, int count) { this.atr = str; this.count = count; } /** メインの処理(スレッド処理) */ public void run() { while (count > 0) { //指定回数繰り返し try { Thread.sleep(100); //100ミリ秒(0.1秒)スリープ System.out.println(str); count--; } catch (InterruptedException ex) { System.err.println(ex); } } } /** main() */ public static void main(String[] args) { Test test = new Test("テスト", 10); new Tread(test).start(); //スレッドを生成してスタート } }
インターフェイスはコンストラクタを持たず、フィールドとメソッドを持っています。
インターフェイスに記述されているフィールドやメソッドを利用するにはインターフェイスを実装しなければなりません。
実装するにはimplementsの後に利用するインターフェイス名を記述します。
このようにインターフェイスを実装したクラスのオブジェクトを作成するには、インターフェイスのメソッドをすべて定義するという作業が必要になります。
上のプログラムのようにRunnableインターフェイスを実装したら、Runnableインターフェイスが持つrun()メソッド
は必ず記述しなければなりません。
記述しなければコンパイル時にエラーとなります。
ここで少しextends(継承)とimplements(実装)の違いについて説明しておきます。
クラスの継承とはスーパークラスの機能を引き継ぎそれに機能を追加して拡張することであり、インターフェイスの実装とはインターフェイスで定義されたメソッドの処理内容を記述することです。
さらにインターフェイスにだけ許されていることもあります。
それは1つのクラスが複数のインターフェイスを継承して実装する多重継承が許可されているということです。
クラスの多重継承が煩雑になるという理由から、C++
で許可されていたクラスの多重継承がJava
では禁止されています。
ただし定数とメソッドのプロトタイプの定義だけなら同時に複数継承しても煩雑にはならないだろうという理由から、Javaではインターフェイスの多重継承が許可されています。
多重継承ではなく多重実装と呼ぶべきかもしれません。
インスタンスを 1 つのスレッドで実行するすべてのクラスでは、Runnable インタフェースを実装する必要があります。
このクラスは引数のないメソッド run を定義しなければなりません。
このインタフェースはアクティブな間にコードを実行したいオブジェクトが使う共通のプロトコルを提供するために設計されています。
たとえばRunnable は Thread クラスによって実装されます。
アクティブであるということは、スレッドが開始されてまだ終了していない状態を意味します。
さらに Runnable はThread をサブクラス化せずにクラスをアクティブにする手段を提供します。
Runnable を実装するクラスはThread のインスタンスを生成し、ターゲットとしてクラス自身を渡すことにより Thread をサブクラス化をしなくても実行できます。
Thread クラスのメソッドのうちrun()
だけをオーバーライドして使用する場合は、Runnable インタフェースを使用します。
これはクラスの基本的な動作を修正または拡張するのでない限り、そのクラスをサブクラス化することは好ましくないため重要です。
次にThreadクラスの子クラスとしてスレッドを利用する場合の例を挙げておきます。
スレッドは「test.start()
」のように、生成したThreadクラスのstart()メソッド
で利用します。
先の例と同様に処理はrun()メソッド
で行います。
public class Test2 implements Runnable { private String str; private int count; /** コンストラクタ */ public Test2(String str, int count) { this.atr = str; this.count = count; } /** メインの処理(スレッド処理) */ public void run() { while (count > 0) { //指定回数繰り返し try { Thread.sleep(100); //100ミリ秒(0.1秒)スリープ System.out.println(str); count--; } catch (InterruptedException ex) { System.err.println(ex); } } } /** main() */ public static void main(String[] args) { Test2 test = new Test2("テスト", 10); test.start(); //スレッドを生成してスタート } }
キャンバスに多数の図形やイメージを描画すると書き替えの際の「ちらつき」が気になる場合が多いです。
それを避けるためには、「ダブルバッファリング」という手法を使います。
ダブルバッファリングは「オフスクリーン」と呼ばれる仮想的な画面に描画して、すべてを描画してから表示可能なキャンバスに描画を行うというものです。
ただしオフスクリーンでの描画が間に合わないと「コマ落ち」のような表示になる場合もあります。
Graphics offg; Image offImage; //オフスクリーン・バッファの領域がない場合は作成 if(offg == null) { //キャンバスと同じ大きさの仮想画面を生成 offImage = createImage(geSize().width, getSize().height); //offImageから仮想画面描画用のグラフィックスコンテキストを取得 offg = offImage.getGraphics(); } //全画面消去 //オフスクリーン・バッファへの書き込み。(実際にはまだ見えない) offg.setColor(Color.black); offg.fillRect(0, 0, WIDTH, HEIGHT); offg.drawString("サンプル画像表示", 10, 200); //オフスクリーン・バッファに書かれたものを画面に表示 //gは表示可能なGraphicsだとする g.drawImage(offImage, 0, 0, this);
drawImageメソッドを利用する際に、 this
というものを使っています。
これは、このクラスのコンストラクタの情報を与えているものです。
少し難しいのですが、今はこのようにして使うものだと思って使用してください。
drawImage(指定するイメージ, int x, int y, イメージの情報);
public class AwtAnimationTest1 extends Canvas implements Runnable {
extends Canvasで、Canvasクラスのフィールド、メソッドをすべて継承しています。
これによりコンストラクタ内ですべてを記述することなく、Canvasの機能を使うことが出来ます。
必要な部分のみ記述することで、その部分のみを書き換えることが出来ます。
さらにRunnableインターフェイスを実装してThreadを使用しています。
なお継承は一つのクラスに一つしか許されていませんが、実装は何個でもすることが出来ます。
今回は1つずつしかしていません。
プログラムを実行してみましょう。
大きさ(500,400)のフレームが表示され、中にキャンバスが表示されています。
キャンバスにはcahr0.gif〜char6.gif
の画像が指定された位置にアニメーション表示されています。
一定の時間でキャラクターが左右に行ったり来たりします。
その下に指定したフォントで「サンプル画像の表示」と表示されます。
フレームの終了ボタンは使えませんので、Ctrlキー+Cキーで強制終了してください。
~/Desktop/Programming/JP $ javac AwtAnimationTest1.java ~/Desktop/Programming/JP $ java AwtAnimationTest1
【28 | 画像と文字列描画】 << 【ホーム】 >> 【30 | イベント処理】
↓↓クリックして頂けると励みになります。