Scalaでゲームプログラミング - vol1 Windowを表示してみる -
Javaで作るようにJFrameを用いて表示します。ゲームプログラミング向けに高速で処理できるようアクティブレンダリングをします。
このあたりはレイヤが低いので、ScalaっていうよりJavaっぽさがあるかもしれません。(ライブラリ的に)
importについて
Scalaの場合、全てを選択する場合に「_」を利用します。
import javax.swing._ import java.awt._ import java.awt.image._
swingというのはawtの拡張です。
awtは " abstruct window toolkit " の略のようでwindow周りの管理に使います。
Environment
まず、環境に合わせた設定ファイルを作りましょう。(後の変更に強くなります)
objectというsingletonキーワードを用いてどこからでもアクセスできるようにしておきます。
ここは定義だけなのでさらっと見といてくださいね。
object Environment { //----------------------------------------------------- // GENERAL //----------------------------------------------------- object General { val VERSION = "0.0.1" } //----------------------------------------------------- // SCREEN //----------------------------------------------------- object Screen { val TITLE = "drons" // GAME TITLE val WIDTH = 640 // WINDOW WIDTH val HEIGHT = 480 // WINDOW HEIGHT val DEPTH = 32 // BIT COLOR DEPTH } }
valは固定値(immutable)を表していてプログラムの実行中に変更されることはありません。
今回はwindowを表示させるだけなので、幅と高さと色情報を載せておきます。
DEPTH=32というのは、32bitカラーで32bit=2^32通りの色を表現することができます。
大きさについては640x480であることが多いです。(800x600もちょくちょく)
MainFrame extends JFrame
Scalaの場合はコンストラクタを定義しなくても何も無いところがコンストラクタにあたるようです。
class A { println("hello") } // => hello
JFrameというウィンドウ管理クラスを継承しましょう。彼が今後管理してくれます。
ちなみに、privateなものは後ろに持ってくると使う立場の人は楽ですね。(共同開発の時なんかに)
frame.scala
class MainFrame extends JFrame { //----------------------------------------------------- // objects //----------------------------------------------------- val WIDTH = Environment.Screen.WIDTH val HEIGHT = Environment.Screen.HEIGHT val DEPTH = Environment.Screen.DEPTH def update() = { val buffer = getBufferStrategy() if( !buffer.contentsLost ) buffer.show //else Debugger.push("\nerror!\n Contents Lost\n") Toolkit.getDefaultToolkit().sync() } //----------------------------------------------------- // initialize //----------------------------------------------------- private val mBufferStrategy = getBufferStrategy() { setTitle(Environment.Screen.TITLE) setBounds(0,0,WIDTH,HEIGHT) setIgnoreRepaint(true) setResizable(false) setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) setVisible(true) val BUFFER_NUM = 2 createBufferStrategy(BUFFER_NUM) } }
とりあえずコンストラクタから見ていきましょうか。
ウィンドウの基本情報
setTitle(Environment.Screen.TITLE) setBounds(0,0,WIDTH,HEIGHT) setIgnoreRepaint(true) setResizable(false)
このあたりは関数名から推測できそうですね。
setTitleはウィンドウのタイトルバーの文字列を設定できます。
この関数は重要です。開発の初期のタイミングではデバッグに使います。
setBoundsはウィンドウの大きさを決めているわけですが(0,0)から(WIDTH,HEIGHT)までの大きさということです。
setResizableはウィンドウの大きさを変更の可否です。
setIgnoreRepaintについてですが再描画を無視するという意味になります。
描画処理は自分で制御したいのでこちらのタイミングのみにするために自動で描画するのは無視させておきます。
終了情報の設定
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
setVisible(true)
ウィンドウを閉じるときの処理を決定します。
右上のバッテンを押すと終了するように設定します。
setVisibleをtrueにすることでようやくウィンドウが表示されます。
ダブルバッファリング
val BUFFER_NUM = 2 createBufferStrategy(BUFFER_NUM)
setVisibleしてからというのも変な気がしますが、順番を変えると動きませんね。
createBufferStrategy()にてバッファを生成します。
ここでは画面2枚分のバッファを生成しているわけですね。
この2枚の画面を交互に切り替えて表示します。それによって激しい描画でもチラツキが防止されるわけです。
さて、そろそろupdate関数を見てみましょう。(def updateですね)
画面の更新(毎フレーム呼ばれる)
def update() = { val buffer = getBufferStrategy() if( !buffer.contentsLost ) buffer.show //else Debugger.push("\nerror!\n Contents Lost\n") Toolkit.getDefaultToolkit().sync() }
まず、getBufferStrategyにて画面のバッファ(メモリ情報)を取得します。
contentsLostの関数ではバッファが消失しているかどうかを判定できます。
正常であれば、buffer.show()にて画面を切り替え、最後にToolkitのsync関数を用いて更新します。
以上が基本的な内容です。それでは、表示させてみましょう。
main.scala
object Drons { def main(args:Array[String]) : Unit = { val frame = new MainFrame; while(true) { frame.update() Thread.sleep(20) } } }
適切にエントリーポイントとなるオブジェクトとメソッドを定義します。
そこに先ほど作ったMainFrameクラスを生成して、無限ループです。
どうでしょう、表示されたでしょうか。
次はキー入力をやってからフルスクリーン化したいと思います。
フルスクリーンから戻れないと困りますからねー。(時間制でもいいのだけれども)