Libgdx- Positioning actor in table row - java

I have an an Actor called tile that I'd like to add to a table to make something like a checker board. I can get the tile to draw, however the batch.draw always asks for a position. If I set the position then the tile won't be drawn at the end of the row like I want. For example,
batch.draw(texture, 0, 0);
Would draw:
As you can see, I have a table with buttons and they line up on the row correctly, since they aren't drawn by position. Is there a way I can get my tile to draw the same way?
Here's my code:
Tile.java
public class Tile extends Actor {
boolean filled;
int row;
int column;
Texture texture;
public Tile(int row, int column){
this.row = row;
this.column = column;
texture = new Texture(Gdx.files.internal("assets/game/tile.png"));
this.addListener(new InputListener() {
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
System.out.println("down");
return true;
}
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
System.out.println("up");
}
});
}
#Override
public void draw (SpriteBatch batch, float parentAlpha) {
//batch.setColor(0.5f, 0.5f, 0.5f, 1);
batch.draw(texture, 0, 0);
}
public int getRow(){
return row;
}
public int getColumn(){
return column;
}
}
MainScreen.java
public class MainScreen implements Screen {
MyGame myGame;
private final Stage stage;
private final SpriteBatch spriteBatch;
Table table;
private TextureAtlas buttonsUi;
Boolean nextScreen= false;
public MainScreen(MyGame myGame) {
spriteBatch = new SpriteBatch();
stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false, spriteBatch);
this.myGame = myGame;
Gdx.input.setInputProcessor(stage);
//Create the text label
Skin pennySkin = new Skin(Gdx.files.internal("uiskin.json"));
Label pennyLabel = new Label("PennyPop", pennySkin);
//Create the sfx and api button button
ImageButton sfxButton = createSfxButton();
ImageButton apiButton = createApiButton();
ImageButton gameButton = createGameButton();
table = new Table();
table.add(sfxButton);
table.add(apiButton).padLeft(10);
table.add(gameButton).padLeft(10);
table.add(new Tile(0,0)).padLeft(10); //Tile added here to the row
table.debug();
//VericalGroup the buttons and the label
VerticalGroup vGroup = new VerticalGroup();
vGroup.addActor(pennyLabel);
vGroup.addActor(table);
//Put in another group to align correctly with api widget
Table rootTable = new Table();
rootTable.setName("root table");
rootTable.setFillParent(true);
rootTable.add(vGroup);
rootTable.debug();
// stage.addActor(pennyLabel);
stage.addActor(rootTable);
}
#Override
public void dispose() {
spriteBatch.dispose();
stage.dispose();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
Table.drawDebug(stage);
if(nextScreen){
myGame.setScreen(new GameScreen(myGame));
dispose();
}
}
#Override
public void resize(int width, int height) {
stage.setViewport(width, height, false);
}
#Override
public void hide() {
Gdx.input.setInputProcessor(null);
}
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
}
#Override
public void pause() {
// Irrelevant on desktop, ignore this
}
#Override
public void resume() {
// Irrelevant on desktop, ignore this
}

Your Tile Actor must set its own size so the Table knows how to place it. Since it's a subclass of Actor, it already has the size fields available for you to fill in (don't create your own height and width variables, which would hide the correct ones).
texture = new Texture(Gdx.files.internal("assets/game/tile.png"));
width = texture.getWidth();
height = texture.getHeight();
Then to draw it, use the x and y fields (also part of the Actor superclass). The Table will set the x and y values for you, so this is all you need to do.
#Override
public void draw (SpriteBatch batch, float parentAlpha) {
batch.setColor(1, 1, 1, parentAlpha);
batch.draw(texture, x, y);
}
Note that you must always set color, even if it's white, because its always possible that the batch's color has been left as something non-white by some other thing it has just drawn.

Related

mirrored against x axis movement

