Javaにおける2Dゲーム描画設計 (Imageクラスの描画)
今回はいよいよImageクラスの描画です。
画面にビットマップが描画されることにより、やっとゲームとしての表現が始まる感が出てくると思われます。
では、前回の流れから、コンポーネントであるJPanelクラスから派生させたMainScreenクラスにImageクラスの内容を描画する説明をしてみます。
コンポーネントの描画はイベント関数である、paintをオーバーライドして実現します。
この引数にはGraphicsクラスが渡され、このクラスに対して描画を行います。
とりあえずですが、以前設計で紹介したImageToolクラスにてビットマップファイルを読み込んだImageクラスがあることを前提とした描画のソースコードは以下のようになります。
いままでの説明ではImageクラスはキャラクター管理のタスククラスに内包されることになりますが、「とりあえず、どこかにImageクラスがある」という考えで読み進めてください。
尚、画面は256x240(ファミリーコンピュータの画素数と同じ)、画像は16x16の大きさのsample.pngというPNG形式のファイルとします。

[sample.png]
【単純なコーディング例】
このようなコーディングを行うと以下のような結果になります。

ここでpaint関数内のコードの説明をすると、まず最初にGraphicsクラスに対して画面を黒く塗り潰します。
これはsetColor関数にて黒色という設定を行い、fillRect関数にて設定した黒色で横256,縦240ドットの塗り潰すという流れです。
fillRect関数の引数はこの場合、スクリーンの座標の左,上,幅,高さとなります。
詳細はJavaのGraphicsクラスの仕様を参照していただくとして、この場合の座標系は(0,0)の位置が画面の左上となることを理解してください。
この塗り潰しを行わないと、この次に画像を描画する時に前に描画した内容が残ってしまい、残像のようになってしまうため、この処理を行います。
次にdrawImage関数ですが、この関数にて画像を描画します。
この場合は10個の引数を取りますが、これらは3種類の情報に区分けできます。
この例では、第1引数が描画をするImagaクラス、第2~5引数がGraphicsクラスの画面に表示する座標、第6~9引数がImageクラス内の描画すべき座標、第10引数が描画に対する通知オブジェクトとなります。このうち第10引数は今回は使用しないのでnullとします。

◇ ◇ ◇
Imageクラスの描画処理の実装が行えたら、次は描画の更新処理を行います。
この処理を行わないと、肝心なゲーム画面の表示が「運で書き変わる」事になります。
これはJavaやOSの仕様に依存している問題で、「画面の再描画」というメカニズムに起因しており、「画面を再描画するべき」とOSとJavaの仮想マシンが判断した際に、コンポーネントのpaint関数イベントが発生します。そこでゲームプログラムとしてはこれを任意に発生させる必要があります。
この方法はコンポーネント(ここではMainScreenクラス)に対して以下の関数を呼び出せば実現できます。
【単純なコーディング例】
m_component.paintImmediately(0, 0, 256 ,240);
※ここではコンポーネントをMainScreen、スクリーンサイズを256x240としています)
この呼び出し方にて画面の再描画イベントが発生し、paint関数イベントが発行されます。
では、ゲームにおける更新タイミングというのはいつになるでしょうか?
それは以前説明したゲームのメインループのタイミングとなります。
1/5(メインループの説明)に説明した内容を例にすると以下のようになります。
【単純なコーディング例】
この実装により約1/60に再描画依頼がVMに依頼され、コンポーネント(ここではMainScreenクラス)のpaint関数が呼び出されます。
尚、高度に再描画処理を実装したい場合にはスレッド化を用いるなどするとフレームスキップ機能等を設計することも可能です。
◇ ◇ ◇
ここまでで画像の描画の説明をしましたが、次回はいままでの説明を組合せて、Javaにおけるフレームワークをサンプルとして挙げたいと思います。
これはプログラム起動から、ゲームのメインループ、タスク管理、キャラクタータスクへの派生、キャラターの描画等を具体例としたものとなります。
画面にビットマップが描画されることにより、やっとゲームとしての表現が始まる感が出てくると思われます。
では、前回の流れから、コンポーネントであるJPanelクラスから派生させたMainScreenクラスにImageクラスの内容を描画する説明をしてみます。
コンポーネントの描画はイベント関数である、paintをオーバーライドして実現します。
この引数にはGraphicsクラスが渡され、このクラスに対して描画を行います。
とりあえずですが、以前設計で紹介したImageToolクラスにてビットマップファイルを読み込んだImageクラスがあることを前提とした描画のソースコードは以下のようになります。
いままでの説明ではImageクラスはキャラクター管理のタスククラスに内包されることになりますが、「とりあえず、どこかにImageクラスがある」という考えで読み進めてください。
尚、画面は256x240(ファミリーコンピュータの画素数と同じ)、画像は16x16の大きさのsample.pngというPNG形式のファイルとします。

