【广而告之】山寨腾讯“爱消除”游戏已经正式入驻GitHub,项目地址是:https://github.com/3125788/CrazyLink
欢迎广大寨友一起来完善这个游戏。
好了,山寨了那么久,今天开始,我们自己也要来点创新的东西了。
今天我们就为山寨“爱消除”游戏加上生命指示器的效果。所谓生命指示器,就是告诉玩家当前还剩余几条命。好,废话不多说,直接上图。
多于三条命时的效果如下,数字5代表5条命,三颗星星代表三条命,总共8条命。
当生命不足三条时的效果如下,这里只剩下两条命了。
详细的代码在DrawLife渲染类中。调用 public void draw(GL10 gl, int life)传入生命数,就可以将生命指示器渲染出来了。
public class DrawLife {
private IntBuffer mLifeVertexBuffer; //顶点坐标数据缓冲
private IntBuffer mNumVertexBuffer; //顶点坐标数据缓冲
private FloatBuffer mLifeTextureBuffer; //顶点纹理数据缓冲
private FloatBuffer mNumTextureBuffer; //顶点纹理数据缓冲
int vCount=0; //顶点数量
int textureId; //纹理索引
int numTextureId;
final int mBitmapW = 64;
final int mBitmapH = 32;
final int mFontSize = 24; //字体大小
float textureRatio; //为了准确获取纹理图片中的素材对象,需要设置纹理的变换率
public DrawLife(GL10 gl, int textureId)
{
this.textureId=textureId;
initNumTextureBuffer();
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
this.numTextureId = textures[0];
}
//顶点坐标数据的初始化
private void initVertexBuffer(int col)
{
int w = 16;
int h = 16;
vCount=6;//顶点的数量,一个正方形用两个三角形表示,共需要6个顶点
int deltaX = ((col + 2)*2*w*CrazyLinkConstent.UNIT_SIZE);
int deltaY = 10*2*h*CrazyLinkConstent.UNIT_SIZE;
int vertices[]=new int[]//顶点坐标数据数组
{
-w*CrazyLinkConstent.UNIT_SIZE+deltaX,h*CrazyLinkConstent.UNIT_SIZE+deltaY,0,
-w*CrazyLinkConstent.UNIT_SIZE+deltaX,-h*CrazyLinkConstent.UNIT_SIZE+deltaY,0,
w*CrazyLinkConstent.UNIT_SIZE+deltaX,-h*CrazyLinkConstent.UNIT_SIZE+deltaY,0,
w*CrazyLinkConstent.UNIT_SIZE+deltaX,-h*CrazyLinkConstent.UNIT_SIZE+deltaY,0,
w*CrazyLinkConstent.UNIT_SIZE+deltaX,h*CrazyLinkConstent.UNIT_SIZE+deltaY,0,
-w*CrazyLinkConstent.UNIT_SIZE+deltaX,h*CrazyLinkConstent.UNIT_SIZE+deltaY,0
};
//创建顶点坐标数据缓冲
//int类型占用4个字节,因此转换为byte的数据时需要*4
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder()); //设置本地的字节顺序
//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
mLifeVertexBuffer = vbb.asIntBuffer(); //转换为int型缓冲
mLifeVertexBuffer.put(vertices); //向缓冲区中放入顶点坐标数据
mLifeVertexBuffer.position(0); //设置缓冲区起始位置
return;
}
private void initNumVertexBuffer()
{
vCount=6;//顶点的数量,一个正方形用两个三角形表示,共需要6个顶点
float deltaX = 160*CrazyLinkConstent.UNIT_SIZE;//((col-3)*64*CrazyLinkConstent.UNIT_SIZE);
float deltaY = 320*CrazyLinkConstent.UNIT_SIZE;//(((float)row-3 + y)*64*CrazyLinkConstent.UNIT_SIZE);
int vertices[]=new int[]//顶点坐标数据数组
{
-mBitmapW/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaX,mBitmapH/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaY,0,
-mBitmapW/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaX,-mBitmapH/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaY,0,
mBitmapW/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaX,-mBitmapH/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaY,0,
mBitmapW/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaX,-mBitmapH/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaY,0,
mBitmapW/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaX,mBitmapH/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaY,0,
-mBitmapW/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaX,mBitmapH/2*CrazyLinkConstent.UNIT_SIZE+(int)deltaY,0
};
//创建顶点坐标数据缓冲
//int类型占用4个字节,因此转换为byte的数据时需要*4
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder()); //设置本地的字节顺序
//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
mNumVertexBuffer = vbb.asIntBuffer(); //转换为int型缓冲
mNumVertexBuffer.put(vertices); //向缓冲区中放入顶点坐标数据
mNumVertexBuffer.position(0); //设置缓冲区起始位置
return;
}
//顶点纹理数据的初始化
private void initTextureBuffer(int witch)
{
textureRatio = (float)(1/2.0f); //图片是2个独立的素材对象组成,每次需要根据witch准确地获取对应的素材
float textureCoors[]=new float[] //顶点纹理S、T坐标值数组
{
witch * textureRatio,0,
witch * textureRatio,1,
(witch+1) * textureRatio,1,
(witch+1) * textureRatio,1,
(witch+1) * textureRatio,0,
witch * textureRatio,0
};
//创建顶点纹理数据缓冲
//int类型占用4个字节,因此转换为byte的数据时需要*4
ByteBuffer cbb = ByteBuffer.allocateDirect(textureCoors.length*4);
cbb.order(ByteOrder.nativeOrder());//设置本地字节顺序
//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
mLifeTextureBuffer = cbb.asFloatBuffer();//转换为int型缓冲
mLifeTextureBuffer.put(textureCoors);//向缓冲区中放入顶点着色数据
mLifeTextureBuffer.position(0);//设置缓冲区起始位置
return;
}
private void initNumTextureBuffer()
{
float textureCoors[]=new float[] //顶点纹理S、T坐标值数组
{
0,0,
0,1,
1,1,
1,1,
1,0,
0,0
};
//创建顶点纹理数据缓冲
//int类型占用4个字节,因此转换为byte的数据时需要*4
ByteBuffer cbb = ByteBuffer.allocateDirect(textureCoors.length*4);
cbb.order(ByteOrder.nativeOrder());//设置本地字节顺序
//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
mNumTextureBuffer = cbb.asFloatBuffer();//转换为int型缓冲
mNumTextureBuffer.put(textureCoors);//向缓冲区中放入顶点着色数据
mNumTextureBuffer.position(0);//设置缓冲区起始位置
return;
}
private void bindTexture(GL10 gl, Bitmap bmp)
{
gl.glBindTexture(GL10.GL_TEXTURE_2D, numTextureId);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);//指定缩小过滤方法
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);//指定放大过滤方法
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);//指定S坐标轴贴图模式
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);//指定T坐标轴贴图模式
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
bmp.recycle();
}
Bitmap genBitmap(int life)
{
//生成的位图要带ALPHA通道信息,否则无法进行透明化处理
Bitmap bitmap = Bitmap.createBitmap(mBitmapW, mBitmapH, Config.ARGB_4444);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.TRANSPARENT);
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
paint.setTextSize(mFontSize);
paint.setColor(Color.WHITE);
String str = Integer.toString(life);
canvas.drawText(str, 20, 28, paint); //书写的位置,根据具体情况可以调整一下
return bitmap;
}
public void drawNumber(GL10 gl, int life)
{
life -= 3;
if (life <= 0) return;
Bitmap bmp = genBitmap(life);
bindTexture(gl, bmp);
initNumVertexBuffer(); //根据col,row初始化顶点坐标
//顶点坐标,允许使用顶点数组
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//为画笔指定顶点坐标数据
gl.glVertexPointer
(
3, //每个顶点的坐标数量为3 xyz
GL10.GL_FIXED, //顶点坐标值的类型为 GL_FIXED
0, //连续顶点坐标数据之间的间隔
mNumVertexBuffer //顶点坐标数据
);
//纹理坐标,开启纹理
gl.glEnable(GL10.GL_TEXTURE_2D);
//允许使用纹理数组
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//为画笔指定纹理uv坐标数据
gl.glTexCoordPointer
(
2, //每个顶点两个纹理坐标数据 S、T
GL10.GL_FLOAT, //数据类型
0, //连续纹理坐标数据之间的间隔
mNumTextureBuffer //纹理坐标数据
);
gl.glBindTexture(GL10.GL_TEXTURE_2D,numTextureId);//为画笔绑定指定名称ID纹理
//绘制图形
gl.glDrawArrays
(
GL10.GL_TRIANGLES,
0,
vCount
);
gl.glDisable(GL10.GL_TEXTURE_2D);//关闭纹理
}
void drawLife(GL10 gl, int pic, int col)
{
initVertexBuffer(col); //根据col,row初始化顶点坐标
initTextureBuffer(pic); //根据witch来初始化纹理顶点数据
//gl.glTranslatef(col * textureRatio, row * textureRatio, 0); //在x=col,y=row的位置绘制选定的素材对象
//顶点坐标,允许使用顶点数组
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//为画笔指定顶点坐标数据
gl.glVertexPointer
(
3, //每个顶点的坐标数量为3 xyz
GL10.GL_FIXED, //顶点坐标值的类型为 GL_FIXED
0, //连续顶点坐标数据之间的间隔
mLifeVertexBuffer //顶点坐标数据
);
//纹理坐标,开启纹理
gl.glEnable(GL10.GL_TEXTURE_2D);
//允许使用纹理数组
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//为画笔指定纹理uv坐标数据
gl.glTexCoordPointer
(
2, //每个顶点两个纹理坐标数据 S、T
GL10.GL_FLOAT, //数据类型
0, //连续纹理坐标数据之间的间隔
mLifeTextureBuffer //纹理坐标数据
);
gl.glBindTexture(GL10.GL_TEXTURE_2D,textureId);//为画笔绑定指定名称ID纹理
//绘制图形
gl.glDrawArrays
(
GL10.GL_TRIANGLES,
0,
vCount
);
gl.glDisable(GL10.GL_TEXTURE_2D);//关闭纹理
}
public void draw(GL10 gl, int life)
{
int lifeCnt = life;
if (life > 3) lifeCnt = 3;
for(int i = 0; i < 3; i++)
{
if(lifeCnt >= 3)
drawLife(gl, 1, i);
else
drawLife(gl, 0, i);
lifeCnt++;
}
drawNumber(gl, life);
}
}
要修改渲染的效果,请修改\CrazyLink\res\drawable-nodpi\life.png图片。如果你设计了更炫的效果,欢迎上传到GitHub上,让更多的寨友一起分享。
当前life.png的效果如下,唯一要注意的是:背景要求是透明的。