效果展示:
图片素材:
1.涉及知识点:Mesh,纹理坐标UV
2.核心思路:
将图片1的外围矩形区域,划分为24个三角形,共26个Mesh顶点。然后根据进度,绘制相应的区域即可。
3.Mesh简述:
public Mesh (VertexDataType type, boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes)
参数依次是:
type,顶点数据类型,
isStatic,网格是否为静态,如果顶点不是变化的,设置为true
maxVertices,最大顶点数
maxIndices,最大指数,一般与maxVertices一样即可
attributes,顶点属性
VertexAttribute
常用顶点属性:顶点坐标,颜色,纹理坐标
属性对应数组元素个数 : 顶点坐标2个(x,y) 颜色4个(r,g,b,a) 纹理2个(u,v)
注:
纹理坐标以左上角为原点(0,0),右下角为(1,1);
顶点坐标以左下角为原点(0,0);
4.代码展示:
package com.oahcfly.chgame.core.ui; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Mesh; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.VertexAttribute; import com.badlogic.gdx.graphics.VertexAttributes.Usage; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.actions.Actions; import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Array; import com.oahcfly.chgame.core.Chao; import com.oahcfly.chgame.core.listener.CHProgressBarListener; /** * * 圆形冷却进度条 * 参考自:http://blog.csdn.net/wuhaishengxxx/article/details/46642791 * * * @author haocao * */ public class CHCirclePngProgressBar extends Actor { // 顶点属性个数:(x,y,color,u,v) static final int VERTEX_SIZE = 2 + 1 + 2; static final int MAX_VERTICES = 26; // 总属性个数 static final int SPRITE_SIZE = MAX_VERTICES * VERTEX_SIZE; // 属性数组元素个数 : 坐标(x,y) 颜色(r,g,b,a) 纹理(u,v) public final int POS_COMPONENTS = 2; public final int COLOR_COMPONENTS = 4; public final int TEXCOORD_COMPONENTS = 2; private Texture topTexture, bgTexture; private Mesh mesh; private ShaderProgram shader; private float[] spriteVertices; // 冷却时长 private float duration; public CHCirclePngProgressBar (String bgPngPath, String topPngPath, float duration) { this.duration = duration; topTexture = Chao.game.getTexture(topPngPath); bgTexture = Chao.game.getTexture(bgPngPath); setSize(topTexture.getWidth(), topTexture.getHeight()); setOrigin(Align.center); /** * 使用默认的shader * 一般不指定ShaderProgram,我们使用SpriteBatch的时候都是使用默认的shader */ this.shader = SpriteBatch.createDefaultShader(); // 设置顶点数据类型为数组类型 Mesh.VertexDataType vertexDataType = Mesh.VertexDataType.VertexArray; /** * :顶点坐标、颜色、纹理坐标 */ VertexAttribute[] vertexAttributes = new VertexAttribute[3]; vertexAttributes[0] = new VertexAttribute(Usage.Position, POS_COMPONENTS, "a_position"); vertexAttributes[1] = new VertexAttribute(Usage.ColorPacked, COLOR_COMPONENTS, "a_color"); vertexAttributes[2] = new VertexAttribute(Usage.TextureCoordinates, TEXCOORD_COMPONENTS, "a_texCoord0"); this.mesh = new Mesh(vertexDataType, false, MAX_VERTICES, MAX_VERTICES, vertexAttributes); // Array<Vector2> pointsArray = generatePoints(); /** * 初始化顶点数据 */ spriteVertices = new float[SPRITE_SIZE]; for (int i = 0; i < MAX_VERTICES; i++) { Vector2 vector2 = pointsArray.get(i); float tx = vector2.x; float ty = vector2.y; spriteVertices[i * 5 + 0] = tx;// 坐标x spriteVertices[i * 5 + 1] = ty;// 坐标y Vector2 uVector2 = getUV(tx, ty); spriteVertices[i * 5 + 3] = uVector2.x; spriteVertices[i * 5 + 4] = uVector2.y; } } private int progressIdx = 0; public void startProgress () { progressIdx = 0; // 时长 float singleDuration = duration / (MAX_VERTICES - 1); addAction(Actions.repeat(MAX_VERTICES - 1, Actions.delay(singleDuration, Actions.run(new Runnable() { @Override public void run () { // progressIdx++; onProgress(); } })))); } private void onProgress () { if (progressIdx == 0 && progressBarListener != null) { progressBarListener.onStart(); } boolean isEnd = progressIdx == MAX_VERTICES - 1; if (isEnd && progressBarListener != null) { progressBarListener.onFinish(); } if (isEnd) { stoped = false; clearActions(); } } private boolean stoped = false; /** * */ public void stopProgress () { stoped = true; } /** * 进度 */ public void resetProgress () { stoped = false; clearActions(); progressIdx = 0; } /** * 恢复进度 */ public void resumeProgress () { stoped = false; } /** * 是否处于进度中 * @return */ public boolean isProgressing () { return progressIdx >= 1; } @Override public void act (float delta) { if (stoped) return; super.act(delta); } @Override public void draw (Batch batch, float parentAlpha) { Color color = getColor(); batch.setColor(color.r, color.g, color.b, color.a); //调用ShapeRender绘制几何图形时无法实现半透明颜色 Gdx.gl.glEnable(GL20.GL_BLEND); Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); // 开启图形附上纹理 Gdx.gl20.glEnable(GL20.GL_TEXTURE_2D); float[] tmpSpriteVertices = new float[5 * (progressIdx + 1)]; float x = getX(Align.center) - bgTexture.getWidth() / 2; float y = getY(Align.center) - bgTexture.getHeight() / 2; float fColor = getColor().toFloatBits(); for (int i = 0; i < MAX_VERTICES; i++) { if (i < progressIdx + 1) { tmpSpriteVertices[i * 5 + 0] = spriteVertices[i * 5 + 0] + x;// 坐标x tmpSpriteVertices[i * 5 + 1] = spriteVertices[i * 5 + 1] + y;// 坐标y tmpSpriteVertices[i * 5 + 2] = fColor;// 颜色 Vector2 uVector2 = getUV(spriteVertices[i * 5 + 0], spriteVertices[i * 5 + 1]); tmpSpriteVertices[i * 5 + 3] = uVector2.x;// 纹理坐标u tmpSpriteVertices[i * 5 + 4] = uVector2.y;// 纹理坐标v } } this.bgTexture.bind(); this.mesh.setVertices(tmpSpriteVertices, 0, tmpSpriteVertices.length); this.mesh.render(this.shader, GL20.GL_TRIANGLE_FAN, 0, tmpSpriteVertices.length / 5); // 顶部蒙版纹理 batch.draw(topTexture, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation(), 0, 0, (int)getWidth(), (int)getHeight(), false, false); } private CHProgressBarListener progressBarListener; public void setProgressBarListener (CHProgressBarListener progressBarListener) { this.progressBarListener = progressBarListener; } /** * * * @param x 以左下角为基准,范围[0,w] * @param y 以左下角为基准,范围[0,h] * @return */ private Vector2 getUV (float x, float y) { Vector2 vector2 = new Vector2(x / bgTexture.getWidth(), 1 - y / bgTexture.getHeight()); return vector2; } private Array<Vector2> generatePoints () { // 原点:左下角 float w = bgTexture.getWidth(); float h = bgTexture.getHeight(); Array<Vector2> array = new Array<Vector2>(); // 圆心 array.add(new Vector2(2 * w / 4, 2 * h / 4)); int MAX_POINTS = 6; // 右上4 for (int i = MAX_POINTS / 2; i <= MAX_POINTS; i++) { array.add(new Vector2(i * w / MAX_POINTS, h)); } // 右侧6 for (int i = MAX_POINTS - 1; i >= 0; i--) { array.add(new Vector2(w, i * h / MAX_POINTS)); } // 底6 for (int i = MAX_POINTS - 1; i >= 0; i--) { array.add(new Vector2(w * i / MAX_POINTS, 0)); } // 左6 for (int i = 1; i <= MAX_POINTS; i++) { array.add(new Vector2(0, i * h / MAX_POINTS)); } // 左上3 for (int i = 1; i <= MAX_POINTS / 2; i++) { array.add(new Vector2(w * i / MAX_POINTS, h)); } return array; } }
package com.oahcfly.chgame.core.listener; public interface CHProgressBarListener { public void onStart (); public void onFinish (); }
基于Libgdx开发的开源游戏框架CHGame:
http://git.oschina.net/oahcfly/CHGameFrame