Using TextField with LibGDX - java

I am working on an Android game with LibGDX and would like to implement two TextFields to login to a server.
As far as I know I need to use Stage to add a TextField as an Actor like this:
this.stage = new Stage();
TextField txtf = new TextField("", mSkin);
//...
stage.addActor(txtf);
// And then set the stage as InputProcessor
Gdx.input.setInputProcessor(this.stage);
But I already have a custom InputHandler which implements InputProcessor. I don't really know how I can handle a TextField with my InputHandler.
I have created my TextField in my InputHandler like this:
public class InputHandler implements InputProcessor {
public InputHandler(float ratioWidth, float ratioHeight, GameWorld world) {
this.world = world;
this.player = world.getPlayer();
this.ratioWidth = ratioWidth;
this.ratioHeight = ratioHeight;
//...
this.usernameTextField = new TextField("", AssetLoader.defaultSkin);
this.usernameTextField.setPosition(24,73);
this.usernameTextField.setSize(88, 14);
this.passwordTextField = new TextField("", AssetLoader.defaultSkin);
this.passwordTextField.setPosition(24, 102);
this.passwordTextField.setSize(88, 14);
this.actors.add(this.usernameTextField);
this.actors.add(this.passwordTextField);
}
And then I have a method in my InputHandler that can get the TextField for the GameRenderer:
public ArrayList<Actor> getActors() { return this.actors; }
And this is my GameRenderer:
public class GameRenderer {
public GameRenderer(GameWorld world) {
this.world = world;
//...
}
//...
private void drawLogUI() {
for(Actor a : ((InputHandler) Gdx.input.getInputProcessor()).getActors()){
a.draw(batcher, 1);
}
//...
}
public void render(float delta) {
// Initialisation
Gdx.gl.glClearColor(32/255.0f, 72/255.0f, 132/255.0f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batcher.begin();
batcher.enableBlending();
//...
drawLogUI();
//...
batcher.end();
}
//...
}
TextFields are rendering correctly but I can't do anything with them, keyboard doesn't show up, etc. Can someone explain how I can use them in my project ?

You know that you need to use Stage and TextField for your requirement, you created TextField but not adding to stage so you're not using scene2d(stage, actor, textfield..)
Tutorial that you following, don't have stage so he is using InputProcessor for his requirement.
TextField usernameTextField = new TextField("", AssetLoader.defaultSkin);
usernameTextField.setPosition(24,73);
usernameTextField.setSize(88, 14);
stage.add(usernameTextField); // <-- Actor now on stage
Gdx.input.setInputProcessor(stage);
Inside render() method call
stage.draw();
stage.act();

Related

Camera doesn't work anymore after implementing Image Button - Libgdx

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
}

LibGDX Tiled (TMX) Maps + Scene2D, bugs when rendering both

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.

TextButton not responding with InputListener on libGDX?

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.

Incomprehensible NullPointerException

I have a very basic program, that is supposed to display a label and a text field. I am able to display the text. For the textfield, I can create it without error, but when I add it to the stage and run the program I have this error :
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.badlogic.gdx.scenes.scene2d.ui.TextField.draw(TextField.java:301)
at com.badlogic.gdx.scenes.scene2d.Group.drawChildren(Group.java:111)
at com.badlogic.gdx.scenes.scene2d.Group.draw(Group.java:58)
at com.badlogic.gdx.scenes.scene2d.Stage.draw(Stage.java:128)
at com.pace.converter.MainMenuScreen.render(MainMenuScreen.java:52)
at com.badlogic.gdx.Game.render(Game.java:46)
at com.pace.converter.MyGdxGame.render(MyGdxGame.java:21)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:207)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)
As I don't know how to put line numbers in the code block of my comments, here are the lines pointed by the error :
MainMenuScreen.java: 52 is
stage.draw();
MyGdxGame.java: 21 is
super.render();
I don't understand why I have this NullPointerException as I created the TextField before adding it to the stage.
And here is my complete code :
Main activity :
public class MyGdxGame extends Game {
SpriteBatch batch;
AssetManager assets;
#Override
public void create () {
batch = new SpriteBatch();
assets = new AssetManager();
this.setScreen(new LoadingScreen(this));
}
#Override
public void render () {
super.render();
}
public void dispose () {
batch.dispose();
}
}
Main menu screen :
public MainMenuScreen(final MyGdxGame gam) {
game = gam;
camera = new OrthographicCamera();
camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
skin = new Skin(Gdx.files.internal("skin.json"));
stage = new Stage();
labelStyle = new LabelStyle(game.assets.get("font1.ttf", BitmapFont.class), Color.WHITE);
labelTest = new Label("Test", labelStyle);
textFieldTest = new TextField("Test", skin);
stage.addActor(labelTest);
stage.addActor(textFieldTest);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0.281f, 0.602f, 0.844f, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.batch.setProjectionMatrix(camera.combined);
stage.act();
stage.draw();
}
First off all you need to check some things which could be null.. wrap your code in try-catch blocks
I don't know what is stage but check if it has been initialized or not ?
If stage is not null
then check in your Main menu Screen
skin = new Skin(Gdx.files.internal("skin.json")); // is the skin null ? if the file 'skin.json' couldn't be found then this would be null.. check this
textFieldTest = new TextField("Test", skin); // If skin is null, you're adding it here
as for the super.render(); is it even supposed to be called here ?

ClickListener and InputHandler interfering

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.

Categories

Resources