четверг, 22 марта 2012 г.

Техподдержка в TRANSAERO

Заказывал авиабилеты через А/К Transaero. Они очень удачно и бюджетно выполняют рейсы Москва-Франция/Лион, куда я собственно собираюсь. Так уж получилось, что выбрал неправильную дату обратного рейса. Обратные тикеты тут же отменил через форму на сайте. Практически моментально позвонили из А/К, спросили причину отмены и предложили просто изменить даты. Не потерял на обмене ни копейки.
Собственно, ничего особенного, но масса положительных впечатлений. Не только отрицательные отзывы же писать :)

пятница, 16 марта 2012 г.

Andengine. Обновление объектов/спрайтов

Оригинал статьи

Это руководство по применение обработчиков обновления спрайтов/объектом. Оно охватывает следующие моменты:
1. Создание спрайта
2. Подключение слушателя к спрайтам/объектам, которые могут быть обновлены
3. Обработка нажатий на спрайты

Итак, приступим. Во-первых нам нужно будет создать новый спрайт. Убедитесь, что у вас есть текстуры и установите TextureRegion, а затем создайте следующую функцию в своём классе:

private void createSprite()
{
 final Sprite sprite = new Sprite(100, 100, mYourSpritesTextureRegion);
 this.mEngine.getScene().getTopLayer().addEntity(sprite);
}

Не забудьте изменить имя TextureRegion mYourSpritesTextureRegion на то, которое вы используете для своего спрайта!
После этого, определите метод onLoadScene() и протестируйте выше приложение. Вы должны увидеть спрайт на вашем экране в точке X=100 и Y=100.

Теперь мы хотим получить возможность что-нибудь сделать с нашим спрайтом. Давайте сделаем изменение положения спрайта при его перетаскивании. Для этого необходимо изменить метод createSprite(), добавить в него слушателя, который будет вызываться при нажатии. Вот обновленный код:

private void createSprite()
{
 final Scene scene = this.mEngine.getScene();
 
 final Sprite sprite = new Sprite(100, 100, mYourSpritesTextureRegion) {
  @Override
  public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float pTouchAreaLocalX, float pTouchAreaLocalY) {
   
  }
 }; 
 scene.registerTouchArea(sprite);
 
 scene.getTopLayer().addEntity(sprite);
}
 
Что мы сделали, так это добавили IOnAreaTouchedListener на спрайт и зарегистрировали на сцене. Теперь, когда мы нажимаем на спрайт, будет вызван метод onAreaTouched() и мы можем управлять в этом методе обновлением спрайта.
Для того, чтобы спрайт двигался за пальцем, когда вы его касаетесь, необходимо добавить следующий код в метод onAreaTouched():

sprite.setPosition(pSceneTouchEvent.getX(), pSceneTouchEvent.getY());

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

sprite.setPosition(pSceneTouchEvent.getX() - this.getWidth()/2, pSceneTouchEvent.getY() - this.getHeight()/2);

Снова запустите приложение, теперь вы должны получить спрайт, послушно бегающий за вашим пальцем. 

Всё это можно оформить в виде отдельного класса MySprite унаследованного от Sprite, перегрузив его методы:

private class MySprite extends Sprite {
  
    public MySprite(float pX, float pY, TextureRegion pTextureRegion) {
        super(pX, pY, pTextureRegion);
 // TODO Auto-generated constructor stub
    }

    @Override
    protected void onManagedUpdate(float pSecondsElapsed) {
        // TODO Auto-generated method stub
 super.onManagedUpdate(pSecondsElapsed);
    }

    @Override
    public boolean onAreaTouched(TouchEvent pSceneTouchEvent) {
        setPosition(pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
 return true;
    }
}

И создавать спрайт следующим образом:
    final MySprite mySprite = new MySprite(100, 100, texReg);
    scene.registerTouchArea(mySprite);
    scene.getTopLayer().addEntity(mySprite);
 
 

четверг, 15 марта 2012 г.

Andengine. Работа с большими спрайтами

Возникла необходимость загружать большую картинку карты мира (4740х2600) с возможностью её скроллить и зуммировать. Немного погуглив, остановился на решении, предложенным в этом топике. А именно - разбить её на несколько частей а затем загружать отдельными спрайтами:


