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.
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);
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();
}
}
I am developing a simple game that is running in its own thread. When I launch the app, everything works great. The thread is initialized, and the update() and render() functions are executed from the thread, which is great.
If I exit the app, the thread is paused. When I launch the app again, the thread resumes fine. (The update and render functions are executed). Again, this is great!
THE PROBLEM IS, I hit the power button to sleep the device. When I resume the app from this state, the render() function appears to work fine; it is drawing all of the elements (background, sprites, etc) exactly where they were before I hit the button. BUT: The sprites are no longer animated! After a bit of tracing and troubleshooting, I found out that although the "running" flag is set to TRUE, it seems like the "RUN" function is no longer running! (On an another note, I am confused why the RENDER function works, but the UPDATE function does not, unless the app is rendering the objects from some other method? Irregardless...)
I am working this problem from two possible angles; I am either missing something in the way Android manipulates my thread, OR would this have something to do with the fact that the member variables inside the objects have been reset...
Can someone please review the basic code I have posted below, and advise any suggestions on how to improve thread manipulation? I am an intelligent person, and I have read so many articles from different sources on the subject, and am more confused now more than ever! Am I on the right track here, or is there a more intelligent way of doing this? Do I really need a separate game thread, or can I run it using the device's main thread instead?
Thanks for your feedback!
public class GameThread extends Thread {
private boolean running=false;
private SurfaceHolder surfaceHolder;
private GameView gameView;
public GameThread(SurfaceHolder surfaceHolder, GameView gameView){
super();
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
#Override
public void run(){
Canvas canvas;
// THIS ONLY RUNS ONCE!
// WHEN I TURN THE POWER OFF AND BACK ON, THE RUN() METHOD DOES NOT RUN!
// ODDLY ENOUGH, IF I WERE TO EXIT THE APP, AND GO BACK IN, IT WORKS FINE!
// THE running FLAG SEEMS TO CHANGE PROPERLY WHEN PAUSING OR RESUMING (SEE gameView, BELOW).
while (running){
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder){
gameView.update();
gameView.render(canvas);
}
} finally {
if (canvas!=null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
public void setRunning(boolean running){
this.running = running;
}
}
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
private GameThread thread;
private GameScreen gameScreen;
public GameView(Context context) {
super(context);
gameScreen = new GameScreen(context);
setFocusable(true);
thread = new GameThread(getHolder(), this);
getHolder().addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new GameThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry){
try {
thread.join();
retry = false;
} catch (InterruptedException e){
// Do nothing; continue trying
}
}
}
public void render(Canvas canvas){
gameScreen.draw(canvas);
}
public void update(){
gameScreen.update();
}
#Override
public boolean onTouchEvent(MotionEvent event){
gameScreen.touch(event);
}
public void resume(){
// TODO: Resume events go here
thread.setRunning(true);
}
public void pause(){
// TODO: Pause events go here
thread.setRunning(false);
}
}
public class GameScreen {
public GameScreen(Context context){
// Initialize private variables here
}
public void draw(Canvas canvas){
// Draw events go here
}
public void update(){
// Update events go here
}
public void touch(MotionEvent event){
// Touch events go here
}
}
The update() method needs to be executed BEFORE trying to post the canvas. Still unsure why this is, but it works as intended now.
Canvas canvas = null;
while (running){
gameView.update();
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder){
gameView.render(canvas);
}
} finally {
if (canvas!=null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
I'm learning Java, and now that I'm over the packages hump, things are going smoothly. I can draw similarities between most things I'm learning with things I already know at least the concept of. But what on earth is going on with the following bit of code? Is it some form of constructor, or anonymous object?
Something obj = new Something()
{
private static final int num = 3;
public void meth()
{
// w/e
}
};
You got it - this creates an anonymous inner class of Something.
See also: Nested Classes (The Java Tutorial) and Anonymous Classes.
/**
* Notice there's only one thing in this that isn't defined:
* It still needs public abstract void triggerEvent();
*/
public abstract static class TopButton extends JPanel implements MouseListener {
protected ButtonPanel parent;
private String text;
public TopButton(ButtonPanel bp, String text) { parent = bp; this.text = text; addMouseListener(this); }
public void mouseClicked(MouseEvent e) { triggerEvent(); }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public abstract void triggerEvent();
public void paintComponent(Graphics g) {
super.paintComponent(g);
Color oldColor = g.getColor();
Font oldFont = g.getFont();
Font newFont = new Font(oldFont.getName(),oldFont.getStyle(),oldFont.getSize());
g.setFont(newFont);
g.setColor(Color.black);
g.drawString(text, 20, 20);
g.setFont(oldFont);
g.setColor(oldColor);
}
}
Now, when I actually define my buttons, I do this. By providing the one line it needs, the only thing that makes it different from others. Now I could make a new file for each one, and define a new class for each one. This is much simpler.
private static void loadButtonPanelButtons() {
/* This button should tell the parent to bring up the save screen */
TopButton save = new TopButton(buttonPanel,"Save") {
public void triggerEvent() { parent.triggerSave(); }
};
save.setBorder(LineBorder.createBlackLineBorder());
buttonPanel.add(save);
/* This button should tell the parent to bring up the load screen */
TopButton load = new TopButton(buttonPanel,"Load") {
public void triggerEvent() { parent.triggerLoad(); }
};
load.setBorder(LineBorder.createBlackLineBorder());
buttonPanel.add(load);
TopButton addTile = new TopButton(buttonPanel,"Add Tile") {
public void triggerEvent() { parent.triggerAddTile(); }
};
addTile.setBorder(LineBorder.createBlackLineBorder());
buttonPanel.add(addTile);
TopButton saveTiles = new TopButton(buttonPanel,"Save Tiles") {
public void triggerEvent() { parent.triggerStyleSave(); }
};
saveTiles.setBorder(LineBorder.createBlackLineBorder());
buttonPanel.add(saveTiles);
}
Now, when I handle the buttons being pressed, remember back in the definition of TopButton... there was
public void mouseClicked(MouseEvent e) { triggerEvent(); }
We know triggerEvent() eventually gets defined. We can define it on a per-button basis, and when the panel gets clicked, no matter what we defined triggerEvent() to be, it gets called.
Such construct creates an anonymous inner class of a class where this construct is executed, and derived from Something (not an inner class of Something).
The idea is to quickly provide implementations for abstract classes, interfaces, or override some functionality of a class.
(new Thread(){ public void run() { System.out.println("executed on another thread"); }}).start();