在学习andengine的过程中,模仿实现StupidZombie的游戏实现,andengine中嵌入box2D的PhysicsWorld,方便碰撞检测、自动物体碰撞后的自动反射移动,但碰到个问题,看andengine的box2d相关的例子,不知道发现实现随意控制小球的初始移动速度(即根据发射原理根据手触屏距离某个点的位置设置小球的x、y方向的移动速度)(其实andengineExample中有该代码的控制,但刚开始没有找到)
试了多种方法,如:自己派生AnimatedSprite,实现控制x、y的控制,但有问题,只在原地附近闪烁,而没有移动,如下代码:
//球:用于设置速度和加速度
class CircleAnimateSprite extends AnimatedSprite{
PhysicsHandler m_handlerPhysics;
public CircleAnimateSprite(float pX, float pY, ITiledTextureRegion pTiledTextureRegion, VertexBufferObjectManager pVertexBufferObjectManager) {
super(pX, pY, pTiledTextureRegion, pVertexBufferObjectManager);
// TODO Auto-generated constructor stub
m_handlerPhysics = new PhysicsHandler(this);
registerUpdateHandler(m_handlerPhysics);
m_handlerPhysics.setVelocity(0, 0);
}
@Override
protected void onManagedUpdate(float pSecondsElapsed) {
// TODO Auto-generated method stub
// if (mX <= 2) //屏不屏蔽代码都一样在附近闪烁
// m_handlerPhysics.setVelocityX(m_handlerPhysics.getVelocityX());
// else if (mX + getWidth() >= N_Camera_Width - 2)
// m_handlerPhysics.setVelocityX(-m_handlerPhysics.getVelocityX());
//
// if (mY <= 2)
// m_handlerPhysics.setVelocityY(m_handlerPhysics.getVelocityY());
// else if (mY + getHeight() >= N_Camera_Height - 2)
// m_handlerPhysics.setVelocityY(-m_handlerPhysics.getVelocityY());
//
// mX += pSecondsElapsed * m_handlerPhysics.getVelocityX();
// mY += pSecondsElapsed * m_handlerPhysics.getVelocityY();
// setPosition(mX, mY);
// m_handlerPhysics.setVelocity(m_fVelocityX, m_fVelocityY);
super.onManagedUpdate(pSecondsElapsed);
}
public void setVelocity(float fx, float fy){
if (m_handlerPhysics != null){
m_handlerPhysics.setVelocity(fx, fy); //看了半天,也没发现该handler的用法,设置了也没产生速度的作用,只是闪烁
// m_handlerPhysics.setAcceleration(fx, fy);
// m_handlerPhysics.setAngularVelocity(0.8f);
}
}
}
当然,估计上面的闪烁是因为和PhysicsWorld产生了冲突:m_physicsWorld.registerPhysicsConnector(new PhysicsConnector(m_asCircle, bodyCircle));
Google查找,可惜google被屏蔽了,悲催的无语,最后搜到一篇文章:AndEngine学习笔记10-Physics中关于PhysicsMouseJointExample 的介绍,才知道andengineExample中有了随意设定速度的代码(andengine没有文档啊没有文档,悲剧的无语,而且搞的实例中连注释都没有,哎,这习惯。。)
andengine中的关于设置速度的代码如下:
private void jumpFace(final AnimatedSprite face) {
final Body faceBody = (Body)face.getUserData(); //此处的getUserData是自己通过setUserData设置的,也可以通过传值、成员变量实现
final Vector2 velocity = Vector2Pool.obtain(this.mGravityX * -50, this.mGravityY * -50);
faceBody.setLinearVelocity(velocity);
Vector2Pool.recycle(velocity);
}
其实这里边关键的一句话就是body.setLinearVelocity,即也可以body.setLinearVelocity(fx, fy);
将自己刚开始做的代码附上,方便自己,也方便想交流的朋友,andengine啊,没有文档、注释,太他妈的无语了
package com.nocom.stupidzombie;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.engine.handler.physics.PhysicsHandler;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.SpriteBackground;
import org.andengine.entity.scene.menu.MenuScene;
import org.andengine.entity.scene.menu.MenuScene.IOnMenuItemClickListener;
import org.andengine.entity.scene.menu.item.IMenuItem;
import org.andengine.entity.scene.menu.item.TextMenuItem;
import org.andengine.entity.scene.menu.item.decorator.ColorMenuItemDecorator;
import org.andengine.entity.shape.IShape;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.entity.sprite.Sprite;
import org.andengine.entity.util.FPSLogger;
import org.andengine.extension.physics.box2d.PhysicsConnector;
import org.andengine.extension.physics.box2d.PhysicsFactory;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.extension.physics.box2d.util.Vector2Pool;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.font.Font;
import org.andengine.opengl.font.FontFactory;
import org.andengine.opengl.texture.ITexture;
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.ITiledTextureRegion;
import org.andengine.opengl.texture.region.TextureRegion;
import org.andengine.opengl.texture.region.TiledTextureRegion;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.ui.activity.SimpleBaseGameActivity;
import org.andengine.util.color.Color;
import org.andengine.util.math.MathUtils;
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 android.hardware.SensorManager;
import android.view.KeyEvent;
public class StupidZombieActivity extends SimpleBaseGameActivity implements IOnMenuItemClickListener, IOnSceneTouchListener{
public static final int N_Camera_Width = 800;
public static final int N_Camera_Height = 480;
private static final int N_MenuItem_Resume = 0;
private static final int N_MenuItem_Reset = 1;
private static final int N_MenuItem_Quit = 2;
private static final String S_MenuItem_Resume = "Resume";
private static final String S_MenuItem_Reset = "Reset";
private static final String S_MenuItem_Quit = "Quit";
private Camera m_camera;
private Scene m_sceneMain;
private MenuScene m_sceneMenu;
private Font m_fontMenu;
private SpriteBackground m_bgMain;
private TiledTextureRegion m_ttrCircle;
private TextureRegion m_trZombie;
private PhysicsWorld m_world;
private FixtureDef m_fdWall;
private AnimatedSprite m_asCircle;
@Override
public EngineOptions onCreateEngineOptions() {
// TODO Auto-generated method stub
m_camera = new Camera(0, 0, N_Camera_Width, N_Camera_Height);
EngineOptions eo = new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(N_Camera_Width, N_Camera_Height), m_camera);
return eo;
}
@Override
protected void onCreateResources() {
// TODO Auto-generated method stub
//设置menu的字体
FontFactory.setAssetBasePath("font/");
final ITexture textureFont = new BitmapTextureAtlas(getTextureManager(), 256, 256, TextureOptions.BILINEAR);
m_fontMenu = FontFactory.createFromAsset(getFontManager(), textureFont, getAssets(), "Plok.ttf", 48, true, android.graphics.Color.WHITE);
m_fontMenu.load();
//创建内存空间及加载背景
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
final BitmapTextureAtlas bta = new BitmapTextureAtlas(getTextureManager(), 1024, 512, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
TextureRegion trBG = BitmapTextureAtlasTextureRegionFactory.createFromAsset(bta, this, "background.png", 0, 0);
m_bgMain = new SpriteBackground(new Sprite(0, 0, trBG, getVertexBufferObjectManager()));
//加载实体
m_ttrCircle = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(bta, this, "circle.png", 800, 0, 2, 1);
m_trZombie = BitmapTextureAtlasTextureRegionFactory.createFromAsset(bta, this, "zombie.png", 864, 0);
bta.load();
}
@Override
protected Scene onCreateScene() {
// TODO Auto-generated method stub
mEngine.registerUpdateHandler(new FPSLogger());
//
m_sceneMain = new Scene();
m_sceneMain.setBackground(m_bgMain);
m_sceneMain.setOnSceneTouchListener(this);
m_sceneMain.registerUpdateHandler(new CollisionDetectionUpdateHandler());
//创建暂停时的menu
createMenuScene();
//创建世界模型
createPhysicsWorld(m_sceneMain);
//创建世界中的实体
createPhysicsWorldEntity(m_sceneMain);
//..temp
addCircleEntity(m_sceneMain, 100, N_Camera_Height - 100);
return m_sceneMain;
}
@Override
public boolean onMenuItemClicked(MenuScene arg0, IMenuItem arg1, float arg2, float arg3) {
// TODO Auto-generated method stub
switch (arg1.getID()){
case N_MenuItem_Resume:
if (m_sceneMain.hasChildScene())
m_sceneMenu.back();
break;
case N_MenuItem_Reset:
m_sceneMain.reset();
m_sceneMain.clearChildScene();
m_sceneMenu.reset();
return true;
case N_MenuItem_Quit:
finish();
return true;
}
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) && event.getAction() == KeyEvent.ACTION_DOWN){
if (m_sceneMenu != null){
if (m_sceneMain.hasChildScene())
m_sceneMenu.back();
else
m_sceneMain.setChildScene(m_sceneMenu, false, true, true);
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onSceneTouchEvent(Scene arg0, TouchEvent arg1) {
// TODO Auto-generated method stub
if (m_asCircle != null){
shoot(m_asCircle, arg1.getX(), arg1.getY());
}
return false;
}
//创建暂停时的menu
void createMenuScene(){
m_sceneMenu = new MenuScene(m_camera);
IMenuItem itemResume = new ColorMenuItemDecorator(new TextMenuItem(N_MenuItem_Resume, m_fontMenu, S_MenuItem_Resume, getVertexBufferObjectManager()), new Color(0.8f, 0.3f, 0), new Color(0, 0.3f, 0.8f));
m_sceneMenu.addMenuItem(itemResume);
IMenuItem itemReset = new ColorMenuItemDecorator(new TextMenuItem(N_MenuItem_Reset, m_fontMenu, S_MenuItem_Reset, getVertexBufferObjectManager()), new Color(0.8f, 0.3f, 0), new Color(0, 0.3f, 0.8f));
m_sceneMenu.addMenuItem(itemReset);
IMenuItem itemQuit = new ColorMenuItemDecorator(new TextMenuItem(N_MenuItem_Quit, m_fontMenu, S_MenuItem_Quit, getVertexBufferObjectManager()), new Color(0.8f, 0.3f, 0), new Color(0, 0.3f, 0.8f));
m_sceneMenu.addMenuItem(itemQuit);
m_sceneMenu.buildAnimations();
m_sceneMenu.setBackgroundEnabled(false);
m_sceneMenu.setOnMenuItemClickListener(this);
}
//创建世界模型
void createPhysicsWorld(Scene scene){
m_world = new PhysicsWorld(new Vector2(0, 0), false); //SensorManager.GRAVITY_PLUTO
Rectangle top = new Rectangle(0, 0, N_Camera_Width, 2, getVertexBufferObjectManager());
Rectangle bottom = new Rectangle(0, N_Camera_Height - 2, N_Camera_Width, 2, getVertexBufferObjectManager());
Rectangle left = new Rectangle(0, 0, 2, N_Camera_Height, getVertexBufferObjectManager());
Rectangle right = new Rectangle(N_Camera_Width - 2, 0, 2, N_Camera_Height, getVertexBufferObjectManager());
//设置墙的密度、弹性、摩擦力
m_fdWall = PhysicsFactory.createFixtureDef(0, 0.8f, 0.3f);
PhysicsFactory.createBoxBody(m_world, top, BodyType.StaticBody, m_fdWall);
PhysicsFactory.createBoxBody(m_world, bottom, BodyType.StaticBody, m_fdWall);
PhysicsFactory.createBoxBody(m_world, left, BodyType.StaticBody, m_fdWall);
PhysicsFactory.createBoxBody(m_world, right, BodyType.StaticBody, m_fdWall);
//添加到场景中
scene.attachChild(top);
scene.attachChild(bottom);
scene.attachChild(left);
scene.attachChild(right);
scene.registerUpdateHandler(m_world);
}
//创建世界中的实体
void createPhysicsWorldEntity(Scene scene){
//关卡1(此处为测试用,待多个时再单独拉出划分模块)
{
//添加世界中的zombie站立的地板
Rectangle rect1 = new Rectangle(30, N_Camera_Height / 3 - 20, N_Camera_Width / 10, 10, getVertexBufferObjectManager());
Rectangle rect2 = new Rectangle(50, N_Camera_Height / 3 * 2 - 10, N_Camera_Width / 8, 12, getVertexBufferObjectManager());
Rectangle rect3 = new Rectangle(N_Camera_Width - N_Camera_Width / 8, N_Camera_Height / 3 + 10, N_Camera_Width / 7, 15, getVertexBufferObjectManager());
Rectangle rect4 = new Rectangle(N_Camera_Width - N_Camera_Width / 14, N_Camera_Height / 3 * 2 + 20, N_Camera_Width / 15, 20, getVertexBufferObjectManager());
rect1.setColor(0, 0, 0);
rect2.setColor(0, 0, 0);
rect3.setColor(0, 0, 0);
rect4.setColor(0, 0, 0);
PhysicsFactory.createBoxBody(m_world, rect1, BodyType.StaticBody, m_fdWall);
PhysicsFactory.createBoxBody(m_world, rect2, BodyType.StaticBody, m_fdWall);
PhysicsFactory.createBoxBody(m_world, rect3, BodyType.StaticBody, m_fdWall);
PhysicsFactory.createBoxBody(m_world, rect4, BodyType.StaticBody, m_fdWall);
scene.attachChild(rect1);
scene.attachChild(rect2);
scene.attachChild(rect3);
scene.attachChild(rect4);
//添加世界中的zombie
FixtureDef fdZombie = PhysicsFactory.createFixtureDef(0.1f, 0, 0.1f);
Sprite spritZombie1 = new Sprite(rect1.getX(), rect1.getY() - m_trZombie.getHeight(), m_trZombie, getVertexBufferObjectManager());
Sprite spritZombie2 = new Sprite(rect2.getX(), rect2.getY() - m_trZombie.getHeight(), m_trZombie, getVertexBufferObjectManager());
Sprite spritZombie3 = new Sprite(rect3.getX(), rect3.getY() - m_trZombie.getHeight(), m_trZombie, getVertexBufferObjectManager());
Sprite spritZombie4 = new Sprite(rect4.getX(), rect4.getY() - m_trZombie.getHeight(), m_trZombie, getVertexBufferObjectManager());
PhysicsFactory.createBoxBody(m_world, spritZombie1, BodyType.DynamicBody, fdZombie);
PhysicsFactory.createBoxBody(m_world, spritZombie2, BodyType.DynamicBody, fdZombie);
PhysicsFactory.createBoxBody(m_world, spritZombie3, BodyType.DynamicBody, fdZombie);
PhysicsFactory.createBoxBody(m_world, spritZombie4, BodyType.DynamicBody, fdZombie);
scene.attachChild(spritZombie1);
scene.attachChild(spritZombie2);
scene.attachChild(spritZombie3);
scene.attachChild(spritZombie4);
}
}
//添加小球
void addCircleEntity(Scene scene, float fx, float fy){
//添加世界中的球
if (m_asCircle == null){
FixtureDef fdCircle = PhysicsFactory.createFixtureDef(100, 0, 0);
m_asCircle = new AnimatedSprite(fx, fy, m_ttrCircle, getVertexBufferObjectManager());
Body bodyCircle = PhysicsFactory.createCircleBody(m_world, m_asCircle, BodyType.DynamicBody, fdCircle);
m_asCircle.animate(150);
m_asCircle.setUserData(bodyCircle);
scene.attachChild(m_asCircle);
m_world.registerPhysicsConnector(new PhysicsConnector(m_asCircle, bodyCircle));
}
// else{
// m_asCircle.setPosition(fx, fy);
// }
}
//发射小球
void shoot(AnimatedSprite as, float fx, float fy){
Body body = (Body)as.getUserData();
// Vector2 vec2 = Vector2Pool.obtain(fx, fy);
// body.setLinearVelocity(vec2);
// Vector2Pool.recycle(vec2);
body.setLinearVelocity(fx, fy); //改变Sprite速度关键的是Body.setLinearVelocity函数
}
//用于设置子弹的位置、碰撞检测的处理等
class CollisionDetectionUpdateHandler implements IUpdateHandler{
@Override
public void onUpdate(float arg0) {
// TODO Auto-generated method stub
}
@Override
public void reset() {
// TODO Auto-generated method stub
}
}
}