 private ITextureRegion mWorldMapTextureRegionSW;
 private ITextureRegion mWorldMapTextureRegionSE;
 private ITextureRegion mWorldMapTextureRegionNW;
 private ITextureRegion mWorldMapTextureRegionNE;

 @Override
 public void onCreateResources() {
                //
                // Указываем путь до графики. В данном случае графика будет загружаться из папки assets/gfx/
                //
  BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
  
  // карта мира
  this.mBitmapTextureAtlasWorldMapSW = new BitmapTextureAtlas(2370, 1300, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  this.mBitmapTextureAtlasWorldMapSE = new BitmapTextureAtlas(2370, 1300, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  this.mBitmapTextureAtlasWorldMapNE = new BitmapTextureAtlas(2370, 1300, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  this.mBitmapTextureAtlasWorldMapNW = new BitmapTextureAtlas(2370, 1300, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  
  this.mWorldMapTextureRegionSW = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mBitmapTextureAtlasWorldMapSW, this, "world_map_sw.png", 0, 0);
  this.mWorldMapTextureRegionSE = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mBitmapTextureAtlasWorldMapSE, this, "world_map_se.png", 0, 0);
  this.mWorldMapTextureRegionNW = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mBitmapTextureAtlasWorldMapNW, this, "world_map_nw.png", 0, 0);
  this.mWorldMapTextureRegionNE = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mBitmapTextureAtlasWorldMapNE, this, "world_map_ne.png", 0, 0);  
  
  this.mBitmapTextureAtlasWorldMapSW.load(this.getTextureManager());
  this.mBitmapTextureAtlasWorldMapSE.load(this.getTextureManager());
  this.mBitmapTextureAtlasWorldMapNW.load(this.getTextureManager());
  this.mBitmapTextureAtlasWorldMapNE.load(this.getTextureManager());
  
  
 } // onCreateResources

 @Override
 public Scene onCreateScene() {
  
  this.mScene = new Scene();
  
  this.mEngine.registerUpdateHandler(new FPSLogger());
                
                // полные размеры картинки 
  final float worldMapWidth = this.mWorldMapTextureRegionNW.getWidth() * 2;
  final float worldMapHeight = this.mWorldMapTextureRegionNW.getHeight() * 2;

  /* Вычисляем координаты левого верхнего угла первой картинки */  
  final float centerX = (CAMERA_WIDTH - worldMapWidth ) / 2;
  final float centerY = (CAMERA_HEIGHT - worldMapHeight) / 2;

  this.worldMapNW = new Sprite(centerX, centerY, this.mWorldMapTextureRegionNW, this.getVertexBufferObjectManager());
  this.worldMapNE = new Sprite(centerX + this.mWorldMapTextureRegionNW.getWidth(), centerY, this.mWorldMapTextureRegionNE, this.getVertexBufferObjectManager());
  this.worldMapSW = new Sprite(centerX, centerY + this.mWorldMapTextureRegionNW.getHeight(), this.mWorldMapTextureRegionSW, this.getVertexBufferObjectManager());
  this.worldMapSE = new Sprite(centerX + this.mWorldMapTextureRegionNW.getWidth(), centerY + this.mWorldMapTextureRegionNW.getHeight(), this.mWorldMapTextureRegionSE, this.getVertexBufferObjectManager());
  
                // добавляем спрайты на сцену
  this.mScene.attachChild( this.worldMapSW );
  this.mScene.attachChild( this.worldMapSE );
  this.mScene.attachChild( this.worldMapNW );
  this.mScene.attachChild( this.worldMapNE );
  
  return mScene;
 } 
 
 
 
Опытным путем было выяснено, что на части устройств изображения больше 2000 пикселей по любой стороне не подгружаются или при попытке загрузить вылетают с ошибкой, на некоторых - всё нормально. Поэтому лучше бить на куски менее 2000.

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

среда, 14 марта 2012 г.

Andengine. HUD. Интерфейс игрового экрана

Введение

HUD происходит от сокращения Head-Up Display и представляет собой все те части пользовательского интерфейса, которые имеют фиксированное положение на экране. В игре это обычно счетчики жизни, боеприпасов, карта уровня, номер уровня и т.д. Andengine имеет легкую встроенную поддержку HUD элементов управления на экране.

Создание экранных элементов управления

Прежде всего, как и в любом andengine приложении, необходимо создать сцену

Scene scene = new Scene();

Затем мы должны создать элементы управления с помощью метода AnalogOnScreenControl