I am trying to implement movement to a point, where mouse was clicked.
But I have a problem with mirrored behaviour agains X axis.
When I click on top -> it moves to the bottom, when I click on bottom -> it moves to the top.
Here is for example original position
I clicked on the screen in position with red cross.
But it moves down (as arrow showed).
What's the problem? It seems something with movement vector I presume.
public class Player {
private static final float PLAYER_CIRCLE_RADIUS = 24f;
private static final float MOVEMENT_SPEED = 200f;
private final Circle playerCircle;
private Vector2 direction = new Vector2();
private Vector2 position;
private Vector2 velocity = new Vector2();
private Vector2 movement = new Vector2();
private Vector2 mouseClick = new Vector2();
public Player(float x, float y) {
position = new Vector2(x, y);
playerCircle = new Circle(x, y, PLAYER_CIRCLE_RADIUS);
}
public void draw(ShapeRenderer shapeRenderer) {
shapeRenderer.circle(position.x, position.y, playerCircle.radius);
}
public void update(float delta) {
movement.set(velocity).scl(delta);
if (position.dst2(mouseClick) > movement.len2()) { position.add(movement); }
else { position.set(mouseClick); }
}
public void setDirection(float x, float y) {
mouseClick.set(x, y);
direction.set(mouseClick).sub(position).nor();
velocity.set(direction).scl(MOVEMENT_SPEED);
}
public Vector2 getDirection() {
return direction;
}
public Circle getPlayerCircle() {
return playerCircle;
}
public Vector2 getMouseClick() {
return mouseClick;
}
}
public class GameScreen extends ScreenAdapter {
private static final float WORLD_WIDTH = 640;
private static final float WORLD_HEIGHT = 480;
private ShapeRenderer shapeRenderer;
private Viewport viewport;
private Camera camera;
private Player player;
private Destination dest;
#Override
public void render(float delta) {
clearScreen();
update(delta);
shapeRenderer.setProjectionMatrix(camera.projection);
shapeRenderer.setTransformMatrix(camera.view);
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
dest.draw(shapeRenderer);
player.draw(shapeRenderer);
shapeRenderer.end();
}
#Override
public void resize(int width, int height) {
viewport.update(width, height);
}
#Override
public void show() {
camera = new OrthographicCamera();
camera.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2, 0);
viewport = new FitViewport(WORLD_WIDTH, WORLD_HEIGHT, camera);
shapeRenderer = new ShapeRenderer();
player = new Player(WORLD_WIDTH / 2, WORLD_HEIGHT / 2);
dest = new Destination();
Gdx.input.setInputProcessor(new InputAdapter() {
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
dest.setPosition(screenX, screenY);
camera.unproject(new Vector3(screenX, screenY, 0));
player.setDirection(screenX, screenY);
return true;
}
});
}
private void clearScreen() {
Gdx.gl.glClearColor(Color.BLACK.r, Color.BLACK.g, Color.BLACK.b, Color.BLACK.a);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
private void update(float delta) {
player.update(delta);
}
}
What you describe is actually mirrored on the Y-Axis.
The reason for this behaviour is most likely that your drawing matrix is set in such a way that the origin is in the bottom-left corner with the Y-Axis pointing up, but gui's have their origin at the top-left corner with the Y-Axis pointing down. So when you get your mouse position you should do something like:
actualPosY = screenHeight - mousePosY
This effectively transforms your mouse position to your drawing space.

Creating a playButton in libGdx with InputHandler

