Meshを継承したカスタムDisplayObjectを作る:Triangle


Starling2.0のMeshクラスを継承して、カスタムDisplayObjectを作ってみました。一番簡単そうなので、ポリゴン1枚のTriangle(三角)を作ります。テクスチャ指定もできます。

ちなみに、Starling2.0では1.xとDisplayObjectの継承ツリーが変更されているので、この記事を読み進む方は先に下記の記事も参照するといいかもしれません。
Starling2.0のDisplayObject継承ツリーを理解する

デモページ


デモはここにあります。
http://harayoki.github.io/qiita_demo/triangle_demo/demo1.html
タッチすると三角形がボヨンと動きます。矩形でないタッチ判定になっていることがわかります。緑の四角はBounds情報がきちんと計算できているか、可視化して確認するためのものです。

AIR書き出しを含むデモ全体のソースはここにあります。
https://github.com/harayoki/-qiita1/tree/triangle_demo1
他の投稿のソースも混ざってpushされちゃってますが、メインのsrc/AirMain.asから追いかけていけばわかるかと。。なお、web上のデモではWireFrame表示とGCができないので画面下部のボタンが少なくなります。

使い方

Triangleのコードはここにあります。仕様はQuadの使い方に合わせてあります。
harayoki.starling.display.Triangle


横幅、縦幅を指定してインスタンス化します。DisplayObjectなので、x,y,rotaion,scaleなどの設定ももちろんできます。デフォルトでは白い二等辺三角形が作られます。Quadを半分に割った形です。上記キャプチャ画像の黒い線が交わっている部分がTriangleの原点となります。
なお、くりかえしですが、緑の線はBoundsを図示したものなので、Triangleの一部ではありません。

sample.as
var tr:Triangle = new Triangle(30, 30);
tr.x = 100;
addChild(tr);


三番めのコンスタラクタ引数で色が指定できます。skewX、skewYの設定も可能なので、これを設定すると三角形のとんがり具合を変更でき、正三角形にすることもできます。

sample.as
var tr:Triangle = new Triangle(10, 10, 0xffff00);
tr.scale = 2.0;
tr.skewX = -30 * Math.PI / 180;
addChild(tr);


テクスチャの指定や原点の調整もできます。デモでは各Triangleを回転させているので、原点がずれているのが確認できると思います。

sample.as
var tr:Triangle = Triangle.fromTexture(someTexture);
tr.pivotX = 48;
tr.pivotY = 48;
addChild(tr);

テクスチャは後から変更もできます。

sample.as
tr.texture = anotherTexture;

非常に簡単です。

[実装] Quad(矩形)を原型にTriangleを作る

以下、実装についてです。ビルトインのクラスのQuadがMeshを継承していますので、このQuadを元に三角形クラスを作りました。変更のポイントは少なくて、

  • 頂点を1つ減らしてメッシュ形状を変更する
  • テクスチャのマッピングを変更する
  • タッチ判定を変更(削除)する
  • Bounds計算処理を変更(削除)する

程度です。以外と簡単。なお、参考用にQuadのソースはここです。
Starling starling.display.Quad

[実装] Triangle.as コード解説 (変更点)

下にずらずら書きます。

コンストラクタ内.as
var vertexData:VertexData = new VertexData(MeshStyle.VERTEX_FORMAT, 4);
var indexData:IndexData = new IndexData(6);
// ↓Triangle ↑Quad
var vertexData:VertexData = new VertexData(MeshStyle.VERTEX_FORMAT, 3);
var indexData:IndexData = new IndexData(3);

↑ポリゴンが三角形2枚から1枚になるので、Quadと比べて、Vertex数とIndex数が変更になります。

setupVertices内.as
indexData.addQuad(0, 1, 2, 3);
vertexData.numVertices = 4;
// ↓Triangle ↑Quad
indexData.addTriangle(0, 1, 2);
vertexData.numVertices = 3;

↑これも同様なんですが、IndexDataクラスにはaddQuadとかaddTriangleというメソッドがあるので便利ですね。

setupVertices内(texture紐付け部分).as
if (texture) {
    texture.setupVertexPositions(vertexData, 0, "position", _bounds);
    texture.setupTextureCoordinates(vertexData, 0, texAttr);
}
// ↓Triangle ↑Quad
if (texture) {
    _setupVertexPositions(texture, vertexData, 0, "position", _bounds);
    _setupTextureCoordinates(texture, vertexData, 0, texAttr);
}

