суббота, 10 марта 2012 г.

Android. Создание многоэкранных игр на AndEngine

Перевод инструкции с форума AndEngine, источник

От переводчика: Движок AndEngine ушел в своём развитии несколько вперед от оригинала статьи, здесь я его адаптировал к текущим реалиям

Примечание: в этой инструкции, "Экран" по существу относится к странице в игре, например, экран "Меню". Каждый "Экран" основан на собственной сцене. Не стоит путать с физическим экраном телефона.

Привет,

Многие жалуются, что в моя старая инструкция по созданию многоэкранных игр устарела, и поэтому я написал новую. В отличие от старой, в этой будет акцентировано внимание только на основных особенностях создания и работы с несколькими экранами. Поэтому, программы основанные на этой статье не будет очень эффективным на практике, так как разные страницы могут занимать значительное время при загрузке, что сделает Ваше приложение неприятным в использовании. Тем не менее, загрузка ваших экранов в фоне не особо сложная задача, я включил ссылки и советы по разработке такой системы в конце этого урока.

Как всегда, я не утверждаю, что метод предложенный в этом уроке является лучшим решением, однако это работает, и я надеюсь, вы найдете статью полезной. Исходники полного проекта можно найти в приложении к этой статье.

Наш базовый класс

Прежде всего мы должны создать базовую Активити, которая служит основой нашего приложения. Я предполагаю, что вы знаете, основные компоненты AndEngine, в этом случае первый вариант кода должен выглядеть очень знакомо для вас.

import org.andengine.entity.util.FPSLogger;
import org.andengine.engine.Engine;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.camera.ZoomCamera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.scene.Scene;
import org.andengine.ui.activity.BaseGameActivity;
import org.andengine.ui.activity.SimpleBaseGameActivity;

import android.view.KeyEvent; 
     public class MultiScreen extends BaseGameActivity { 
 private static final int CAMERA_WIDTH = 800;
 private static final int CAMERA_HEIGHT = 480; 
 
 private ZoomCamera camera;
 
 @Override
 public EngineOptions onCreateEngineOptions() {
  this.camera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);

  return new EngineOptions(
     true                                                          // fullscreen
      , ScreenOrientation.LANDSCAPE_FIXED                             // горизонтальная ориентация
      , new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT)
      , this.camera                                                   // основная камера для движка
  );
 }

 @Override
 protected void onCreateResources() {
  SceneManager.init(this);
  // запуск метода загрузки из нашего первого экрана,
  // загрузка всех ресурсов, которые необходимы на данный момент
  FirstScreen.load();
 }

 @Override
 protected Scene onCreateScene() {
  this.mEngine.registerUpdateHandler(new FPSLogger());
  return FirstScreen.run();
 }
}
Здесь стоит обратить внимание на три небольших фрагмента кода, которые мы использовали в этом базовом классе. В методе onLoadResources(), мы прежде всего должны инициализировать SceneManager, который является вспомогательным классом, и который мы будем использовать в дальнейшем для замены сцены и загрузки в нее ресурсов. Когда мы инициализируем SceneManager мы передает контекст, с которым мы работаем - SceneManager.init(this). Это позволяет SceneManager получить доступ к функциям для загрузки сцены и загрузки ресурсов, как вы увидите позже.

После инициализации SceneManager, мы вызываем FirstScreen.load(), который готовит сцену и загружает необходимые ресурсы. Мы будем использовать подобные макеты класса для первого и второго экранов, каждый из которых будет иметь методы load() и run(). Последнее, что нужно cделать в нашем базовом классе - это вызов run() нашего первого экрана в методе onLoadScene(). Это возвратит нашу первую сцену.

Создание SceneManager

SceneManager это служебный класс, который мы будем использовать для переключения между различными сценами. Как уже говорилось ранее, SceneManager имеет доступ к базовым классам, который необходим для того, чтобы иметь возможность переключать сцены. Для удобства в этом уроке, мы также будем использовать класс, чтобы загрузить ресурсы приложения.



import org.andengine.entity.scene.Scene;
import org.andengine.opengl.font.Font;
import org.andengine.opengl.texture.Texture;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory; 
     public class SceneManager {

 private static MultiScreen core;               // здесь наша базовая активити
 
 public static void init(MultiScreen base) {
  core = base;
  BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
 }
 
 /**
  * setScene() это функция при помощи которой мы будем переключаться
  * с одного экрана на другой.
  */
 public static void setScene(Scene scene) {
  core.getEngine().setScene(scene);
 }
 
 public static void loadTexture(Texture texture) {
  core.getEngine().getTextureManager().loadTexture(texture);
 }
 
 public static void loadFont(Font font) {
  core.getEngine().getFontManager().loadFont(font);
 }
}


Этот код должен быть понятным и, я надеюсь, вы не увидите здесь ничего, что вы раньше не видели. Этот класс позволяет нам перейти от одной сцены к другой (например, от экрана меню к игровому экрану). Когда позже мы создадим различные экраны, мы можем просто использовать SceneManager.setScene(AnotherScreen.run ()) для переключения на новый экран.

Теперь пришло время для создания двух экранов, которые имеются в нашей игре.

FirstScreen

Оба экрана в нашей игре будут иметь очень похожую структуру. Хоть я здесь не сделал этого, для простоты урока, но вы можете создать интерфейс или абстрактный класс, который можно будет использовать для каждого из ваших экранов.

