I have an options menu from which you can save the game and it should briefly close the menu and take a screenshot of the current game.
However, the menu does not close until after calling getScreenCapture even though it runs before causing the screenshot to always be of the settings menu.
(The screenshot itself is working it's just controlling what displays at the time of the screenshot)
!! Without the screenshot line it updates the scene immediately even though the scene change line comes before.
So far I have tried Thread.sleep but no length of time allows for the scene to update
//The initial call to save:
private void saveRoom() {
program.changeState(previous);
room.save();
}
//The changing of scenes: (State extends Scene)
public void changeState(State state) {
currentState = state;
stage.setScene(currentState);
currentState.paintAll();
}
//How the game scene draws to screen:
#Override
public void paintAll() {
ArrayList<Shape> toAdd = new ArrayList<>();
for(Furniture f : furniture) {
f.paint(toAdd);
}
optionsButton.paint(toAdd);
addFurnitureButton.paint(toAdd);
root.getChildren().clear();
root.getChildren().addAll(toAdd);
}
//How the options menu draws to screen (same as game):
#Override
public void paintAll() {
ArrayList<Shape> toAdd = new ArrayList<>();
toolsButton.paint(toAdd);
saveButton.paint(toAdd);
saveAndExitButton.paint(toAdd);
exitButton.paint(toAdd);
closeButton.paint(toAdd);
root.getChildren().clear();
root.getChildren().addAll(toAdd);
}
//The capture command:
robot.getScreenCapture(null, new Rectangle2D(x,y,x1,y1));
Update: I am now using the Scene.snapshot command to take the screenshot instead and it does not cause the problem, but am still open to finding out how to go about using Robot to take a screenshot since unlike Scene.snapshot, it captures the whole screen rather than just the program which could come in handy in a future project.
I am creating a simple event-driven GUI for a video game I am making with LibGDX. It only needs to support buttons (rectangular) with a single act() function called when they are clicked. I would appreciate some advice on structuring because the solution I've thought of so far seems to be far from ideal.
My current implementation involves all buttons extending a Button class. Each button has a Rectangle with its bounds and an abstract act() method.
Each game screen (e.g. main menu, character select, pause menu, the in-game screen) has a HashMap of Buttons. When clicked, the game screen iterates through everything in the HashMap, and calls act() on any button that was clicked.
The problem I'm having is that Buttons have to have their act() overridden from their superclass in order to perform their action, and that the Buttons aren't a member of the Screen class which contains all the game code. I am subclassing Button for each button in the game. My main menu alone has a ButtonPlay, ButtonMapDesigner, ButtonMute, ButtonQuit, etc. This is going to get messy fast, but I can't think of any better way to do it while keeping a separate act() method for each button.
Since my mute button isn't a part of the main menu screen and can't access game logic, it's act() is nothing more than mainMenuScreen.mute();. So effectively, for every button in my game, I have to create a class class that does nothing more than <currentGameScreen>.doThisAction();, since the code to actually do stuff must be in the game screen class.
I considered having a big if/then to check the coordinates of each click and call the appropriate action if necessary. For example,
if (clickWithinTheseCoords)
beginGame();
else if(clickWithinTheseOtherCoords)
muteGame();
...
However, I need to be able to add/remove buttons on the fly. When a unit is clicked from the game screen, a button to move it needs to appear, and then disappear when the unit is actually moved. With a HashMap, I can just map.add("buttonMove", new ButtonMove()) and map.remove("buttonMove") in the code called when a unit is clicked or moved. With the if/else method, I won't need a separate class for every button, but I would need to keep track of whether each clickable area tested is visible and clickable by the user at this point in the game, which seems like an even bigger headache that what I have right now.
I would provide a runnable to all the buttons which u will run in the act method. To give u a simple example.
private final Map<String, Button> buttons = new HashMap<>();
public void initialiseSomeExampleButtons() {
buttons.put("changeScreenBytton", new Button(new Runnable() {
#Override
public void run() {
//Put a change screen action here.
}
}));
buttons.put("muteButton", new Button(new Runnable() {
#Override
public void run() {
//Do a mute Action here
}
}));
}
public class Button {
//Your other stuff like rectangle
private final Runnable runnable;
protected Button(Runnable runnable) {
this.runnable = runnable;
}
public void act() {
runnable.run();
}
}
You keep track of your buttons via the map and just need to pass a runnable action to every button in the constructor. I intentionally skipped some code so that you can try yourself. If you have any questions, let me know.
Sneh's response reminded me of a fairly major oversight - instead of having to create a separate class for every button, I could use anonymous inner classes whenever I created a button, specifying its coordinates and act() method every time. I explored lambda syntax as a possible shorter method to do this, but ran into limitations with it. I ended up with a flexible solution, but ended up reducing it a bit further to fit my needs. Both ways are presented below.
Each game screen in my game is subclassed from a MyScreen class, which extends LibGDX's Screen but adds universal features like updating the viewport on resize, having a HashMap of Buttons, etc. I added to the MyScreen class a buttonPressed() method, which takes in as its one parameter an enum. I have ButtonValues enum which contains all the possible buttons (such as MAINMENU_PLAY, MAINMENU_MAPDESIGNER, etc.). In each game screen, buttonPressed() is overriden and a switch is used to perform the correct action:
public void buttonPressed(ButtonValues b) {
switch(b) {
case MAINMENU_PLAY:
beginGame();
case MAINMENU_MAPDESIGNER:
switchToMapDesigner();
}
}
The other solution has the button store a lambda expression so that it can perform actions on its own, instead of requiring buttonPressed() to act as an intermediary that performs the correct action based on what button was pressed.
To add a button, it is created with its coordinates and type (enum), and added to the HashMap of buttons:
Button b = new Button(this,
new Rectangle(300 - t.getRegionWidth() / 2, 1.9f * 60, t.getRegionWidth(), t.getRegionHeight()),
tex, ButtonValues.MAINMENU_PLAY);
buttons.put("buttonPlay", b);
To remove it, just buttons.remove("buttonPlay"). and it'll disappear from the screen and be forgotten by the game.
The arguments are the game screen which owns it (so the button can call buttonPressed() on the game screen), a Rectangle with its coordinates, its texture (used to draw it), and its enum value.
And here's the Button class:
public class Button {
public Rectangle r;
public TextureRegion image;
private MyScreen screen;
private ButtonValues b;
public Button(MyScreen screen, Rectangle r, TextureRegion image, ButtonValues b) {
this.screen = screen;
this.r = r;
this.image = image;
this.b = b;
}
public void act() {
screen.buttonPressed(b);
}
public boolean isClicked(float x, float y) {
return x > r.x && y > r.y && x < r.x + r.width && y < r.y + r.height;
}
}
isClicked() just takes in an (x, y) and checks whether that point is contained within the button. On mouse click, I iterate through all the buttons and call act() if a button isClicked.
The second way I did it was similar, but with a lambda expression instead of the ButtonValues enum. The Button class is similar, but with these changes (it's a lot simpler than it sounds):
The field ButtonValues b is replaced with Runnable r, and this is removed from the constructor. Added is a setAction() method which takes in a Runnable and sets r to the Runnable passed to it. The act() method is just r.run(). Example:
public class Button {
[Rectangle, Texture, Screen]
Runnable r;
public Button(screen, rectangle, texture) {...}
public void setAction(Runnable r) { this.r = r; }
public void act() { r.run(); }
}
To create a button, I do the following:
Button b = new Button(this,
new Rectangle(300 - t.getRegionWidth() / 2, 1.9f * 60, t.getRegionWidth(), t.getRegionHeight()),
tex);
b.setAction(() -> b.screen.doSomething());
buttons.put("buttonPlay", b);
First, a button is created with its containing game screen class, its bounding box, and its texture. Then, in the second command, I set its action - in this case, b.screen.doSomething();. This can't be passed to the constructor, because b and b.screen don't exist at that point. setAction() takes a Runnable and sets it as that Button's Runnable that is called when act() is called. However, Runnables can be created with lambda syntax, so you don't need to create an anonymous Runnable class and can just pass in the function it performs.
This method allows much more flexibility, but with one caveat. The screen field in Button holds a MyScreen, the base screen class from which all of my game screens are extended. The Button's function can only use methods that are part of the MyScreen class (which is why I made buttonPressed() in MyScreen and then realized I could just scrap the lambda expressions completely). The obvious solution is to cast the screen field, but for me it wasn't worth the extra code when I could just use the buttonPressed() method.
If I had a beginGame() method in my MainMenuScreen class (which extends MyScreen), the lambda expression passed to the button would need to involve a cast to MainMenuScreen:
b.setAction(() -> ((MainMenuScreen) b.screen).beginGame());
Unfortunately, even wildcard syntax doesn't help here.
And finally, for completeness, the code in the game loop to operate the buttons:
public abstract class MyScreen implements Screen {
protected HashMap<String, Button> buttons; // initialize this in the constructor
// this is called in every game screen's game loop
protected void handleInput() {
if (Gdx.input.justTouched()) {
Vector2 touchCoords = new Vector2(Gdx.input.getX(), Gdx.input.getY());
g.viewport.unproject(touchCoords);
for (HashMap.Entry<String, Button> b : buttons.entrySet()) {
if (b.getValue().isClicked(touchCoords.x, touchCoords.y))
b.getValue().act();
}
}
}
}
And to draw them, located in a helper class:
public void drawButtons(HashMap<String, Button> buttons) {
for (HashMap.Entry<String, Button> b : buttons.entrySet()) {
sb.draw(b.getValue().image, b.getValue().r.x, b.getValue().r.y);
}
}
Inside my game I have this code. It renders a texture that serve as a button:
private void drawStart(){
startTexture = new Texture(Gdx.files.internal("start.png"));
startTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
stageStart = new Stage();
stageStart.clear();
buttonStart = new Image(startTexture);
buttonStart.setX(10);
buttonStart.setY(Gdx.graphics.getHeight()/2.75f);
buttonStart.setWidth(Gdx.graphics.getWidth()/4);
buttonStart.setHeight(Gdx.graphics.getHeight()/4);
Gdx.input.setInputProcessor(stageStart);
buttonStart.addListener(new ClickListener() {
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button)
{
currentState = GameState.RESET;
startTexture.dispose();
stageStart.dispose();
return true;
}
});
stageStart.addActor(buttonStart);
stageStart.draw();
startTexture.dispose();
}
However, whenever I put drawStart(); into my render method, the Java Heap and Native Heap slowly increases by 1 every 10 seconds. So, if the user leaves the game on the menu for about 5 minutes the game will crash on their phone. I've tested it and it only occurs when the texture is rendered.
I would appreciate any help on fixing this. I have tried an if statement that states if rendered = 0, render the texture then set rendered 1 but that didn't work.
This might help you. You only need draw in your render. So now you can put drawStart() in your render method which will only draw the stage, while leaving screen dont forget to call dispose.
private void drawStart(){
stageStart.draw();
}
public void initialize() {
startTexture = new Texture(Gdx.files.internal("start.png"));
startTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
stageStart = new Stage();
stageStart.clear();
buttonStart = new Image(startTexture);
buttonStart.setX(10);
buttonStart.setY(Gdx.graphics.getHeight()/2.75f);
buttonStart.setWidth(Gdx.graphics.getWidth()/4);
buttonStart.setHeight(Gdx.graphics.getHeight()/4);
Gdx.input.setInputProcessor(stageStart);
buttonStart.addListener(new ClickListener() {
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button)
{
currentState = GameState.RESET;
startTexture.dispose();
stageStart.dispose();
return true;
}
});
stageStart.addActor(buttonStart);
}
public void dispose() {
startTexture.dispose();
}
Your problem is, that in drawStart() you are creating new Textures and a new Stage.
If you call this every render loop, you create new Textures and a new Stage about 60 times/second.
This ofc causes a memory leak.
You should load/create Textures and the Stage only once, in the constructor or in the create() or show() method.
Also think about disposing them when needed. Here is a list of the things you need to dispose manually.
In the render loop you should then only update and draw the things.
But as you only have 3 month of experience i suggest you to learn the basics first. Don't rush into game programming, it will kill your motivation.
First learn the basics, then start with some ASCII-Games (commandline) and then you can start with libgdx.
If you are ready for libgdx, read the Wiki (at least the parts you need) as well as some tutorials (maybe they don't use the latest version of libgdx, but the concept should be more or less the same and it should help you understanding it.)
Thank you in advance for any attempts at trying to help me figure this one out. I've searched all over stack and google and haven't been able to find anything similar to the problem I am having.
I am designing a small program using NetBeans that switches between multiple frames
login screen > settings screen > main screen
My program can take a while in-between frames to load the next one due to a large amount of frame initialization code. To prevent it from looking like the program is unresponsive. I wrote code which displays an intermediary splash screen JWindow which performs all the initialization for the next frame. I realize that there is a splash screen class, but I could not get it to work as desired, this custom implementation of mine does almost everything I need except when I attempt to display the splash screen a second time, during a single program run, the image does not show up on the JWindow.
So run program > splash screen (works) > login > splash screen (image does not load) > settings.
Here is a sample clip of code which displays the splash screen JWindow:
public class Splash extends JPanel {
private static Calendar date;
private static long startTime;
private static final long MAX_TIME = 3000l;
public static final int LOAD_LOGIN = 0;
public static final int LOAD_SETTINGS = 1;
public static JWindow win;
private final ImageIcon img;
public Splash() {
date = Calendar.getInstance(TimeZone.getTimeZone("EST"), Locale.ENGLISH);
img = new ImageIcon("loader.gif");
startTime = date.getTimeInMillis();
this.setSize(300, 300);
win = new JWindow();
win.setSize(300, 300);
win.getContentPane().add(this);
Login.centerWindow(win); // method from login class that centers window
win.setVisible(true);
}
public void runSplash(int flag) {
if (flag == LOAD_LOGIN)
firstRunCheck();
else {
initSettings();
}
long currTime = date.getTimeInMillis();
// holds splash display for at least 3 secs
while ((currTime - startTime) < MAX_TIME) {
currTime = System.currentTimeMillis();
}
win.dispose(); // has been replaced with win.setVisible(false) before,
// same problem
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponents(g);
Graphics2D g2 = (Graphics2D) g;
//I have read several other questions and sample codes on stack and google
but using this method is the only way for me to get the animated gif image to
load correctly
g2.drawImage(img.getImage(), null, this);
}
}
In order to display this splash screen I instantiate it using new Splash().runSplash(value); where value runs the method that loads the following frames required values.
When I first begin run the program everything works perfectly. When I attempt to move from the login screen to the settings screen the JWindow loads but it just displays as a blank frame.
The program works fine still, the settings frame eventually loads, but the image never shows up and the window just closes. I am unsure as to what the problem is the class is instantiated the exact same way as the first time.
I suspect perhaps I may be incorrectly disposing of resources, but win.dispose() appears to be the correct method according to some of the other problems I have read.
Any help is appreciated thank you.
UPDATE: I am still attempting to resolve this problem. I have used a variety of different implementations to display the splash screen. I have used implementations which use a JLabel instead of JPanel, and use setIcon instead of overriding paintComponent. But the same result is observed whenever the implemented splash screen class is called a second time the image does not appear. Since different implementations are also not working I can only assume that that perhaps I have too many events in the event queue or something. To clarify.
My main method is in the Splash class detailed above. I call the class in the main method using new Splash.runSplash(value). This call works perfectly. I then call the login JFrame using:
if (value == LOAD_LOGIN) //RUN_LOGIN is a final variable int
{
java.awt.EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
/* I have also tried this without the invokeLater
command and simply using new Login().setVisible(true).
The program still runs fine, but the image still
does not display */
new Login().setVisible(true);
}
});
}
This call is made at the end of the runSplash(value) method. After the user inputs the correct login information they are taken to the splash screen again using new Splash().runSplash(value) on this call however I cannot get the image to show up on the JWindow.
dispose() method releases the resource. to be able to use it again you have to use setVisible(false) and when you be sure that your code will not use this window again then only use dispose().In your case after first splash just use setVisible(False) and dispose after the second time when setting frame loads if you really want it so.
This code is inside of a method and that method runs onStart for an android application. what this does is that it presses a button for 3 seconds which changes its color from blue to red and then goes back to its orginal state; then the next button does the same thing. This process is repeated for three different buttons. The code below works perfectly fine but is there a way to put all this code into one runabble object? I tried but a have problems with the .postDelayed because it would just press all the buttons at the same time instead of each button turn off and on at the given time.
The reason why I want this is because I created a random generator class that builds random patterns for Buttons class on android and I put that in a 2d array called list which is of type Button. however when I put something like list[x][y] and int x and int y are outside of r or r1 which are Runnable I get an error that says that only static variables can go inside of this new object. Is there any way I can use these variables so I can make a for loop or while loop to make something like list[x][y] instead of hardcore list[0][0] like how it is right now. Please help!!!
handler = new Handler();
r = new Runnable(){
public void run(){
list[0][0].setPressed(false);
}
};
handler.postDelayed(r, 3000);
r1 = new Runnable(){
public void run(){
list[0][1].setPressed(true);
}
};
handler.postDelayed(r1, 3000);
r2 = new Runnable(){
public void run(){
list[0][1].setPressed(false);
list[0][2].setPressed(true);
}
};
handler.postDelayed(r2, 6000);
r3 = new Runnable(){
public void run(){
list[0][2].setPressed(false);
}
};
handler.postDelayed(r3, 9000);
You can make correction with run method of runnable and make it generic method for all the buttons.
You can replace logic for the code
list[0][0].setPressed(false);
and use something like, In place of doing list[0][0] just take the common view (button) variable and initialize it with clicked button so your runnable will take action as per the view click and in this case you don't need to have 3 different runnable different for each button.