I'm coding a Menu using the latest version of Libgdx. Each screen (MainMenuScreen, OptionsMenuScreen, CreditsMenuScreen) contains some buttons. Each button should change the screen.
If I'm on MainMenuScreen and I press "credits", I get the CreeditsMenuScreen. But when I use "back button" to get back in the Main Menu, I get a stackOverflow error.
Here is some parts of the code:
//In the MainMenuScreen.java:
if( creditsButton.isPressed() ) {
menuManager.setScreen("credits");
}
//In the CreditsMenuScreen.java I have
if( backButton.isPressed() ) {
menuManager.setScreen("main");
}
Here is the error. I think it's connected to the touchevent but I don't know how to fix it..
Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: java.lang.StackOverflowError
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:133)
Caused by: java.lang.StackOverflowError
at org.lwjgl.BufferChecks.checkDirect(BufferChecks.java:138)
at org.lwjgl.opengl.GL20.glVertexAttribPointer(GL20.java:856)
at com.badlogic.gdx.backends.lwjgl.LwjglGL20.glVertexAttribPointer(LwjglGL20.java:829)
at com.badlogic.gdx.graphics.glutils.ShaderProgram.setVertexAttribute(ShaderProgram.java:657)
at com.badlogic.gdx.graphics.glutils.VertexArray.bind(VertexArray.java:115)
at com.badlogic.gdx.graphics.Mesh.bind(Mesh.java:380)
at com.badlogic.gdx.graphics.Mesh.bind(Mesh.java:371)
at com.badlogic.gdx.graphics.Mesh.render(Mesh.java:479)
at com.badlogic.gdx.graphics.Mesh.render(Mesh.java:449)
at com.badlogic.gdx.graphics.g2d.SpriteBatch.flush(SpriteBatch.java:975)
at com.badlogic.gdx.graphics.g2d.SpriteBatch.setTransformMatrix(SpriteBatch.java:1037)
at com.badlogic.gdx.scenes.scene2d.Group.resetTransform(Group.java:210)
at com.badlogic.gdx.scenes.scene2d.Group.draw(Group.java:58)
at com.badlogic.gdx.scenes.scene2d.Stage.draw(Stage.java:128)
at com.rander.GameMenu.MainMenuScreen.show(MainMenuScreen.java:86)
at com.badlogic.gdx.Game.setScreen(Game.java:61)
at com.rander.GameMenu.MenuManager.setScreen(MenuManager.java:44)
at com.rander.GameMenu.CreditsMenuScreen.show(CreditsMenuScreen.java:74)
When you switch from one screen back to the the other, the other one has not had update() called on it since it last called setScreen() in response to its button press, so its button is still pressed. So the screens will keep switching back and forth because both buttons are in pressed state and never get an opportunity to release.
Instead of using isPressed(), add a ChangeListener on each button, and in the listener, override public void changed (ChangeEvent event, Actor actor) and change the screen there. Like this:
/// in screen constructor
creditsButton.addListener(new ChangeListener(){
public void changed (ChangeEvent event, Actor actor){
menuManager.setScreen("credits");
}
});
I solved the problem (I added the super.render() in the render method) but now, every screen is getting flicking when the mouse is moving in the window.
Here is the MenuManager class:
public class MenuManager {
private static MenuManager uniqueInstance;
HashMap<String, Screen> menuMap;
Game game;
private MenuManager(Game game) {
this.game = game;
menuMap = new HashMap<String, Screen>();
}
public static MenuManager getInstance(Game game) {
if(uniqueInstance == null) {
uniqueInstance = new MenuManager(game);
}
return uniqueInstance;
}
public void addMenuItem(String type, Screen screen) {
menuMap.put(type, screen);
}
public void removeMenuItem(String type) {
Screen remove = menuMap.remove(type);
}
public Screen getScreen(String type) {
return menuMap.get(type);
}
public void setScreen(String type) {
game.setScreen(getScreen(type));
}
}
And here is the main class:
public class TestGame extends Game {
MenuManager menuManager;
Screen mainMenuScreen;
Screen creditsMenuScreen;
Screen optionsMenuScreen;
#Override
public void create() {
Gdx.graphics.setContinuousRendering(false);
menuManager = MenuManager.getInstance(this);
mainMenuScreen = new MainMenuScreen(menuManager);
creditsMenuScreen = new CreditsMenuScreen(menuManager);
optionsMenuScreen = new OptionsMenuScreen(menuManager);
menuManager.addMenuItem("main", mainMenuScreen);
menuManager.addMenuItem("credits", creditsMenuScreen);
menuManager.addMenuItem("options", optionsMenuScreen);
//Gdx.gl.glClearColor(0, 0, 0, 0);
//Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
setScreen(menuManager.getScreen("main"));
}
#Override
public void resize(int width, int height) {
super.resize(width, height);
}
#Override
public void render () {
super.render(); //important!
}
#Override
public void pause() {
//super.pause();
}
#Override
public void resume() {
//super.resume();
}
#Override
public void dispose () {
super.dispose();
}
}
Related
I am trying to simplify the Javax swing graphics classes in order to make it easier for other people to get into Java graphics, but I am facing a problem with testing it.
Keep in mind, that I am writing the main method as a user of the code and not the developer. I need answers that will change the code of the class methods and not the main method.
What my main method code is supposed to do is print 'hovering' when the user hovers over the button. However, when I add a SOP statement before the if statement, it works...
The method for the mouse hovering is in the Button class.
Here is my main method code -
public static void main(String[] args) {
GraphWin win = new GraphWin(1000, 1000, "Graphics Window - Test");
win.show();
Button button = new Button(new Point(380, 300), new Point(620, 400));
button.draw(win);
enter code herewhile(true) {
//System.out.println(button.hovering);
if(button.hovering) {
System.out.println("hovering");
}
}
}
And here is my code for the Button class -
public class Button implements MouseListener{
public JButton button;
public boolean clicked = false, hovering = false, pressed = false;
public Button(Point p, Point p2) { //This is the default constructor of the button with only 2 points specified
this.button = new JButton();
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(Point p, Point p2, String text) { //This constructor requires text to be displayed`enter code here`
this.button = new JButton(text);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(String icon, Point p, Point p2) { //This constructor sets an Icon for the button
this.button = new JButton();
this.setIcon(icon);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(Point p, Point p2, String text, String icon) { //Here, both the text and Icon is specified
this.button = new JButton(text);
this.setIcon(icon);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public void draw(GraphWin win) {
win.window.add(this.button);}
public void setBounds(Point p, Point p2) {
this.button.setBounds(p.x, p.y, p2.x - p.x, p2.y - p.y);
}
public void setEnabled(boolean enable) {
this.button.setEnabled(enable);}
public void disable() {
this.button.setEnabled(false);}
public void enable() {
this.button.setEnabled(true);
}
public void setColor(Color color) {
this.button.setBackground(color);}
public void setColor(String color) {
this.button.setBackground(Color.decode(color));}
public void setText(String text) {
this.button.setText(text);}
public void setIcon(String icon) {
File imageCheck = new File(icon);
if(!imageCheck.exists())
System.out.println("Image file not found!");
else
this.button.setIcon(new ImageIcon(icon));
}
public void resizeIcon(String icon, int width, int height) {
Image img = new ImageIcon(icon).getImage();
img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
this.button.setIcon(new ImageIcon(img));
}
public void setCustomMargins(int top, int bottom, int left, int right) {
this.button.setMargin(new Insets(top, left, bottom, right));}
public void setMargins(int m) {
this.button.setMargin(new Insets(m, m, m, m));}
public void setLabel(String label) {
this.button.setToolTipText(label);
}
public void setBorderVisible(boolean border) {
this.button.setBorderPainted(border);}
public void setOpaque(boolean opaque) {
this.button.setContentAreaFilled(opaque);}
#Override
public void mouseEntered(MouseEvent arg0) {
this.hovering = true;
System.out.println(1);
}
#Override
public void mouseExited(MouseEvent arg0) {
this.hovering = false;
}
#Override
public void mousePressed(MouseEvent arg0) {
this.pressed = true;
}
#Override
public void mouseReleased(MouseEvent arg0) {
this.pressed = false;
}
#Override
public void mouseClicked(MouseEvent e) {
this.clicked = true;
System.out.println(1);
}
}
This sort of thing is usually to do with threading.
Events in Swing are dispatched on the AWT Event Dispatch Thread (EDT). In order to be thread-safe, practically everything dealing with Swing/AWT should be done on the EDT.
In your case, there is no kind of locking between the variable being set and read. Adding a println causes a pause (with all sorts of memory barriers or whatnot) that happens to allow the program to run in the desired sequence.
You've probably seen main methods written to pass execution straight over to the AWT.
class MyGUI {
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(MyGUI::go);
}
private static void go() {
...
It might be better to supply the main class yourself, implemented such that it takes the application class as an argument and passes execution on once everything is setup. Whilst traditionally command lines use a main static method/function, everywhere else subtypes: Applets, Servlets, etc.
best approach would be to use a isHovering() method but educated guess on the behavior inside a while(true) with or without a Sysout might be related to a compiler optimisation. Might be fixed by putting the hovering variable as transient
We are working on a game with libgdx and we want to be able to switch screens.
I have made a GameOverScreen, which implements Screen:
public class GameOverScreen implements Screen {
}
My problem is that i don't know how to set the screen in my main class. Most of the examples i have looked at shows a main class which extends Game (com.badlogic.gdx.Game). But our main class implements ApplicationListener and doesn't extends Game:
public class Game implements ApplicationListener {
}
Therefore i can't use the setScreen method from the Game class. So how can i change the screen in my main class Game?
First of all Game is predefined class so use different class name instead of Game for your own implementation.
com.badlogic.gdx.Game is nothing more than ApplicationListener, it has only a reference of Screen so having setScreen() method.
Extend your Main(origin) class with Game instead of writing own implementation because you need Screen transition in your game.
Some Rules of SE :
Never Write the Same Code Twice.
Don't use hand to break a brick if already you've hammer.
com.badlogic.gdx.Game does nothing else but also implement ApplicationListener. There are some simple options:
So you could just extend com.badlogic.gdx.Game instead of implementing ApplicationListener
Do the same as com.badlogic.gdx.Game does. For example:
public void setScreen (Screen screen) {
if (this.screen != null) this.screen.hide();
this.screen = screen;
if (this.screen != null) {
this.screen.show();
this.screen.resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
}
Use Composition / Adapter: Create a field of com.badlogic.gdx.Game in your ApplicationListener implementing class and delegate the methods you implement.
Mainly you need 3 classes
ScreenManager Class
Abstract Screen Class (Optional)
ScreenEnum (Enum)
Play screen and main menu screen will be extended from Abstract screen.
Screenmanager switches the screen according to screen code from screenenum
****Screen Enum Class****
public enum ScreenEnum {
MAIN_MENU {
public AbstractScreen getScreen(Object... params) {
return new MainMenuScreen();
}
},
PLAY {
public AbstractScreen getScreen(Object... params) {
return new PlayScreen();
}
};
public abstract AbstractScreen getScreen(Object... params);
}
ScreenManager Class
public class ScreenManager {
private static ScreenManager instance;
private Game game;
private ScreenManager() {
super();
}
public static ScreenManager getInstance() {
if (instance == null) {
instance = new ScreenManager();
}
return instance;
}
public void initialize(Game game) {
this.game = game;
}
public void showScreen(ScreenEnum screenEnum, Object... params) {
Screen currentScreen = game.getScreen();
AbstractScreen newScreen = screenEnum.getScreen(params);
newScreen.buildStage();
game.setScreen(newScreen);
// Dispose previous screen
if (currentScreen != null) {
currentScreen.dispose();
}
}
}
AbstarctScreen Class (Optional class)
public abstract class AbstractScreen implements Screen {
protected AbstractScreen() {
super( );
}
public abstract void buildStage();
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
#Override
public void show() {
}
#Override
public void resize(int width, int height){
}
#Override public void hide() {}
#Override public void pause() {}
#Override public void resume() {}
}
Screen Switch
ScreenManager.getInstance().showScreen(ScreenEnum.PLAY, 0);
ScreenManager.getInstance().showScreen(ScreenEnum.MAIN_MENU, 0);
So I have a button that when pressed needs to write the current mouse position out to a text box until the user presses shift, then it stops and leaves the most recent mouse position as the final text in the text box. Heres what I have done:
First a created the following class.
public class KeyListener extends KeyAdapter {
private boolean wasPressed = false;
private int keyCode;
public KeyListener(int keyCode) {
this.keyCode = keyCode;
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("CALLED");
if(e.getKeyCode() == keyCode)
wasPressed = true;
}
public void setState(boolean state) {
wasPressed = state;
}
public boolean getState() {
return wasPressed;
}
}
Then in my "main" class I have the following code.
JButton track1 = new JButton("Track");
KeyListener kl = new KeyListener(KeyEvent.VK_SHIFT);
...
public DisplayFrame() {
this.addKeyListener(kl);
track1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
kl.setState(false);
while(!kl.getState()) {
Point p = MouseInfo.getPointerInfo().getLocation();
topLeft.setText(p.getX() + "," + p.getY());
}
}
});
}
I then of course added the text box to a JPanel and it's displaying everything correctly, however, when I click the Track button nothing happens. I can tell that it is entering the loop, but no text is displayed in the textbox and pressing shift doesn't break the loop.
Try to make a new thread within the actionPerformed method like this:
Thread exampleThread = new Thread() {
public void run() {
//Do your actions within the new thread
}
};
//After the thread is made, we start it.
exampleThread.start();
You have to do this because the actionListener runs in a different thread.
Please have a look at the following code
First, Please note I am a 100% newbie to Java Mobile.
In here, I am making the light on and vibrate on when user click the button. However, I really wanted to create a SOS application which turn the whole screen into white, and go to black, like that, in the thread. I guess I didn't achieve that by this app because even the lights are on, the buttons are still there. I tried to turn the "Form" color to "white" but it seems like JME has no "Color" class.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Midlet extends MIDlet{
private Form f;
private Display d;
private Command start,stop;
private Thread t;
public Midlet()
{
t = new Thread(new TurnLightOn());
}
public void startApp()
{
f = new Form("Back Light On");
d = Display.getDisplay(this);
d.setCurrent(f);
start = new Command("Turn On",Command.OK,0);
stop = new Command("Turn Off",Command.OK,1);
f.addCommand(start);
f.setCommandListener(new Action());
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional)
{
this.notifyDestroyed();
}
private class Action implements CommandListener
{
public void commandAction(Command c, Displayable dis)
{
f.append("Light is Turnning On");
t.start();
}
}
private class ActionOff implements CommandListener
{
public void commandAction(Command c, Displayable dis)
{
}
}
private class TurnLightOn implements Runnable
{
public void run()
{
f.append("Working");
for(int i=0;i<100;i++)
{
try
{
d.flashBacklight(200);
d.vibrate(200);
Thread.sleep(1000);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
}
}
}
Use the javax.microedition.lcdui.Canvas instead of Form. This example can get you started
public void startApp()
{
f = new Form("Back Light On");
d = Display.getDisplay(this);
start = new Command("Turn On",Command.OK,0);
stop = new Command("Turn Off",Command.OK,1);
f.addCommand(start);
f.setCommandListener(new Action());
myCanvas = new MyCanvas();
d.setCurrent(myCanvas);
myCanvas.repaint();
}
Now create a canvas and implement paint method like this:
class MyCanvas extends Canvas {
public void paint(Graphics g) {
// create a 20x20 black square in the center
// clear the screen first
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0xffffff); // make sure it is white color
// draw the square, <b>changed to rely on instance variables</b>
<b>g.fillRect(x, y, getWidth(), getHeight());</b>
}
}
I wrote these 3 classes to test them on android. If I just press Menu button and hide the app, then open it again, then hide and reopen it again and again ram usage increases every time I hide the app.
The same problem occurs if I just switch between classes inside the app, app uses more and more ram.
I was looking for a solution of this problem here and I saw tutorials on yotube, but I have not found it. I don't understand what can be wrong here.
Can someone explain what am I doing wrong?
public class MyEF extends Game {
#Override
public void create() {
setScreen(new TestScreen1(this));
}
#Override
public void dispose() {
super.dispose();
}
#Override
public void render() {
super.render();
}
#Override
public void resize(int width, int height) {
super.resize(width, height);
}
#Override
public void pause() {
super.pause();
}
#Override
public void resume() {
super.resume();
}
}
// TEST1
public class TestScreen1 implements Screen {
private MyEF game;
public TestScreen1(MyEF game) {
this.game = game;
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(Gdx.input.justTouched()){
game.setScreen(new TestScreen2(game));
dispose();
System.out.println("TOUCHED ");
}
}
#Override
public void show() {
}
#Override
public void dispose() {
game.dispose();
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void resize(int width, int height) {
}
}
// TEST2
public class TestScreen2 implements Screen {
private MyEF game;
public TestScreen2(MyEF game) {
this.game = game;
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(Gdx.input.justTouched()){
game.setScreen(new TestScreen1(game));
dispose();
System.out.println("TOUCHED ");
}
}
#Override
public void show() {
}
#Override
public void dispose() {
game.dispose();
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void resize(int width, int height) {
}
}
I'd not have any static Screens because of this. "Resetting" screens will introduce bugs and not very well maintainable code. It is easier in many cases to just create a new "clean" screen.
Also you are describing an effect here. Not a problem. Yes the memory usage increases. But is it a problem? No! Why not? Because the user won't send the app to the background and restart it 1000 times in a row. And even if he does, the memory usage will not grow endlessly. It keeps growing because the amount of memory which gets added is so small that the JVM does not even invoke the garbage collector.
After enough iterations of resuming the app, the garbage collector will eventually kick in and clean up all your unused screens.
In general you should not change your architecture now, based on this behaviour. As a rule of thumb you can always instantiate new objects, for example when switching screens, or when doing things only once. The user will not be bothered if the garbage collector kicks in in a loading screen, or when the game just started. You should only be concerned when doing it in the render() method, because then the garbage collector might clean up while the actual gameplay and may cause lags.
Stuntmania is correct. Creating new objects = increased RAM usage, especially when you do it during the render() method. In game development in general, and LibGDX in particular, you should always question your usage of the new keyword. Make things static wherever possible.
Specifically, your main game class should do something like this:
public class MyEF extends Game {
public static TestScreen1 myScreen1;
public static TestScreen2 myScreen2;
#Override
public static void create() {
myScreen1 = new TestScreen1(this);
myScreen2 = new TestScreen2(this);
setScreen(myScreen1);
}
(...)
}
Then, in your render() method for each of the screens, you would simply call game.setScreen(myScreen2). This way, you are no longer creating new objects during each render cycle.