在游戏开发中,最耗费性能的是显示文本,因为必须临时在内存中将文本生成bitmap,然后将bitmap绑定到OpenGL的纹理并渲染出来,AndEngine的作者估计是深入Java开发中缓存这个概念的重要性的影响,他对文本精灵作了一个精心的规划,分为不可变的文本精灵(Text)和可变的文本精灵(ChangeableText),并将生成文本bitmap并转换到纹理的职责委托给一个叫Font的类,Font类的实现很特别,它将要显示的字符串切分为单个的字符,通过canvas生成bitmap后缓存起来,然后在绑定纹理的时候合并,这样做的好处是当显示重复的字符时性能非常高,缺点是实现复杂导致若缓存池没有此字符时则性能稍低下。在实际使用中,当一个场景里已经存在很多精灵时,Text或ChangeableText的内容经常无法显示或者显示为一片空白区域。
在IPhone,文本精灵分别是CCLabelTTF和CCLabelAtlas,作用相当于AndEngine的Text和ChangeableText,但实现机制完全不一样,CCLabelTTF是将要显示的字符串先生成一张整体的bitmap然后使用Opengl渲染,而CCLabelAtlas则要求预先传入制作好的文字序列图片以便缓存起来。因此我参考Cocos2d的做法增加了一个Label类,此类结合LoadingScene使用起来还靠谱,缺点是在运行时同时改变两个以上的Label的文字时会有时其中一个Label的文字无法显示。以下是代码:
UltraTextSource:
package com.weedong.opengl;
import org.anddev.andengine.opengl.texture.source.ITextureSource;
import org.anddev.andengine.util.HorizontalAlign;
import org.anddev.andengine.util.MathUtils;
import org.anddev.andengine.util.StringUtils;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Bitmap.Config;
import android.graphics.Paint.FontMetrics;
import android.util.FloatMath;
import android.util.Log;
/**
* 性能较强的Font TextureSource
* @author
*
*/
public class UltraFontTextureSource implements ITextureSource {
private int mWidth;
private int mHeight;
private Bitmap mBitmap;
public UltraFontTextureSource(String text, Typeface typeface, float fontSize, final int pColor, final boolean pAntiAlias, HorizontalAlign alignment) {
initialize(text, alignment, fontSize, typeface, pAntiAlias, pColor);
}
public UltraFontTextureSource(String text, Typeface typeface, float fontSize, final int pColor, final boolean pAntiAlias) {
this(text, typeface, fontSize, pColor, pAntiAlias, HorizontalAlign.LEFT);
}
public UltraFontTextureSource(String text, Typeface typeface, float fontSize, final int pColor) {
this(text, typeface, fontSize, pColor, true);
}
private void initialize(String text, HorizontalAlign alignment, float fontSize, Typeface typeface, final boolean pAntiAlias, final int pColor) {
Paint textPaint = new Paint();
textPaint.setTypeface(typeface);
textPaint.setTextSize(fontSize);
textPaint.setColor(pColor);
textPaint.setAntiAlias(pAntiAlias);
FontMetrics fontMetrics = textPaint.getFontMetrics();
int lineHeight = (int) FloatMath.ceil(Math.abs(fontMetrics.ascent) + Math.abs(fontMetrics.descent));
int lineGap = (int)(FloatMath.ceil(fontMetrics.leading));
String[] aryLines = StringUtils.split(text, '\n', null);
int lineCount = aryLines.length;
int nMaxLineWidth = getMaxLineWidth(textPaint, aryLines);
int nMaxLineHeight = lineCount * lineHeight + (lineCount - 1) * lineGap;
int width = MathUtils.nextPowerOfTwo(nMaxLineWidth);
int height = MathUtils.nextPowerOfTwo(nMaxLineHeight);
mWidth = width;
mHeight = height;
Bitmap.Config config = Bitmap.Config.ARGB_8888;
Bitmap bitmap = Bitmap.createBitmap(width, height, config);
Canvas canvas = new Canvas(bitmap);
bitmap.eraseColor(0);
Log.i("UltraFont", "font width:" + bitmap.getWidth() + ",height:" + bitmap.getHeight());
mBitmap = bitmap;
int centerOffsetHeight = (height - nMaxLineHeight) / 2;
int centerOffsetWidth = (width - nMaxLineWidth) / 2;
switch (alignment) {
case LEFT:
centerOffsetWidth = 0;
break;
case CENTER:
//centerOffsetWidth = (effectiveTextWidth - textWidth) / 2;
break;
case RIGHT:
centerOffsetWidth = width - nMaxLineWidth;
break;
}
float originalY = -fontMetrics.ascent + centerOffsetHeight;
for(int i = 0; i < aryLines.length; ++i) {
canvas.drawText(aryLines[i], centerOffsetWidth, originalY + i * (lineHeight + lineGap), textPaint);
}
}
private int getMaxLineWidth(Paint paint, String[] aryLines) {
int maximumLineWidth = 0;
Rect getStringWidthTemporaryRect = new Rect();
for (int i = aryLines.length - 1; i >= 0; i--) {
paint.getTextBounds(aryLines[i], 0, aryLines[i].length(), getStringWidthTemporaryRect);
maximumLineWidth = Math.max(maximumLineWidth, getStringWidthTemporaryRect.width());
}
return maximumLineWidth;
}
@Override
public int getHeight() {
return mHeight;
}
@Override
public int getWidth() {
return mWidth;
}
@Override
public Bitmap onLoadBitmap(Config pBitmapConfig) {
return mBitmap;
}
public ITextureSource clone() {
return null;
}
}
Label:
package com.weedong.sprite;
import org.anddev.andengine.entity.Entity;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.opengl.texture.Texture;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import android.graphics.Typeface;
import com.weedong.opengl.UltraFontTextureSource;
import com.weedong.scene.AbstractBaseScene;
import com.weedong.utils.TextureUtils;
/**
* 文本精灵
* @author
*
*/
public class Label extends Entity {
private Sprite mSprite;
private Typeface mFace;
private float mFontSize;
private int mColor;
private AbstractBaseScene mScene;
public Label(final float pX, final float pY, AbstractBaseScene scene, String text, Typeface face, float fontSize, int color) {
this.mFace = face;
this.mFontSize = fontSize;
this.mColor = color;
this.mScene = scene;
TextureRegion region = TextureUtils.createFontTextureRegion(scene, text, face, fontSize, color);
mSprite = new Sprite(pX, pY, region);
this.attachChild(mSprite);
}
public Sprite getSprite() {
return mSprite;
}
public float getX() {
return mSprite.getX();
}
public float getY() {
return mSprite.getY();
}
public float getWidth() {
return mSprite.getWidth();
}
public float getHeight() {
return mSprite.getHeight();
}
public void setText(final String text, final Typeface typeface, final float fontSize, final int pColor) {
UltraFontTextureSource font = new UltraFontTextureSource(text, typeface, fontSize, pColor);
Texture texture = mSprite.getTextureRegion().getTexture();
texture.clearTextureSources();
texture.addTextureSource(font, 0, 0);
}
public void setText(String text) {
setText(text, mFace, mFontSize, mColor);
}
public void setText(String text, int pColor) {
setText(text, mFace, mFontSize, pColor);
}
}