 analog = new AnalogOnScreenControl(0, padY, game.camera,
 TextureManager.mOnScreenControlBaseTextureRegion,
 TextureManager.mOnScreenControlKnobTextureRegion, 0.3f, 200,
 new IAnalogOnScreenControlListener() {
 @Override
 public void onControlChange(final BaseOnScreenControl pBaseOnScreenControl,
 final float pValueX, final float pValueY) {
 //TODO your analog movement control events
 }
 @Override
 public void onControlClick(final AnalogOnScreenControl pAnalogOnScreenControl) {
 //TODO your onClick controll events
    } 
});

Если необходимо, можно изменить формат основного изображения или кнопок при помощи методов подкласса Entity


analog.getControlBase().setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
 analog.getControlBase().setAlpha(0.65f);
 analog.getControlKnob().setAlpha(0.65f);
 analog.getControlKnob().setScale(scale);
 analog.getControlBase().setScaleCenter(0, 0);
 analog.getControlBase().setScale(scale);
 analog.refreshControlKnobPosition();

Последний шаг - добавление элементов на нашу сцену.

scene.setChildScene(analog);

Примечание: мы добавляем новые элементы сцены. Они будут фиксированы и не будут скроллироваться вашей камерой

Удаление элементов управления

Удаляем элементы управления аналогично удалению объекта со сцены:

mEngine.runOnUpdateThread(new Runnable() {
 
  @Override
  public void run() {
   mCamera.getHUD().detachSelf();
  }
});

Andengine. Использование таймеров - пример создания спрайтов

Оригинал статьи: http://www.andengine.org/forums/tutorials/using-timer-s-sprite-spawn-example-t463.html

Поговорим о таймерах и как вы можете использовать их в игре.

Итак, что такое таймер? В общем, вы можете создать объект типа таймер, чтобы ваша игра выполняла определенные действия через определенные промежутки времени. При регистрации этого обработчика в AndEngine, он будет выполнять callback функцию один раз в указанное время.

Итак, где мы можем использовать такую ​​функциональность? Ну я использую это в моем
Explosion Live Wallpaper (можете загрузить и посмотреть их совершенно бесплатно), чтобы создавать взрывы в определенные промежутки времени, которые регулирует пользователь. Мне поступило предложение создать эту статью, чтобы показать вам, как можно создавать новые спрайты на экране, по событиям таймера  с помощью его callback вызова. Итак, приступим!

Прежде всего, создаем основной метод, создающий для нас спрайты:

/**
 * Создает спрайт в определенном месте экрана
 * @param pX координата X вашего спрайта
 * @param pY координата Y вашего спрайта
 */
private void createSprite(float pX, float pY) {
 Sprite sprite = new Sprite(pX, pY, this.mSpriteTextureRegion);
 this.mEngine.getScene().getTopLayer().addEntity(sprite);
}

Хорошо, теперь пишем метод, который создает наш обработчик таймера и регистрирует его:

/**
 * Создание хэндлера таймера, используется для создания спрайтов
 */
private void createSpriteSpawnTimeHandler()
{
 TimerHandler spriteTimerHandler;
 
 this.getEngine().registerUpdateHandler(spriteTimerHandler = new TimerHandler(mEffectSpawnDelay, new ITimerCallback()
 {   
     @Override
     public void onTimePassed(final TimerHandler pTimerHandler)
     {

     }
 }));
}
Посмотрите на код выше. Он довольно простой. Все, что он делает - это создает новый TimerHandler и регистрирует его. TimerHandler принимает на вход два параметра. Первый параметр, pTimerSeconds, определяет период времени, через который TimerHanlder вызывает callback функцию. Второй параметрITimerCallbak, определяет callback функцию.

Итак, как теперь создать спрайт? Нужно изменить  метод createSpriteSpawnTimeHandler() следующим образом:


/**
 * Создание хэндлера таймера, используется для создания спрайтов
 */
private void createSpriteSpawnTimeHandler()
{
 TimerHandler spriteTimerHandler;
 
 this.getEngine().registerUpdateHandler(spriteTimerHandler = new TimerHandler(mEffectSpawnDelay, new ITimerCallback()
 {   
     @Override
     public void onTimePassed(final TimerHandler pTimerHandler)
     {      
      //Random Position Generator
      final float xPos = MathUtils.random(30.0f, (CAMERA_WIDTH - 30.0f));
  final float yPos = MathUtils.random(30.0f, (CAMERA_HEIGHT - 30.0f));
   
  createSprite(xPos, yPos);
     }
 }));
}
Если вы сейчас запустите таймер, вы увидите как создается спрайт через заданный промежуток времени, который задан в TimerHandler. Но сейчас таймер срабатывает только один раз. Что нужно сделать чтобы спрайты создавались постоянно? Для этого нужно каждый раз сбрасывать TimerHandler. Законченный код выглядит следующим образом:



/**
 * Создание хэндлера таймера, используется для создания спрайтов
 */
private void createSpriteSpawnTimeHandler()
{
 TimerHandler spriteTimerHandler;
 
 this.getEngine().registerUpdateHandler(spriteTimerHandler = new TimerHandler(mEffectSpawnDelay, new ITimerCallback()
 {   
     @Override
     public void onTimePassed(final TimerHandler pTimerHandler)
     {
      spriteTimerHandler.reset();
      
      //Random Position Generator
      final float xPos = MathUtils.random(30.0f, (CAMERA_WIDTH - 30.0f));
   final float yPos = MathUtils.random(30.0f, (CAMERA_HEIGHT - 30.0f));
   
   createSprite(xPos, yPos);
     }
 }));
}


И это всё! Добавьте эти два метода в Ваше приложение, измените их по своему усмотрению, а затем вызовите createSpawnTimerHandler() из метода onLoadScene(). И вы сможете в вашем приложении создавать спрайты через определенные промежутки времени.

Android. Andengine. Разработка под различные разрешения экрана

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

public static final int CAMERA_WIDTH = 720;
public static final int CAMERA_HEIGHT = 480;

и инициализации EngineOptions в явном виде

EngineOptions options = new EngineOptions(
      true
    , ScreenOrientation.LANDSCAPE
    , new RatioResolutionPolicy( CAMERA_WIDTH, CAMERA_HEIGHT )
    , mCamera 
);

пишем функцию:

private float getScreenResolutionRatio() {
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);

