AndEngine进阶之自定义可拖动的背景

18 篇文章 0 订阅
7 篇文章 0 订阅

AndEngine是Android上一个很出色的基于OpenGL的游戏引擎,其特点是所有代码都是用Java编写,代码之间层次非常分别,组件颗粒度非常小,直接带来的优点就是非常容易用,扩展也非常轻松,但由于Android的VM虽然是优化过的,但性能也是一般般。

         AndEngine内置了对TMX地图的支持,我用一张1024*1024的jpg图片测试过,在我的Dell Venue上只能达到45~50帧/秒的速度,而在模拟器上更是惨不忍睹。这种速度显然在真正的游戏开发中是难以接受的,于是我就着手改进。

    由于TMX地图能提供很强大的功能,首先它几乎可以分割无限大的地图,而且还可以添加层和对象,因此要想使用它则必须要容忍它的慢,我的思路是在某些需要大地图,但却不太需要在地图中添加大量的层和对象的地方,抛弃使用TMX地图,转而自己切割地图。

    原理很简单,就是参考AndEngine对大图片的切割,直接上代码,有OpenGL基础的很容易就能看明白。

    MultiSpriteLayer

package com.weedong.background;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;

import org.anddev.andengine.collision.RectangularShapeCollisionChecker;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.entity.shape.RectangularShape;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.opengl.buffer.BufferObjectManager;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.util.GLHelper;
import org.anddev.andengine.opengl.vertex.RectangleVertexBuffer;
import org.anddev.andengine.util.MathUtils;
import org.anddev.andengine.util.constants.Constants;

import android.util.Log;

public class MultiSpriteLayer extends RectangularShape {
	private Sprite[][] arySprite = null;
	
	public MultiSpriteLayer(Sprite[][] sprites) {
		super(0, 0, 0, 0, null);
		arySprite = sprites;
		int tiledWidth = (int)arySprite[0][0].getWidth();
		int tiledHeight = (int)arySprite[0][0].getHeight();
		this.mSharedVertexBuffer = new RectangleVertexBuffer(GL11.GL_STATIC_DRAW, true);
		BufferObjectManager.getActiveInstance().loadBufferObject(this.mSharedVertexBuffer);
		this.mSharedVertexBuffer.update(tiledWidth, tiledHeight);
		
		super.mWidth = tiledWidth * arySprite.length;
		final float width = super.mWidth;
		super.mBaseWidth = width;

		super.mHeight = tiledHeight * arySprite[0].length;
		final float height = super.mHeight;
		super.mBaseHeight = height;

		this.mRotationCenterX = width * 0.5f;
		this.mRotationCenterY = height * 0.5f;

		this.mScaleCenterX = this.mRotationCenterX;
		this.mScaleCenterY = this.mRotationCenterY;
	}
		
	private final float[] mCullingVertices = new float[2 * RectangleVertexBuffer.VERTICES_PER_RECTANGLE];
	private final RectangleVertexBuffer mSharedVertexBuffer;
	
	@Override
	protected void onInitDraw(final GL10 pGL) {
		super.onInitDraw(pGL);
		GLHelper.enableTextures(pGL);
		GLHelper.enableTexCoordArray(pGL);
	}
	
	@Override
	protected void onApplyVertices(final GL10 pGL) {
		if(GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS) {
			final GL11 gl11 = (GL11)pGL;
			this.mSharedVertexBuffer.selectOnHardware(gl11);
			GLHelper.vertexZeroPointer(gl11);
		} else {
			GLHelper.vertexPointer(pGL, this.mSharedVertexBuffer.getFloatBuffer());
		}
	}
	
