サイトのトップへ戻る

AndEngineドキュメント 日本語訳

AndEngineを使ったAndroid ゲーム開発チュートリアル - Part 4 - シーン と シーンマネージャー

« 前へ | 次へ »

第三パートでは、空のactivityを作成しました。onCreateScene メソッド内で、scene として"null"を表示するよう engine に設定しました。 ネット上にあるサンプルを見ると、常にonCreateScene 内で実際のscene を作成しています。 ですが、今回は複数のSceneを使用したいので、Sceneを切り替える仕組みが必要です。

また、重要なオブジェクトを各Sceneに渡す仕組みも必要です。Sceneのコンストラクタを呼び出す際にオブジェクトの引渡しができます。 もしくは、ResourceManagerと呼ばれる別のクラスを使用することもできます。



Resource manager

このクラスは、TextureやSoundやMusicのようなリソースのために使用されます。 リソースは全てのSceneで広範囲に使用されるので、Resource managerには重要なオブジェクトもまたいくつか定義します。 Textureなどの読み込みについては次のパートで説明します。必要に応じて順次加筆していきます。 今のところは、以下のような本当にシンプルなリソースマネージャーを作成します。:


package is.kul.squongtutorial.resources;

import is.kul.squongtutorial.GameActivity;

import org.andengine.engine.Engine;
import org.andengine.engine.camera.Camera;
import org.andengine.opengl.vbo.VertexBufferObjectManager;

public class ResourceManager {
  private static final ResourceManager INSTANCE = new ResourceManager();
  
  //common objects
  public GameActivity activity;
  public Engine engine;
  public Camera camera;
  public VertexBufferObjectManager vbom;
  
  private ResourceManager() {}
  
  public static ResourceManager getInstance() {
    return INSTANCE;
  }
  
  public void init(GameActivity activity) {
    this.activity = activity;
    this.engine = activity.getEngine();
    this.camera = engine.getCamera();
    this.vbom = engine.getVertexBufferObjectManager();
  }
}

GameActivity を変更してResourceManagerを初期化します:


  @Override
  public void onCreateResources(
      OnCreateResourcesCallback pOnCreateResourcesCallback)
      throws IOException {
    ResourceManager.getInstance().init(this);
    pOnCreateResourcesCallback.onCreateResourcesFinished();
  }

このクラスはシングルトンパターンと呼ばれるものに準拠しています。 これはプログラム全体でインスタンスは一つだけということを意味します。そのためコンストラクタは非公開となっており、誰もインスタンスを新規作成できません。 ハードウェアの制限のためにメモリ内で使われるアセットのインスタンスは一つだけにしたいでしょうから、リソースマネージャーを一つだけにするのは重要です。

今のところ、このリソースマネージャではactivity, engine, camera , vertex buffer object managerへの参照を持っているだけです。

Vertex Buffer Object は OpenGLの一部です。これにより頂点データの描写を行うことができます。 AndEngine はこの描写処理を見えないところであなたに代わって行います。少なくとも初めてのゲーム制作では、常に VBO Managerを使うだけでも問題ないでしょう。

Scene は現在画面に表示されている基本的なentityの集まりです。 通常はメニュー画面とゲーム画面ではまったく別のentityを使いたいでしょう。 そのためメニューの scene とゲームの sceneを作成します。



Scenes

