サイトのトップへ戻る

libGDX ドキュメント 日本語訳

サイト内検索

Scene2D Part 1

このページでは、Scene2D ライブラリについて見ていきます。 まず最初に注意しておくべきことは、scene2d を使用するかどうかについては完全に任意です。 使いたくない場合は使わなくて構いません。 Scene2Dを使わなくても、他ページで説明した内容は問題なく動作します。 さらに、Scene2Dをゲームの一部分だけで使用したい場合は(ゲームのHUDオーバレイのような)、それも可能です。

では scene2Dとは一体何なのでしょうか?一言で言えば、これは2Dシーングラフです。 するとあなたは「シーングラフとは何ですか?」と疑問に思うかもしれません。 良い質問ですね! 本質的に、シーングラフとはゲーム内世界の物質を保持するためのデータ構造です。 ですから、ゲーム内世界が数百のSpriteで構成されている場合、これらのSpriteはシーングラフ内に格納されています。 ゲーム内世界の中身を保持するのに加え、Scene2D にはデータに対して実行できるいくつかの機能が実装されています。 これには、当たり判定、ゲームオブジェクト間の階層の作成、入力の割り振り、時間経過ごとにノードを操作するためのactionの作成などのようなものがあります。

Scene2D は、LibGDX ライブラリ上に構築された、ゲームを作成するためのより高レベルのフレームワークと考えることができます。 これを使って非常に素晴らしいUIウィジットライブラリを作ることもできます… これについては後ほど説明します。

The object design of Scene2D is built around the metaphor of a play ( or at least I assume it is ).  階層の最上位は Stageになります。これはプレイ(ゲーム)が行われる場所です。 そのStageにはViewportが格納されています… Viewportとは、そうですね…ゲームを録画するカメラのようなものと考えてください (もしくは、観客の誰かの視点 )。 次に重要な抽象化された概念は、Actorです。ActorとはStageを成り立たせるための… スタッフです。 Actor とは必ずしもstage上で目に見える演者を意味するものではないので、この名前は少し誤解を招きそうです。 Actors could also include the guy running the lighting, a piece of scenery on stage, etc.  基本的に Actor とはゲームを構成するものです。 ですから基本的に、ゲームはActorによって構成された論理的なシーンごとに分割します( be it screens, stages, levels, whatever makes sense )。 改めて言いますが、この考え方があなたのゲームに合わない場合は、 Scene2Dを使う必要はありません。



これまでの説明はデザインの概念的なものなので、より実用的な例を見てみましょう。 一つのStageのシーンを作成して、それに一つのActorを追加しているだけです。

最近Batch/SpriteBatchに変更が行われたため、古いバージョンのLibGDXで以下コードは動作しません!最新のバージョンのLibGDXを使うようにしてください

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;

public class SceneDemo implements ApplicationListener {
    
    public class MyActor extends Actor {
        Texture texture = new Texture(Gdx.files.internal("data/jet.png"));
        @Override
        public void draw(Batch batch, float alpha){
            batch.draw(texture,0,0);
        }
    }
    
    private Stage stage;
    
    @Override
    public void create() {        
        stage = new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(),true);
        