        return ((float) dm.widthPixels) / ((float) dm.heightPixels);
}

И соответственно инициализация EngineOptions принимает вид похожий на следующий:

new EngineOptions(
              true                                                        
            , ScreenOrientation.LANDSCAPE_FIXED                            
            , new RatioResolutionPolicy( getScreenResolutionRatio() )
            , mCamera                                                          
);

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

Android. AndEngine. Загрузка ресурсов в фоне

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

Первое, что вам нужно сделать, это создать свою собственную реализацию AsyncTask.





Путь, который описан в Android API - это создание AsyncTask с параметром и использование некоторых действий в самом классе. Я предпочитаю, чтобы он был более гибким, поэтому я создал callback функцию, которую будет вызывать  AsyncTask при необходимости. Таким образом:


public interface IAsyncCallback {
    // ===========================================================
    // Методы
    // ===========================================================
 
    public abstract void workToDo();
 
    public abstract void onComplete();
 
}
Затем я создаю свой подкласс AsyncTask:

import android.os.AsyncTask;
 
public class AsyncTaskLoader extends AsyncTask<IAsyncCallback, Integer, Boolean> {
 
    // ===========================================================
    // Свойства
    // ===========================================================
 
    IAsyncCallback[] _params;
 
    // ===========================================================
    // Наследуемые методы
    // ===========================================================
 
    @Override
    protected Boolean doInBackground(IAsyncCallback... params) {
        this._params = params;
        int count = params.length;
        for(int i = 0; i < count; i++){
            params[i].workToDo();
        }
        return true;
    }
 
    @Override
    protected void onPostExecute(Boolean result) {
        int count = this._params.length;
        for(int i = 0; i < count; i++){
            this._params[i].onComplete();
        }
    }
}


Итак, теперь у меня есть AsyncTask готовый сделать то что я захочу

