2013年02月09日 16:01

Haxe + NME 5回目 テキストフィールド、お前もか!!

 ボードゲームのヘクスマップには、記録用にヘクスの番号がついている物があった。あれを表示してみるのもいいかもしれない。というか、どのみちマップのステータスやら、移動距離やら、敵味方の脅威度やらといった情報を、デバッグのために表示させる必要がある。なら、早いうちに簡単に表示させるルーチンを作っておくべきかもしれない。

 というわけで、早速追加。適当なテキストフィールドを作って、描画と同じような感じでループを回して、ループの内側で必要な文字列を書き込んでBitmapにdrawしてやれば良い。座標は、縦方向は六角形の上端に書くとしたら、1個前の縦座標に合わせれば良い。横方向はというと、中心点に合わせて適当に中央合わせしてやれば簡単な事だ。そう…簡単な事のはずである。

 が…
…また…はまった…。
 Bitmap.drawが上手く行かないのは4回目で書いたとおりであるから、同じ方法で逃げるとして…「TextFieldAutoSize.CENTER」がHTML5で機能してくれない。早い話、文字のセンタリングをしてくれないのである。

 あぁ…TextField、お前もか…。もし仮にNMEに「Brute」というクラスがあったとして、そのクラスの挙動が出力する環境によって異なるバグを持っていたとしたら、かのガイウス・ユリウス・カエサルでなくとも、誰もがこう言うだろう。「Et tu, Brute?」 すなわち「ブルータス、お前もか」、と。

 …はい、はい。わかりました。便利機能に頼っちゃいけないってことですね。…こうなると、もうそろそろ諦観の域である。とりあえず問題無さそうな左詰め、つまり「TextField.autoSize = TextFieldAutoSize.LEFT」にして、文字列をセットしたうえでTextFieldの幅を見てx座標を調整しましょうか。


// 文字を描画する
private function drawString(target:BitmapData):Void
{
// 文字列のフォーマットなどを指定…あんまり細かい事はしない方が安全っぽいので、フォントサイズだけ決める
var textFormat:TextFormat = new TextFormat();
textFormat.size = ySize / 2;
// 描画元になるテキストフィールドを設定。NMEを信じてはいけない。なるべくシンプルな設定で。
var textField:TextField = new TextField();
textField.defaultTextFormat = textFormat;
textField.autoSize = TextFieldAutoSize.LEFT; // うかつにCENTERとか使おうとするとNMEはコケる模様

var m:Matrix = new Matrix();
// Matrixを指定しないと、Flashで位置が動かない!!
// Shape.xとShape.yを指定しないと、HTML5で位置が動かない!!
for (y in 0 ... mapMax) {
m.ty = y * ySize - ySize;
textField.y = m.ty;
for (x in 0 ... mapMax) {
if ((x + y) % 2 == oddEven) {
// 文字列を代入。
textField.text = drawStringFunc(x, y);
// widthを元に横位置を調整。
m.tx = x * xSize - textField.width / 2;
textField.x = m.tx;
target.draw(textField, m);
}
}
}
}


 これだけ色々出てくると、クロスプラットフォーム云々はまだまだ夢なのかなぁ、と思ってしまうのだが…否!! この程度の工夫で何とかなるなら、十分クロスプラットフォームと言っていいのではなかろうか。ゲームを作ったとして、3Dがグリグリ動くとかでもない限り、中核部分はプラットフォームに左右されない物だ。つまり表示系だけちょこちょこっと気をつけつつ、なるべく基本的な描画方法で実装さえしてしまえば、あとは何とかなるはずだ…と思いたい。思うことにする。いや、別にクロスプラットフォームにこだわってる訳じゃないので、どっかで捨てるかもしれないけどさ。

 何はともあれ。とりあえず現時点でこんな感じのコードに。ヘクスの描画部分だけ微妙に一般化してみたが、これならクラス全体ももうちょっと一般化した方が良さそうかな、とも思う次第である。

package ;
import nme.display.Bitmap;
import nme.display.BitmapData;
import nme.display.Shape;
import nme.display.Sprite;
import nme.events.MouseEvent;
import nme.geom.Matrix;
import nme.text.TextField;
import nme.text.TextFieldAutoSize;
import nme.text.TextFormat;

/**
* ...
* @author kani-miso
*/