        MyActor myActor = new MyActor();
        stage.addActor(myActor);
    }

    @Override
    public void dispose() {
        stage.dispose();
    }

    @Override
    public void render() {    
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        stage.draw();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

また、前回の例で使用した飛行機の画像を、jet.pngという名前でassets フォルダーに追加するのも忘れないでください。 やり方がわからない場合は、前回のチュートリアルを参照してください。このアプリケーションを実行すると以下が表示されます:

image

ご覧の通り、Stage2Dでの作業は非常に簡単です。Actor を継承したMyActorという名前の内部クラスを作成します。 MyActor はファイルから自身のtexture を読み込むだけです。 重要な部分は draw() メソッドです。draw() メソッドは、このActorの上位にあるStageから毎フレームごとに呼び出されます。 ここでは引数として渡されたBatchを使ってactorをstageへ描写しています。 Batch とは以前に見たSpriteBatch が実装しているインタフェースで、OpenGLへの描写呼び出しの一括処理を司っています。 今回の例では、batch の(0,0)の位置にTextureを描写しているだけです。 Actor はSpritesheetなどからプログラム上で簡単に生成することもできます。 ここで一つ指摘しておかなければいけないことは、今回の例は簡略化した内容になっています。 MyActor が破棄された時に使用しているTexture がリークしてしまうので、実際にゲームを作る際には別の方法でこれらを管理してください!

アプリケーションの create() メソッドでは、アプリの解像度を引数として渡してstageを作成します。 引数にtrue の値を設定した場合は、端末のアスペクト比を維持するということを示します。 stage を作成した後にMyActorのインスタンスを作成し、stage.addActor()を呼び出してMyActorをstageに追加します。 次にrender()メソッドの最初のほうで、画面をクリアした後に draw() を呼び出してstage を描写します。 stage のdraw()メソッドでは、stage に含まれる全てのactgorのdraw()メソッドを順々に呼び出します。 最後に、アプリのdispose()呼び出し内でstageを破棄し、リークの発生を防いでいることが分かるでしょう。



以上では、Scene2D ベースアプリケーションの基本的な構造について見てきました。 実際にactorに何かをさせたり、actorをどのようにして制御するかについては説明していません。 このプロセスは非常に簡単ですが、いくつか落とし穴があります。 コードを修正したので、それを見てみましょう。修正部分は強調表示をしています。:

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;

public class SceneDemo2 implements ApplicationListener {
    
    public class MyActor extends Actor {
        Texture texture = new Texture(Gdx.files.internal("data/jet.png"));
        float actorX = 0, actorY = 0;
        public boolean started = false;

        public MyActor(){
            setBounds(actorX,actorY,texture.getWidth(),texture.getHeight());
            addListener(new InputListener(){
                public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
                    ((MyActor)event.getTarget()).started = true;
                    return true;
                }
            });
        }
        
        
        @Override
        public void draw(Batch batch, float alpha){
            batch.draw(texture,actorX,actorY);
        }
        
        @Override
        public void act(float delta){
            if(started){
                actorX+=5;
            }
        }
    }
    
    private Stage stage;
    
    @Override
    public void create() {        
        stage = new Stage();
        Gdx.input.setInputProcessor(stage);
        
        MyActor myActor = new MyActor();
        myActor.setTouchable(Touchable.enabled);
        stage.addActor(myActor);
    }

    @Override
    public void dispose() {
    }

    @Override
public void render() {    
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    stage.act(Gdx.graphics.getDeltaTime());
    stage.draw();
}

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

上記コードを実装すると、以下が表示されます:

scene2

飛行機のspriteをクリックするとaction が開始されます。 それでは、コードを詳しく見てみましょう。

MyActor クラスの変更内容を見てみましょう。一目で分かる変更内容は、コンストラクタが追加されたことです。 これによりイベントリスナーをactorへ追加できます。これは以前入力制御をした時に扱ったイベントリスナーと非常に似た働きをします。 ですが今回は、getTarget()メソッドが定義されたInputEvent クラスを引数として渡しています。getTarget()では、タッチされたActor を取得できます。 取得したActorをMyActor オブジェクトへ変換し、boolean 値のstarted をtrueに設定するだけです。 注意すべき重大な点は、setBounds()を呼び出すことです。 この呼び出しは非常に重要です! Actorを継承した場合は、setBoundを設定する必要があるのです。そうしないとクリックやタッチができません。 私はこの落とし穴にはまって多くの時間を費やしてしまいました。 setBounds()で境界を設定して、Actor が持つTextureとサイズを合わせるだけです。 もう一つ注意すべき重要な点は、Actor イベントの制御に関するサンプルコードや文書の多くは現時点から見て古すぎるものであり、なおかつActor イベントでは過去に重大な変更があったということです!

コンストラクター以外でMyActor へ行った大きな変更は、act()メソッドの追加です。 draw()と同じく、stageのact() メソッドを呼び出すと、stage上にある全てのactorのact()メソッドが呼び出されます。ここでは、時間経過ごとにactor を更新します。 他の多くのフレームワークでは、これはact ではなく update()と呼ばれているでしょう。 今回のコードでは、毎フレームごとにMyActor のX位置に5ピクセル追加しているだけです。 もちろん、この処理はstarted フラグがtrueになった場合のみに行われます。

create() メソッド内では、細かい変更がいくつかあります。 一つ目は。InputProcessorを登録する必要があるということです。 Stage はInputProcessorを実装しているので、stage オブジェクトをsetInputProcessor()に引数として渡すだけです。 以前も見たように、stage は全ての子ActorのInputListeners の呼び出しを処理します。 またactor をタッチ可能なものとして設定していますが、きっとactor は既定でもタッチ可能となっていることでしょう。 actor をタッチやクリック不可として設定したい場合は、代わりにTouchable.disabledを引数として渡してください。 その他の変更はrender()メソッドだけで、前回のフレームからの経過時間を stage.act()に渡して呼び出しています。 stage.act()を実行すると、様々なactorに自身のact()を呼び出させます。

Scene2D は非常に大きな内容なので、何回かのパートに別けて説明していきます。