ゲーム中では以下のscene を使用します:
  • Splash - あなたのロゴや AndEngine のロゴを表示します
  • Menu - 何種類かの選択肢。 Squong の場合は、基本設定とプレイ開始のボタンを設置します
  • Game - ゲーム
  • Info - このチュートリアルへのリンク
  • Loading scene - Sceneを切り替えてリソースを読み込んでいる間に表示するもの
  • まず最初に抽象scene を作成し、次に上記各sceneのクラスを作成し、最後にそれらを切り替えるScene Managerを作成します。



    抽象 Scene

    Javaでは、抽象クラスとはインスタンス化することができないクラスのことです。 これはいくつかの機能を定義できる親クラスですが、"new"を使って呼び出すことができません。 抽象クラスを持つことができ、それを強制的に子クラスにも実装させます。

    Entity とは仮想オブジェクトです。これは位置情報、親、子を持ちますが画面には描写されません。 これの拡張クラスにはShape (およびその子クラスRectangle, ...)やSprite (TiledSprite, ...)があり、それらは画面上に物理的に表示されます。

    Scene とは画面上に表示されるEntity(AndEngine class Entity)の階層です。 任意のEntity を Sceneに貼り付けることができます。そして任意のEntityに別のEntityを貼り付けて親子関係を作ることができます。 実際のところ、Scene もまたEntityです。

    AbstractScene では基本的なオブジェクトと重要なメソッドを定義します。またユーザーがバックキーを押した時の既定の振る舞いも定義します。

    
    package is.kul.squongtutorial.scene;
    
    import is.kul.squongtutorial.GameActivity;
    import is.kul.squongtutorial.resources.ResourceManager;
    
    import org.andengine.engine.Engine;
    import org.andengine.engine.camera.Camera;
    import org.andengine.entity.scene.Scene;
    import org.andengine.opengl.vbo.VertexBufferObjectManager;
    import org.andengine.util.debug.Debug;
    
    public abstract class AbstractScene extends Scene {
    
      protected ResourceManager res = ResourceManager.getInstance();
    
      protected Engine engine;
      protected GameActivity activity;
      protected VertexBufferObjectManager vbom;
      protected Camera camera;
      
      public void initialize(GameActivity activity, ResourceManager res) {
        this.res = res;
        this.activity = activity;
        this.vbom = activity.getVertexBufferObjectManager();
        this.engine = activity.getEngine();
        this.camera = engine.getCamera();
      }
      
      public abstract void loadResources();
      public abstract void create();
      public abstract void unloadResources();
      public abstract void destroy();
      public void onBackKeyPressed() {
        Debug.d("Back key pressed");
      }
      public abstract void onPause();
      public abstract void onResume();
    }
    

    クラス内で上書きや実装しなければならないメソッドは以下の通りです:

    • loadResources - ResourceManager にこれらリソースの読み込みを指示する
    • create - entityを作成する
    • unloadResources - ResourceManager にリソースの解放を指示する (loadResourcesで読み込んだリソースを解放してください)
    • destroy - 必要な任意のクリーン処理を実行する
    • onPause - アプリケーションが一時停止された時(ホームボタンや電源ボタンが押されたり、電話がかかってきた時)に実行される処理
    • onResume - ユーザーがゲーム画面に戻ってきた時に実行される処理


    空のscene

    各scene ごとにJavaクラスを作成し、その全てでAbstractSceneを継承します。 そしてそれぞれの見た目を少し変えるために、各Sceneごとに色を変えて以下の一行を記述します:

    
      @Override
      public void create() {
        getBackground().setColor(Color.BLUE);
      }
    


    SceneManager

    最後に、scene の切り替えを制御するもう一つのシングルトンクラスが必要です。 これは少しトリッキーなもので、Androidのより高度な概念 - AsyncTasksを使用します。 詳細については別のチュートリアルで説明します。 ここでは最もシンプルな形式のAsyncTaskを使用しています。 Async タスクや Asynchronous タスクとは、別のスレッド上で動作するタスクのことです。 以下にクラスの完全な内容を記載しました。これはどんなゲームでも変わらないので、変更する必要がありません。 次のパートで説明する、共通リソースの読み込みを追加するだけです。

    重要なメソッドは以下です:
    • showSplash - これは最初のscene切り替えです。splash 画面が表示されている間に、共通リソースの読み込み(後で説明します)とメニューの読み込み・作成を行います。
    • showScene - これは最初に読み込み中のSceneを表示し、それからscene Aのリソースを読み込み解除し、scene Bのリソースを読み込んで最後にBを表示します。
    
    package is.kul.squongtutorial;
    
    import is.kul.squongtutorial.resources.ResourceManager;
    import is.kul.squongtutorial.scene.AbstractScene;
    import is.kul.squongtutorial.scene.LoadingScene;
    import is.kul.squongtutorial.scene.MenuScene;
    import is.kul.squongtutorial.scene.SplashScene;
    
    import org.andengine.util.debug.Debug;
    
    import android.os.AsyncTask;
    
    public class SceneManager {
      
      private static final SceneManager INSTANCE = new SceneManager();
      
      private ResourceManager res = ResourceManager.getInstance();
      private AbstractScene currentScene;
      private LoadingScene loadingScene;
      
      private SceneManager() {}
      
      /**
       * Shows splash screen and loads resources on background
       */
      public void showSplash() {
        Debug.i("Scene: Splash");
        final SplashScene splash = new SplashScene();
        setCurrentScene(splash);
        splash.loadResources();
        splash.create();
        res.engine.setScene(splash);
    
        new AsyncTask<Void, Void, Void>() {
    
          @Override
          protected Void doInBackground(Void... params) {
            long timestamp = System.currentTimeMillis();
            // TODO later load common resources here
            
            MenuScene menu = new MenuScene();
            menu.loadResources();
            menu.create();
            loadingScene = new LoadingScene();
            loadingScene.loadResources();
            loadingScene.create();
            // we want to show the splash at least SPLASH_DURATION miliseconds
            long elapsed = System.currentTimeMillis() - timestamp;
            if (elapsed < GameActivity.SPLASH_DURATION) {
              try {
                Thread.sleep(GameActivity.SPLASH_DURATION - elapsed);
              } catch (InterruptedException e) {
                Debug.w("This should not happen");
              }
            }
            setCurrentScene(menu);
            res.engine.setScene(menu);
            splash.destroy();
            splash.unloadResources();
            return null;
          }
        }.execute();
      }
      
      public void showScene(Class<? extends AbstractScene> sceneClazz) {
        if (sceneClazz == LoadingScene.class) {
          throw new IllegalArgumentException("You can't switch to Loading scene");
        }
    
        try {
          final AbstractScene scene = sceneClazz.newInstance();
          Debug.i("Showing scene " + scene.getClass().getName());
          
          final AbstractScene oldScene = getCurrentScene();
          setCurrentScene(loadingScene);
          res.engine.setScene(loadingScene);
          new AsyncTask<Void, Void, Void>() {
    
            @Override
            protected Void doInBackground(Void... params) {
              if (oldScene != null) {
                oldScene.destroy();
                oldScene.unloadResources();
              }
              scene.loadResources();
              scene.create();
              setCurrentScene(scene);
              res.engine.setScene(scene);
              return null;
            }
          }.execute();
        } catch (Exception e) {
          String message = "Error while changing scene";
          Debug.e(message, e);
          throw new RuntimeException(message, e);
        }
      }
     
      public static SceneManager getInstance() {
        return INSTANCE;
      }
      public AbstractScene getCurrentScene() {
        return currentScene;
      }
      private void setCurrentScene(AbstractScene currentScene) {
        this.currentScene = currentScene;
      }
    }
    
    
    

    このsplash は少なくともSPLASH_DURATION ミリ秒表示されるので注意してください。 基本的にはこの時間を設定してユーザーがあなたのバッジを(3-4秒)視認できるようにします。 読み込みに時間がかかる場合は、リソースが読み込まれてメニューScene の準備ができるまでこのSceneManager はSplash を表示し続けます。 GameActivityに以下を追加してください:

    
      @Override
      public void onPopulateScene(Scene pScene,
          OnPopulateSceneCallback pOnPopulateSceneCallback)
          throws IOException {
        SceneManager.getInstance().showSplash();
        pOnPopulateSceneCallback.onPopulateSceneFinished();
      }
    
      @Override
      public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
          try {
            SceneManager.getInstance().getCurrentScene().onBackKeyPressed();
          } catch (NullPointerException ne) {
            // in highly unlikely situation when this there is no scene, do nothing
            Debug.e("The current scene is null", ne);
          }
        }
        return false;
      }
    

    これで準備ができました。このアプリを実行できます。 splash sceneがSPLASH_DURATION ミリ秒間表示された後、メニューsceneになります。 それぞれのsceneで背景色が違うことを確認してください! また、バックキーを押してLogCatを見てください。

    メモ: The AsyncTask には多くの利点があります。例えば、タスクがバックグラウンドで動作している間に表示中のsceneでアニメーションを行うことができます。 タスクから進捗状況についての情報を送って表示することもできます。



    現在のコード

    あなたのプロジェクトは現時点でこのようになっているでしょう。どこか違っている場合はダウンロードをしてください。



    次のパート

    次のチャプターでは、 Splash Sceneとリソースの読み込み・解放について説明します。