I've created a game that uses 4 different GameStates: READY,RUNNING,GAMEOVER,and HIGHSCORE(variation of GAMEOVER except this one notifies the player that a highscore has been reached). My issue is that the way I've set up my InputHandler is that in the GameState.READY form the user can touch anywhere in the screen to advance into the GameState.RUNNING form. I've tried multiple tactics into creating a playButton, however nothing seems to be working my way. I created a PlayButton class as such:
public class PlayButton {
private Vector2 position;
private Rectangle boundingRectangle;
private int width;
private int height;
private PlayScreen playScreen;
public PlayButton (float x, float y, int width, int height) {
this.width = width;
this.height = height;
position = new Vector2(x, y);
boundingRectangle = new Rectangle();
}
public void update(float delta) {
boundingRectangle.set(position.x,(position.y),width,height);
}
public float getTheX() {
return position.x;
}
public float getY() {
return position.y;
}
public float getWidth() {
return width;
}
public float getHeight() {
return height;
}
public Rectangle getBoundingRectangle(){
return boundingRectangle;
}
}
and in my InputHandler i tried this:
public InputHandler(GameWrold myWorld, float scaleFactorX, float scaleFactorY){
this.myWorld = myWorld;
mySam = myWorld.getSam();
playButton = new PlayButton(45,gameHeight-75,50,-35);
buttonPlay = myWorld.getPlayButton();
int midPointY = myWorld.getMidPointY();
this.scaleFactorX = scaleFactorX;
this.scaleFactorY = scaleFactorY;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector2 touchPos = new Vector2();
touchPos.set(Gdx.input.getX(), Gdx.input.getY());
Vector2 tuch = new Vector2(screenX,screenY);
buttonPlay.getBoundingRectangle();
touch = new Rectangle(touchPos.x,touchPos.y,5,5);
if (myWorld.isReady()) {
if(playButton.getBoundingRectangle().contains(touchPos)){
myWorld.start();
}
}
mySam.onClick();
if (myWorld.isGameOver() || myWorld.isHighScore()) {
// Reset all variables, go to GameState.READ
myWorld.restart();
}
return true;
}
}
As you can see I tried creating a Rectangle with the touch variable and I tried checking if touch collided with the playButton boundingRectangle like this:
if(Intersector.overlaps(touch,playButton.getBoundingRectangle())){ or
(playButton.getBoundingRectangle().overlaps(playButton.getBoundingRectangle()))
Do not invent a circle and don't break open doors.
Use Scene2d :)
It is a simple UI framework fully compatible with libGDX that allows you to create Buttons, Sliders, Windows and another widgets in about one line of code.
You will find a nice description in a link I've attached above but TLDR version is:
Create Skin and pack a texture atlas for this using TexturePacker (free version is enough usually)
Create a Stage with appropriate Viewport
Stage stage = new Stage(new FitViewport(WIDTH, HEIGHT));
Set the stage as input processor (it means that all event on the stage will be catched)
Gdx.input.setInputProcessor(stage);
Create a Button with appropriate style (defined in Skin)
Button button = new Button(skin, "style");
Attach a ClickListener with appropriate action to the Button
button.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y)
{
//some action
}
});
Set it's position and add it to the stage
button.setPosition(x, y);
stage.addActor(button);
Fire act() and draw() stage's methods in your render()
//render()
stage.act();
stage.draw();
You've earned few hours ;)

Clicklistener - different coordinates while resizing

