I am working on an game for Android. I want the user to be able to "scroll" to the left and the right side by dragging the finger. That worked perfectly till now. Right now I am trying to implement the Gui with some buttons.
It should look kinda like this:
https://i.stack.imgur.com/jf0uZ.png
Code:
public class MainGameScreen implements Screen, InputProcessor {
Texture background, background_left, background_right;
public SpriteBatch batch;
//Graphical user interface
private Stage GUIStage;
private InputMultiplexer multiplexer;
//Camera
OrthographicCamera camera;
//Font
private BitmapFont font;
private String message = "Touch me";
//Buttons
private Stage button;
private Texture myTexture;
private TextureRegion myTextureRegion;
private TextureRegionDrawable myTexRegionDrawable;
private ImageButton imageButton;
public MainGameScreen (Trench_Warfare game) {
this.game = game;
batch = new SpriteBatch();
button = new Stage();
//GUI - Graphical user interface
GUIStage = new Stage(new ScreenViewport());
Image gui_background = new Image(new Texture("gui/GUI.png"));
gui_background.setPosition(0,0);
gui_background.setTouchable(Touchable.disabled);
GUIStage.addActor(gui_background);
//Buttons
myTexture = new Texture(Gdx.files.internal("gui/button/paper_riflemen.png"));
myTextureRegion = new TextureRegion(myTexture);
myTexRegionDrawable = new TextureRegionDrawable(myTextureRegion);
imageButton = new ImageButton(myTexRegionDrawable); //Set the button up
button = new Stage(new ScreenViewport()); //Set up a stage for the ui;
imageButton.isTouchable();
imageButton.setBounds(0,500,194,200);
button.addActor(imageButton); //Add the button to the stage to perform rendering and take input.
//Font
font = new BitmapFont();
font.setColor(Color.BLACK);
font.getData().scale(5);
//Background
background = new Texture("level1.png");
background_left = new Texture("level1_seiten.png");
background_right = new Texture("level1_seiten.png");
//Camera
camera = new OrthographicCamera(Gdx.graphics.getWidth()/*4000*/, Gdx.graphics.getHeight()/*2200*/);
camera.update();
camera.setToOrtho(false, Gdx.graphics.getWidth()*1.5f, Gdx.graphics.getHeight()*1.5f);
button.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
message = "Touch";
event.handle();//the Stage will stop trying to handle this event
}
});
InputMultiplexer multiplexer = new InputMultiplexer(button, this);
multiplexer.addProcessor(button);
multiplexer.addProcessor(this);
Gdx.input.setInputProcessor(multiplexer);
}
The Camera movement happens in the "#Override touchDragged" stuff. I don't think I have to show it here.
I am now trying to implement the buttons for two days now but I can't get them to work.
The problem is right at the bottom:
InputMultiplexer multiplexer = new InputMultiplexer(this, button);
Gdx.input.setInputProcessor(multiplexer);
In this order I can move the camera around but I cant touch the button. If I write (...)(button, this); I can't move the camera around anymore but can click the button. I can also activate the button's function when I click anywhere on the screen.
Hopefully you can help me with this problem!
According to the LibGDX Wiki on Event-Handling with InputMultiplexer:
The InputMultiplexer will hand any new events to the first InputProcessor that was added to it. If that processor returns false from the method invoked to handle the event, this indicates the event was not handled and the multiplexer will hand the event to the next processor in the chain. Through this mechanism, the MyUiInputProcessor can handle any events that fall inside one of its widgets and pass on any other events to the MyGameInputProcessor.
Your problem can be solved by setting the returning value of your overriden methods in your InputAdapters' children classes to false, here's an example:
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
[Some Logic]
return false; // Input has not been processed, next processor in the multiplexer will try to handle this
}
Related
So I've recently started tinkering with LibGDX (desktop only, at the moment) in Java. I'm mildly experienced with other type of OGL libraries in other languages (namely SFML in C++) and quite experienced with the Java language itself, so I tackled LibGDX pretty confidently; and while the results were mostly positive, there's a bug that I just can't figure out.
What I want to achieve is simple: draw a map (Tiled .tmx), draw a Sprite (soon-to-be "player") inbetween layers 1 and 2, and render a couple of GUI widgets on top of all that, using a scene2D stage. I've managed to achieve this partially.
Here's my Game class, it's pretty messy from so much switching things around just to try, but it's pretty clear what it attempts to do:
public class Game extends ApplicationAdapter
{
private SpriteBatch batch;
private Texture img;
private Sprite playerSprite;
private TiledMap tiledMap;
private OrthographicCamera camera;
private OrthogonalTiledMapRenderer tiledMapRenderer;
private Stage stage;
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("trchar000.png");
playerSprite = new Sprite(img, 0, 0, 32, 48);
playerSprite.setPosition(32, 192);
InputHandler.init();
Gdx.input.setInputProcessor(new InputHandler());
stage = new TestStage(playerSprite);
// inputs
InputMultiplexer im = new InputMultiplexer(stage, new InputHandler());
Gdx.input.setInputProcessor(im);
// tiles
camera = new OrthographicCamera();
camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.update();
tiledMap = new TmxMapLoader().load("map2.tmx");
tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
tiledMapRenderer.setView(camera);
int[] groundLayers = {0};
int[] topLayers = {1};
// render layer1 -> player -> layer 2
// the .tmx map doesn't have any other layers (just those 2)
tiledMapRenderer.render(groundLayers);
// removing these 3 lines solves the issue, but doesn't render the player
// I've also tried using my regular SpriteBatch instance; same results
// Also, I tried creating a class that extends OrthogonalTiledMapRenderer and overriding the render() method, so that it would draw the player inbetween layers, but again, same results.
tiledMapRenderer.getBatch().begin();
tiledMapRenderer.getBatch().draw(playerSprite, playerSprite.getX(), playerSprite.getY());
tiledMapRenderer.getBatch().end();
tiledMapRenderer.render(topLayers);
batch.begin();
// just to try, I'm rendering the stage here (not calling it's draw() method). Removing this for loop solves the issue, but doesn't render the GUI
for(Actor a : stage.getActors())
a.draw(batch, 1);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
img.dispose();
tiledMapRenderer.dispose();
stage.dispose();
}
}
Also, here's my TestStage class:
public class TestStage extends Stage {
private Skin skin;
private BitmapFont newDefaultFont;
public TestStage(Sprite player)
{
super(new ScreenViewport());
// skin
FreeTypeFontGenerator freeTypeFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("segoeui.ttf"));
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = 14;
newDefaultFont = freeTypeFontGenerator.generateFont(parameter);
skin = new Skin();
skin.add("default-font", newDefaultFont, BitmapFont.class);
FileHandle fileHandle = Gdx.files.internal("uiskin.json");
FileHandle atlasFile = fileHandle.sibling("uiskin.atlas");
if (atlasFile.exists()) {
skin.addRegions(new TextureAtlas(atlasFile));
}
skin.load(fileHandle);
final TextButton button = new TextButton("This is a Button", skin, "default");
button.setPosition(250, 250);
button.setWidth(150);
button.setHeight(40);
final Label lbl = new Label("Test", skin);
this.addActor(lbl);
this.addActor(button);
}
}
Now here's the deal; this is what it looks like if a try to render everything (as shown in my Game class' render() method):
Image 1
The player, GUI, and layer 2 are rendered correctly, but the layer 1 is all bugged out, and I can't figure out why.
Now, here's how it looks like if a don't render the player, removing the line
tiledMapRenderer.getBatch().draw(playerSprite, playerSprite.getX(), playerSprite.getY());
Image 2
Everything renders fine.
Also, here's what it looks like if I decide not to render the stage actors, commenting this loop:
for(Actor a : stage.getActors())
a.draw(batch, 1);
Image 3
Again, everything renders fine.
The only other classes in my project are the default DesktopLauncher class, and an InputProcessor which I'm sure has nothing to do with this.
This is my first question here ever; I just can't figure this out.
Thanks in advance.
I was able to draw my buttons, but nothing happens when I click/"touch" them, and I have no idea what I'm doing wrong. Can anyone please help? Shouldn't the textButtonStyle change to "ToothButton" when it's touched? Am I not supposed to use an InputListener for an Android app?
MainMenuButtons.java
public class MainMenuButtons extends Stage {
Stage buttons;
MMButton startButton, optionButton;
public MainMenuButtons(Viewport viewport) {
startButton = new MMButton(634,550, "Start");
optionButton = new MMButton(634,450, "Options");
buttons = new Stage(viewport);
buttons.addActor(startButton);
buttons.addActor(optionButton);
Gdx.input.setInputProcessor(this);
}
MMButton.java (Main Menu Button)
public class MMButton extends Actor{
TextButton button;
TextButton.TextButtonStyle textButtonStyle;
BitmapFont font;
Skin skin;
TextureAtlas buttonAtlas;
public MMButton(int x, int y, String name) {
font = new BitmapFont();
skin = new Skin();
buttonAtlas = new TextureAtlas(Gdx.files.internal("menuButton.atlas"));
skin.addRegions(buttonAtlas);
textButtonStyle = new TextButton.TextButtonStyle();
textButtonStyle.font = font;
textButtonStyle.up = skin.newDrawable("ToothButtonUp");
textButtonStyle.down = skin.newDrawable("ToothButton");
button = new TextButton(name, textButtonStyle);
button.setBounds(x, y, 246, 90);
button.setTouchable(Touchable.enabled);
button.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 ) {
super.touchUp( event, x, y, pointer, button );
}
});
}
MainMenuScreen.java (I'm sorry for all the code I really just want this problem solved!) :(
OrthoCamera is a class I got online that simplified the use of a camera.
public class MainMenuScreen extends Screen{
private OrthoCamera camera;
MainMenuButtons buttons;
#Override
public void create() {
camera = new OrthoCamera();
buttons = new MainMenuButtons(new ScreenViewport());
}
#Override
public void update() {
camera.update();
buttons.draw();
}
#Override
public void render(SpriteBatch batch) {
batch.setProjectionMatrix(camera.combined);
buttons.draw();
}
#Override
public void resize(int width, int height) {
camera.resize();
buttons.getViewport().update(width,height,true);
}
Your problem is with the MainMenuButtons class. It has two stages. One (buttons) has the button actors added to it, and the other (this) is set as the input processor. That's no good.
There are two solutions.
Either...
Replace this line...
Gdx.input.setInputProcessor(this);
With this...
Gdx.input.setInputProcessor(buttons);
And don't bother extending stage.
Or (if you really want to extend stage)...
Get rid of the buttons variable altogether, and replace these lines...
buttons.addActor(startButton);
buttons.addActor(optionButton);
With this...
addActor(startButton);
addActor(optionButton);
I see a few issues.
You have a stage within a stage. The inner stage has your buttons in it, but it's not set as the input processor. Remove the inner stage and use the outer one directly. Or if you're trying to organize your code here, make your MainMenuButtons class not extend Stage (and give it a getStage() method so the game class can call act and draw on it).
You never call act on your stage, so it won't respond to or act on anything.
Your MMButton class contains an inner Button instance that is never added to the Stage, so the inner Button will never receive touch events, even though it has a listener. You could make the MMButton class extend Group instead of Actor, and add the inner Button to it via addActor(). But I think you are reinventing the wheel here. You don't need an MMButton class when you can use the existing TextButton class directly--it's already an Actor itself. From what I can see, MMButton is a redundant wrapper.
Sidenote: It is inadvisable to use separate texture atlases and fonts for each button. A texture atlas is meant to be shared by many objects to take advantage of sprite batching.
I have a class called InputHandler which implements InputProcessor and is the InputProcessor for my Gameworld. That works just fine. But now I'm trying to built a main menu and my clickListeners do not work, but instead touchDown() from my InputHandler-class is called. I created an instance of all my screens to be able to switch between them easily but I have no idea how to fix that. I've heard of an InputMultiplexer but I have no plan how to integrate such a thing in my code to solve my problems. I tried to return false from my touchDown() and other methods but my ClickListeners don't do anything at all even after that.
Here is my code:
1st my "Main"Class where I create all the screens:
public void create(){
mainMenuScreen = new MainMenuScreen(this);
gameScreen = new GameScreen(this);
setScreen(mainMenuScreen);
}
the game class with its inputProcessor:
public GameScreen(final Stapler gam) {
this.game = gam;
world = new World(new Vector2(0, StaplerValues.WORLD_GRAVITY), true);
Gdx.input.setInputProcessor(new InputHandler(world));
my InputHandler:
public class InputHandler implements InputProcessor {
World world;
public InputHandler(World world) {
this.world = world;
}
public boolean touchDown(int x, int y, int pointer, int button) {
// this is called even when i'm in my main menu and want to click a button
return false;
}
public boolean touchUp(int x, int y, int pointer, int button) {
// your touch up code here
return false; // return true to indicate the event was handled
}
public boolean touchDragged(int x, int y, int pointer) {
return false;
}
and my main menu with its clickListeners:
public class MainMenuScreen implements Screen {
public MainMenuScreen(final Stapler gam) {
game = gam;
stage = new Stage();
table = new Table();
table.setFillParent(true);
stage.addActor(table);
Gdx.input.setInputProcessor(stage);
// Add widgets to the table here.
TextureRegion upRegion = new TextureRegion(new Texture(
Gdx.files.internal("boxLila.png")));
TextureRegion downRegion = new TextureRegion(new Texture(
Gdx.files.internal("boxGruen.png")));
BitmapFont buttonFont = new BitmapFont(
Gdx.files.internal("fonts/bodoque.fnt"), false);
buttonFont.setScale(2);
TextButtonStyle style = new TextButtonStyle();
style.up = new TextureRegionDrawable(upRegion);
style.down = new TextureRegionDrawable(downRegion);
style.font = buttonFont;
play = new TextButton("Play", style);
play.addListener(new ClickListener() {
public void clicked(InputEvent e, float x, float y) {
game.setScreen(game.gameScreen);
}
});
// add the button with a fixed width
table.add(play).width(500);
// then move down a row
table.row();
}
The click listener works but only if i didnt create an instance of GameWorld in the first place. How can I solve that they grab the right input depending on which screen is currently shown? Please try give an answer as detailed as possible because I'm quite new to all that stuff. And sorry for that mess of code and my bad english an thanks in advance!!!
There is globally only one InputProcessor for the entire game. When you call Gdx.input.setInputProcessor(new InputHandler(world));, it replaces the InputProcessor that you set in your MainMenuScreen class.
One simple solution is to just change the game's input processor every time you switch between screens (in the show() method).
If you want two InputProcessors to both work simultaneously, you need to combine them using an InputMultiplexer, and set the multiplexer as your game's InputProcessor.
I use libgdx's scene2d built-in UI library to make an UI in my game. I'm interesting how can I draw 2 images(or actors) in a table one on another? I'm looking to the similar like LayerDrawable in Android. Is there any exists stuff to make it?
There is a Stack widget in libGDX UI library for these purposes.
UPDATE: if you want to place one widget on top of another widget and these widgets have different size then you should wrap the smaller widget. Something like:
//First add actual content
stack.add(content);
//Second add wrapped overlay object
Table overlay = new Table();
overlay.add(overlayWidget).expand().fillX().bottom().left();
stack.add(overlay);
stack variable is a Stack instance
It's an actual excerpt from my project. You should tune it for your case.
You can use somethings like this :
//create stage
stage = new Stage(new StretchViewport(game.width, game.height));
//create table
Table table = new Table();
table.setSize(game.width,game.height); //fullscreen
//add Image 1
table.add(new Image(game.skin.getDrawable("image1")));
//add Image 2 (create new col)
table.add(new Image(game.skin.getDrawable("image2"))).width(xx).height(xx);
//add new row
table.row();
//add Image 3 - padding to draw over image1/2 && colspan
table.add(new Image(game.skin.getDrawable("image3"))).width(xx).height(xx).padTop(-50f).colspan(2);
//add table to stage
stage.addActor(table);
/*
* Add action
*
*/
#Override
public void render(float delta) {
...
//don't forget to draw ur stage
stage.act(delta); // or stage.act(Gdx.graphics.getDeltaTime())
stage.draw();
}
Also you can add action to stage/table or image etc ... :
stage.addAction(Actions.sequence(
Actions.fadeOut(0.0001f),
Actions.fadeIn(0.5f),
Actions.delay(0.1f),
Actions.run(new Runnable() {
#Override
public void run() {
//mmmm new screen
dispose();
game.setScreen(new BoobsScreen(game));
}
})));
Of course you can draw more than image : button, textbutton, label etc or add scrollPane for Table but before take a look at Skin on libgdx
For exemple a programmatically ninepatch textbutton :
private NinePatch processNinePatchFile(String fname) {
final Texture t = new Texture(Gdx.files.internal(fname));
final int width = t.getWidth() - 2;
final int height = t.getHeight() - 2;
return new NinePatch(new TextureRegion(t, 1, 1, width, height), 3, 3, 3, 3);
}
//create skin
skin = new Skin();
...
//create font
...
//create ninepatch button from android/assets/style/button
NinePatch ninepatch = processNinePatchFile("style/button.9.png");
skin.add("button.9", ninepatch);
//create textbuttonstyle
TextButton.TextButtonStyle textButtonStyle = new TextButton.TextButtonStyle();
textButtonStyle.font = skin.getFont("font_black_38");
textButtonStyle.fontColor = Color.DARK_GRAY;
textButtonStyle.up = skin.getDrawable("button.9");
skin.add("buttonTextStyle", textButtonStyle);
//now you can create textbutton
TextButton btn = new TextButton("Hello", game.skin.get("buttonTextStyle", TextButton.TextButtonStyle.class));
//add a clicklistener
btn.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
//mmmmmm add action for exemple
stage.addAction(Actions.sequence(Actions.fadeOut(0.5f), Actions.run(new Runnable() {
#Override
public void run() {
//dispose and load screen
dispose();
game.setScreen(new MoreBoobsScreen(game));
}
})));
}
});
table.add(btn);
To create bitmapfont take a look at Gdx freetype, it's really easy to use.
I have a menu screen in libgdx and I had a text button that started a new game like this.
textButton.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
g.setScreen( new level1(g));
}
});
It looked like crap so I changed it to an image.
playbuttontexture = new Texture(Gdx.files.internal("data/playbutton.png"));
playbuttontexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion playbuttonregion = new TextureRegion(playbuttontexture, 0, 0, 512, 256);//powers of 2
playbutton = new Image(playbuttonregion);
playbutton.setSize(512,256);
playbutton.setBounds(width/2-playbutton.getWidth()/2, height/2-playbutton.getHeight()/2, 512, 256);
//playbutton.setOrigin(playbutton.getWidth()/2, playbutton.getHeight()/2);
playbutton.setPosition(width/2-playbutton.getWidth()/2, height/2-playbutton.getHeight()/2);
and
playbutton.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
g.setScreen( new level1(g));
}
});
Now when I click it nothing happens? What am I doing wrong?
The problem here is that Image does not fire a changed(...) event anymore. This event is only fired by the TextButton you've used before when the status changes from clicked to not-clicked and the other way around. It can also be fired in other cases, since it is kind of a "generic" event, as the JavaDoc states, but that varies from actor to actor.
Change it to a ClickListener and use the clicked(...) method instead. This event should be fired by all actors in the scene2d.ui package.
for me this is what it looks like when I implemented the code ( I was having a similar issue. noone did a great job at pointing me to the right places to look. )
playbutton.addListener( new ClickListener(){
#Override
public void clicked (InputEvent event, float x, float y) {
//your code to do stuff when the button is clicked
}
});