Вот моя загрузка Activity:

import static org.anddev.andengine.extension.physics.box2d.util.constants.PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
 
import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.extension.physics.box2d.FixedStepPhysicsWorld;
import org.anddev.andengine.extension.physics.box2d.PhysicsConnector;
import org.anddev.andengine.extension.physics.box2d.PhysicsFactory;
import org.anddev.andengine.extension.physics.box2d.PhysicsWorld;
import org.anddev.andengine.opengl.texture.Texture;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.texture.region.TextureRegionFactory;
import org.anddev.andengine.ui.activity.BaseGameActivity;
 
import android.view.Display;
import android.widget.RelativeLayout;
 
import com.adwhirl.AdWhirlLayout;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.revstudios.codestores.AsyncTaskLoader;
import com.revstudios.codestores.IAsyncCallback;
 
/**
 * Written by Tyler Reid of Studio Reveries.
 * 
 * All work provided can be modified and used freely so long as this statement stays in the source code.
 * 
 * 
 * @author Tyler Reid @ www.studioreveries.com
 *
 */
public abstract class LoadingGameActivity extends BaseGameActivity {
 
 // ===========================================================
 // Свойства
 // ===========================================================
 
 protected Engine engine;
 protected PhysicsWorld physEngine;
 
 private Texture loadScreenBGTexture;
 private TextureRegion loadScreenBGRegion;
 
 private Texture carTexture;
 private TextureRegion carRegion;
 
 private ArrayList<Body> loadingBodies = new ArrayList<Body>();
 
 // ===========================================================
 // Константы
 // ===========================================================
 
 protected static final float CAMERA_WIDTH = 320, CAMERA_HEIGHT = 480;
 protected static final int AD_SIZE_Y = 50;
 protected static final int PHYSICS_TIME_STEP = 60;
 
 
 // ===========================================================
 // Наследуемые методы
 // ===========================================================
 
 @Override
 public Engine onLoadEngine() {
  Camera camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
  return engine = new Engine(new EngineOptions(true, ScreenOrientation.PORTRAIT, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera));
 }
 