I have a tiled map on which I set characters(every character is of the size of one tile). I managed to make them clickable, even when screen resizes everything works perfect. Every time I click on character I want a button to show up above it. For a button I use stage and place the button in the place I clicked with small transition and it also works.
My problem is when I try to use clicklistener on this button. If the screen does not resize, clicklistener works. Problem starts when the screen get resized - clicks on players works well, only button does not work - after a resize the clicking space and the button space are not aligned. For test purposes I made a test map that shows me grids. It seems that stage is not properly resized. Picture for a reference. I tried many solutions I came upon on the Internet and still can't find a solution to my problem. I have shortened my code down to a minimum basic example:
public class Test extends ApplicationAdapter {
public static Stage stage;
public TiledMap tiledMap;
static OrthographicCamera camera;
TiledMapRenderer tiledMapRenderer;
public static boolean showMenu;
GestureDetector gesture;
InputMultiplexer myInputMultiplexer;
public static int posx, posy;
public static Image move;
public Texture moveMenu;
#Override
public void create() {
moveMenu = new Texture(Gdx.files.internal("move.png"));
gesture = new GestureDetector(new MyGestureListener());
myInputMultiplexer = new InputMultiplexer();
float unitScale = 1 / 32f;
camera = new OrthographicCamera();
camera.setToOrtho(true, 33, 21);
stage = new Stage(new ScreenViewport());
stage.getViewport().setCamera(camera);
tiledMap = new TmxMapLoader().load("test.tmx");
tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap, unitScale);
myInputMultiplexer.addProcessor(stage);
myInputMultiplexer.addProcessor(gesture);
Gdx.input.setInputProcessor(myInputMultiplexer);
move = new Image(moveMenu);
move.setWidth(2);
move.setHeight(2);
move.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
move(); //my action, works fine
showMenu = false;
}
});
stage.addActor(move);
}
#Override
public void render() {
super.render();
stage.act();
tiledMapRenderer.setView(camera);
camera.update();
tiledMapRenderer.render();
if (showMenu) {
mainMenuDraw();
}
}
public static void mainMenuDraw() {
move.setPosition(posx, posy-2);
stage.draw();
}
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
public static OrthographicCamera getCamera() {
return camera;
}
public static Vector3 unprojectCoords(Vector3 coords) {
camera.unproject(coords);
return coords;
} }
and part of my gesturelistener:
public boolean touchDown(float x, float y, int pointer, int button) {
Vector3 temp_coord = new Vector3(x, y, 0);
Vector3 coords = Test.unprojectCoords(temp_coord);
return false;
}
#Override
public boolean tap(float x, float y, int count, int button) {
Vector3 temp_coord = new Vector3(x, y, 0);
Vector3 coords = Test.unprojectCoords(temp_coord);
Test.posx = (int) coords.x;
Test.posy = (int) coords.y;
tap = true;
Test.showMenu = true;
return false;
}
I would suggest to you read more about re-sizing and displaying pixels.
You need to recalculate pixels always when you render something.
- player, background image, buttons, events.
Actually you don't need to use resize method, just get the camera width and height.
You need to share more code, because it depends on everything.
I don't see how you are doing rendering.
tiledMapRenderer.render(), background rendering / layers?
player rendering?
menu rendering?
buttons rendering?
Example: (the same should be for event handler)
public GameButton(TextureRegion reg, float x, float y, OrthographicCamera cam) {
this.reg = reg;
this.x = x;
this.y = y;
this.cam = cam;
width = reg.getRegionWidth();
height = reg.getRegionHeight();
vec = new Vector3();
Texture tex = Game.res.getTexture("hud");
font = new TextureRegion[11];
for(int i = 0; i < 11; i++) {
font[i] = new TextureRegion(tex, 32 + i * 9, 16, 9, 9); //use height and width here)
}
}

Libgdx sceene2d and Input Processor not updating screen