[sample.png]
【単純なコーディング例】
// とりあえずグローバルなどこかでImageクラスが読み込まれているとします public class Global { // 描画するイメージ public static Image m_image; // コンストラクタ public Global() { m_image = ImageTool.Load("/images/sample.png"); } } /** * メインスクリーン */ public class MainScreen extends JPanel { : /** * 描画を行います * @param g コンポーネントグラフィックス */ public void paint(Graphics g) { // 背景を塗り潰します g.setColor(Color.BLACK); g.fillRect(0, 0, 256, 240); // イメージの描画 g.drawImage( Global.m_image, 0, 0, 16, 16, // 表示する矩形座標 0, 0, 16, 16, // 表示する画像の座標と大きさ null); } } |
このようなコーディングを行うと以下のような結果になります。

ここでpaint関数内のコードの説明をすると、まず最初にGraphicsクラスに対して画面を黒く塗り潰します。
これはsetColor関数にて黒色という設定を行い、fillRect関数にて設定した黒色で横256,縦240ドットの塗り潰すという流れです。
fillRect関数の引数はこの場合、スクリーンの座標の左,上,幅,高さとなります。
詳細はJavaのGraphicsクラスの仕様を参照していただくとして、この場合の座標系は(0,0)の位置が画面の左上となることを理解してください。
この塗り潰しを行わないと、この次に画像を描画する時に前に描画した内容が残ってしまい、残像のようになってしまうため、この処理を行います。
次にdrawImage関数ですが、この関数にて画像を描画します。
この場合は10個の引数を取りますが、これらは3種類の情報に区分けできます。
この例では、第1引数が描画をするImagaクラス、第2~5引数がGraphicsクラスの画面に表示する座標、第6~9引数がImageクラス内の描画すべき座標、第10引数が描画に対する通知オブジェクトとなります。このうち第10引数は今回は使用しないのでnullとします。

◇ ◇ ◇
Imageクラスの描画処理の実装が行えたら、次は描画の更新処理を行います。
この処理を行わないと、肝心なゲーム画面の表示が「運で書き変わる」事になります。
これはJavaやOSの仕様に依存している問題で、「画面の再描画」というメカニズムに起因しており、「画面を再描画するべき」とOSとJavaの仮想マシンが判断した際に、コンポーネントのpaint関数イベントが発生します。そこでゲームプログラムとしてはこれを任意に発生させる必要があります。
この方法はコンポーネント(ここではMainScreenクラス)に対して以下の関数を呼び出せば実現できます。
【単純なコーディング例】
m_component.paintImmediately(0, 0, 256 ,240);
※ここではコンポーネントをMainScreen、スクリーンサイズを256x240としています)
この呼び出し方にて画面の再描画イベントが発生し、paint関数イベントが発行されます。
では、ゲームにおける更新タイミングというのはいつになるでしょうか?
それは以前説明したゲームのメインループのタイミングとなります。
1/5(メインループの説明)に説明した内容を例にすると以下のようになります。
【単純なコーディング例】
long m_nowTime; long m_framerate = 17; while(true) { try { // 処理開始時間を取得します m_nowTime = System.currentTimeMillis(); // 入力処理、タスク、描画、サウンド等の処理 // 演算が完了したので再描画を行います m_component.paintImmediately(0, 0, 256, 240); // 処理した時間を計算して、ウエイトが必要か判断します long gap = System.currentTimeMillis() - m_nowTime; if((m_framerate - gap) < 0) { // System.out.println("処理落ち発生!!"); } else { // 処理が早く終わったので、少し待ちます Thread.sleep(m_framerate - gap); } } catch(Exception e) {} } |
この実装により約1/60に再描画依頼がVMに依頼され、コンポーネント(ここではMainScreenクラス)のpaint関数が呼び出されます。
尚、高度に再描画処理を実装したい場合にはスレッド化を用いるなどするとフレームスキップ機能等を設計することも可能です。
◇ ◇ ◇
ここまでで画像の描画の説明をしましたが、次回はいままでの説明を組合せて、Javaにおけるフレームワークをサンプルとして挙げたいと思います。
これはプログラム起動から、ゲームのメインループ、タスク管理、キャラクタータスクへの派生、キャラターの描画等を具体例としたものとなります。
コメント
コメントの投稿
« 昔のゲームの想い出 [0017] 「ザ・スキーム」 [ボーステック] [1988] [PC-8801mkIISR] l Home l 昔のゲームの想い出 [0016] 「ボコスカウォーズ」 [アスキー] [1983] [X1] »