class HexBase extends Sprite
{
static public var ODD_TYPE = 1;
static public var EVEN_TYPE = 0;

// 定数
var mapMax = 21; // マップの縦横最大値
var hexSize = 40; // ヘックスの直径
// 偶数系か奇数系か
var oddEven = 0;
// ヘックスの縦横サイズ(全体で何度も使うのでここで定義)
var xSize:Int;
var ySize:Int;
// マウス位置のヘックスを示すためのカーソル
var cursor:Shape;
// 描画用のbitmapセット
var bitmapData:BitmapData;
var bitmap:Bitmap;

// クラス初期化
public function new()
{
super();
// Hexの縦幅、横幅の計算
xSize = Std.int(1 + hexSize * 3 / 4 / 8) * 8; // 8の倍数にする
ySize = Std.int(xSize / 3 * Math.sqrt(3));

// 表示位置を少しずらす
x = 10;
y = 10;

// bitmapを背景色つきで作成
bitmapData = new BitmapData(xSize * (mapMax - 1), ySize * (mapMax - 1), 0xFFEEFFEE);
bitmap = new Bitmap(bitmapData);
addChild(bitmap);

// bitmapに描画
draw(bitmapData);
drawString(bitmapData);

// カーソルを準備してマウスが動いたら動かす
initCursor();
addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}

// 描画ルーチン
private function draw(target:BitmapData
, centerCircleColor:Int = 0xFF0000
, hexLineColor:Int = 0xFF0000
, rangeLine:Int = -1
):Void
{

// 六角形1個分を描画するためのShape
var shape:Shape = new Shape();

// 中心円を描く
if (centerCircleColor >= 0) {
shape.graphics.lineStyle(1, centerCircleColor);
shape.graphics.drawCircle(0, 0, 4);
}

// Hexの枠線を描く
if (hexLineColor >= 0) {
var octantXSize:Int = xSize >> 3;
shape.graphics.lineStyle(1, hexLineColor);
shape.graphics.moveTo(-5 * octantXSize, 0);
shape.graphics.lineTo(-3 * octantXSize, -ySize);
shape.graphics.lineTo( 3 * octantXSize, -ySize);
shape.graphics.lineTo( 5 * octantXSize, 0);
}

// 四角線を描く
if (rangeLine >= 0) {
var halfYSize:Int = ySize >> 1;
var halfXSize:Int = xSize >> 1;
shape.graphics.lineStyle(1, 0x0000FF, 0.6);
shape.graphics.moveTo(-halfXSize, halfYSize);
shape.graphics.lineTo(-halfXSize, -halfYSize);
shape.graphics.lineTo( halfXSize, -halfYSize);
shape.graphics.lineTo( halfXSize, halfYSize);
shape.graphics.lineTo(-halfXSize, halfYSize);
}

// 出来上がったShapeを、位置をずらして描画
// Matrixを指定しないと、Flashで位置が動かない!!
// Shape.xとShape.yを指定しないと、HTML5で位置が動かない!!
var m:Matrix = new Matrix();
for (y in 0 ... mapMax) {
shape.y = y * ySize;
m.ty = shape.y;
for (x in 0 ... mapMax) {
if ((x + y) % 2 == oddEven) {
shape.x = x * xSize;
m.tx = shape.x;
target.draw(shape, m);
}
}
}
}

// 文字を描画する
private function drawString(target:BitmapData):Void
{
// 文字列のフォーマットなどを指定…あんまり細かい事はしない方が安全っぽいので、フォントサイズだけ決める
var textFormat:TextFormat = new TextFormat();
textFormat.size = ySize / 2;
// 描画元になるテキストフィールドを設定。NMEを信じてはいけない。なるべくシンプルな設定で。
var textField:TextField = new TextField();
textField.defaultTextFormat = textFormat;
textField.autoSize = TextFieldAutoSize.LEFT; // うかつにCENTERとか使おうとするとNMEはコケる模様

var m:Matrix = new Matrix();
// Matrixを指定しないと、Flashで位置が動かない!!
// Shape.xとShape.yを指定しないと、HTML5で位置が動かない!!
for (y in 0 ... mapMax) {
m.ty = y * ySize - ySize;
textField.y = m.ty;
for (x in 0 ... mapMax) {
if ((x + y) % 2 == oddEven) {
// 文字列を代入。
textField.text = drawStringFunc(x, y);
// widthを元に横位置を調整。
m.tx = x * xSize - textField.width / 2;
textField.x = m.tx;
target.draw(textField, m);
}
}
}
}

// Hex番号を文字列で返す関数
private function drawStringFunc(x:Int, y:Int):String
{
//return( ("0" + Std.string(x)) + " " + ("0" + Std.string(y)).substr(1,2) );
return( ("0" + Std.string(x)).substr(-2) + " " + ("0" + Std.string(y + 1 >> 1)).substr(-2) );
}

// カーソル初期化
private function initCursor(thickness:Float = 2
, cursorColor:Int = 0x00FF00
, cursorAlpha:Float = 0.7
, radius:Float = 7
):Void
{
// Shapeを初期化して、適当なサイズの丸を中心に描画
cursor = new Shape();
cursor.graphics.lineStyle(2, cursorColor, cursorAlpha);
cursor.graphics.drawCircle(0, 0, radius);
addChild(cursor);
}

// カーソル移動ルーチン
private function onMouseMove(evt:MouseEvent):Void
{
// 青マス上での座標を得る
var x:Int = Std.int((evt.localX + xSize / 2 ) / xSize);
var y:Int = Std.int((evt.localY + ySize / 2 ) / ySize);
// ヘックス上で有効な値ならカーソルを動かす
cursor.x = x * xSize;
if ((x + y) % 2 == oddEven) {
cursor.y = y * ySize;
} else {
var mod:Int = Std.int(evt.localY + ySize / 2) % Std.int(ySize) - Std.int(ySize / 2);
if (mod > 0 ) {
cursor.y = (y + 1) * ySize;
} else {
cursor.y = (y - 1) * ySize;
}
}
}

}


この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。