Hi,
Its been long since we wrote any post about game development. In this post we would be writing a snippet to draw a part of image which can be used to simulate effect of health bar we see in game huds.
We needed to this functionality in our next game though in a little different way so we thought we would just share it. This post is more specific to Libgdx but the basic concept can be used in any other library which has similar features.
Lets start with the beginning. We have a textureatlas which has our bar which we need to show on the screen and reduce its size depending on the health (or mana) of the character .
[sourcecode language=”java”]
TextureAtlas atlas;
TextureRegion healthBarRegion;
void Init(){
//Assuming you have the image pack generated from texturepacker named as "imagepack"
//is located in assets/images/ folder.
String texturedir="images/";
String texturefile=texturedir+"/imagepack";
atlas=new TextureAtlas(Gdx.files.internal(texturefile),Gdx.files.internal(texturedir));
healthBarRegion=atlas.findRegion("healthbar"); //Assuming the image of health bar name is "healthbar"
}
[/sourcecode]
If you have no idea about textureatlas, textureregion please check one of our older tuts where we explained about them and the tool TexturePacker. We will make a new class to handle texture and call it TexturePart (naming convention is not my forte).
[sourcecode language=”java”]
public class TexturePart{
Texture tex;
Vector2 position;
//Target Dimension of image
int targetWidth;
int targetHeight;
//Src Dimensions of Image
int srcWidth;
int srcHeight;
int srcX;
int srcY;
//Ratio of dimension of target and source
float srcTargetRatioX;
float srcTargetRatioY;
//ImagePart variables with values between 0-100 to draw part of image
int startPercentX;
int endPercentX;
int startPercentY;
int endPercentY;
int clipWidth;
int clipHeight;
int clipSrcWidth;
int clipSrcHeight;
public TexturePart(TextureRegion reg,float x,float y){
tex=reg.getTexture();
position=new Vector2(x,y);
srcX=reg.getRegionX();
srcY=reg.getRegionY();
srcWidth=region.getRegionWidth();
srcHeight=region.getRegionHeight();
clipSrcWidth=srcWidth;
clipSrcHeight=srcHeight;
startPercentX=0;
startPercentY=0;
endPercentX=100;
endPercentY=100;
SetTargetDimension(srcWidth,srcHeight);
}
public void SetTargetDimension(int targetWidth,int targetHeight){
this.targetWidth=targetWidth;
this.targetHeight=targetHeight;
clipWidth=targetWidth;
clipHeight=targetHeight;
srcTargetRatioX=(float)targetWidth/(float)srcWidth;
srcTagetRaioY=(float)targetHeight/(float)srcHeight;
}
public void SetStart(int x,int y){
startPercentX=x;
startPercentY=y;
}
public void SetEnd(int x,int y){
endPercentX=x;
endPercentY=y;
}
public void Draw(SpriteBatch sp){
clipSrcWidth=(int)(Math.abs(startXPercent-endXPercent)/100f*srcWidth);
clipSrcHeight=(int)(Math.abs(startYPercent-endYPercent)/100f*srcHeight);
int startX=srcX+(int)((float)startXPercent/100f*(float)srcX);
int startY=srcY+(int)((float)startYPercent/100f*(float)srcY);
clipWidth=(int) (srcTargetRatioX*clipSrcWidth);
clipHeight=(int) (srcTargetRatioY*clipSrcHeight);
sp.draw(tex, position.x-targetWidth/2, position.y-targetHeight/2, targetWidth/2, targetHeight/2,
clipWidth, clipHeight,1, 1, 0, startX, startY,
clipSrcWidth, clipSrcHeight, false, false);
}
}
[/sourcecode]
Thats it. You will see so many many fields in the class. Well I could not help it. Sorry. So we have a textureregion (suppose a 80×20 image of health bar!) and it looks like this. Awesome ain’t it?
Now in your game you might need to draw it as maybe of dimensions 60×15. And then when the you suffer injury you want to show part of the image until it reaches 0 and the hero dies and game over sign pops up.
So srcX and srcY are holding x,y position of image in the atlas file created from TextureWrapper. SrcWidth and srcHeight are fields holding the width and height of the original file (for our example it would be 80 and 20).
To have the power to draw any part of the image we had added four fields. startPercentX and startPercentY hold the starting point (in percentage) for the image and endPercentX and endPercentY hold the end point of the part of the image we want to draw. Lets say we want to draw mid half of image 25 to 75 in x dimension then we would have values as starPercentX=25,endPercentX=75 and startPercentY=0 and endPercentY=100. Which would like this.
But mostly for health bar startPercentX will be 0 and you would be modifying the endPercentX values to show less/full health.
Now we have clipSrcWidth,clipSrcHeigth, clipWidth and clipHeightfields. Well clipSrcWidth and clipSrcHeight are to store the width/height of the part of original image we are going to draw.
So for example if we are drawing the above image we would have width as srcWidth*(75-25)/100 which is half width (40 pixels) of the actual source image width (80 pixels). clipWidth and clipHeight store the dimensions of the target image which will be drawn on screen. Target image width is 60. So clipWdith would be (60/80*30) which when truncated comes to 22. (Note for target image width and height we can use float values).
Nows lets dissect the big function call.
[sourcecode language=”java”]
sp.draw(tex, // The texture
position.x-targetWidth/2, //The left of image
position.y-targetHeight/2, //The bottom of image
targetWidth/2, //Pivot Point(X), used for rotating the image
targetHeight/2, //Pivot Point(X), used for rotating the image
clipWidth, //The final size(Width) of the image part to be drawn
clipHeight, //The final size(Width) of the image part to be drawn
1, //Scale in x dimension
1, //Scale in y dimension
0, //Rotation in degrees
startX, //The left of part of the original image in textureatlas
startY, //The bottom of part of the original image in textureatlas
clipSrcWidth, //the width of part of the original image in textureatlas
clipSrcHeight, //the height of part of the original image in textureatlas
false, //flip image in x direction
false //flip image in y direction
);
[/sourcecode]
So that was a really simple function call after all. Well there are optimizations that can be done in the above class for example removing the part of calculating clip dimensions from draw function and calling that part whenever image part size is changed. But this was just to get you started and hopefully this will help you.
Thats all. We are also learning while making games so if you find anything which is wroing/inefficient and have an alternative solution please post it in comments section. Also any feedback/queires are appreciated.
Thanks for the patience.
How about a filled rectangle which is best?
Hi,
Depends on the requirements i guess. If you need a flat color image then drawing filled rectangle would be better but if you want to show bit more styled health/energy bar then an image would be better.
There is a bug on line 74 and 75, it should be srcWidth and srcHeight instead of srcX and srcY in the end of the lines.
Nice class otherwise 🙂
I dont remember well but as it is calculating the starting point of image to be taken from atlas, it should be srcX and srcY, but i will confirm and get back
Is there any eclipse demo project for this? Still not quite sure how to do … Thanks!
Hi,
For this we dont have any eclipse demo project. Can you tell what is the issue you are having?
Thanks
So: I just need new TexturePart, and then in the place where need to change the size, call method like SetEnd(int x,int y), then OK? Seems some typo in the above source code … Thanks
I guess the targetWidth and targetHeight were not set any value by default which may be causing some issue. So either call SetTargetDimension() with some values or add one line in constructor -> SetTargetDimension(srcWidth,srcHeight); as edited above. Then it should work.
Thanks
I fixed the type, and below are the code with no typo:
package com.jaygame;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
public class TexturePart {
Texture tex;
Vector2 position;
// Target Dimension of image
float targetWidth;
float targetHeight;
// Src Dimensions of Image
int srcWidth;
int srcHeight;
int srcX;
int srcY;
// Ratio of dimension of target and source
float srcTargetRatioX;
float srcTargetRatioY;
// ImagePart variables with values between 0-100 to draw part of image
float startPercentX;
float endPercentX;
float startPercentY;
float endPercentY;
float clipWidth;
float clipHeight;
int clipSrcWidth;
int clipSrcHeight;
public TexturePart(TextureRegion reg, float x, float y) {
tex = reg.getTexture();
position = new Vector2(x, y);
srcX = reg.getRegionX();
srcY = reg.getRegionY();
srcWidth = reg.getRegionWidth();
srcHeight = reg.getRegionHeight();
clipSrcWidth = srcWidth;
clipSrcHeight = srcHeight;
startPercentX = 0;
startPercentY = 0;
endPercentX = 100;
endPercentY = 100;
}
public void SetTargetDimension(float targetWidth, float targetHeight) {
this.targetWidth = targetWidth;
this.targetHeight = targetHeight;
clipWidth = targetWidth;
clipHeight = targetHeight;
srcTargetRatioX = (float) targetWidth / (float) srcWidth;
srcTargetRatioY = (float) targetHeight / (float) srcHeight;
}
public void SetStart(int x, int y) {
startPercentX = x;
startPercentY = y;
}
public void SetEnd(float x, float y) {
endPercentX = x;
endPercentY = y;
}
public void Draw(SpriteBatch sp) {
clipSrcWidth = (int) (Math.abs(startPercentX – startPercentY) / 100f * srcWidth);
clipSrcHeight = (int) (Math.abs(startPercentY – endPercentY) / 100f * srcHeight);
int startX = srcX + (int) ((float) startPercentX / 100f * (float) srcX);
int startY = srcY + (int) ((float) startPercentY / 100f * (float) srcY);
clipWidth = (int) (srcTargetRatioX * clipSrcWidth);
clipHeight = (int) (srcTargetRatioY * clipSrcHeight);
sp.draw(tex, position.x – targetWidth / 2, position.y – targetHeight
/ 2, targetWidth / 2, targetHeight / 2, clipWidth, clipHeight,
1, 1, 0, startX, startY, clipSrcWidth, clipSrcHeight, false,
false);
}
}
Fixed version: (Tested with latest nightly)
public class TexturePart
{
Texture tex;
Vector2 position;
// Target Dimension of image
int targetWidth;
int targetHeight;
// Src Dimensions of Image
int srcWidth;
int srcHeight;
int srcX;
int srcY;
// Ratio of dimension of target and source
float srcTargetRatioX;
float srcTargetRatioY;
// ImagePart variables with values between 0-100 to draw part of image
int startPercentX;
int endPercentX;
int startPercentY;
int endPercentY;
int clipWidth;
int clipHeight;
int clipSrcWidth;
int clipSrcHeight;
public TexturePart(TextureRegion reg, float x, float y)
{
tex = reg.getTexture();
position = new Vector2(x, y);
srcX = reg.getRegionX();
srcY = reg.getRegionY();
srcWidth = reg.getRegionWidth();
srcHeight = reg.getRegionHeight();
clipSrcWidth = srcWidth;
clipSrcHeight = srcHeight;
startPercentX = 0;
startPercentY = 0;
endPercentX = 100;
endPercentY = 100;
SetTargetDimension(srcWidth, srcHeight);
}
public void SetTargetDimension(int targetWidth, int targetHeight)
{
this.targetWidth = targetWidth;
this.targetHeight = targetHeight;
clipWidth = targetWidth;
clipHeight = targetHeight;
srcTargetRatioX = (float) targetWidth / (float) srcWidth;
srcTargetRatioY = (float) targetHeight / (float) srcHeight;
}
public void SetStart(int x, int y)
{
startPercentX = x;
startPercentY = y;
}
public void SetEnd(int x, int y)
{
endPercentX = x;
endPercentY = y;
}
public void Draw(SpriteBatch sp)
{
clipSrcWidth = (int) (Math.abs(startPercentX – endPercentX) / 100f * srcWidth);
clipSrcHeight = (int) (Math.abs(startPercentX – endPercentY) / 100f * srcHeight);
int startX = srcX + (int) ((float) startPercentX / 100f * (float) srcX);
int startY = srcY + (int) ((float) startPercentY / 100f * (float) srcY);
clipWidth = (int) (srcTargetRatioX * clipSrcWidth);
clipHeight = (int) (srcTargetRatioY * clipSrcHeight);
sp.draw(tex, position.x – targetWidth / 2, position.y – targetHeight / 2, targetWidth / 2, targetHeight / 2, clipWidth, clipHeight, 1, 1, 0, startX, startY, clipSrcWidth, clipSrcHeight, false, false);
}
}