Надеюсь, в этом классе для вас будет знакома каждая строчка кода.


public class FirstScreen {
 
 private static Scene scene;
 private static Text text;
 
 /**
  * Загрузка сцены и всех необходимых ресурсов
  */
 public static void load() {
     scene = new Scene();
     scene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
    
     BitmapTextureAtlas fontTexture = new BitmapTextureAtlas(256, 256, TextureOptions.BILINEAR);
     Font font = new Font(fontTexture, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 32, true, Color.BLACK);
     SceneManager.loadFont(font);
     SceneManager.loadTexture(fontTexture);
    
     text = new Text(
        100, 60
      , font
      , "First Scene"
      , new TextOptions(HorizontalAlign.CENTER)
      , new VertexBufferObjectManager()
    );
    scene.attachChild(text); 
    
    // Here we register a simple touch listener.
    // This is so that we can switch to the second screen when 
    // the user taps our first screen.

    scene.setOnSceneTouchListener( new IOnSceneTouchListener() {
        public boolean onSceneTouchEvent(final Scene scene, final TouchEvent touchEvent) {
            // load the assets for the second screen
            SecondScene.load();
            // switch to the second scene
            SceneManager.setScene(SecondScene.run());
            return true;
        }
    });
 }
 
 /**
  * Return the scene for when the scene is called to become active.
  */
 public static Scene run()
 {
  return scene;
 }
 
 /**
  * Unload any assets here - to be called later.
  */
 public static void unload()
 {
  
 }
}

Вы должны довольно быстро понять, что здесь происходит. Прежде всего, наш метод загрузки создает сцену, задает цвет фона и добавляет текст. Затем мы добавляем слушателя нажатия на экран для перехода на вторую сцену. There isn’t too much else to cover here, as all we’re really doing is constructing the scene as normal.
Метод Run(), как вы помните, мы вызываем когда сцена загружена и мы хотим показать её на экране. Как видно выше, вызывая метод run в качестве параметра для SceneManager.setScene(), можно легко отобразить сценy.

Последний метод, который я включил в оба класса
экранов, unload(). Как вы можете догадаться, это метод, в котором вы можете выгрузить любые ресурсы, которые больше не требуются. Например, при переходе от вашей игры в главное меню, скорее всего вам больше не нужно часть ресурсов. Поэтому, как только вы перешли на другой экран, вы можете вызвать метод unload(). Я не включил код unload, т.к. это выходит за рамки данного руководства, однако примеры для выгрузки ресурсов можно легко найти. 

Второй, и последний экраны

Наш второй экран очень похож на первый

public class SecondScene {
 
 private static Scene scene;
 
 /**
  * Load the scene and any assets we need.
  */
 public static void load()
 {
   scene = new Scene();
   scene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
   
   BitmapTextureAtlas fontTexture = new BitmapTextureAtlas(256, 256, TextureOptions.BILINEAR);
   Font font = new Font(fontTexture, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 32, true, Color.BLACK);
   
   // note the font/font texture are being loaded
   // for the second time. In practice, it would be best to have
   // some kind of 'GenericAsset' object to handle assets you'll
   // use throughout the application.
   
   SceneManager.loadFont(font);
   SceneManager.loadTexture(fontTexture);
   
   Text text = new Text(
      100
       , 60
       , font
       , "Second Scene"
       , new TextOptions(HorizontalAlign.CENTER)
       , new VertexBufferObjectManager()
   );
   scene.attachChild(text);
 }
 
 /**
  * Return the scene for when the scene is called to become active.
  */
 public static Scene run()
 {
  // now would be a good time to unload all the assets used in FirstScreen
  FirstScreen.unload();
  return scene;
 }
 
 /**
  * Unload any assets here - to be called later.
  */
 public static void unload()
 {
  
 }
}


Как вы можете видеть, здесь не так уж много нового. Одним заметным изменением является то, что мы назвали unload() первого экрана в методе run(). Это делается для того чтобы, выгрузить ресурсы, которые нам больше не нужны. В совокупности, при помощи этих 4-х классов, у вас уже есть простое приложение, которое можно использовать чтобы перемещаться с первого "Экрана" на второй, с помощью простой системы, основанной на сценах. 

Как развивать эту систему в дальнейшем

Как я уже говорил, это руководство учит только фундаментальным основам создания мультиэкранной игры, и я думаю, что дал вам по крайней мере несколько идей о том, как можно развивать его дальше.
 


Предварительная загрузка

Вы скорее всего захотите осуществлять предварительную загрузку ресурсов, прежде чем получить возможность переключения сцен, для того, чтобы иметь возможность переключать сцены быстрее. Rev.Tyler уже написал большой учебник по загрузке ресурсов в фоновом режиме, с помощью которого можно легко создать загрузочный экран. Ну или мой перевод этой статьи.


Управление ресурсами

Как вы могли заметить, шрифт и текстуры, используемые в данном руководстве на самом деле подгружаются дважды (я записал это в комментариях). Этого вы определенно постараетесь обойти на практике, и один из способов - оформить общий класс ресурсов, с помощью которого можно хранить простые ресурсы (шрифты и т.д.), к которым вы будете получать доступ из экранов.
Когда дело доходит до обработки экранами конкретных ресурсов, оформите свой собственный тип класса AssetHandler что сделает загрузку и выгрузку ресурсов легче.


Исходники приложения

Комментариев нет: