How to use Bitmap font as scene 2d actor? - libgdx

I am developing a game using libgdx framework. How can i achieve scene2d action on bitmap font object ? so that i can write some text like score,message and run action like scene2d actor.

Take a look at the Label class, particularly the constructor that takes a CharSequence and a LabelStyle. When you initialize your LabelStyle you can supply a BitmapFont.
Please note, if you'd like to scale or rotate the label you'll need to wrap it in a Container or add it to Table with setTransform() enabled. (This flushes the SpriteBatch so use it wisely.)

you can extend actor class to achieve the same.
like:-
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFontCache;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.scenes.scene2d.Actor;
public class FontActor extends Actor
{
private Matrix4 matrix = new Matrix4();
private BitmapFontCache bitmapFontCache;
private GlyphLayout glplayout;
public FontActor(float posX, float posY, String fontText)
{
BitmapFont fnt=new BitmapFont(Gdx.files.internal("time_newexport.fnt"),
Gdx.files.internal("time_ne-export.png"),false);
bitmapFontCache = new BitmapFontCache(fnt);
glplayout=bitmapFontCache.setText(fontText, 0, 0);
setPosition(posX, posY);
setOrigin(glplayout.width / 2, -glplayout.height/2);
}
#Override
public void draw(Batch batch, float alpha)
{
Color color = getColor();
bitmapFontCache.setColor(color.r, color.g, color.b, color.a*alpha);
matrix.idt();
matrix.translate(getX(), getY(), 0);
matrix.rotate(0, 0, 1, getRotation());
matrix.scale(getScaleX(), getScaleY(), 1);
matrix.translate(-getOriginX(), -getOriginY(), 0);
batch.setTransformMatrix(matrix);
bitmapFontCache.draw(batch);
}
public void setAlpha(int a)
{
Color color = getColor();
setColor(color.r, color.g, color.b, a);
}
public void setText(String newFontText)
{
glplayout = bitmapFontCache.setText(newFontText, 0, 0);
setOrigin(glplayout.width / 2, -glplayout.height/2);
}
}
and you can use it like.
Actor actor=new FontActor(20,30,"test");
stage.addActor(actor);
actor.addAction(Actions.moveTo(10,10,1));

Related

LibGdx - Adding array of actors to a table, with delay

I want to have several comets falling in the background of my UI,I have a working comet Actor that does what it is supposed to, but I am not sure how to create a continuous spawn with these comets (with a random delay between each) in a table, without scene2d/actors it would look something like:
cometTimer += delta
if(cometTimer >= interval){
addCometToArray();
cometTimer = 0;
}
With the cometArray being looped over and drawn every frame, and then removing the entity when it goes out of bounds.
The only way I know how to add Actors to a table is like this:
table().add(new DialogComet());
How would I go about adding this type of behaviour using Scene2d?
Not sure if this is what you were looking for, but the below is a small working app that shows comets "falling" from the top to bottom, using Tables and having the tables manage the comets (no separate array/data structure). I created a small Comet class that extends Actor as well, to allow for movement and placement.
"main" class:
import java.util.Iterator;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
public class StageComet implements ApplicationListener {
private static final float INTERVAL = 0.3f;
private Batch batch;
private ShapeRenderer shapeRenderer;
private OrthographicCamera camera;
private BitmapFont font;
private Table rootTable;
private Table cometTable;
private Stage stage;
private Iterator<Actor> iter;
private Comet comet;
private float cometTimer = 0;
private float delta = 0;
#Override
public void create() {
camera = new OrthographicCamera();
camera.setToOrtho(false, 960, 640);
shapeRenderer = new ShapeRenderer();
batch = new SpriteBatch();
font = new BitmapFont();
stage = new Stage();
/*
* The root table could contain main "play" actors. It is empty in this example.
*/
rootTable = new Table();
rootTable.setFillParent(true);
/*
* Usually in Scene2d I think the practice is only to have 1 root table that takes up the entire screen (above),
* but for simplicity/illustrative purposes, I created a cometTable only, set it to Fill Parent as well, and the
* getChildren() of the table will have our array of comets in play at any given time.
*/
cometTable = new Table();
cometTable.setFillParent(true);
stage.addActor(rootTable);
stage.addActor(cometTable);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
batch.setProjectionMatrix(camera.combined);
delta = Gdx.app.getGraphics().getDeltaTime();
stage.act(delta); // make sure the comets "fall"
shapeRenderer.begin(ShapeType.Filled); // simple rendering of comets, they are just a circle ...
iter = cometTable.getChildren().iterator(); // Table subclasses Group, which has a snapshot array of its Actors
while ( iter.hasNext() ) {
comet = (Comet)iter.next();
shapeRenderer.circle(comet.getX(), comet.getY(), 20.0f); // Draw the comet
if ( comet.getY() < -100 ) { // Hack/hardcode, if the comet fell far enough "off stage" ...
iter.remove(); // ... remove it from the stage
}
}
shapeRenderer.end();
/*
* Sample code from original question on how to create a comet without scene2d ...
*/
cometTimer += delta;
if ( cometTimer > INTERVAL ) {
cometTable.add(new Comet()); // ... but in this case, we use scene2d
cometTimer = 0;
}
/*
* To keep track, display a simple message of # of comets on stage at any given time.
*/
batch.begin();
font.draw(batch, "Comets on stage: " + cometTable.getChildren().size, 100, 100);
batch.end();
}
/*
* I may have missed disposing something, but you get the idea ...
*/
#Override
public void dispose() {
shapeRenderer.dispose();
batch.dispose();
stage.dispose();
font.dispose();
}
#Override
public void resize(int width, int height) { }
#Override
public void pause() { }
#Override
public void resume() { }
}
And the small Comet class:
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.scenes.scene2d.Actor;
public class Comet extends Actor{
/*
* Spawn a comet at the top of the screen, in the middle
*/
public Comet() {
super();
this.setY(Gdx.app.getGraphics().getHeight());
this.setX(Gdx.app.getGraphics().getWidth()/2.0f);
}
/*
* Let the comet fall (same speed) to the bottom of the screen ...
*/
#Override
public void act (float delta) {
this.setY(this.getY() - 10);
super.act(delta);
}
}

