What I try to make is:
Draw a BitmapFont on the top of the screen and let it go down to the bottom, where it gets deleted.
While that BitmapFont is still making its way down, draw another BitmapFont with different text.
Repeat 1 and 2.
Is this achievable with one BitmapFont or do I have to make multiple BitmapFonts in order for this to work?
EDIT:
private BitmapFont font;
public PlayState(GameStateManager gsm) {
super(gsm);
cam.setToOrtho(false, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
// TODO: change the font of the random word
font = new BitmapFont();
font.setColor(Color.BLACK);
#Override
public void render(SpriteBatch batch) {
batch.setProjectionMatrix(cam.combined);
batch.begin();
for (Rectangle word1 : word.words) {
font.draw(batch, word.getWordString(), word1.x, word1.y);
}
batch.end();
}
word.getWordString() is the text I want to show, which changes with every loop. What it does now is change the text of the newly drawn word that spawns at the top, but also the previous one.
EDIT2:
public class Word {
public Array<Rectangle> words;
public Word(){
words = new Array<Rectangle>();
spawnWord();
}
public void spawnWord() {
Rectangle word = new Rectangle();
word.x = MathUtils.random(0, Gdx.graphics.getWidth() / 2 - 64);
word.y = Gdx.graphics.getHeight();
words.add(word);
}
public String getWordString(){
return wordString;
}
}
You are looping over the Rectangle objects in word.words and getting the coordinates from word1 (the object you get from the array) but you call word.getWordString() for them all. The unless you change the value of word.getWordString() inside the loop the string you get will be the same. That is why you have the same string for all your objects.
If you want to have different text on eatch word1 object you need to store them on eatch word1.
Right now it looks like you are just using the Rectangle class to keep track of its position.
If you make a class called Word with a Rectangle and a String on it you can get the result you want.
public class Word{
private Rectangle bound;
private String wordString;
public Word(Rectangle bound, String wordString){
this.bound = bound;
this.theWord = wordString;
}
public String getWordString(){
return wordString;
}
public Rectangle getBound(){
return bound;
}
public void updateBound(float x, float y){
bound.x = x;
bound.y = y;
}
}
Then store your Word objects in an array called words and loop over them like this:
batch.begin();
for (Word word : words) {
font.draw(batch, word.getWordString(), word.getBound().x, word.getBound.y);
}
batch.end();
EDIT1
I made a quick example. pastebin I still can't see how you get your wordString, but this way you store it on every Word object. You don't have to keep track of a String and changing it all the time. You create a Word with a string and thats it.
This also make it possible to remove a certain word from the screen or change the text on a specific object.
[EDIT2]
I made a quick example project:
link
Related
I'm trying to animate all the actors in a stage but FadeOut Action doesn't work
although the other actions work fine
I have the following code
getRoot().setPosition(Gdx.graphics.getWidth(), 0);
getRoot().getColor().a = 0;
SequenceAction sequenceAction = new SequenceAction();
sequenceAction.addAction(Actions.moveTo(0, 0, 3.0f));
sequenceAction.addAction(Actions.fadeOut(3.0f));
getRoot().addAction(sequenceAction);
I also tried another way to animate them by TweenEngine using class GroupAccessor implements TweenAccessor<Group> and All work fine except manipulating alpha value
public class GroupAccessor implements TweenAccessor<Group> {
public static final int ALPHA = 0;
public static final int POS_XY = 1;
#Override
public int getValues(Group target, int tweenType, float[] returnValues) {
switch (tweenType) {
case ALPHA :
returnValues[0] = target.getColor().a;
return 1;
case POS_XY :
returnValues[0] = target.getX();
returnValues[1] = target.getY();
return 2;
default:
assert false;
return -1;
}
}
#Override
public void setValues(Group target, int tweenType, float[] newValues) {
switch (tweenType) {
case ALPHA :
target.setColor(target.getColor().r, target.getColor().g, target.getColor().b,
newValues[0]);
break;
case POS_XY :
target.setPosition(newValues[0], newValues[1]);
break;
default:
assert false;
}
}
}
Animation using TweenEngine
Timeline.createSequence().
push(Tween.set(getRoot(), GroupAccessor.POS_XY).target(Gdx.graphics.getWidth(),0))
.push(Tween.to(getRoot(), GroupAccessor.POS_XY, 3.0f).target(0,0))
.push(Tween.set(getRoot(), GroupAccessor.ALPHA).target(0))
.push(Tween.to(getRoot(), GroupAccessor.ALPHA, 3.0f).target(1)).start(manager);
I have multiple actors and I made arrays of some of these actors. Here are 2 examples of draw() method of 2 actors
public void draw(Batch batch, float parentAlpha) {
batch.draw(wallSprite, 0, 0, Constants.WIDTH, Constants.HEIGHT);
}
public void draw(Batch batch, float parentAlpha) {
batch.draw(gearSprite, getX(), getY(),
gearSprite.getOriginX(), gearSprite.getOriginY(), gearSprite.getWidth(),
gearSprite.getHeight(), gearSprite.getScaleX(), gearSprite.getScaleY()
, gearSprite.getRotation());
try {
batch.draw(permittedCSprite, getX() + gearSprite.getWidth()/2
- permittedCSprite.getWidth()/2, getY() +
gearSprite.getHeight()/2 - permittedCSprite.getHeight()/2,
permittedCSprite.getWidth(), permittedCSprite.getHeight());
}
catch (NullPointerException e) {}
}
The draw method of an Actor absolutely must set a color on the Batch. This is necessary to support fading. Even if you don't care about fading or changing the color of the actor, you would still need to change the color of the Batch to WHITE to ensure what color the actor is drawn with (because there is no guarantee of what color some other actor has left the batch at.
Typically, you would have something like this at the top of your draw method:
public void draw (Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
//...
}
However, if your Actor is drawing a Sprite instead of a TextureRegion, then you must set the color on the Sprite instead. This is because sprites have their own color which they pass on to the batch when you call sprite.draw.
If you are using a Sprite (which I don't recommend with Actors), your draw method should look something like this:
public void draw (Batch batch, float parentAlpha) {
sprite.setColor(getColor());
sprite.draw(batch, parentAlpha);
}
One "gotcha" is that Sprite is a subclass of TextureRegion. Sprite is a TextureRegion that holds information about color, size, orientation, position, etc, so it can be drawn a little bit faster (possibly) than a TextureRegion. It does this at the cost of using more memory. If you are using a Sprite, you should be calling sprite.draw not batch.draw(sprite ...), because the second option treats the sprite as a TextureRegion.
If you want to use Sprite, then you need to apply all the attributes you need to the sprite. For example:
public void draw (Batch batch, float parentAlpha) {
sprite.setColor(getColor());
sprite.setPosition(getX(), getY());
sprite.setSize(getWidth(), getHeight());
//etc. etc. etc. :(
sprite.draw(batch, parentAlpha);
}
For this reason, it really doesn't make sense to use a Sprite in an Actor at all. Way too much redundancy. Just use TextureRegions.
So I am using the libgdx framework and am new to android games development. I have an array of yellow circle objects stored in each index which are shown on the game screen when I run them, all the circle objects are in different x positions but on the same y axis. I want to basically set the visibility of each circle for a given amount of time before the next one becomes visible for say 1000 ms per circle. So for example circle 1 will be visible for 1000 ms then it will become invisible and circle 2 will then become visible for 1000ms so on and so forth, till I reach the end of the list.
public class Spot {
private float x;
private float y;
private float radius;
private Circle spot;
private Circle spotList[];
public Spot(float x, float y, float radius, int amount){
this.x = x;
this.y = y;
this.radius = radius;
spot = new Circle(x,y,radius);
spotList = new Circle[amount];
for(int i = 0; i < spotList.length; i++){
spotList[i] = new Circle(this.x+(i*15), this.y, this.radius);
}
}
public void update(float delta){
}
public Float getX(){
return x;
}
public Float getY(){
return y;
}
public float getRadius(){
return radius;
}
public Circle[] getSpots(){
return spotList;
}
public Circle getSpot(){
return spot;
}
}
The rendering of the shape has been outsourced to a different class I want to be able to handle the visibility of the circles etc in the update method.
Since you're not using Actors (https://github.com/libgdx/libgdx/wiki/Scene2d), you need to keep track of time yourself and make the circles visible/not visible.
You will want to add
private float elapsedTime = 0.0f;
private float currentCircle = 0;
two fields, one for keeping track of elapsed time and one for the currently visible circle.
public void update(float delta){
elapsedTime += delta;
if (elapsedTime >= 1.0f) { // more than one second passed
spot[currentCircle].setVisible(false);
if (currentCircle + 1 < spotList.size()) {
currentCircle++;
spot[currentCircle].setVisible(true);
elapsedTime -= 1.0f; // reset timer (don't just set to 0.0f to avoid time shifts)
}
}
}
In the update method, count elapsedTime and if more then one seconds passed, make old circle not visible and new circle visible. Also make sure that initially the first circle is visible and that the timer is 0.
A possible solution is to use the Universal Tween Engine which works well with the libGDX framework. Visit the link to see how to include it in your project and for documentation.
As a quick demonstration on how to use it:-
Create and instantiate a TweenManager and your array of Spots in your class responsible for rendering.
protected TweenManager tweenManager = new TweenManager();
Spot spots[] = new Spot[...];
...
Create your timer by implementing the TweenCallback()
final int numberOfSpots = 5;
int spotArrayIndex = 0;
float timeDelay = 1000f; /*1000 milliseconds*/
Tween.call(new TweenCallback() {
#Override
public void onEvent(int type, BaseTween<?> source) {
/*set or unset the visibility of your Spot objects here i.e. */
spots[spotArrayIndex++].setVisible(false); /*Set current spot invisible*/
spots[spotArrayIndex].setVisible(true); /*Set next spot to be visible*/
}
}).repeat(numberOfSpots, timeDelay).start(tweenManager);
Update the tweenManager in your render method
public void render(float delta) {
...
tweenManger.update(delta);
...
}
Please check the code for errors if you use it, as I am not sure of the rest of your code.
I have a sprite that is supposed to act like a loadbar. I have tried this by using an example image that has been created like a 9patch-type (http://cdn.dibbus.com/wp-content/uploads/2011/03/btn_black.9.png). It seems okay in the start, but as the width of the sprite increases the sprite starts to look pixeled. Anyone know what the problem could be, or have any solution? The code is shown below.
public Sprite loaded;
public void init()
{
atlas = new TextureAtlas(Gdx.files.
internal("data/misc/menu_button.pack"));
loaded = atlas.createSprite("loadbar");
loaded.setPosition((Misc.WIDTH/2) - unloaded.getWidth()/2,
Misc.HEIGTH - unloaded.getHeight());
}
public void draw_load_bar() //render function
{
if(loaded.getWidth() < 600)
{
loaded.setSize(loaded.getWidth()+ 0.5f, loaded.getHeight());
}
loaded.draw(batch);
}
Dont use a Sprite to stretch it. I'd recommend a real Ninepatch from libgdx for it.
public NinePatch loaded;
private float posX, posY, width, height;
public void init()
{
loaded = new NinePatch(the Texture here,10, 10, 10, 10); //bounds outside
//set right pos here...
}
public void draw_load_bar(SpriteBatch batch) //render function
{
if(loaded.getWidth() < 600)
{
//update the size of it here (guess pos is static)
width++;
}
//need to parse the batch and the right sizes.
loaded.draw(batch, posx, posy, width, height);
}
after that you can handle it like a Sprite or Texture but it does stratch right without issues. If you want to the full Picture to be Stretched simply do net set any bounds at the creation new NinePatch(texture, 0,0,0,0)
new to android dev and andengine in general. trying to animate a sprite using the AndEngineTexturePackerExtension but im unsure how the tiledTextureRegion gets created for the animated sprite. below is what im trying which i have gotten from guides and other posts in this forum. Im creating the xml,png and java from texturepacker
private TexturePackTextureRegionLibrary mSpritesheetTexturePackTextureRegionLibrary;
private TexturePack texturePack;
try
{
TexturePackLoader texturePackLoader = new TexturePackLoader(activity.getTextureManager());
texturePack = texturePackLoader.loadFromAsset(activity.getAssets(), "spritesheet.xml");
texturePack.loadTexture();
mSpritesheetTexturePackTextureRegionLibrary = texturePack.getTexturePackTextureRegionLibrary();
}
catch (TexturePackParseException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
TexturePackerTextureRegion textureRegion = mSpritesheetTexturePackTextureRegionLibrary.
get(spritesheet.00000_ID);
TiledTextureRegion tiledTextureRegion = TiledTextureRegion.create(texturePack.getTexture(),
textureRegion.getSourceX(), textureRegion.getSourceY(),
textureRegion.getSourceWidth() , textureRegion.getSourceHeight() ,
COLUMNS, ROWS);
AnimatedSprite sprite = new AnimatedSprite((activity.CAMERA_WIDTH - tiledTextureRegion.getWidth()) / 2,
(activity.CAMERA_HEIGHT - tiledTextureRegion.getHeight()) / 2,
tiledTextureRegion, activity.getVertexBufferObjectManager());
the problem is that i dont understand where the values from COLUMNS and ROWS comes from? the sprite sheet itself has uneven rows and columns as it includes rotated sprites etc. So im confused as to where these values come from. Any help on getting this working would be great thanks
edit: Ok i can get the sprite sheet animation working if i just use the basic algorithm within texture packer and not the MaxRects algorithm. But this doesnt make use of all the space within a sheet so i would rather get it working using a MaxRects generated sprite sheet. I see with in the xml that it pass a bool for being rotated or not so the information is there to make this work i just cant figure out how. how do i use a texturepackertexture region to make an animated sprite when some of the textures are rotated on the sheet
TexturePacker doesn't know how much columns and rows have your sprites, not even if they are tiled or not, it just packs everything into a single spritesheet (png file for example). Also, its goal isn't to create TiledSprites from separated Sprites.
So, in order to get back a TiledSprite (or AnimatedSprite) from a spritesheet, you have to know how much columns and rows (it can be hardcoded somewhere) it had before being put into the spritesheet, since TexturePacker won't give you that kind of information.
I personally use a TextureRegionFactory which looks like this:
public class TexturesFactory {
public static final String SPRITESHEET_DIR = "gfx/spritesheets/";
public static final String TEXTURES_DIR = SPRITESHEET_DIR+"textures/";
private TexturePack mTexturePack;
private TexturePackTextureRegionLibrary mTextureRegionLibrary;
/**
*
* #param pEngine
* #param pContext
* #param filename
*/
public void loadSpritesheet(Engine pEngine, Context pContext, String filename) {
try {
this.mTexturePack = new TexturePackLoader(
pEngine.getTextureManager(), TEXTURES_DIR).loadFromAsset(
pContext.getAssets(), filename);
this.mTextureRegionLibrary = this.mTexturePack.getTexturePackTextureRegionLibrary();
this.mTexturePack.getTexture().load();
} catch (TexturePackParseException ex) {
Log.e("Factory", ex.getMessage(), ex);
}
}
public TextureRegion getRegion(int id) {
return this.mTextureRegionLibrary.get(id);
}
public TiledTextureRegion getTiled(int id, final int rows, final int columns) {
TexturePackerTextureRegion packedTextureRegion = this.mTextureRegionLibrary.get(id);
return TiledTextureRegion.create(
packedTextureRegion.getTexture(),
(int) packedTextureRegion.getTextureX(),
(int) packedTextureRegion.getTextureY(),
(int) packedTextureRegion.getWidth(),
(int) packedTextureRegion.getHeight(),
columns,
rows);
}
}
Edit: About the rotated problem, it is written inside the xml generated by TexturePacker, so you can get it by calling
TexturePackerTextureRegion packedTextureRegion = this.mTextureRegionLibrary.get(id);
packedTextureRegion.isRotated()
Then, you can create the tiledTextureRegion according to that value with:
TiledTextureRegion.create(
packedTextureRegion.getTexture(),
(int) packedTextureRegion.getTextureX(),
(int) packedTextureRegion.getTextureY(),
(int) packedTextureRegion.getWidth(),
(int) packedTextureRegion.getHeight(),
columns,
rows,
packedTextureRegion.isRotated());
Also, I hope it is clear to you that TexturePacker isn't meant to create tiled sprites. You must create your tiled sprites (nice fit or rows and columns) before using TexturePacker.
I had a small question.If i want to make a man run in android one way of doing this is to get images of the man in different position and display them at different positions.But often,this does not work very well and it appears as two different images are being drawn.Is there any other way through which i can implement custom animation.(Like create a custom image and telling one of the parts of this image to move).
The way i do it is to use sprite sheets for example (Not my graphics!):
You can then use a class like this to handle your animation:
public class AnimSpriteClass {
private Bitmap mAnimation;
private int mXPos;
private int mYPos;
private Rect mSRectangle;
private int mFPS;
private int mNoOfFrames;
private int mCurrentFrame;
private long mFrameTimer;
private int mSpriteHeight;
private int mSpriteWidth;
public AnimSpriteClass() {
mSRectangle = new Rect(0,0,0,0);
mFrameTimer =0;
mCurrentFrame =0;
mXPos = 80;
mYPos = 200;
}
public void Initalise(Bitmap theBitmap, int Height, int Width, int theFPS, int theFrameCount) {
mAnimation = theBitmap;
mSpriteHeight = Height;
mSpriteWidth = Width;
mSRectangle.top = 0;
mSRectangle.bottom = mSpriteHeight;
mSRectangle.left = 0;
mSRectangle.right = mSpriteWidth;
mFPS = 1000 /theFPS;
mNoOfFrames = theFrameCount;
}
public void Update(long GameTime) {
if(GameTime > mFrameTimer + mFPS ) {
mFrameTimer = GameTime;
mCurrentFrame +=1;
if(mCurrentFrame >= mNoOfFrames) {
mCurrentFrame = 0;
}
}
mSRectangle.left = mCurrentFrame * mSpriteWidth;
mSRectangle.right = mSRectangle.left + mSpriteWidth;
}
public void draw(Canvas canvas) {
Rect dest = new Rect(getXPos(), getYPos(), getXPos() + mSpriteWidth,
getYPos() + mSpriteHeight);
canvas.drawBitmap(mAnimation, mSRectangle, dest, null);
}
mAnimation - This is will hold the actual bitmap containing the animation.
mXPos/mYPos - These hold the X and Y screen coordinates for where we want the sprite to be on the screen. These refer to the top left hand corner of the image.
mSRectangle - This is the source rectangle variable and controls which part of the image we are rendering for each frame.
mFPS - This is the number of frames we wish to show per second. 15-20 FPS is enough to fool the human eye into thinking that a still image is moving. However on a mobile platform it’s unlikely you will have enough memory 3 – 10 FPS which is fine for most needs.
mNoOfFrames -This is simply the number of frames in the sprite sheet we are animating.
mCurrentFrame - We need to keep track of the current frame we are rendering so we can move to the next one in order.~
mFrameTimer - This controls how long between frames.
mSpriteHeight/mSpriteWidth -These contain the height and width of an Individual Frame not the entire bitmap and are used to calculate the size of the source rectangle.
Now in order to use this class you have to add a few things to your graphics thread. First declare a new variable of your class and then it can be initialised in the constructor as below.
Animation = new OurAnimatedSpriteClass();
Animation.Initalise(Bitmap.decodeResource(res, R.drawable.stick_man), 62, 39, 20, 20);
In order to pass the value of the bitmap you first have to use the Bitmap Factory class to decode the resource. It decodes a bitmap from your resources folder and allows it to be passed as a variable. The rest of the values depend on your bitmap image.
In order to be able to time the frames correctly you first need to add a Game timer to the game code. You do this by first adding a variable to store the time as show below.
private long mTimer;
We now need this timer to be updated with the correct time every frame so we need to add a line to the run function to do this.
public void run() {
while (mRun) {
Canvas c = null;
mTimer = System.currentTimeMillis(); /////This line updates timer
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
Animation.update(mTimer);
doDraw(c);
}....
then you just have to add Animation.draw(canvas); your Draw function and the animation will draw the current frame in the right place.
When you describe : " one way of doing this is to get images of the man in different position and display them at different positions", this is indeed not only a programming technique to render animation but a general principle that is applied in every form of animation : it applies to making movies, making comics, computer gaming, etc, etc.
Our eyes see at the frequency of 24 images per second. Above 12 frames per second, your brain gets the feeling of real, fluid, movement.
So, yes, this is the way, if you got the feeling movement is not fuild, then you have to increase frame rate. But that works.
Moving only one part of an image is not appropriate for a small sprite representing a man running. Nevertheless, keep this idea in mind for later, when you will be more at ease with animation programming, you will see that this applies to bigger areas that are not entirely drawn at every frame in order to decresase the number of computations needed to "make a frame". Some parts of a whole screen are not "recomputed" every time, this technique is called double buffer and you should soon be introduced to it when making games.
But for now, you should start by making your man run, replacing quickly one picture by another. If movement is not fuild either increase frame rate (optimize your program) or choose images that are closer to each other.
Regards,
Stéphane