本篇隨筆記錄如何制作一個技能冷卻的圖標。拋磚引玉了,如需實際應用還得好好整理代碼。
表示技能冷卻,計時等無非就兩種吧,一是長條狀,參照/擴展PRogressbar即可,另外一個就是方形或者圓形的了吧。
很多有技能條的游戲UI一般都是用的是方形技能圖標,如魔獸世界,暗黑三,War3.....
在這里我們試著做一個出來,先看看效果圖吧(很次,見尿了,以后可以在指針和邊框上加上動畫或粒子效果)

該組件分為4層,從下往上依次為:技能原圖標ground,裁剪效果層,指針層,外邊框。當然也可以加入更多,或者只有兩層(ground和裁剪效果)。
上代碼:
1 public class ColdDownIcon extends Image { 2 3 4 private TextureRegion texture;//裁剪畫的紋理 5 private TextureRegion ground;//背景紋理,裁剪鏤空后要露出來的,也就是原始技能圖標 6 private TextureRegion outerRing;//圖標外圈 7 private Image handEffect;//指針效果,本來打算使用粒子效果的 8 9 private PolygonSpriteBatch polyBatch;//畫多邊形的 10 11 private Vector2 center; 12 private Vector2 centerTop;//從上面中間開始 13 private Vector2 leftTop; 14 private Vector2 leftBottom; 15 private Vector2 rightBottom; 16 private Vector2 rightTop; 17 private Vector2 progressPoint; 18 private float[] fv;//裁剪畫圖使用的點陣{point1.x,point1.y,point2.x,point2.y ......} 19 private Vector2 intersectPoint;//當前切割在邊上的點 20 21 //當前正在切割的位置 22 private IntersectAt intersectAt; 23 private float liveTime;//本次cd已執行時間 24 private float coldDownTime;//cd一次所需時間 25 private boolean startColdDown; 26 27 //當前切割位置的枚舉 28 public enum IntersectAt { 29 NONE, TOP, BOTTOM, LEFT, RIGHT; 30 } 31 32 public ColdDownIcon(TextureRegion ground,TextureRegion outerRing, PolygonSpriteBatch polyBatch,Image handEffect,float coldDownTime) 33 { 34 super(ground); 35 this.ground = ground; 36 this.texture = ground; 37 this.outerRing = outerRing; 38 this.polyBatch = polyBatch; 39 this.handEffect = handEffect; 40 this.handEffect.setVisible(false); 41 42 handEffect.setOrigin(this.getWidth()/2,this.getHeight()/2); 43 this.coldDownTime = coldDownTime; 44 //計算各點內部坐標 45 center = new Vector2(this.getWidth()/2, this.getHeight()/2); 46 centerTop = new Vector2(this.getWidth()/2, this.getHeight()); 47 leftTop = new Vector2(0, this.getHeight()); 48 leftBottom = new Vector2(0, 0); 49 rightBottom = new Vector2(this.getWidth(), 0); 50 rightTop = new Vector2(this.getWidth(), this.getHeight()); 51 progressPoint = new Vector2(this.getWidth()/2, this.getHeight()/2); 52 53 setColor(Color.RED); 54 55 setPercentage(0); 56 } 57 58 public void startColdDown(){ 59 this.startColdDown = true; 60 this.liveTime = 0; 61 this.setPercentage(0); 62 this.handEffect.setVisible(true); 63 } 64 65 public void endColdDown(){ 66 this.startColdDown = false; 67 this.liveTime = 0; 68 this.setPercentage(0); 69 this.handEffect.setVisible(false); 70 } 71 72 //計算切線的最遠點 73 private Vector2 IntersectPoint(Vector2 line) 74 { 75 Vector2 v = new Vector2(); 76 boolean isIntersect; 77 78 //check top 79 isIntersect = Intersector.intersectSegments(leftTop, rightTop, center, line, v);//切割線和上邊的交點v 80 81 //check bottom 82 if (isIntersect) { intersectAt = IntersectAt.TOP; return v; } 83 else isIntersect = Intersector.intersectSegments(leftBottom, rightBottom, center, line, v); 84 85 //check left 86 if (isIntersect) { intersectAt = IntersectAt.BOTTOM; return v; } 87 else isIntersect = Intersector.intersectSegments(leftTop, leftBottom, center, line, v); 88 89 //check bottom 90 if (isIntersect) { intersectAt = IntersectAt.LEFT; return v; } 91 else isIntersect = Intersector.intersectSegments(rightTop, rightBottom, center, line, v); 92 93 if (isIntersect) { intersectAt = IntersectAt.RIGHT; return v; } 94 else 95 { 96 intersectAt = IntersectAt.NONE; 97 return null; 98 } 99 }100 101 //設置百分比,順時針102 private void setPercentage(float percent)103 {104 //100 % = 360 degree105 //==> percent % => (percent * 360 / 100) degree106 107 float angle = convertToRadians(90); //percent = 0 => angle = -90108 angle -= convertToRadians(percent * 360 / 100);109 110 float len = this.getWidth() > this.getHeight() ? this.getWidth() : this.getHeight();111 float dy = (float) (Math.sin(angle) * len);112 float dx = (float) (Math.cos(angle) * len);113 Vector2 line = new Vector2(center.x + dx, center.y + dy);114 115 intersectPoint = IntersectPoint(line);116 //117 float l = intersectPoint.dst(center.x,center.y);118 float sy = 2*l/getHeight();119 120 handEffect.setScaleY(sy);121 122 123 if (intersectAt == IntersectAt.TOP)124 {125 if (intersectPoint.x >= this.getWidth()/2) //126 {127 //128 fv = new float[] {129 center.x,130 center.y,131 centerTop.x,132 centerTop.y,133 leftTop.x,134 leftTop.y,135 leftBottom.x,136 leftBottom.y,137 rightBottom.x,138 rightBottom.y,139 rightTop.x,140 rightTop.y,141 intersectPoint.x,142 intersectPoint.y143 };144 }145 else146 {147 fv = new float[] { // c?t bên trái c?nh148 center.x,149 center.y,150 centerTop.x,151 centerTop.y,152 intersectPoint.x,153 intersectPoint.y154 };155 156 }157 }158 else if (intersectAt == IntersectAt.BOTTOM)159 {160 fv = new float[] {161 center.x,162 center.y,163 centerTop.x,164 centerTop.y,165 leftTop.x,166 leftTop.y,167 leftBottom.x,168 leftBottom.y,169 intersectPoint.x,170 intersectPoint.y171 };172 173 }174 else if (intersectAt == IntersectAt.LEFT)175 {176 fv = new float[] {177 center.x,178 center.y,179 centerTop.x,180 centerTop.y,181 leftTop.x,182 leftTop.y,183 intersectPoint.x,184 intersectPoint.y185 };186 187 }188 else if (intersectAt == IntersectAt.RIGHT)189 {190 fv = new float[] {191 center.x,192 center.y,193 centerTop.x,194 centerTop.y,195 leftTop.x,196 leftTop.y,197 leftBottom.x,198 leftBottom.y,199 rightBottom.x,200 rightBottom.y,201 intersectPoint.x,202 intersectPoint.y203 };204 }205 else // if (intersectAt == IntersectAt.NONE)206 {207 //不繪制208 fv = null;209 }210 }211 212 //重新繪制函數213 @Override214 public void draw(Batch batch, float parentAlpha) {215 // super.draw(batch, parentAlpha);216 batch.draw(ground,this.getX(),this.getY());217 218 if (fv != null&&this.startColdDown) {//畫裁剪了的圖219 batch.end(); //注意這里!!!先把原來的停掉220 drawMe();221 batch.begin(); //注意這里!!再開始!222 }223 if(handEffect.isVisible()){224 handEffect.setX(this.getX());225 handEffect.setY(this.getY());226 handEffect.draw(batch,parentAlpha);227 }228 229 batch.draw(outerRing,this.getX(),this.getY());230 }231 232 @Override233 public void act(float delta) {234 super.act(delta);235 if(this.startColdDown){//開始冷卻了,計時236 this.liveTime = this.liveTime+delta;237 if(this.liveTime>this.coldDownTime){//超出停止238 this.endColdDown();239 }else{240 float percent = this.liveTime*100/this.coldDownTime;241 this.setPercentage(percent);242 handEffect.setVisible(true);243 handEffect.setRotation(-percent * 360 / 100);244 }245 }246 }247 248 //按點陣列區域繪制圖像249 public void drawMe()250 {251 //裁剪252 EarClippingTriangulator e = new EarClippingTriangulator();253 ShortArray sv = e.computeTriangles(fv);254 255 //創建 polygonRegion.256 PolygonRegion polyReg = new PolygonRegion( texture, fv, sv.toArray());257 258 //創建 polySprite.259 PolygonSprite poly = new PolygonSprite(polyReg);260 261 //(position, origin, rotation, color)262 poly.setOrigin(this.getOriginX(), this.getOriginY());263 poly.setPosition(this.getX(), this.getY());264 poly.setRotation(this.getRotation());265 poly.setColor(this.getColor());266 267 //繪制268 polyBatch.begin();269 poly.draw(polyBatch);270 polyBatch.end();271 }272 273 274 275 //-----------------------------------------------------------------276 277 278 float convertToDegrees(float angleInRadians)279 {280 float angleInDegrees = angleInRadians * 57.2957795f;281 return angleInDegrees;282 }283 284 float convertToRadians(float angleInDegrees)285 {286 float angleInRadians = angleInDegrees * 0.0174532925f;287 return angleInRadians;288 }289 290 }public class ColdDownTester2 extends applicationAdapter implements InputProcessor { private Stage stage; private static final Logger LOGGER = new Logger(ColdDownTester2.class.getName(),Application.LOG_DEBUG); private Texture ground; private Texture hand; private Texture outerRing; private ColdDownIcon icon; @Override public void create () { Gdx.app.setLogLevel(Application.LOG_DEBUG); stage = new Stage(); Gdx.input.setInputProcessor(this); ground = new Texture(Gdx.files.internal("frostbolt-1.png")); hand = new Texture(Gdx.files.internal("frostbolt-3.png")); outerRing = new Texture(Gdx.files.internal("frostbolt-4.png")); icon = new ColdDownIcon(new TextureRegion(ground),new TextureRegion(outerRing),new PolygonSpriteBatch(),new Image(hand),3.0f); stage.addActor(icon); } @Override public void render () { Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); stage.act(); stage.draw(); } @Override public void dispose() { stage.dispose(); super.dispose(); } @Override public void resize(int width, int height) { stage.getViewport().update(width,height); super.resize(width, height); } @Override public boolean keyDown(int keycode) { if(keycode == Input.Keys.J){ icon.startColdDown(); } return false; } @Override public boolean keyUp(int keycode) { return false; } @Override public boolean keyTyped(char character) { return false; } @Override public boolean touchDown(int screenX, int screenY, int pointer, int button) { return false; } @Override public boolean touchUp(int screenX, int screenY, int pointer, int button) { return false; } @Override public boolean touchDragged(int screenX, int screenY, int pointer) { return false; } @Override public boolean mouseMoved(int screenX, int screenY) { return false; } @Override public boolean scrolled(int amount) { return false; }}
裁剪計算代碼參考了越南人的代碼,從哪來的找不到了,官網應該也有。
以上代碼只為了顯示看看效果,并未關心dispose等。icon代碼仍需完善,縮放旋轉現在應該是有問題的,自行解決,無關本文了。
新聞熱點
疑難解答