В сегодняшней "лекции" пойдет разбор как сделать живые обои они же (LiveWallpaper) на движке Andengine GLES2, ну т.е. на OpenGL 2. Я с трудом нашел рабочий пример на форуме andengine, да и то после того как написал свой :)
Итак, я почти уверен что сам AndEngine у Вас уже стоит, причем свежайшая версия. Потому что ветка правится каждый день. Помимо него, нужно еще расширение AndEngineWallpaperExtension. Т.е. скачиваем, распаковываем, кладем в наш workspace для Eclipse, в самом эклипсе делаем Import->Exiting Projects into Workspace, выбираем сначала один, потом второй каталог.
Возможно, придется обновить SDK до последних версий.
Теперь создаем проект под названием LiveWallpaperService, в Properties->Android->Library добавляем AndEngine и AndEngineWallpaperExtension. Проект готов. Главная активити у нас должна наследоваться от BaseLiveWallpaperService, т.е. болванка выглядит следующим образом:
За основу я взял проект GLES1 http://code.google.com/p/andenginelivewallpaperextensionexample/. Оттуда можно взять картинки и положить их в каталог /assets/gfx
Правим файл /res/values/strings.xml:
Добавляем файл /res/xml/wallpaper.xml:
Здесь тоже надеюсь всё понятно.
И правим AndroidManifest.xml, приводим его к следующему виду:
Да, в каталог /res/drawable не забываем добавить превьюшку обоев, которая будет отображаться в списке :). Подготовка на этом закончена, возвращаемся к LiveWallpaperService.java.
Как обычно, (если вы уже сделали HelloWorld на AndEngine), создаем камеру и Engine в методе onCreateEngineOptions:
и подгрузим картинки в onCreateResources:
Все вроде откомментировано и понятно. Единственное, наше приложение будет реагировать на датчик положения, поэтому нужно добавить интерфейс IAccelerationListener в объявление класса, чтобы наше приложение реагировало на акселерометр. И соответственно создадутся заглушки на два новых метода - onAccelerationAccuracyChanged и onAccelerationChanged.
В конце вызывается метод onCreateResourcesFinished(), что говорит приложению об окончательной загрузке ресурсов.
Переходим к созданию сцены, правим метод onCreateScene:
Сначала считаем координаты спрайтов относительно экрана, аттачим их к сцене. Создаем SpriteParticleSyste - "генератор дыма". В функции обратного вызова возвращаем готовую сцену.
Осталось только написать обработчик для акселерометра. Оформляем метод следующим образом:
Тут по движению акселерометра изменяется скорость частиц в mVelocityInitializer.
Общий код приложения
Итак, я почти уверен что сам AndEngine у Вас уже стоит, причем свежайшая версия. Потому что ветка правится каждый день. Помимо него, нужно еще расширение AndEngineWallpaperExtension. Т.е. скачиваем, распаковываем, кладем в наш workspace для Eclipse, в самом эклипсе делаем Import->Exiting Projects into Workspace, выбираем сначала один, потом второй каталог.
Возможно, придется обновить SDK до последних версий.
Теперь создаем проект под названием LiveWallpaperService, в Properties->Android->Library добавляем AndEngine и AndEngineWallpaperExtension. Проект готов. Главная активити у нас должна наследоваться от BaseLiveWallpaperService, т.е. болванка выглядит следующим образом:
public class LiveWallpaperService extends BaseLiveWallpaperService {
@Override
public EngineOptions onCreateEngineOptions() {
}
@Override
public void onCreateResources(
OnCreateResourcesCallback pOnCreateResourcesCallback)
throws Exception {
}
@Override
public void onCreateScene(OnCreateSceneCallback pOnCreateSceneCallback)
throws Exception {
}
@Override
public void onPopulateScene(Scene pScene,
OnPopulateSceneCallback pOnPopulateSceneCallback) throws Exception {
}
@Override
public void onAccelerationAccuracyChanged(AccelerationData pAccelerationData) {
}
}
За основу я взял проект GLES1 http://code.google.com/p/andenginelivewallpaperextensionexample/. Оттуда можно взять картинки и положить их в каталог /assets/gfx
Правим файл /res/values/strings.xml:
Я думаю тут всё понятно. Вторая и третья строка - соответственно название и дескрипшн, которые будут отображаться в списке живых обоев.Cigarette Live-Wallpaper Cigarette Live-Wallpaper A cigarette smoking on your home-screen.
Добавляем файл /res/xml/wallpaper.xml:
Здесь тоже надеюсь всё понятно.
И правим AndroidManifest.xml, приводим его к следующему виду:
Да, в каталог /res/drawable не забываем добавить превьюшку обоев, которая будет отображаться в списке :). Подготовка на этом закончена, возвращаемся к LiveWallpaperService.java.
Как обычно, (если вы уже сделали HelloWorld на AndEngine), создаем камеру и Engine в методе onCreateEngineOptions:
@Override
public EngineOptions onCreateEngineOptions() {
mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
return new EngineOptions(
true
, mScreenOrientation
, new FillResolutionPolicy()
, mCamera
);
}
и подгрузим картинки в onCreateResources:
@Override
public void onCreateResources(
OnCreateResourcesCallback pOnCreateResourcesCallback)
throws Exception {
// каталог, откуда берем все картинки
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
// подготовка атласа
mTexture = new BitmapTextureAtlas(
getTextureManager()
, 512
, 128
, TextureOptions.BILINEAR_PREMULTIPLYALPHA
);
// установка положения текстур в атласе
this.mCigaretteTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mTexture, this, "cigarette.png", 0, 0); // 400x120
this.mSmokeTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mTexture, this, "smoke.png", 381, 0); // 64x64
// загрузка текстур в атлас
mTexture.load();
// включаем акселерометр
this.enableAccelerationSensor(this);
pOnCreateResourcesCallback.onCreateResourcesFinished();
Все вроде откомментировано и понятно. Единственное, наше приложение будет реагировать на датчик положения, поэтому нужно добавить интерфейс IAccelerationListener в объявление класса, чтобы наше приложение реагировало на акселерометр. И соответственно создадутся заглушки на два новых метода - onAccelerationAccuracyChanged и onAccelerationChanged.
В конце вызывается метод onCreateResourcesFinished(), что говорит приложению об окончательной загрузке ресурсов.
Переходим к созданию сцены, правим метод onCreateScene:
@Override
public void onCreateScene(OnCreateSceneCallback pOnCreateSceneCallback)
throws Exception {
// сцена
mScene = new Scene();
// координаты левого верхнего угла спрайта сигареты
final int cigaretteX = 0;
final int cigaretteY = CAMERA_HEIGHT - (int)this.mCigaretteTextureRegion.getHeight();
// координаты левого верхнего угла спрайта дыма
final int smokeX = cigaretteX + (int)this.mCigaretteTextureRegion.getWidth() - 100;
final int smokeY = cigaretteY - 25;
// создаем спрайты сигареты, дыма
final Sprite sigarette = new Sprite(cigaretteX, cigaretteY, this.mCigaretteTextureRegion, this.getVertexBufferObjectManager());
final Sprite smoke = new Sprite(smokeX, smokeY, this.mSmokeTextureRegion, this.getVertexBufferObjectManager());
// добавляем слои сигареты и дыма на сцену
mScene.attachChild(sigarette);
mScene.attachChild(smoke);
// генератор частиц
final SpriteParticleSystem particleSystem = new SpriteParticleSystem(
new PointParticleEmitter(smokeX, smokeY) // Particle Emitter. Координаты генератора частиц
, 6 // минимальная частота
, 8 // максимальная частота
, 150 // максимум частиц
, this.mSmokeTextureRegion // текстура дыма
, this.getVertexBufferObjectManager()
);
// скорость частиц
mVelocityInitializer = new VelocityParticleInitializer(
-20 // min X
, 20 // max X
, -100 // min Y
, -120 // max Y
);
particleSystem.addParticleInitializer(mVelocityInitializer);
// прозрачность частиц
particleSystem.addParticleModifier( new AlphaParticleModifier(
1f // from time
, 0f // to time
, 0f // from alfa
, 10f // to alfa
));
mScene.attachChild(particleSystem);
pOnCreateSceneCallback.onCreateSceneFinished(mScene);
}
Сначала считаем координаты спрайтов относительно экрана, аттачим их к сцене. Создаем SpriteParticleSyste - "генератор дыма". В функции обратного вызова возвращаем готовую сцену.
Осталось только написать обработчик для акселерометра. Оформляем метод следующим образом:
@Override
public void onAccelerationChanged(AccelerationData pAccelerationData) {
final float minVelocityX = (pAccelerationData.getX() + 2) * 5;
final float maxVelocityX = (pAccelerationData.getX() - 2) * 5;
final float minVelocityY = (pAccelerationData.getY() - 8) * 10;
final float maxVelocityY = (pAccelerationData.getY() - 10) * 10;
this.mVelocityInitializer.setVelocity(minVelocityX, maxVelocityX, minVelocityY, maxVelocityY);
}
Тут по движению акселерометра изменяется скорость частиц в mVelocityInitializer.
package com.osmsoft.lws;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.FillResolutionPolicy;
import org.andengine.entity.particle.SpriteParticleSystem;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.sprite.Sprite;
import org.andengine.extension.ui.livewallpaper.BaseLiveWallpaperService;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.input.sensor.acceleration.AccelerationData;
import org.andengine.input.sensor.acceleration.IAccelerationListener;
import org.andengine.entity.particle.emitter.PointParticleEmitter;
import org.andengine.entity.particle.initializer.VelocityParticleInitializer;
import org.andengine.entity.particle.modifier.AlphaParticleModifier;
public class LiveWallpaperService extends BaseLiveWallpaperService implements IAccelerationListener {
private static final int CAMERA_WIDTH = 800;
private static final int CAMERA_HEIGHT = 480;
private static final int LAYER_SMOKE = 1;
private static final int LAYER_CIGARETTE = 0;
public static Camera mCamera;
private BitmapTextureAtlas mTexture;
private ScreenOrientation mScreenOrientation;
private VelocityParticleInitializer mVelocityInitializer;
private Scene mScene;
private ITextureRegion mSmokeTextureRegion;
private ITextureRegion mCigaretteTextureRegion;
@Override
public EngineOptions onCreateEngineOptions() {
mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
return new EngineOptions(
true
, mScreenOrientation
, new FillResolutionPolicy()
, mCamera
);
}
@Override
public void onCreateResources(
OnCreateResourcesCallback pOnCreateResourcesCallback)
throws Exception {
// каталог, откуда берем все картинки
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
// подготовка атласа
mTexture = new BitmapTextureAtlas(
getTextureManager()
, 512
, 128
, TextureOptions.BILINEAR_PREMULTIPLYALPHA
);
// установка положения текстур в атласе
this.mCigaretteTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mTexture, this, "cigarette.png", 0, 0); // 400x120
this.mSmokeTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mTexture, this, "smoke.png", 381, 0); // 64x64
// загрузка текстур в атлас
mTexture.load();
// включаем акселерометр
this.enableAccelerationSensor(this);
pOnCreateResourcesCallback.onCreateResourcesFinished();
}
@Override
public void onCreateScene(OnCreateSceneCallback pOnCreateSceneCallback)
throws Exception {
// сцена
mScene = new Scene();
// координаты левого верхнего угла спрайта сигареты
final int cigaretteX = 0;
final int cigaretteY = CAMERA_HEIGHT - (int)this.mCigaretteTextureRegion.getHeight();
// координаты левого верхнего угла спрайта дыма
final int smokeX = cigaretteX + (int)this.mCigaretteTextureRegion.getWidth() - 100;
final int smokeY = cigaretteY - 25;
// создаем спрайты сигареты, дыма
final Sprite sigarette = new Sprite(cigaretteX, cigaretteY, this.mCigaretteTextureRegion, this.getVertexBufferObjectManager());
final Sprite smoke = new Sprite(smokeX, smokeY, this.mSmokeTextureRegion, this.getVertexBufferObjectManager());
// добавляем слои сигареты и дыма на сцену
mScene.attachChild(sigarette);
mScene.attachChild(smoke);
// генератор частиц
final SpriteParticleSystem particleSystem = new SpriteParticleSystem(
new PointParticleEmitter(smokeX, smokeY) // Particle Emitter. Координаты генератора частиц
, 6 // минимальная частота
, 8 // максимальная частота
, 150 // максимум частиц
, this.mSmokeTextureRegion // текстура дыма
, this.getVertexBufferObjectManager()
);
// скорость частиц
mVelocityInitializer = new VelocityParticleInitializer(
-20 // min X
, 20 // max X
, -100 // min Y
, -120 // max Y
);
particleSystem.addParticleInitializer(mVelocityInitializer);
// прозрачность частиц
particleSystem.addParticleModifier( new AlphaParticleModifier(
1f // from time
, 0f // to time
, 0f // from alfa
, 10f // to alfa
));
mScene.attachChild(particleSystem);
pOnCreateSceneCallback.onCreateSceneFinished(mScene);
}
@Override
public void onPopulateScene(Scene pScene,
OnPopulateSceneCallback pOnPopulateSceneCallback) throws Exception {
pOnPopulateSceneCallback.onPopulateSceneFinished();
}
@Override
public void onAccelerationAccuracyChanged(AccelerationData pAccelerationData) {
// TODO Auto-generated method stub
}
@Override
public void onAccelerationChanged(AccelerationData pAccelerationData) {
final float minVelocityX = (pAccelerationData.getX() + 2) * 5;
final float maxVelocityX = (pAccelerationData.getX() - 2) * 5;
final float minVelocityY = (pAccelerationData.getY() - 8) * 10;
final float maxVelocityY = (pAccelerationData.getY() - 10) * 10;
this.mVelocityInitializer.setVelocity(minVelocityX, maxVelocityX, minVelocityY, maxVelocityY);
}
}
Ссылки по теме:
http://www.andengine.org/forums/live-wallpaper-extension/ - форум по расширению
http://www.andengine.org/forums/post32197.html - болванка для livewallpaper
http://www.andengine.org/forums/gles2/no-particle-system-examples-t6159.html?p27526 - Еще какие-то обои