LibGDX Stage and Actor, Events and Actor properties

I'm just starting android game development with LibGdx framework.
I read many online tutorial so far and the more I read the more I got confused: ApplicationListener, ApplicationAdapter, Stages, Game, Screens, Actors, Sprites, Images... not mentioning Input and Gesture listeners of all king).
I finally understood what kind of "model" I should use for the game I have in mind (a kind of puzzle game): Game, Screens, Stage and Actor.
So here is my first code.
This is the main application (Game):
package com.my.game1;
import com.badlogic.gdx.Game;
public class MyGame extends Game {
#Override
public void create () {
setScreen(new StarterScreen());
}
}
This is the main screen class:
package com.my.game1;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.Stage;
public class StarterScreen implements Screen {
private Stage stage;
private float screenW, screenH;
private Tess tessera;
#Override
public void show() {
tessera = new Tess("image.png");
stage = new Stage();
screenW = stage.getViewport().getWorldWidth();
screenH = stage.getViewport().getWorldHeight();
Gdx.input.setInputProcessor(stage);
stage.addActor(tessera);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
stage.draw();
}
#Override
public void resize(int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void pause() {
// TODO Auto-generated method stub
}
#Override
public void resume() {
// TODO Auto-generated method stub
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
stage.dispose();
}
}
And the following is the class that extends Actor:
package com.my.game1;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener;
public class Tess extends Actor {
private Texture texture;
private boolean selected = false;
public Tess (String img) {
this.texture = new Texture(Gdx.files.internal(img));
this.setBounds(0f, 0f, this.texture.getWidth(), this.texture.getHeight());
this.setOrigin(this.texture.getWidth() / 2, this.texture.getHeight() / 2);
this.setScale(0.25f);
this.addListener(new ActorGestureListener() {
public void tap(InputEvent event, float x, float y, int pointer, int button) {
((Tess)event.getTarget()).toggleSelect();
((Tess)event.getTarget()).setColor(0.5f, 0f, 0.5f, 1f);
}
});
}
#Override
public void draw(Batch batch, float alpha){
batch.draw(texture, 0, 0);
}
public void finalize() {
this.texture.dispose();
}
public void toggleSelect(){
this.selected = !this.selected;
if (this.selected == true)
this.setColor(0.5f, 0f, 0.5f, 1f);
else
this.setColor(0f, 0f, 0f, 0f);
}
}
The screen shows correctly the actor, but I cannot set the Actor's position or its scale, nor the "tap" event seems to get detected; and the color doesn't change.
What I did wrong?
Several things were wrong. First, just on the side, you don't want to call dispose() from the Screen's hide() method. hide() can be called simply when the screen is turned off, or when the app is switched to the background, and disposing of the Screen during that would cause serious issues on resume.
With that out of the way, here's what your Actor should have looked like:
package com.my.game1;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Touchable;
public class Tess extends Actor {
private Sprite sprite;
private boolean selected = false;
public Tess (String img) {
this.sprite = new Sprite(new Texture(Gdx.files.internal(img)));
this.setTouchable(Touchable.enabled);
this.setBounds(this.sprite.getX(), this.sprite.getY(), this.sprite.getWidth(), this.sprite.getHeight());
this.setOrigin(this.sprite.getWidth() / 2, this.sprite.getHeight() / 2);
this.setScale(0.25f);
this.addListener(new ActorGestureListener() {
#Override
public void tap (InputEvent event, float x, float y, int pointer, int button) {
((Tess)event.getTarget()).toggleSelect();
}
});
}
#Override
public void draw(Batch batch, float alpha){
sprite.draw(batch);
}
#Override
public void positionChanged(){
sprite.setPosition(getX(), getY());
}
public void toggleSelect(){
this.selected = !this.selected;
if (this.selected == true)
sprite.setColor(0.5f, 0f, 0.5f, 1f);
else
sprite.setColor(0f, 0f, 0f, 0f);
}
}
First thing changed: you should use a Sprite, not a Texture, to handle color, drawing and transformations easily. Texture is possible, but is not as straightforward as Sprite is.
Next, you need to call setTouchable(Touchable.enabled) inside the actor to actually enable hit detection. Without this, no touch events are passed to the Actor.
After that, with setBounds(), you need to use sprite.getX() and sprite.getY(), to utilize the Sprite's positional values. Setting them to any arbitrary number seems to disable any touch capacity for that Actor.
Another thing, if all of that had been OK, is that you were setting the color twice for each touch, once based on the selected field, and then immediately after straight to the dark purple, so I removed the second set and just used your toggle method.
Next, since we have a Sprite now, we can use the draw() method attached to the Sprite itself and feed it the Batch, instead of calling the Batch's draw.
Finally, when you want to change the position of the image, call setPosition on the actor itself, and utilize an override of the positionChanged() method to set the Sprite's position based on the Actor's new position.

Distance field font in libgdx

I'm trying to rendering smooth scalable bitmap fonts. After checking this question one of the answers mentioned using distance field fonts.
I'm doing exactly as mentioned in LibGDX wiki article about distance filed fonts. However I can't get it working. Fonts are rendered hazy.
Here's the code I used to generate this output
public class FontRenderTest implements ApplicationListener {
private Texture texture;
private SpriteBatch spriteBatch;
private BitmapFont font;
#Override
public void create() {
spriteBatch = new SpriteBatch();
Texture texture = new Texture(Gdx.files.internal("Raleway.png"), true); // true enables mipmaps
texture.setFilter(TextureFilter.MipMapLinearNearest, TextureFilter.Linear); // linear filtering in nearest mipmap image
font = new BitmapFont(Gdx.files.internal("Raleway.fnt"), new TextureRegion(texture), false);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
spriteBatch.begin();
font.draw(spriteBatch, "This is hazy !!", 100, 150);
spriteBatch.end();
}
}
I'm not sure if I properly understand the function of distance field font. If anyone could explain how to render font smooth.
I think it needs a shader and if I recall right the shaders require GL20. As it said in the wiki you would need .frag and .vert files. I modified your code with the help from this Libgdx test: http://git.io/-yAmNg .
It looks like this with different smoothing.
public class FontRenderTest implements ApplicationListener {
private Texture texture;
private SpriteBatch spriteBatch;
private BitmapFont font;
private DistanceFieldShader distanceFieldShader;
private static class DistanceFieldShader extends ShaderProgram {
public DistanceFieldShader () {
// The vert and frag files are copied from http://git.io/yK63lQ (vert) and http://git.io/hAcw9Q (the frag)
super(Gdx.files.internal("data/shaders/distancefield.vert"), Gdx.files.internal("data/shaders/distancefield.frag"));
if (!isCompiled()) {
throw new RuntimeException("Shader compilation failed:\n" + getLog());
}
}
/** #param smoothing a value between 0 and 1 */
public void setSmoothing (float smoothing) {
float delta = 0.5f * MathUtils.clamp(smoothing, 0, 1);
setUniformf("u_lower", 0.5f - delta);
setUniformf("u_upper", 0.5f + delta);
}
}
#Override
public void create() {
spriteBatch = new SpriteBatch();
Texture texture = new Texture(Gdx.files.internal("hiero.png"), true); // true enables mipmaps
texture.setFilter(TextureFilter.MipMapLinearNearest, TextureFilter.Linear); // linear filtering in nearest mipmap image
font = new BitmapFont(Gdx.files.internal("hiero.fnt"), new TextureRegion(texture), false);
distanceFieldShader = new DistanceFieldShader();
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
spriteBatch.begin();
spriteBatch.setShader(distanceFieldShader);
font.draw(spriteBatch, "This is pretty sharp !!", 100, 120);
distanceFieldShader.setSmoothing(0f);
spriteBatch.setShader(distanceFieldShader);
font.draw(spriteBatch, "This is hazy !!", 100, 150);
distanceFieldShader.setSmoothing(1f);
spriteBatch.setShader(distanceFieldShader);
font.draw(spriteBatch, "This is pretty smooth !!", 100, 180);
distanceFieldShader.setSmoothing(1/2f);
spriteBatch.end();
}
Use the shader created by DistanceFieldFont.createDistanceFieldShader.

How to properly use setposition in libgdx?

This is the java file created by gdx-setup-ui.jar of v0.9.7
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
Why we need to set position to negative value in order to center the picture?
Where is the reference base point of the libgdx picture? (left-bottom corner?)
I was told that the origin of libgdx is left-bottom corner. Given the above values, part of the picture should have been outside the screen....It turns out not! I am very confused.
Thanks in advance
Complete listing:
package com.packtpub.libgdx.basic;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
public class Basic implements ApplicationListener {
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
private Sprite sprite;
#Override
public void create() {
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
camera = new OrthographicCamera(1, h/w);
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("data/libgdx.png"));
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);
sprite = new Sprite(region);
sprite.setSize(0.9f, 0.9f * sprite.getHeight() / sprite.getWidth());
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
}
#Override
public void dispose() {
batch.dispose();
texture.dispose();
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);
batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
It's because the default center point for the orthographic camera is (0,0,0), so if you just draw your image at (0,0) its bottom left cornet will be in the center of the screen.
You can change this center point by using cam.position.set(w / 2, h / 2, 0) method
See here for more detailed example of OrthographiCamera use: https://github.com/libgdx/libgdx/wiki/Orthographic-camera

Issues with ActionListener (Java)

I am trying to implement action listener on two buttons in JFrame, but the issue is one of the two button is performing both the functions; but i've not configured it to do so. Please find the sample code:-
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class MyChangingCirlce implements ActionListener{
JButton colorButton, labelButton;
JLabel myLabel;
MyDrawPanel mdp;
JFrame frame;
public static void main(String [] args)
{
MyChangingCirlce mcc = new MyChangingCirlce();
mcc.createFrame();
}
public void createFrame()
{
frame = new JFrame();
colorButton = new JButton("Changing Colors");
labelButton = new JButton("Change Label");
myLabel = new JLabel("BA");
mdp = new MyDrawPanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.CENTER, mdp);
frame.getContentPane().add(BorderLayout.SOUTH,colorButton);
frame.getContentPane().add(BorderLayout.EAST,labelButton);
frame.getContentPane().add(BorderLayout.WEST,myLabel);
colorButton.addActionListener(this);
labelButton.addActionListener(this);
frame.setSize(300,300);
frame.setVisible(true);
} // end of createFrame Method
public void actionPerformed(ActionEvent e)
{
if(e.getSource()== colorButton)
{
frame.repaint();
}
else
{
myLabel.setText("AB");
}
} //end of interface method...
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel{
public void paintComponent(Graphics g)
{
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue= (int) (Math.random() * 255);
Color randomColor = new Color(red,green,blue);
g.setColor(randomColor);
g.fillOval(20,70,100,100);
}
}
You think the button triggers both the if and else statement but that is not the case. If you would adjust your code in the following way:
add a setColor, changeColor or something similar to your MyDrawPanel class
adjust the MyDrawPanel#paintComponent method to use a fixed color instead of a random color, and only adjust the color through the method created in the first step
your color change button should use the method created in the first step to adjust the color of the MyDrawPanel
The thing is that paintComponent can be called by Swing itself. It is not only called when you call repaint (which is a good thing, or all code you write for Swing components would be filled with repaint calls).
Side note: when overriding the paintComponent method I would recommended to call super.paintComponent as well