 @Override
 public void onLoadResources() {
  TextureRegionFactory.setAssetBasePath("gfx/load/");
 
  loadScreenBGTexture = new Texture(512, 256, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  loadScreenBGRegion = TextureRegionFactory.createFromAsset(loadScreenBGTexture, this, "logo.png", 0, 0);
 
  carTexture = new Texture(128,128);
  carRegion = TextureRegionFactory.createFromAsset(carTexture, this, "car.png", 0, 0);
 
 
  engine.getTextureManager().loadTextures(loadScreenBGTexture, carTexture);
 }
 
 /**
   * Здесь вы можете создать свой экран загрузки. Я создал свой мир физики 
     * чтобы продемонстрировать что вы можете сделать тут.
   */
 @Override
 public Scene onLoadScene() {
  Scene scene = new Scene(1);
  //scene.setBackground(new ColorBackground(10, 200, 10));
 
  physEngine = new FixedStepPhysicsWorld((int)PHYSICS_TIME_STEP, new Vector2(0, 0), false);
  scene.registerUpdateHandler(physEngine);
 
  // отображаем логотип загрузки
  Sprite s = new Sprite(100, 100, loadScreenBGRegion);
  s.setPosition((CAMERA_WIDTH / 2) - (s.getWidthScaled() / 2), (CAMERA_HEIGHT / 3) - (s.getHeightScaled() / 2));
  scene.getBottomLayer().addEntity(s);
 
  // car fixturedef
  FixtureDef carDef = PhysicsFactory.createFixtureDef(1, 3, 0);
 
  // create initial car
  Sprite queCar = new Sprite((CAMERA_WIDTH / 5) * 1, (CAMERA_HEIGHT / 3) * 2, carRegion);
  scene.getTopLayer().addEntity(queCar);
 
  Body queCarBody = PhysicsFactory.createCircleBody(physEngine, queCar, BodyType.DynamicBody, carDef);
  queCarBody.CollisionType = TrafConst.CollisionMisc;
 
  physEngine.registerPhysicsConnector(new PhysicsConnector(queCar, queCarBody));
 
  // create second car
  Sprite ballCar = new Sprite((CAMERA_WIDTH / 5) * 3, (CAMERA_HEIGHT / 3) * 2, carRegion);
  scene.getTopLayer().addEntity(ballCar);
 
  Body ballCarBody = PhysicsFactory.createCircleBody(physEngine, ballCar, BodyType.DynamicBody, carDef);
  ballCarBody.CollisionType = TrafConst.CollisionMisc;
 
  physEngine.registerPhysicsConnector(new PhysicsConnector(ballCar, ballCarBody));
 
  // impulse que car
  queCarBody.setLinearVelocity(new Vector2(100 / PIXEL_TO_METER_RATIO_DEFAULT,0));
 
  /*
   * Это вызывается когда все ресурсы будут загружены в фоне
   */
  IAsyncCallback callback = new IAsyncCallback() {
 
   @Override
   public void workToDo() {
    assetsToLoad();
   }
 
   @Override
   public void onComplete() {
    unloadLoadingScene();
    engine.setScene(onAssetsLoaded());
   }
  };
 
  new AsyncTaskLoader().execute(callback);
 
  return scene;
 }
 
 // ===========================================================
 // Методы
 // ===========================================================
 
 private void unloadLoadingScene(){
 
 }
 
 /**
  * Это вызывается после того как все asyc ресурсы загружены
  * Загрузчик будет отвечать за изменения сцены.
  */
 protected abstract Scene onAssetsLoaded();
 
 
 /**
  * Это вызывается когда ресурсы должны быть загружены в фоне.
  */
 protected abstract void assetsToLoad();
 
}
Это будет базовая активити для любой, имеющей много ресурсов, которые должны быть предварительно загружены (либо подгружаются при отображении экрана загрузки).

Вот простой пример активити, которая позволяет расширить данную. Есть ряд важных особенностей, которые освещены в комментариях. Читайте внимательно.

import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.extension.physics.box2d.FixedStepPhysicsWorld;
import org.anddev.andengine.opengl.texture.Texture;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.texture.region.TextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
 
import com.badlogic.gdx.math.Vector2;
import com.revstuidios.trafficjam.LayoutLoadingGameActivity;
import com.revstuidios.trafficjam.PedalHUD;
import com.revstuidios.trafficjam.PedalHUD.IPedalListener;
import com.revstuidios.trafficjam.sprites.PlayerCarSprite;
 
public class GameActivity extends LayoutLoadingGameActivity {
 
 // ===========================================================
 // Свойства
 // ===========================================================
 
 Texture playerTexture;
 TiledTextureRegion playerTiledRegion;
 
 Texture hudTexture;
 TextureRegion brakeRegion, gasRegion, bgRegion;
 
 PlayerCarSprite Player;
 
 
 // ===========================================================
 // Константы
 // ===========================================================
 
 // ===========================================================
 // Наследуемые методы
 // ===========================================================
 
 @Override
 public Engine onLoadEngine() {
  // Это необходимая заглушка! Если здесь этого не будет, ваш экран загрузки не будет работать
  // This is all you will ever put in to your loading activity.
  return super.onLoadEngine();
 }
 
 @Override
 public void onLoadResources() {
  // Это необходимая заглушка! Если здесь этого не будет, ваш экран загрузки не будет работать
  // This is all you will ever put in to your loading activity.
  super.onLoadResources();
 }
 
 @Override
 public Scene onLoadScene() {
  // Это необходимая заглушка! Если здесь этого не будет, ваш экран загрузки не будет работать
  // This is all you will ever put in to your loading activity.
  return super.onLoadScene();
 }
 
 @Override
 public void onLoadComplete() {
  // Ничего здесь не помещайте
 }
 
 @Override
 protected Scene onAssetsLoaded() {
  // Здесь вы можете написать все, что вы обычно помещаете в onLoadScene.
  // Это вызывается когда все ресурсы будут загружены
  Scene scene = new Scene(3);
 
  Sprite x = new Sprite(100,100, brakeRegion);
  scene.getBottomLayer().addEntity(x);
 
  return scene;
 }
 
