JavaScript
cocos2d-JS

cocos2d-jsでRenderTextureでSpriteの数を減らす

More than 1 year has passed since last update.

Spriteは少ないほうが良い

よくゲームエンジンのパフォーマンス比較でスプライトを増やしてFPSを計測していることからも分かるように、スプライトが増えればその分計算が増えるのでパフォーマンスは落ちる。つまり、古い端末でも十分滑らかに動くことを意図するならスプライト数は減らすに限る。特にWebブラウザ上で動くことを想定しているcocos2d-jsでは重要になる。

cc.RenderTexture

例えばアクションゲームの背景がオブジェクトの組み合わせで表現されるとき、ステージごとに背景を替えたい場合、パフォーマンス的には予め合成された画像を持っておくのが有利だが、容量的には不利だ。
これを解決する一つの方法が、RenderTextureである。

cc.RenderTexture()
cc.RenderTexture is a generic rendering target. To render things into it,
simply construct a render target, call begin on it, call visit on any cocos
scenes or objects to render them, and call end. For convenience, render texture
adds a sprite as it's display child with the results, so you can simply add
the render texture to your scene and treat it like any other CocosNode.
There are also functions for saving the render texture to disk in PNG or JPG format.

cc.RenderTexture | JsDoc Reference

ざっくり言えばスプライトを組み合わせた大きな画像を内部的に作ってしまうようなもの。

使い方

  1. cc.RenderTextureをサイズ指定して作成
  2. これからRenderTextureに焼き付ける事をbeginで宣言する
  3. 焼き付けたいSpriteを配置する
  4. Spriteのメソッドretainを呼んで焼きこむSpriteであることを宣言する
  5. Spriteのメソッドvisitを呼んで焼き付ける
  6. Spriteのメソッドreleaseを呼んで解放する
  7. 3から6を焼き付けたいSpriteの分だけ繰り返す
  8. RenderTextureへの焼き付けが終わった事をendで宣言する

retainとかreleaseとかはC++版のcocos2dやってないとわかりづらいと思う。

var rt = cc.RenderTexture.create(320,240);
rt.begin();
var sprite;
for(var i=0;i<10;i++){
    sprite = cc.Sprite.create('res/object-'+i+'.png');
    sprite.retain();
    sprite.setPosition(0,i*sprite.width);
    sprite.visit();
    sprite.release();
}
rt.end();
var rtSprite = cc.Sprite.createWithTexture(rt.sprite.texture); // 別にそのままrt使っても良いが

だいたいこんな感じになる。が、これだと上手く動かない時がある。というかモダンな環境では多分上手く動かない。スプライトが逆さまに移っているし、順序もおかしい筈。

WebGLだと上下が逆さま

これはccConfigで設定したrenderModeが2、または0でWebGLが有効な場合に起こる。どうしてかというと、CANVASテクスチャはDOMと同じく左上が基点なのだが、WebGLでは上下反転しているからだ。だから、どちらにも対応しようとした場合はrenderModeがCANVASなのかWebGLなのかで処理を分けなければならない。

WebGL/Canvas両対応版

var rt = cc.RenderTexture.create(320,240);
rt.begin();
var sprite;
for(var i=0;i<10;i++){
    sprite = cc.Sprite.create('res/object-'+i+'.png');
    sprite.retain();
    if (cc._renderType === cc._RENDER_TYPE_WEBGL) { // renderTypeで処理を分ける
        sprite.setScale(1,-1); // 上下反転
        sprite.setPosition(0,rt.height - i*sprite.width); // 上下逆から配置
    }else if(cc._renderType === cc._RENDER_TYPE_CANVAS){
        sprite.setPosition(0,i*sprite.width);
    }
    sprite.visit();
    sprite.release();
}
rt.end();
var rtSprite = cc.Sprite.createWithTexture(rt.sprite.texture); // 別にそのままrt使っても良いが