最近Cocos2d-x(version 3.0)を使っている。と言ってもまだ使い始めて3日程度だけれど。
Cocos2dはプログラムでシーングラフを構築していくのが基本的な使い方っぽい。ほぼ2D専用なので、シーングラフを構成するメインとなるオブジェクトはスプライトになる。スプライトとなれば、テクスチャをパックしてドローコールの数を減らすのが常套手段なわけだけれども、Cocos2dの場合、SpriteFrameCacheクラスを使う。
このクラスにはaddSpriteFramesWithFileというメンバ関数があって、この関数にパックしたテクスチャの情報を含むplistファイルを指定するのだが、plistの形式がよくわからん。ドキュメントを見てもTextuePackerを使うのをオススメするとしか書いていない。オススメの前にちゃんと仕様を書け!と言いたいところだが、cocos2d-xの良いところはオープンソースなところ。ソースを読めば仕様は明確なのだ。
plist自体はAppleが良く使っているXMLファイルで、key-valueのペアで情報を格納する。問題はどういうkeyがあるのかということ。
基本的なフォーマットとしては、metadataとframesがある。metadataにはフォーマットのバージョンを記述して、framesに各スプライトの画像がテクスチャのどこにあるのかなどの情報を記述する。XMLの記述例としてはこんな感じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>metadata</key> <dict> <key>format</key> <integer>1</integer> </dict> <key>frames</key> <dict> <key>sprite1</key> <dict> <!-- ここに sprite1 の情報を記述する --> </dict> <key>sprite2</key> <dict> <!-- ここに sprite2 の情報を記述する --> </dict> </dict> </dict> </plist> |
ソースを見ると、formatの値には4種類(0から3)あって、formatによって各スプライトの情報の記述の仕方が違うことがわかる。Googleで検索してよく見かけるのはformat == 2のケース。TexturePackerが出力するのがこの形式ということだろうか?
format == 0 の場合
もっともシンプルなフォーマット。でも手書きするならformat==1の方がスッキリして良いと思う。画像を横にして(90度回転して)パックするのには対応していない。
<key> | valueの型 | valueの意味 |
x | <real> | パックされたテクスチャの中でこのスプライトが使う画像の左上のx座標(ピクセル単位でテクスチャの左上が原点) |
y | <real> | パックされたテクスチャの中でこのスプライトが使う画像の左上のy座標(ピクセル単位でテクスチャの左上が原点) |
width | <real> | パックされたテクスチャの中でこのスプライトが使う画像で使うテクスチャの幅(ピクセル単位) |
height | <real> | パックされたテクスチャの中でこのスプライトが使う画像で使うテクスチャの高さ(ピクセル単位) |
offsetX | <real> | このスプライトが使う画像の中心位置が実際のスプライトの中心からx方向にどれだけずれているか(ピクセル単位) |
offsetY | <real> | このスプライトが使う画像の中心位置が実際のスプライトの中心からy方向にどれだけずれているか(ピクセル単位) |
originalWidth | <real> | トリミングされていない実際のスプライトの幅(ピクセル単位)。スプライトが透明な部分を多く含む場合、透明な部分をトリミングしてパックすることでより多くのスプライトを1枚のテクスチャに収めることができる。その場合、widthはoriginalWidthよりも小さくなる。 |
originalWidth | <real> | トリミングされていない実際のスプライトの高さ(ピクセル単位)。スプライトが透明な部分を多く含む場合、透明な部分をトリミングしてパックすることでより多くのスプライトを1枚のテクスチャに収めることができる。その場合、heightはoriginalHeightよりも小さくなる。 |
XMLの記載例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>metadata</key> <dict> <key>format</key> <integer>0</integer> </dict> <key>frames</key> <dict> <key>sprite1</key> <dict> <key>x</key> <real>0</real> <key>y</key> <real>0</real> <key>width</key> <real>32</real> <key>height</key> <real>32</real> <key>offsetX</key> <real>0</real> <key>offsetY</key> <real>0</real> <key>originalWidth</key> <real>32</real> <key>originalHeight</key> <real>32</real> </dict> <key>sprite2</key> <dict> <key>x</key> <real>32</real> <key>y</key> <real>0</real> <key>width</key> <real>32</real> <key>height</key> <real>32</real> <key>offsetX</key> <real>0</real> <key>offsetY</key> <real>0</real> <key>originalWidth</key> <real>32</real> <key>originalHeight</key> <real>32</real> </dict> </dict> </dict> </plist> |
format == 1 又は format == 2 の場合
インターネットで検索するとよく見かけるフォーマット。format == 2では画像を横にして(時計回りに90度回転して)パックするのに対応している。
<key> | valueの型 | valueの意味 |
frame | <string> | パックされたテクスチャの中でこのスプライトが使う画像の範囲。{{x,y},{width,height}}の形式で記述する。値はピクセル単位でテクスチャの左上が原点。画像が時計回りに90度回転されてパックされている場合、widthとheightは回転前のものでなければならない。 |
offset | <string> | このスプライトが使う画像の中心位置が実際のスプライトの中心からx方向にどれだけずれているかを{x,y}の形式で記述する(ピクセル単位) |
rotated (format == 2の場合のみ) | <true/> 又は <false/> | このスプライトが使う画像が時計回りに90度回転されてテクスチャにパックされている場合は<true/>、そうでなければ<false/>。<true/> の場合、frameのwidthとheightは回転前の幅と高さを指定する必要がある。 |
sourceSize | <string> | トリミングされていない実際のスプライトのサイズを{width,height}の形式で記述する(ピクセル単位)。スプライトが透明な部分を多く含む場合、透明な部分をトリミングしてパックすることでより多くのスプライトを1枚のテクスチャに収めることができる。その場合、frameの{width,height}はsourceSizeの{width,height}よりも小さくなる。 |
XMLの記載例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>metadata</key> <dict> <key>format</key> <integer>2</integer> </dict> <key>frames</key> <dict> <key>sprite1</key> <dict> <key>frame</key> <string>{{0,0},{32,32}}</string> <key>offset</key> <string>{0,0}</string> <key>rotated</key> <false/> <key>sourceSize</key> <string>{32,32}</string> </dict> <key>sprite2</key> <dict> <key>frame</key> <string>{{32,0},{32,32}}</string> <key>offset</key> <string>{0,0}</string> <key>rotated</key> <false/> <key>sourceSize</key> <string>{32,32}</string> </dict> </dict> </dict> </plist> |
format == 3 の場合
スプライトに別名(alias)をつけられるようだ。実際の挙動は確認していない。format == 2の場合に比べると冗長な部分があるのが気になる。
<key> | valueの型 | valueの意味 |
spriteSize | <string> | パックされたテクスチャの中でこのスプライトが使う画像のサイズを{width,height}の形式で記述する(ピクセル単位)。画像が時計回りに90度回転されてパックされている場合、widthとheightは回転前のものでなければならない。 |
spriteOffset | <string> | このスプライトが使う画像の中心位置が実際のスプライトの中心からx方向にどれだけずれているかを{x,y}の形式で記述する(ピクセル単位) |
spriteSourceSize | <string> | トリミングされていない実際のスプライトのサイズを{width,height}の形式で記述する(ピクセル単位)。スプライトが透明な部分を多く含む場合、透明な部分をトリミングしてパックすることでより多くのスプライトを1枚のテクスチャに収めることができる。その場合、frameの{width,height}はsourceSizeの{width,height}よりも小さくなる。 |
textureRect | <string> | パックされたテクスチャの中でこのスプライトが使う画像の範囲。{{x,y},{width,height}}の形式で記述する。値はピクセル単位でテクスチャの左上が原点。{width,height}の部分はspriteSizeと重複する気がするが、実際にtextureRectの{width,height}は使われていない。 |
textureRotated | <true/> 又は <false/> | このスプライトが使う画像が時計回りに90度回転されてテクスチャにパックされている場合は<true/>、そうでなければ<false/>。<true/> の場合、spriteSizeのwidthとheightは回転前の幅と高さを指定する必要がある。 |
aliases | <array> | このスプライトの別名の配列。 |
XMLの記載例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>metadata</key> <dict> <key>format</key> <integer>3</integer> </dict> <key>frames</key> <dict> <key>sprite1</key> <dict> <key>spriteSize</key> <string>{32,32}</string> <key>spriteOffset</key> <string>{0,0}</string> <key>spriteSourceSize</key> <string>{32,32}</string> <key>textureRect</key> <string>{{0,0},{32,32}}</string> <key>textureRotated</key> <false/> <key>aliases</key> <array/> </dict> <key>sprite2</key> <dict> <key>spriteSize</key> <string>{32,32}</string> <key>spriteOffset</key> <string>{0,0}</string> <key>spriteSourceSize</key> <string>{32,32}</string> <key>textureRect</key> <string>{{32,0},{32,32}}</string> <key>textureRotated</key> <false/> <key>aliases</key> <array> <string>alias1</string> <string>alias2</string> </array> </dict> </dict> </dict> </plist> |
どういう歴史的経緯があるのか知らないけど、formatの値によって形式がバラバラ。format == 3の場合なんか、どうしてkeyの名前を変えなきゃいけないのかがよくわからない。まあ元々の名前がわかりにくすぎたんだろうけど。
まだ使い始めたばかりだけど、全体的に見ても、過去の互換性を保つためか意味のわかりにくい部分があるように思う。ドキュメントも全然足りてない。リファレンスなんか全然役に立たないし。別にUnityの肩を持つわけではないけど、これがオープンソースの限界かなとも感じる。
とは言え、ソースが公開されているので、いざとなったら自分で何でもできるところが良い。Unityだとエンジン内部に手を付けたくても出来ないので、簡単な2Dゲームを作るぶんには当面Cocos2d-xを使ってみようと思う。