	@Override
	protected void drawVertices(GL10 pGL, Camera pCamera) {
		final float cameraMinX = pCamera.getMinX();
		final float cameraMinY = pCamera.getMinY();
		final float cameraWidth = pCamera.getWidth();
		final float cameraHeight = pCamera.getHeight();
		
		final Sprite[][] tmxTiles = arySprite;

		final int tileColumns = tmxTiles[0].length;
		final int tileRows = tmxTiles.length;
		final int tileWidth = (int)tmxTiles[0][0].getWidth();
		final int tileHeight = (int)tmxTiles[0][0].getHeight();

		final float scaledTileWidth = tileWidth * this.mScaleX;
		final float scaledTileHeight = tileHeight * this.mScaleY;

		final float[] cullingVertices = this.mCullingVertices;
		RectangularShapeCollisionChecker.fillVertices(this, cullingVertices);

		final float layerMinX = cullingVertices[Constants.VERTEX_INDEX_X];
		final float layerMinY = cullingVertices[Constants.VERTEX_INDEX_Y];


		/* Determine the area that is visible in the camera. */
		final float firstColumnRaw = (cameraMinX - layerMinX) / scaledTileWidth;
		final int firstColumn = MathUtils.bringToBounds(0, tileColumns - 1, (int)Math.floor(firstColumnRaw));
		final int lastColumn = MathUtils.bringToBounds(0, tileColumns - 1, (int)Math.ceil(firstColumnRaw + cameraWidth / scaledTileWidth));

		final float firstRowRaw = (cameraMinY - layerMinY) / scaledTileHeight;
		final int firstRow = MathUtils.bringToBounds(0, tileRows - 1, (int)Math.floor(firstRowRaw));
		final int lastRow = MathUtils.bringToBounds(0, tileRows - 1, (int)Math.floor(firstRowRaw + cameraHeight / scaledTileHeight));

		final int visibleTilesTotalWidth = (lastColumn - firstColumn + 1) * tileWidth;
		
		pGL.glTranslatef(firstColumn * tileWidth, firstRow * tileHeight, 0);

		for(int row = firstRow; row <= lastRow; row++) {
			final Sprite[] tmxTileRow = tmxTiles[row];

			for(int column = firstColumn; column <= lastColumn; column++) {
				final TextureRegion textureRegion = tmxTileRow[column].getTextureRegion();
				if(textureRegion != null) {
					textureRegion.onApply(pGL);
					pGL.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
				}
				pGL.glTranslatef(tileWidth, 0, 0);
			}
			pGL.glTranslatef(-visibleTilesTotalWidth, tileHeight, 0);
		}
		pGL.glLoadIdentity();
	}

	@Override
	protected void onUpdateVertexBuffer() {
	}
		
}

AbstractMultiSpriteBackgroundScene

package com.weedong.scene;

import org.anddev.andengine.entity.sprite.Sprite;
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 com.weedong.activity.BaseWeedongLayoutGameActivity;
import com.weedong.background.MultiSpriteLayer;

/**
 * 抽象类<br/>
 * 由多个精灵组成的背景的Scene<br/>
 * 其特点是是可以像TMX一样支持超过1024*1024的背景图,并且速度很快<br/>
 * 若要使背景地图可以拖动,可以参考AndEngine的例子重写onSceneTouchEvent方法并使用SurfaceScrollDetector
 * @author 
 *
 */
public abstract class AbstractMultiSpriteBackgroundScene extends AbstractGameScene {

	public AbstractMultiSpriteBackgroundScene(int nLayerCount, BaseWeedongLayoutGameActivity gameActivity) {
		super(nLayerCount, gameActivity);
	}
	
	public AbstractMultiSpriteBackgroundScene(int nLayerCount, BaseWeedongLayoutGameActivity gameActivity, ILoadingScene loadingScene) {
		super(nLayerCount, gameActivity, loadingScene);
	}

	@Override
	protected void onLoadScene() {
		super.onLoadScene();
		initializeBackground();
	}
	
	private void initializeBackground() {
		String[][] aryBackgroundFilePath = getBackgroundFilePath();
		Sprite[][] arySprite = new Sprite[aryBackgroundFilePath.length][aryBackgroundFilePath[0].length];
		for(int i = 0; i < arySprite.length; i++) {
			for(int j = 0; j < arySprite[0].length; j++) {
				//将所有精灵的TextureOptions设为TextureOptions.NEAREST可以达到最快速度
				//如果设成BILINEAR_PREMULTIPLYALPHA会导致精灵的边界出现一条黑线
				Texture backgroundTexture = new Texture(512, 512, TextureOptions.NEAREST);
				loadTextureAndAppendToContainer(backgroundTexture);
				TextureRegion backgroundRegion = TextureRegionFactory.createFromAsset(backgroundTexture, mGameActivity, aryBackgroundFilePath[i][j], 0, 0);
				Sprite eachSprite = new Sprite(0, 0, backgroundRegion);
				arySprite[i][j] = eachSprite;
			}
			
		}
		MultiSpriteLayer layer = new MultiSpriteLayer(arySprite);
		layer.setCullingEnabled(true);
		this.attachChild(layer);
		this.mGameActivity.mCamera.setBounds(0, layer.getWidth(), 0, layer.getHeight());
		this.mGameActivity.mCamera.setBoundsEnabled(true);
	}
	
	/**
	 * 子类必须实现此方法,以按顺序返回组成背景的所有精灵的图片路径
	 * @author 
	 * @return
	 */
	protected abstract String[][] getBackgroundFilePath();
}

使用很简单,继承AbstractMultiSpriteBackgroundScene实现其中的getBackgroundFilePath方法即可。若要使背景地图可以拖动,可以参考AndEngine的例子重写onSceneTouchEvent方法并使用SurfaceScrollDetector



  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值