↑テクスチャ指定がある場合にテクスチャマッピングを調整している部分です。テクスチャを引き延ばしています。Quadではtexture側のメソッドにそこを任せているのですが、四角形専用の処理内容になっていました。しょうがないので、三角形用の処理をTriangle側に_setupVertexPositionsと_setupTextureCoordinatesという名前で書きました。処理難しい感じですが、Textureクラスの処理から頂点の扱いを1つ省いただけです。

_setupVertexPositionsはリンクだけ貼ります。
https://github.com/harayoki/-qiita1/blob/triangle_demo1/src/harayoki/starling/display/Triangle.as#L75-L105

_setupTextureCoordinates側のコードについては解説します。
テクスチャに余白情報がある場合の考慮と、width、height設定を考慮して頂点位置を調整します。

_setupTextureCoordinatesメソッド部分.as
// ↓Triangle
private function _setupTextureCoordinates(
        texture:Texture, vertexData:VertexData,
        vertexID:int=0, attrName:String="texCoords"):void
{
    texture.setTexCoords(vertexData, vertexID    , attrName, 0.0, 0.0);
    texture.setTexCoords(vertexData, vertexID + 1, attrName, 1.0, 0.0);
    texture.setTexCoords(vertexData, vertexID + 2, attrName, 0.0, 1.0);
}

↑3つの頂点に対してのテクスチャ座標を指定しています。

sample.as
// ↓Triangle
else {
    vertexData.setPoint(0, posAttr, _bounds.left, _bounds.top);
    vertexData.setPoint(1, posAttr, _bounds.right, _bounds.top);
    vertexData.setPoint(2, posAttr, _bounds.left, _bounds.bottom);
    vertexData.setPoint(0, texAttr, 0.0, 0.0);
    vertexData.setPoint(1, texAttr, 1.0, 0.0);
    vertexData.setPoint(2, texAttr, 0.0, 1.0);
}

↑テクスチャ設定がない場合は、素直にwidth,heightを適用した位置に頂点情報を調整します。Quadから呼び出したTexture側の処理から1つの頂点の扱いを少なくしただけです。

getBoundsとhitTestはQuadの実装を消去する

Triangleを覆い囲う矩形を返すgetBoundsメソッドと、タッチ判定を行うhitTestメソッドは自前で実装すると大変そうですが、Quadで設定してある内容を削除すると、親クラスのMeshでの実装が適用されます。 Meshでの実装は1つ〜たくさんの三角形ポリゴンに対して汎用的な処理が書いてありますので、Triangleクラスでもそのまま使うことができます。デモで確認すると正しく動いているのがわかります。便利ですね。 この2つの処理の実態はstarling.utils.MeshUtilに記載されていたので、こちらを直接利用する形でもよさそうです。

[実装] ほぼ変更されない部分

  • readjustSizeメソッド
    指定されたテクスチャの大きさにメッシュの大きさを合わせるメソッドです。完全にQuadと同じです。というより同じでいいような三角形の頂点位置を選択したのですが。

  • fromTextureメソッド(static)
    テクスチャ指定でインスタンスをつくる静的メソッドです。クラス名が違うだけで内容は一緒です。

  • texture (セッタ)
    完全に同じです。

さて、実装はこれだけです!DisplayObjectのツリー構造がしっかりしているので、簡単にTriangleクラスを作ることができました。ポリゴンの数が増えて、メッシュが複雑になっても、上記の方法をなぞることでカスタムDisplayObjectを作成することができると思います。

その他、考察など

なお、今回作成したクラスは3Dでの動作は確認していません。三角形が1つ減った分で動作速度も速くなっているでしょうか。。?で、あれば、実際の案件でTriangleクラスは使えるかもしれませんね。次の投稿でパフォーマンス計測してみようと思います。

hitTestとgetBoundsについては、Meshの実装をそのまま借りましたが、汎用的な実装になっている分で動作は遅めかもしれません。複雑な形状のDisplayObjectを作った場合は、割り切ってhitTestはnullをgetBoundsも適当な固定の矩形を返す実装にしてしまってもいいかもしれません。タッチできなかったり、大きさの計算がおかしくなりますが、そういう仕様と言うことにすれば高速に動作するのでよいのではないかな、と。

以上です!