Androidリバーシ
ソースをダウンロード:Reversiソース
今日、社内の有志が集まり、Androidの発表会をしました。といっても、まだAndroidを始めたばかりなので、それぞれ思い思いのプログラムを作って発表するというものでした。私は描画部分に興味があったのでリバーシを作ることにしました。
つくりかた
普通に作ってもつまらないので、なるべく移植性を高くするような作りにしようと思いました。そこでボード状態の管理や、AI等のゲームのコアとなる部分と描画処理やキー操作などの環境依存の部分を分けて設計しました。
コアとなるクラスは、以下のクラスです(クリックで拡大)。ソースコードは簡単なので略。
リバーシは、静的なゲームなのでアクションゲームのように毎フレーム更新するような仕組みは不要です。ゲームに何らかの変化があったときだけ画面を更新すればよいのです。この部分は、Javaではおなじみのリスナを使用しました。ゲームスレッドで何らかの状態が変化した時、描画スレッドにイベントが通知されます。本来であれば、イベントクラスも作成して、もう少し詳しい情報(現在のプレイヤーや裏返った石の場所等)も渡してあげるべきなのですが、今回は、手を抜きました。
このゲームクラスを使用したAndroidのソースは下記のようになります。
package net.hakkaku.android.reversi;
import net.hakkaku.reversi.Board;
import net.hakkaku.reversi.BoardPoint;
import net.hakkaku.reversi.Game;
import net.hakkaku.reversi.SimpleAI;
import net.hakkaku.reversi.Stone;
import net.hakkaku.reversi.Game.OnGameStateChangeListener;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
public class Reversi extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Game game = new Game();
ReversiView reversiView = new ReversiView(this, game.getBoard());
setContentView(reversiView);
TouchPlayer touchPlayer = new TouchPlayer();
reversiView.setOnTouchListener(touchPlayer);
//プレイヤを設定
game.setPlayer(Stone.Black, touchPlayer);
//game.setPlayer(Stone.White, touchPlayer);
// game.setPlayer(Stone.Black, new SimpleAI());
game.setPlayer(Stone.White, new SimpleAI());
game.setOnGameStateChangeListener(reversiView);
//ゲームスレッド開始
new Thread(game).start();
}
class ReversiView extends View implements OnGameStateChangeListener {
private Board board;
public ReversiView(Context context, final Board board) {
super(context);
this.board = board;
setBackgroundColor(Color.GREEN);
this.invalidate();
}
private float getCellSize() {
int width = getWidth();
int height = getHeight();
return width < height ? width / 8 : height / 8;
}
/**
* 画面上の座標からボード上の座標に変換する。
* @return
*/
public BoardPoint getScreenToPoint(final float screenX, final float screenY) {
float cellSize = getCellSize();
int x = (int)(screenX / cellSize);
int y = (int)(screenY / cellSize);
return (0 <= x && x < 8 && 0 <= y && y < 8)?new BoardPoint(x,y):null;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint pBlack = new Paint(Paint.ANTI_ALIAS_FLAG);
pBlack.setColor(Color.rgb(0, 0, 0));
Paint pWhite = new Paint(Paint.ANTI_ALIAS_FLAG);
pWhite.setColor(Color.rgb(255, 255, 255));
float cellSize = getCellSize();
// draw lines
for (int i = 0; i < 9; i++) {
canvas.drawLine(i * cellSize, 0, i * cellSize, 8 * cellSize,
pBlack);
canvas.drawLine(0, i * cellSize, 8 * cellSize, i * cellSize,
pBlack);
}
// draw stones
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
final Stone stone = this.board.stoneAt(x, y);
if (stone == Stone.White) {
canvas.drawCircle((x + 0.5f) * cellSize, (y + 0.5f)
* cellSize, cellSize * 0.40f, pBlack);
canvas.drawCircle((x + 0.5f) * cellSize, (y + 0.5f)
* cellSize, cellSize * 0.36f, pWhite);
} else if (stone == Stone.Black) {
canvas.drawCircle((x + 0.5f) * cellSize, (y + 0.5f)
* cellSize, cellSize * 0.40f, pBlack);
}
}
}
}
private Handler handler = new Handler();
public void onGameStateChanged() {
handler.post(new Runnable() {
public void run() {
invalidate();
}
});
}
}
}
ちなみにSwing版
つくってみて
実は、Android版をほとんど作り終えてから、Swing版に移植したのですが移植にかかった時間は20分ほどでした。AndoridのCanvasクラスとSwingのGraphics(Graphics2D)クラスのAPIがかなり似ていたので、コピペ&微修正であっさりと動作しました。
Androidは、Swing(AWT)のコード資産の流用も比較的容易な、良いプラットフォームだと思いました。
このサイトについて
TrackBack URL :
