Libgdx Box2d collision detection using physics body editor - java

Hy guys,
I am developing a game for android using libgdx. I am completely stuck at the part of detecting collision between two bodies.
I have a player which I create through the function below
public Body createPlayer(String file_path, String fixture_name) {
// 0. Create a loader for the file saved from the editor.
BodyEditorLoader loader = new BodyEditorLoader(Gdx.files.internal(file_path));
// 1. Create a BodyDef, as usual.
BodyDef bd = new BodyDef();
bd.type = BodyDef.BodyType.DynamicBody;
// 2. Create a FixtureDef, as usual.
FixtureDef fd = new FixtureDef();
fd.density = 1;
fd.friction = 0.5f;
fd.restitution = 0.3f;
// 3. Create a Body, as usual.
body= world.createBody(bd);
//body.setBullet(true);
// 4. Create the body fixture automatically by using the loader.
loader.attachFixture(body, fixture_name, fd, 1);
body.setUserData(this);
return body;
}
and an enemy that I create with the same function of the player where I change only the file_path and the fixture_name.
The file_path points to a .json file that I created with box2d editor (site: http://www.aurelienribon.com/blog/projects/physics-body-editor/).
After the creation of the body I draw the player and the enemy with two similar functions ( I only post one):
private void drawPlayer(){
player_sprite = new Sprite(player_TR);
player_sprite.setSize(player.getWidth(), player.getHeight());
player_sprite.setPosition(player.getX(), player.getY());
player_sprite.setOrigin(0, 0);
player_sprite.draw(sb);
}
If I start the game everything is drawn where it should be. Obviously if the player touches the enemy nothing happen.
So i started trying to search how to make the two bodies collides but I don't really understand how to use ContactListener and beginContact.
beginContact wants as input a Contact but what is a Contact?
I have found this code online which appears to solve my problem but I don't know how to use it:
worldbox.setContactListener(new ContactListener() {
#Override
public void beginContact(Contact contact) {
if(contact.getFixtureA().getBody().getUserData()== "body1" &&
contact.getFixtureB().getBody().getUserData()== "body2")
Colliding = true;
System.out.println("Contact detected");
}
Can you help me (if it is possible through some code) to solve my problem?
Thanks in advance,
Francesco
Update of my question
Here is my render method:
public class GameRenderer{
private GameWorld myWorld;
private ShapeRenderer shapeRenderer;
private SpriteBatch sb;
private Camera camera;
private Constants constant;
private Rectangle viewport;
//dichiaro le variabili per caricare gli asset
private Player player;
private ScrollHandler scroller;
private Bordo frontBordoSX_1, backBordoSX_1, frontBordoSX_2, backBordoSX_2;
private Bordo frontBordoDX_1, backBordoDX_1, frontBordoDX_2, backBordoDX_2;
/*
private Ostacolo ob1_sx, ob2_sx, ob3_sx;
private Ostacolo ob4_dx, ob5_dx, ob6_dx;
*/
private TextureRegion player_TR;
private TextureRegion bordoSX_1, bordoSX_2;
private TextureRegion bordoDX_1, bordoDX_2;
private TextureRegion obstacleSX, obstacleSX_flip;
private TextureRegion obstacleDX, obstacleDX_flip;
private TextureRegion enemyS;
private TextureRegion blackBar;
//box2dpart
private World worldbox;
private Sprite fbDx_1,fbDx_2,fbSx_1,fbSx_2;
private Sprite player_sprite;
private Body player_body, bordo_destro;
private MyContactListener contactListener;
public GameRenderer(GameWorld world) {
myWorld = world;
constant = new Constants();
camera = new OrthographicCamera(constant.getWidth(), constant.getHeight());
shapeRenderer = new ShapeRenderer();
shapeRenderer.setProjectionMatrix(camera.combined);
sb = new SpriteBatch();
sb.setProjectionMatrix(camera.combined);
contactListener = new MyContactListener();
worldbox= new World(new Vector2(0,-10),true);
worldbox.setContactListener(contactListener);
//initialize objects and assets
initGameObjects();
initAssets();
}
private void initGameObjects(){
player = myWorld.getPlayer();
scroller = myWorld.getScroller();
frontBordoSX_1 = scroller.getFrontBordoSX_1();
backBordoSX_1 = scroller.getBackBordoSX_1();
frontBordoSX_2 = scroller.getFrontBordoSX_2();
backBordoSX_2 = scroller.getBackBordoSX_2();
frontBordoDX_1 = scroller.getFrontBordoDX_1();
backBordoDX_1 = scroller.getBackBordoDX_1();
frontBordoDX_2 = scroller.getFrontBordoDX_2();
backBordoDX_2 = scroller.getBackBordoDX_2();
/* other objects
ob1_sx = scroller.getOb1_sx();
ob2_sx = scroller.getOb2_sx();
ob3_sx = scroller.getOb3_sx();
ob4_dx = scroller.getOb4_dx();
ob5_dx = scroller.getOb5_dx();
ob6_dx = scroller.getOb6_dx();
*/
}
private void initAssets(){
player_TR = AssetLoader.player;
bordoSX_1 = AssetLoader.bordoSX;
bordoSX_2 = AssetLoader.bordoSX;
bordoDX_1 = AssetLoader.bordoDX;
bordoDX_2 = AssetLoader.bordoDX;
obstacleDX = AssetLoader.obstacleDX;
obstacleSX = AssetLoader.obstacleSX;
obstacleDX_flip = AssetLoader.obstacleDX_flip;
obstacleSX_flip = AssetLoader.obstacleSX_flip;
enemyS = AssetLoader.enemyS;
blackBar = AssetLoader.blackBar;
//box2d part
}
private void drawMargin(){
//bordo SX
/*
sb.draw(bordoSX_1, frontBordoSX_2.getX(), frontBordoSX_2.getY(), frontBordoSX_2.getWidth(),
frontBordoSX_2.getHeight());
sb.draw(bordoSX_2, frontBordoSX_1.getX(), frontBordoSX_1.getY(), frontBordoSX_1.getWidth(),
frontBordoSX_1.getHeight());
*/
fbSx_1 = new Sprite(bordoSX_1);
fbSx_1.setSize(frontBordoSX_1.getWidth(),frontBordoSX_1.getHeight());
fbSx_1.setPosition(frontBordoSX_1.getX(), frontBordoSX_1.getY());
fbSx_1.setOrigin(0, 0);
fbSx_1.draw(sb);
fbSx_2 = new Sprite(bordoSX_2);
fbSx_2.setSize(frontBordoSX_2.getWidth(),frontBordoSX_2.getHeight());
fbSx_2.setPosition(frontBordoSX_2.getX(), frontBordoSX_2.getY());
fbSx_2.setOrigin(0, 0);
fbSx_2.draw(sb);
fbDx_1 = new Sprite(bordoDX_1);
fbDx_1.setSize(frontBordoDX_1.getWidth(),frontBordoDX_1.getHeight());
fbDx_1.setPosition(frontBordoDX_1.getX(), frontBordoDX_1.getY());
fbDx_1.setOrigin(0, 0);
fbDx_1.draw(sb);
fbDx_2 = new Sprite(bordoDX_2);
fbDx_2.setSize(frontBordoDX_2.getWidth(),frontBordoDX_2.getHeight());
fbDx_2.setPosition(frontBordoDX_2.getX(), frontBordoDX_2.getY());
fbDx_2.setOrigin(0, 0);
fbDx_2.draw(sb);
sb.draw(blackBar,-constant.getWidth()/2,-constant.getHeight()/2,
(float) (0.573913)*(constant.getWidth()/6),constant.getHeight());
sb.draw(blackBar,(float)(constant.getWidth()/2-(0.573913)*(constant.getWidth()/6)),-constant.getHeight()/2,
(float)(0.573913)*(constant.getWidth()/6),constant.getHeight());
}
private void drawPlayer(){
player_sprite = new Sprite(player_TR);
player_sprite.setSize(player.getWidth(), player.getHeight());
player_sprite.setPosition(player.getX(), player.getY());
player_sprite.setOrigin(0, 0);
player_sprite.draw(sb);
}
/*
private void drawOstacoli(){
sb.draw(obstacleSX_flip,ob1_sx.getX(),ob1_sx.getY(),ob1_sx.getWidth(),ob1_sx.getHeight());
sb.draw(obstacleSX,ob2_sx.getX(),ob2_sx.getY(),ob2_sx.getWidth(),ob2_sx.getHeight());
sb.draw(obstacleSX_flip,ob3_sx.getX(),ob3_sx.getY(),ob3_sx.getWidth(),ob3_sx.getHeight());
sb.draw(obstacleDX,ob4_dx.getX()+constant.getWidth()/2-ob4_dx.getWidth(),ob4_dx.getY(),ob4_dx.getWidth(),ob4_dx.getHeight());
sb.draw(obstacleDX_flip,ob5_dx.getX()+constant.getWidth()/2-ob5_dx.getWidth(),ob5_dx.getY(),ob5_dx.getWidth(),ob5_dx.getHeight());
sb.draw(obstacleDX,ob6_dx.getX()+constant.getWidth()/2-ob6_dx.getWidth(),ob6_dx.getY(),ob6_dx.getWidth(),ob6_dx.getHeight());
}
*/
public void render(float runTime) {
Box2D.init();
int width = constant.getWidth();
int height = constant.getHeight();
float ratio = constant.getRatio();
//viewport
float aspectRatio = (float) width / (float) height;
float scale = 1f;
Vector2 crop = new Vector2(0f, 0f);
if(aspectRatio > ratio)
{
scale = (float)height/(float)height;
crop.x = (width - width * scale) / 2f;
} else if (aspectRatio < ratio) {
scale = (float)width/(float)width;
crop.y = (height - height*scale)/2f;
}
else
{
scale = (float) width / (float) width;
}
float w = (float) width * scale;
float h = (float) height * scale;
viewport = new Rectangle(crop.x, crop.y, w, h);
// update camera
camera.update();
// clear previous frame
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
// Draw Background color
shapeRenderer.setColor(255 / 255.0f, 255 / 255.0f, 250 / 255.0f, 1);
shapeRenderer.rect(-constant.getWidth() / 2, -constant.getHeight() / 2, constant.getWidth(),
constant.getHeight());
// End ShapeRenderer
shapeRenderer.end();
// set viewport
Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
(int) viewport.width, (int) viewport.height);
// Begin SpriteBatch
sb.begin();
// The player needs transparency, so we enable that again.
sb.enableBlending();
// Draw player at its coordinates.
drawPlayer();
//draw right and left side
drawMargin();
// End SpriteBatch
sb.end();
worldbox.step(1 / 60f, 6, 2);
}
}
And if I start the program my view is my player in the middle and to margins ,one on the left and one on the right. (unfortunatly I cannot post images of my view because I don't have enough rep).
Everithing is fine. I move my player with the accelerometer and it works fine without any problem. The only problem is that if I move the player near the margin the two entities overlap instead of colliding and I don't understand why.
I also fixed the line:
loader.attachFixture(body, fixture_name, fd, 1);
to
loader.attachFixture(body, fixture_name, fd, player_width);
but nothing changes.

First, from here, the object Contact manages contact between two shapes, and from here, the listener ContactListener will be called when two fixtures begin to touch.
So, to make your code work, you should set a custom object to your bodies with the method: setUserData(Object userData). Usually this method is used to link the sprite or the actor with the physic body, but for example purpose you could just send a simple ID (like a string).
So in this part:
// 3. Create a Body, as usual.
body= world.createBody(bd);
//body.setBullet(true);
You could add an identificator to your object like this:
body.setUserData("player");
to idenfity your object, and then, when the listener get fired, you could retrieve this value:
#Override
public void beginContact(Contact contact) {
String userDataA = contact.getFixtureA().getBody().getUserData().toString();
String userDataB = contact.getFixtureB().getBody().getUserData().toString();
if(userDataA.equals("player") && userDataB.equals("otherEntity")){
colliding = true;
//do stuffs when collision has started
} else if(userDataB.equals("player") && userDataA.equals("otherEntity")){
colliding = true;
//do stuffs when collision has started
}
System.out.println("Contact detected");
}
After that, you could be able to do whatever you want to do with this collision.
Hope you find this useful!

Related

Libgdx Animation not working

I have been reading about libgdx and I have been stuck on this animation problem. I have reading about animation in libgdx from their github and when I run my app on my phone, the app crashes after the loading screen. Here is my player Actor that extends from Actor. I create the player actor in my menu screen class and add it to the stage. It is suppose to appear flying up and down in the menu screen but the app crashes. I know it is my player actor because when I comment out the player actor in my menu screen class the app does not crash. I use texture packer on my plane sprite and load it into my game.
public class PlayerActor extends Actor{
private DrunkPilot pGame;
private static final int NUM_ROWS = 5, NUM_COLS = 5;
private Animation<TextureRegion> drunkFlying;
private float stateTimer;
private TextureRegion region;
private Texture planeTex;
public PlayerActor(){
planeTex = pGame.assetManager.manager.get(Constants.plane);
planeTex = new Texture(Gdx.files.internal(Constants.plane));
drunkFlyingAnimation();
}
private void drunkFlyingAnimation(){
TextureRegion[][] tmp = TextureRegion.split(planeTex, planeTex.getWidth() / NUM_COLS, planeTex.getHeight() / NUM_ROWS);
TextureRegion[] flyFrames = new TextureRegion[NUM_COLS * NUM_ROWS];
int index = 0;
for (int i = 0; i < NUM_ROWS; i++) {
for (int j = 0; j < NUM_COLS; j++) {
flyFrames[index++] = tmp[i][j];
}
}
drunkFlying = new Animation<TextureRegion>(0.025f, flyFrames);
stateTimer = 0;
region = drunkFlying.getKeyFrame(0);
}
#Override
public void draw(Batch batch, float alpha){
super.draw(batch, alpha);
GdxUtils.clearScreen();
stateTimer += Gdx.graphics.getDeltaTime();
TextureRegion drunk = drunkFlying.getKeyFrame(stateTimer, true);
batch.draw(drunk, getX(), getY(), getWidth() / 2, getHeight() / 2, getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());
}
public void dispose(){
planeTex.dispose();
}
}
Here is where I add my actor to the stage.
#Override
public void show() {
Gdx.input.setInputProcessor(menuStage);
menuTitle = new Image(menuTitleTexture);
menuStartImg = new Image(menuStartTexture);
menuTable = new Table();
menuTable.setFillParent(true);
menuTable.add(menuTitle).pad(20).align(Align.top);
menuTable.row();
menuTable.add(menuStartImg).align(Align.bottom).pad(30);
menuStage.addActor(parallaxBackground);
menuStage.addActor(menuTable);
menuStage.addActor(player);
}
When you use TexturePacker there is an easier way to create animations.
When we want to create a run animation
First you ad all animation images to TexturePacker you have to look that every frame has the same name + underscore + frameNumber.
So the name of the frames must be: run_1, run_2, run_3 etc.
When we created it we have a run.txt and run.png in our assets.
Now we can load the TextureAtlas with our AssetManager:
AssetManager assetManager = new AssetManager();
assetManager.load("run.txt", TextureAtlas.class);
assetManager.finishLoading();
TextureAtlas runAnimationAtlas = assetManager.get("run.txt", TextureAtlas.class);
With this TextureAtlas we can create our Animation:
float frameDuration = 0.1f; // the duration between the animation frames
Animation<TextureRegion> runAnimation = new Animation<TextureRegion>(frameDuration, runAnimationAtlas.findRegions("run"), Animation.PlayMode.LOOP);
And render the Animation:
batch.draw(runAnimation.getKeyFrame(stateTimer),posX, posY)

Libgdx - PPM world conversion

My sprite moves too slowly. Basically I want to apply less force to move my player. Current this:
getBody().applyForceToCenter(new Vector2(-10000000f,0f), true);
is the force needed to make it move a tiny bit.
I know the reason why I am not able to move it is since I haven't scaled the sprite (64x64) it weights more than 400kg. What should be the correct scale?
This is my game screen.
public class GameScreen implements Screen {
//Reference to our Game, used to set Screens
private Logang game;
//basic playscreen variables
private OrthographicCamera gamecam;
private Viewport gamePort;
//Box2d variables
private World world;
private Box2DDebugRenderer b2dr;
boolean drawn = true;
private Player p;
private int pX = 100, pY = 300;
public GameScreen(Logang game) {
this.game = game;
//create cam used to follow mario through cam world
gamecam = new OrthographicCamera();
gamePort = new ScalingViewport(Scaling.stretch, Logang.GWIDTH, Logang.GHEIGHT, gamecam);
gamePort.apply();
gamecam.position.set(gamecam.viewportWidth / 2, gamecam.viewportHeight / 2, 0);
gamecam.update();
Box2D.init();
//create our Box2D world, setting no gravity in X, -10 gravity in Y, and allow bodies to sleep
world = new World(new Vector2(0, Logang.GRAVITY), true);
//allows for debug lines of our box2d world.
b2dr = new Box2DDebugRenderer();
//create a FitViewport to maintain virtual aspect ratio despite screen size
p = new Player(new Sprite(new Texture("hud_p3.png")), world, pX, pY, 1);
//initially set our gamcam to be centered correctly at the start of of map
line();
}
#Override
public void show() {
}
public void update(float dt) {
//handle user input first
p.update(dt);
//update our gamecam with correct coordinates after changes
}
#Override
public void render(float delta) {
//separate our update logic from render
update(delta);
//Clear the game screen with Black
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
world.step(1f / 60f, 6, 2);
gamecam.position.set(p.getSprite().getX(),Logang.GHEIGHT / 2, 0); // x and y could be changed by Keyboard input for example
gamecam.update();
game.getBatch().setProjectionMatrix(gamecam.combined);
//renderer our Box2DDebugLines
b2dr.render(world, gamecam.combined);
System.out.println("Player x: " + p.getSprite().getX() + " Camera X: " + gamecam.position.x + " Body X: " + p.getBody().getPosition().x);
//System.out.println("Player y: " + p.getSprite().getY() + " Camera Y: " + gamecam.position.y + " Body Y: " + p.getBody().getPosition().y);
game.getBatch().begin();
if (p.getBody() != null)
p.render(game.getBatch());
EntityManager.renderTerra(game.getBatch(), delta);
game.getBatch().end();
}
public void line() {
Texture tmp = new Texture("hud_p3.png");
tmp.setWrap(Texture.TextureWrap.MirroredRepeat, Texture.TextureWrap.MirroredRepeat);
for (int i = 0; i < 50; i++) {
EntityManager.add(new Ground(new Sprite(tmp), world, (int)(i * Logang.TILE), 1, 2));
}
// EntityManager.changeSize(((Logang.TILE) * 5),Logang.TILE);
}
#Override
public void resize(int width, int height) {
//updated our game viewport
gamePort.update(width, height);
gamecam.position.set(gamecam.viewportWidth / 2, gamecam.viewportHeight / 2, 0);
}
public World getWorld() {
return world;
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
world.dispose();
b2dr.dispose();
}
And this is my entity class
private World world;
private Sprite sprite;
private Body body;
private int tipo;
public Entity(Sprite sprite, World world, int x, int y, int tipo) {
this.sprite = sprite;
this.world = world;
getSprite().setPosition(x, y);
sprite.setSize(Logang.TILE, Logang.TILE);
sprite.setOriginCenter();
define(tipo);
this.tipo = tipo;
}
public void update(float dt){
if(Gdx.input.isKeyPressed(Input.Keys.LEFT)){
getBody().applyForceToCenter(new Vector2(-10000000f,0f), true);
}
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)){
getBody().applyForceToCenter(new Vector2(10000000f,0f), true);
}
if(Gdx.input.isKeyPressed(Input.Keys.SPACE)){
//getBody().applyLinearImpulse(0f,-Logang.GRAVITY,
getBody().getPosition().x, getBody().getPosition().y, true);
}
}
public void define(int tipo) {
BodyDef bdef = new BodyDef();
bdef.position.set((getSprite().getX() + getSprite().getWidth() / 2),
(getSprite().getY() + getSprite().getHeight() / 2));
switch (tipo) {
case 1: {
bdef.type = BodyDef.BodyType.DynamicBody;
break;
}
case 2: {
bdef.type = BodyDef.BodyType.StaticBody;
break;
}
case 3: {
bdef.type = BodyDef.BodyType.DynamicBody;
break;
}
}
body = world.createBody(bdef);
FixtureDef fdef = new FixtureDef();
fdef.density=0.001f; // (weight: range 0.01 to 1 is good)
PolygonShape shape = new PolygonShape();
shape.setAsBox(getSprite().getWidth() / 2, getSprite().getHeight() / 2);
fdef.shape = shape;
body.createFixture(fdef);
body.setUserData(this);
shape.dispose();
}
public void render(SpriteBatch batch) {
if (tipo != 2) {
float posX = getBody().getPosition().x;
float posY = getBody().getPosition().y;
getSprite().setPosition(posX - getSprite().getWidth() / 2, posY -
getSprite().getHeight() / 2);
}
getSprite().draw(batch);
}
public Sprite getSprite() {
return sprite;
}
public void setSprite(Sprite sprite) {
this.sprite = sprite;
}
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
And this are the in game variables
public static final int GWIDTH = 800;
public static final int GHEIGHT = (GWIDTH/16)*9;
public static final float PPM = 100f;
public static final float GRAVITY = -10f;
public static final float TILE = 64;
Could you please give me a fix?
I already tried to divide body and gamecam position still no effect
What should be the correct scale?
The right scale would be the scale in real life where 1 unit in LibGDX (Box2D) represents 1 meter in real life. I always advice people to use this scale and zoom the camera properly.
Mind though, if you are using very large objects and zoom the camera all the way back objects appear to be falling slowly. This is obviously because your camera contains a much larger space. Not only would it fall slowly but it won't interact properly with the world if the item is supposed to be smaller.
Adept the camera to your world, not your world to your camera.
More detailed answer I gave

libgdx, movement specific

I have issue with movement. My goal is to make player move once key is pressed for its own width/height. For example:
if (Gdx.input.isKeyJustPressed(Input.Keys.A))
player.position.x = player.position.x-moveSpeed;
that works precise, but I want it more smooth, to move exact distance in a second, but each millisecond a bit, not all at once.
This makes player move smooth, but it's not precise:
if (Gdx.input.isKeyJustPressed(Input.Keys.A))
player.position.x = player.position.x-moveSpeed*deltaTime;
How can I make smooth transition, but precise?
Change (or extend) your player class, so that it contains the following:
class Player {
public final Vector2 position = new Vector2(); // you already have this
public final Vector2 target = new Vector2();
private final float speed = 10f; // adjust this to your needs
private final static Vector2 tmp = new Vector2();
public void update(final float deltaTime) {
tmp.set(target).sub(position);
if (!tmp.isZero()) {
position.add(tmp.limit(speed*deltaTime));
}
}
//The remainder of your Player class
}
Then in your render method you call:
if (Gdx.input.isKeyJustPressed(Input.Keys.A)) {
player.target.x -= moveSpeed; //consider renaming moveSpeed to moveDistance
}
player.update(deltaTime);

How to make font text clickable?

My game has 3 BitmapFont (later more) on screen. I Want to be able to touch the font and output it's string in the console so I know which one is pressed. I tried to create a rectangle but I was unable to get the string of the touched BitmapFont.
Here is my code to create BitmapFont:
public class simple implements ApplicationListener {
private OrthographicCamera camera;
private SpriteBatch batch;
BitmapFont font;
GlyphLayout layout;
String a1 = "aa";
String a2 = "bb";
String a3 = "cc";
int a = 0;
#Override
public void create() {
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
batch = new SpriteBatch();
layout = new GlyphLayout();
font = new BitmapFont(Gdx.files.internal("arial-15.fnt"));
}
#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);
batch.begin();
for (int i =1; i< 4;i++){
layout.setText(font, "a"+i);
font.draw(batch, layout,200+(15*i),200 );
}
batch.end();
}
You want to have combined functionality of String, BitmapFont, Layout and positioning. The best way is to create a class for this clickable font that contains all that we need. I did some work for you since I am a nice guy and I actually have plenty of other things to do :D.
public class ClickableFont {
//Declare the fields
private GlyphLayout layout;
private BitmapFont font;
private String text;
private int posX;
private int posY;
/**
* Constructs clickable text from a font
* #param text Text to display
* #param posX X position of the text
* #param posY Y position of the text
*/
public ClickableFont(String text, int posX, int posY) {
this.text = text;
this.posX = posX;
this.posY = posY;
font = new BitmapFont(Gdx.files.internal("arial-15.fnt"));
layout = new GlyphLayout(font, text);
}
/**
* #param batch Draws the text using the given SpriteBatch.
* #param camera Requires a camera to calculate touches between screen and world.
*/
public void update(SpriteBatch batch, OrthographicCamera camera)
{
checkClicked(camera);
font.draw(batch, layout, posX, posY);
}
/**
* Checks if this object is clicked and outputs to console
* #param camera the camera
*/
private void checkClicked(OrthographicCamera camera)
{
if (Gdx.input.justTouched())
{
//Get screen coordinates
Vector3 touch = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
//Transform screen touch to world coordinates using the camera you are drawing with
camera.unproject(touch);
//System.out.println(getRectangle());
//System.out.println(touch);
if (getRectangle().contains(touch.x, touch.y))
{
System.out.println(text + " has been clicked.");
}
}
}
/**
* Creates a rectangle for the sprite to perform collision calculations.
* Since it seems font.draw draws from top to bottom (coordinate system of LibGDX is not consistent)
* We have to adept the rectangle position Y position
* #return rectangle of font bounds
*/
private Rectangle getRectangle()
{
return new Rectangle(posX, posY - (int)layout.height, (int)layout.width, (int)layout.height);
}
}
As you can see it tackles your problem in steps. Tackling problems is all you are doing in programming. A rule of thumb is to never make a method more then 10 lines, excluding comments. Exceptions can be made but any large method can be broken down into much more readable smaller methods.
Now how to use this ClickableFont class?
public class simple implements ApplicationListener {
private OrthographicCamera camera;
private SpriteBatch batch;
//folowing are not nececary anymore since it's handled by the new class
//BitmapFont font;
//GlyphLayout layout;
//String a1 = "aa";
//String a2 = "bb";
//String a3 = "cc";
int a = 0;
//Declare a list to hold your clickable fonts
List<ClickableFont> clickableFonts = new ArrayList<ClickableFont>();
#Override
public void create() {
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
batch = new SpriteBatch();
//Add clickable fonts to the list
clickableFonts.add(new ClickableFont("aa", 200, 200));
clickableFonts.add(new ClickableFont("bb", 200 + 150, 200));
clickableFonts.add(new ClickableFont("cc", 200 + 150 * 2, 200));
}
#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);
batch.begin();
/* replace your loop
for (int i =1; i< 4;i++){
layout.setText(font, "a"+i);
font.draw(batch, layout,200+(15*i),200 );
}*/
for (ClickableFont font : clickableFonts)
{
font.update(batch, camera);
}
batch.end();
}

Updating camera position with Libgdx

I am attempting to get an OrthographicCamera to follow a user controlled sprite. I can't get the camera to properly update position at all. I can't seem to see what is wrong with my code compared to what others have done.
I am still learning and at this point I would assume the problem is being caused by something simple I do not fully understand at this point.
Any help is appreciated, thank you.
This is my renderer:
public class WorldRenderer {
private static final float CAMERA_WIDTH = 10;
private static final float CAMERA_HEIGHT = 7;
private World world;
private OrthographicCamera oCam;
private Hero hero;
ShapeRenderer debugRenderer = new ShapeRenderer();
/** TEXTURES **/
private Texture heroTexture;
private Texture tileTexture;
private SpriteBatch spriteBatch;
private int width, height;
private float ppuX; // Pixels per unit on the X axis
private float ppuY; // Pixels per unit on the Y axis
public void setSize (int w, int h) {
this.width = w;
this.height = h;
ppuX = (float)width / CAMERA_WIDTH;
ppuY = (float)height / CAMERA_HEIGHT;
}
public WorldRenderer(World world, boolean debug) {
hero = world.getHero();
this.world = world;
spriteBatch = new SpriteBatch();
oCam = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
oCam.update();
loadTextures();
}
private void loadTextures() {
tileTexture = new Texture(Gdx.files.internal("images/tile.png"));
heroTexture = new Texture(Gdx.files.internal("images/hero_01.png"));
}
public void render() {
oCam.update();
spriteBatch.begin();
spriteBatch.disableBlending();
drawTiles();
spriteBatch.enableBlending();
drawHero();
spriteBatch.end();
}
private void drawHero() {
spriteBatch.draw(heroTexture, hero.getPosition().x * ppuX, hero.getPosition().y * ppuY, Hero.SIZE * ppuX, Hero.SIZE * ppuY);
oCam.position.set(hero.getPosition().x, hero.getPosition().y, 0);
}
}
SpriteBatch manages its own projection and transformation matrixes. So, you have to set its matrixes (if possible, before calling begin()).
Unless you need to access your matrixes separately (projection and model-view, eg. in shaders), setting the projection matrix to the projection-model-view matrix will be enough.
Anyway, this should work in your code:
oCam.update();
spriteBatch.setProjectionMatrix(oCam.combined);
try calling oCam.apply(Gdx.gl10); after your oCam.update();
update() only does the calculations but you never applied them.
In relation to idaNakav's answer, I can't see an apply function on cameras in LibGDX anymore, in case anyone else stumbles upon this! So update() should be sufficient now I would imagine.
My problem was a bit different, I was trying to put my camera in certain positions/lookAts with a perspective camera and it had to be actioned twice to work.
I was calling:
camera.lookAt(xyz), camera.position.set(xyz), camera.up.set(xyz)
The first call made the camera update to a really odd transform. I should have been doing:
camera.position.set(xyz), camera.lookAt(xyz), camera.up.set(xyz)

Categories

Resources