I am rather new to the libgdx Framework so I hope I am not asking anything stupid, but I have a problem with updating my screen on the toucheEvent. It seems that the touch event fires, but the stage is not updated so the screen is all the time the same. Here is the code
MainClass
public class MainGame implements Screen {
public LabirintGame game;
public Stage stage;
public OrthographicCamera camera;
public ActorM rigth;
public ActorM wrong;
public MainGame(LabirintGame game) {
this.game = game;
this.camera = new OrthographicCamera();
}
#Override
public void show() {
this.camera.setToOrtho(false, 800, 480);
stage = new Stage(new ScreenViewport());
stage.clear();
Words group = new Words(stage);
InputMultiplexer inputMultiplexer = new InputMultiplexer();
inputMultiplexer.addProcessor(stage);
inputMultiplexer.addProcessor(new MyInputProcessor(stage, camera));
Gdx.input.setInputProcessor(inputMultiplexer);
//Add wrong and rigth boxes
rigth = new ActorM("box", 0, 0, 200,200);
wrong = new ActorM("box",(game.width - 230), 0, 200, 200);
wrong.moveBy(200,200);
Button createButtons = new Button();
createButtons.setStyle("atlas-besede/besede.atlas", "buttonOff", "buttonOn");
TextButton ValidationButton = createButtons.createButton("Validate", (game.width/2), 0, 150, 150);
ValidationButton.addListener(new InputListener() {
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
game.setScreen(new Labirint(game));
return true;
}
});
stage.addActor(ValidationButton);
stage.addActor(rigth);
stage.addActor(wrong);
List<String> backgrounds = Arrays.asList("s", "z");
for (int i = 0; i < backgrounds.size(); i++) {
Word actor = new Word(backgrounds.get(i),(i + 1) * 300, 300, 100, 100);
actor.setPosition((i + 1) * 300, 300);
actor.setName(backgrounds.get(i));
group.addActor(actor);
}
stage.addActor(group);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
game.batch.begin();
game.batch.setProjectionMatrix(camera.combined);
game.batch.end();
}
ActorM
package com.mygdx.game;
public class ActorM extends Actor {
public SpriteBatch batch;
public TextureAtlas atlas;
public TextureAtlas.AtlasRegion region;
Sprite sprite;
public int x;
public int y;
public int width;
public int height;
public ActorM(String actorName, int x, int y, int width, int height) {
//this.region = region;
super();
batch = new SpriteBatch();
atlas = new TextureAtlas(Gdx.files.internal("atlas-start/atlas-start.atlas"));
sprite = atlas.createSprite(actorName);
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.setBounds(0, 0, sprite.getWidth(), sprite.getHeight());
setTouchable(Touchable.disabled);
setName(actorName);
setPosition(x,y);
}
#Override
public void draw (Batch batch, float parentAlpha) {
batch.draw(sprite, x,y, width, height);
}
public void move(int posX){
this.x = this.x + posX;
}
}
MyInputProcessor
public class MyInputProcessor implements InputProcessor {
private OrthographicCamera camera;
private Stage stage;
private Vector2 coordinates;
private Music sound;
public MyInputProcessor( Stage stage, OrthographicCamera camera) {
this.stage = stage;
this.camera = camera;
}
#Override
public boolean keyDown(int keycode) {
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)
{
//Gdx.app.log("", "x " + screenX + " y " + screen`enter code here`Y + " pointer " + pointer);
Vector2 coordinates = stage.screenToStageCoordinates(new Vector2((float)screenX,(float)screenY));
Actor hitactor = stage.hit(coordinates.x, coordinates.y, true);
Gdx.app.log("", coordinates.toString());
if (hitactor != null){
//Gdx.app.log("", "HIT" + hitactor.getName());
Gdx.app.log("", "HIT" + hitactor.getRotation());
hitactor.setRotation(hitactor.getRotation() + 1f);
hitactor.setPosition(5,5);
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
return true;
}
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
return false;
}
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
coordinates = stage.screenToStageCoordinates(new Vector2((float)screenX,(float)screenY));
Actor hitactor = stage.hit(coordinates.x, coordinates.y, true);
if (hitactor != null){
Gdx.app.log("", "Drag");
hitactor.setRotation(hitactor.getRotation() + 1f);
}
camera.update();
return true;
}
#Override
public boolean mouseMoved(int screenX, int screenY) {
return false;
}
#Override
public boolean scrolled(int amount) {
return false;
}
}
Your "ValidationButton" uses an InputProcessor that always returns true and it's the first actor in the stage, so nothing else in the stage will ever get an opportunity to respond to touch down events. Furthermore, since your stage is the first input processor in your InputMultiplexer, your other input processor never gets an opportunity to respond to touch down events either.
You should use an EventListener on your button instead of an InputListener, so much of the logic will be taken care of for you.
By the way, your ActorM class is spawning a SpriteBatch that it never uses. SpriteBatch takes up significant memory, and there's no need for there to be more than one of them in your game. The Stage already has a reference to a SpriteBatch that it passes into your Actor's draw method, so the Actor does not need to create or even reference a SpriteBatch.
Also, your ActorM class is loading a complete copy of a TextureAtlas for itself so there will be duplicate Textures loaded for each instance of ActorM, and you lose all the benefits of a TextureAtlas, since you won't be using it for sprite batching. You need to load the TextureAtlas only one time, and pass a reference of it into the constructor of your ActorM class, so they can all share the same Texture.

how to check if background is touched and not the Actor in libgdx?

i have random boxes coming on the screen over and over, when the box is touch it switches positions. I am not sure how to check if the screen is touched and not the box. anywhere but the box.
private Stage stage;
private Texture boxImage;
private Image pop;
#Override
public void show() {
stage = new Stage();
Gdx.input.setInputProcessor(stage);
boxImage = new Texture(Gdx.files.internal("box.png"));
pop = new Image(boxImage);
pop.setPosition(20,20);
pop.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y)
{
int num1 = (int)MathUtils.random(50,500);
int num2 = (int)MathUtils.random(50,500);
pop.setPosition(num1,num2);
}
});
stage.addActor(pop);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0 , 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
How about just adding a ClickListener to your stage?
stage.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
//check if box is hit
if (pop.equals(stage.hit(x, y, false))) {
//box hit
} else {
//box was not hit
}
}
});

Categories