libGDXでActorクラスを使う際には、draw()メソッドの中に以下の2行を記述した方が良いという話です。
public void draw (Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
//描写処理
}
##Actorとは
libGDXのStage内で画像を描写する際には主にImageクラスを使うと思います。
ActorとはそのImageクラスの大元ともいうべきクラスです。
ImageクラスはActorクラス(正確にはActorを継承したWidgetクラス)を継承して作成されています。
そのためActorでもImageと同じく画像描写ができます。
例えば、test.pngという画像を描写する際、Imageでは以下のように記述しますが、
final Texture textureTest = new Texture(Gdx.files.internal(test.png));
Image image=new Image(textureTest);
image.setBounds(0,0,64,64);
stage.addActor(image);
Actorでは以下のように記述すると同じことができます。
final Texture textureTest = new Texture(Gdx.files.internal(test.png));
Actor actor=new Actor()
{
public void draw (Batch batch, float parentAlpha) {
batch.draw(textureTest ,getX(),getY(),getWidth(),getHeight());
}
};
actor.setBounds(0,0,64,64);
stage.addActor(actor);
Actorクラスは内部に何も実装されていない空クラスなので全ての処理を自分で記述する必要がありますが、裏を返せば手動で細かい部分までいじれるということです。
例えば、以下のようにして画像と文字を両方同時に描写したりできます。
final Texture textureTest = new Texture(Gdx.files.internal(test.png));
final BitmapFont font = new BitmapFont();//追加部分
font.setColor(0.0f, 0.0f, 0.0f, 1.0f);//追加部分
Actor actor=new Actor()
{
public void draw (Batch batch, float parentAlpha) {
batch.draw(textureTest ,getX(),getY(),getWidth(),getHeight());
font.draw(batch,"E",getX(),getY()+getHeight());//追加部分
}
};
actor.setBounds(0,0,64,64);
stage.addActor(actor);
細部まで自分で細かく設定したい人はActorを、細かい部分の設定はLibGDXに任せたい人はImageを使うことになるでしょう。
##本題
さて、本題に戻りましょう。
まずはImageとActorを両方使って画面に描写を行ってみます。
final Texture textureTest = new Texture(Gdx.files.internal(test.png));
Image image=new Image(textureTest);
image.setBounds(100,100,64,64);
stage.addActor(image);
Actor actor=new Actor()
{
public void draw (Batch batch, float parentAlpha) {
batch.draw(textureTest ,getX(),getY(),getWidth(),getHeight());
}
};
actor.setBounds(200,100,64,64);
stage.addActor(actor);
特に問題なく描写されました。次にImageクラスに色味を付けてみます。
Imageクラスでは、setColorメソッドを使って画像に色合いをつけることができます。
ゲームなどで敵味方識別のためにユニットを色分けをする場合に使います。
final Texture textureTest = new Texture(Gdx.files.internal(test.png));
Image image=new Image(textureTest);
image.setBounds(100,100,64,64);
image.setColor(0.8f,0,0,1f);//追加部分
stage.addActor(image);
Actor actor=new Actor()
{
public void draw (Batch batch, float parentAlpha) {
batch.draw(textureTest ,getX(),getY(),getWidth(),getHeight());
}
};
actor.setBounds(200,100,64,64);
stage.addActor(actor);
なぜかImage だけではなくActor にまで赤みが付いてしまいました。
##原因を探る
原因を探るためにImageクラスのdraw()メソッドを見てみましょう。
public void draw (Batch batch, float parentAlpha) {
validate();
Color color = getColor();//ここに注目
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);//ここに注目
float x = getX();
float y = getY();
float scaleX = getScaleX();
float scaleY = getScaleY();
if (drawable instanceof TransformDrawable) {
float rotation = getRotation();
if (scaleX != 1 || scaleY != 1 || rotation != 0) {
((TransformDrawable)drawable).draw(batch, x + imageX, y + imageY, getOriginX() - imageX, getOriginY() - imageY,
imageWidth, imageHeight, scaleX, scaleY, rotation);
return;
}
}
if (drawable != null) drawable.draw(batch, x + imageX, y + imageY, imageWidth * scaleX, imageHeight * scaleY);
}
4~5行目で、Imageの持つColor情報を取得してbatchに設定しています。
これによりbatchを使って赤みが付いた描写ができるわけですね。
Javaではクラスは参照渡しになるので、draw()内でbatchの情報が変更されたら呼び出し元に戻っても情報は変更されたままです。
つまり、あるクラスでsetColorを使って色情報を変更した後、何もしないと次に描写するクラスでもその色情報が残ったまま描写が行われてしまうのです。
そこで、最初のコードで示したように、ActorでもImageと同じ様にbatchに自身の色情報を上書きして描写を行うと言うわけです。
Actor actor=new Actor()
{
public void draw (Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
//描写処理
batch.draw(textureTest ,getX(),getY(),getWidth(),getHeight());
}
};
##大した問題ではないと思っている人も
色合いを付ける処理は使っていないので対処は不要だと考えている方も、なるべくは対策コードを記述することをお勧めします。
というのも、Image以外にも様々なクラスのdraw()メソッド内でbatchの色情報が弄られるからです。
色みをつけた場合だと因果関係が分かりやすいのですぐに原因を特定できますが、それ以外の状況で発生すると解決が困難だったりします。
例えば、ScrollPaneというスクロールができるレイアウトがあります。
このScrollPaneのdrawメソッド内では、スクロールバーのフェードアウト処理をするためにbatchの透明度情報を変更しています。
そのためScrollPaneを対策をしていないActorと一緒に使用すると、ScrollPaneで透明度の情報が変更されたbatchがActorの描写でもそのまま使われてしまい、Actorが徐々に透明になってフェードアウトしてしまうという事象が発生してしまいます。
自分の設定が起因して起こるのではなく各クラスの内部処理によって発生する事象ですから、身に覚えがないため原因特定は困難となるでしょう。
参考にしたページ
libGDX 公式ページ
上記の日本語訳サイト(非公式)