 @Override
 protected void assetsToLoad() {
  // здесь вы загружаете любые ресурсы, которые вы хотите грузить в фоне.
 
  TextureRegionFactory.setAssetBasePath("gfx/game/");
 
  playerTexture = new Texture(64,128);
  playerTiledRegion = TextureRegionFactory.createTiledFromAsset(playerTexture, this, "car.png", 0, 0, 2, 2);
 
  hudTexture = new Texture(512,128);
  bgRegion = TextureRegionFactory.createFromAsset(hudTexture, this, "pedalsBG.png", 0, 0);
  brakeRegion = TextureRegionFactory.createFromAsset(hudTexture, this, "brake.png", 341, 0);
  gasRegion = TextureRegionFactory.createFromAsset(hudTexture, this, "gas.png", 399, 0);
 
  engine.getTextureManager().loadTextures(playerTexture, hudTexture);
 }
 
 // ===========================================================
 // Методы
 // ===========================================================
}

Чтобы использовать данный код, просто создайте свою активити. Экран загрузки будет отображаться автоматически. Будет запускаться onAssetsLoaded() и возвращаемая им сцена станет основной.

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 что сделает загрузку и выгрузку ресурсов легче.


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

пятница, 9 марта 2012 г.

Прошел ТО в AVIS-MOTORS

Очередное ТО-30000 решил по традиции сделать в AVIS-MOTORS. И после обслуживания зарекся ездить туда еще когда-либо (может конечно эти яркие впечатления отойдут, сервис-то нормальный...).
Начнем с того, что записывался по e-mail и задал несколько насущных вопросов - а именно можно ли обслужиться на первом или втором подъемнике, потому что если во 2-й еще можно кое-как пропихнуться, то на 3-й нужно сделать неимоверное количество лишних телодвижений. И возможно ли заменить передний габарит. Ни на один вопрос ответа я не дождался, дождался только предложения подъехать.
Михайловский проезд, где обосновался AVIS, это очень прикольная улица. Подъезжая за 20 минут до назначенного времени, я умудрился напороться на пробку за 30 метров до заветного шлагбаума. Какие-то товарищи загружали фургон и перегородили им половину Михайловского проезда. Соответственно выезжающие и въезжающие в этот суженный проездик упёрлись друг в друга и получилась очень живописная пробка. На шлагбауме меня "обрадовали" во первых тем что с этим пропуском, который я распечатал с сайта необходимо еще пройти на оформление на КПП, во-вторых что пассажир также должен идти через этот долбанный КПП, а в-третьих что меня оказывается в списках нет, и что я с проходной должен созваниваться с AVIS, чтобы меня пропустили!
Здесь я уже почуял первое неладное. Наконец добравшись-таки до техцентра, мне предложили-таки заехать на 3й подъемник. "Любимый" мной как Билл Гейтс любит Apple. Тут я попросил помощи зала, а именно Авис. Помогли без проблем, но осадочек остался.
По работам. Помимо самого ТО-30000 собирался делать промывку инжектора. Это рекомендует сделать AVIS в карте ТО с ценами. Так и пишут на 30000 - "Рекомендуем промыть инжектор". Улыбчивый мастер Александр, а улыбался он постоянно, видимо его развеселило моё упорное нежелание заезжать в эту пердь на 3й подъемник, почему-то ненавязчиво решил отговорить меня делать промывку инжектора, типа 30000 - не срок. Но Авис-то рекомендует. Ну почему я, клиент, должен упрашивать сделать какие-либо дополнительные процедуры, рекомендуемые вами же! Лишние пара тысяч не нужны или времени жалко? Очередной осадочек. В-остальном сделал всё нормально, обещал даже посмотреть есть ли у них лампочки габаритов.
Два первых раза я упорно забывал про замену салонного фильтра, о чем мне упорно напоминали мастера - "Будем менять? - Конечно будем!". В этот раз я опять про него забыл и вспомнил лишь под конец обслуживания. "Мне б еще салонный фильтр поменять. - Ну что ж вы раньше не сказали, надо заранее было говорить, ща поменяю".
Но, спасибо мастеру Александру, сделал всё быстро и качественно. Даже рекомендовал передние колодки поменять и сам машину выгнал. А лампочек у них не оказалось :(.
В-общем, впечатления как положительные - качественный сервис, так и отрицательные - куча мелких организационных косяков, как зависящих, так и не зависящих от Авис.