Button click to execute a code repeatedly in Libgdx - button

I've developed an Android application using Libgdx in 3D space to render some buildings with interaction buttons to help user navigating in the environment. For example, left and right buttons to move the camera in left and right direction. While pushing a button causes to execute the code once, I've used this trick to keep executing the code as the user holding the button down.
private void createStage() {
stage = new Stage();
intervalTime = 15L;
buttonLeft = new TextButton("", leftStyle);
buttonLeft.addListener(new InputListener() {
// repeat an action with ScheduledExecutorService
final Runnable leftRunnable = new Runnable() {
#Override
public void run() {
Vector3 dir = new Vector3();
dir.fromString(cam.direction.toString()).scl(0.5f);
cam.position.add(dir.z, 0, -dir.x); // camera moves to left
cam.update();
}
};
// add on thread to object
final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
ScheduledFuture<?> future; // future schedule to run and stop task
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
future = executor.scheduleAtFixedRate(leftRunnable, 0L, intervalTime, TimeUnit.MILLISECONDS);
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
super.touchUp(event, x, y, pointer, button);
//leftFlag = false;
if (future != null) {
future.cancel(true);
}
}
});
This method is invoked in the create() function of AndroidListener and the stage will be drawn in render() function as well. There are about 12 buttons which use the same approach but it gives me some lagging in rendering process while the user holds the buttons down for seconds or pushing two buttons simultaneously. Is there something wrong with the approach or is it an appropriate structure for executing a code frequently?

Camera is not a thread-safe class, so you would need to use synchronization if modifying it from your background thread.
That said, updating a camera is a trivial operation, so multi-threading is adding a lot of needless complexity. You're generating a fair amount of garbage, although I don't know if that's the only reason you're seeing some lagging.
Here's how I'd do it more simply.
stage = new Stage();
float camSpeed = 0.5f / 15; // Units per ms
float camDisp = camSpeed * Gdx.graphics.getDeltaTime();
buttonLeft = new TextButton("", leftStyle){
public void act(float delta){
super.act(delta);
if (isPressed()){
camera.position.add(camera.direction.z * camDisp,
0,
-camera.direction.x * camDisp);
camera.update();
}
}
}
Not quite sure what you're doing with the camera direction, but I tried to copy the same behavior. If I just wanted to pan the camera to the left, I'd do it like this. The temp variable is to avoid instantiating objects and triggering GC.
private static final Vector3 TMP = new Vector3();
//...
TMP.set(camera.direction).crs(camera.up); // right vector of camera
camera.position.add(TMP.scl(-camDisp));

Related

Modifying interface with key interactions in javafx

I'm trying to code a 2048 game using JavaFX and I'm facing a problem.
#Override
public void start(Stage primaryStage){
primaryStage.setResizable(false);
Scene scene = new Scene(firstContent());
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnKeyPressed(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent e){
KeyCode key = e.getCode();
if((key.equals(KeyCode.UP))){
System.out.println("recieved UP");
Scene scene = new Scene(createContent());
primaryStage.setScene(scene);
primaryStage.show();
} else if(key.equals(KeyCode.DOWN)){
System.out.println("recieved DOWN");
}
}
});
}
So here I open my window initialised with firstContent (basically it creates an array of empty tiles, and fills two of them with 2 or 4 randomly), display it and start listening for key presses. The idea is to have a behavior for each arrow key (UP DOWN LEFT RIGHT) which will move the tiles accordingly. This is done by the following createContent() method :
public Parent createContent(){
String c = "";
List<Integer> known = new ArrayList<Integer>();
Pane root = new Pane();
root.setPrefSize(740, 700);
Random rand = new Random();
int pos1 = rand.nextInt(15);
if(tiles.get(pos1) != new Tile("")){
known.add(pos1);
pos1 = rand.nextInt(15);
if(known.contains(pos1)){
known.add(pos1);
pos1 = rand.nextInt(15);
}
}
for(int i = 0; i < NB_TILES; i++){
tiles.add(new Tile(c));
}
tiles.set(pos1, new Tile("2048"));
for(int i = 0; i < tiles.size(); i++){
// boring stuff to set the tile display to the right size
}
return root;
}
Now for the problem : when the application is running, if I press the down arrow, I do get on my terminal the "recieved DOWN" text as many times as I press the key as expected. But if I press the up arrow, the application will only recieve it once and the application seems to be frozen (meaning if I press down again, nothing happens).
As you could guess, I want to be able to call my method for each key press to be able to move my tiles around and utltimately combine them to get a playable version of 2048... Anyone know why my app gets frozen ?
If needed, I can provide other bits of code but I think I provided the essential. Just know that firstContent() works basically the same as createContent for now except it genereates two random numbers to get the first tiles of the game.
Thanks in advance for your help.

How to wait for user's action in JavaFX (in the same stage)

Do you know how to wait for the user's input in a for loop? I don't mean the showAndWait() method, because I am not opening a new dialogue stage for the user. So for example, each round of the for loop should be waiting for the user to push a button before going ahead with the next round.
How is it possible? Many thanks!
UPDATE:
Now it came to my mind, that it would work with a while(buttonNotPressed){} but is it a good solution? I mean the while loop is running in this case as crazy until the user won't push the button. Or doest it work somehow similarly with wait methods?
Imagine it as a session:
User starts session with handleStart() You give the user 5 questions, one after one. In every iteration, the user can answer the upcoming question and he can save or submit the answer by handleSaveButton() You process the answer as you want, and go ahead with the next iteration. The point is, that the iteration must stop, until the save button hasn't been pressed.
Don't do it like that. The FX toolkit, like any event-driven GUI toolkit, already implements a loop for the purposes of rendering the scene graph and processing user input each iteration.
Just register a listener with the button, and do whatever you need to do when the button is pressed:
button.setOnAction(event -> {
// your code here...
});
If you want the action to change, just change the state of some variable each time the action is performed:
private int round = 0 ;
// ...
button.setOnAction(event -> {
if (round < 5) {
System.out.println("Round "+round);
System.out.println("User's input: "+textArea.getText());
round++ ;
}
});
I recently ran into a similar problem where I wanted something to be executed with an interval (if that's what you mean), until the user fired an event. I found 3 ways to do this:
UPDATE
You should use the stop/cancel method for the custom runnable and timer or else the thread will still be running when you exit the application. Timeline seems do it by itself.
Using a Timer:
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
System.out.println("Printed every second.");
}
};
timer.scheduleAtFixedRate(task, 0, 1000);
//timer.cancel();
With a TimeLine:
Timeline tl = new Timeline(new KeyFrame(Duration.millis(1000), e -> {
System.out.println("Timeline");
}));
tl.setCycleCount(Timeline.INDEFINITE);
tl.play();
//tl.stop();
Or making your own runnable class:
public class Runner implements Runnable {
private final Thread thread = new Thread(this);
private boolean run;
#Override
public void run() {
while(run) {
try {
System.out.println("Printed from loop");
Thread.sleep(1000);
} catch (InterruptedException e) {
run = false;
}
}
}
public void start() {
run = true;
thread.start();
}
public void stop() {
if(run) {
thread.interrupt();
System.out.print("Thread has stopped.");
}
}
}
And then when a person clicks fx. a button the event would stop using the example James_D posted:
Button btn = new Button("Button");
btn.setOnAction(e -> {
timer.cancel();
tl.stop();
runner.stop();
});
In my case, for inside for, had to create 2 index in class, use:
//start method
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
public void run()
{
Platform.runLater(()->{
//... code to run after time, calling the same mehtod, with condition to stop
});
}
};
timer.schedule(task, time);
//end method
Had to use recursive method, incrementing the index with conditions, cause the tasks were been schedule all at the same time, without wait time.
I do not know if it is rigth, but was the solution that i found.
Hope it helps.
ALTERNATIVE SOLUTION W/O PAUSING:
I'm creating a game where I want the user to pick the game difficulty before the game starts. Instead of trying to pause the program midway through, I just put the next step of the code in a separate method which you call once a button is clicked:
private static difficulty;
public static void main(String[] args) {
try {
Application.launch(args);
} catch (UnsupportedOperationException e) {
}
}
public void start(Stage startStage) {
HBox buttons = new HBox();
Button easyButton = new Button("Easy");
Button mediumButton = new Button("Medium");
Button hardButton = new Button("Hard");
buttons.getChildren().addAll(easyButton, mediumButton, hardButton);
buttons.setAlignment(Pos.CENTER);
hbox.getChildren().addAll(buttons);
Scene startScene = new Scene(buttons, 200, 200);
startStage.setScene(startScene);
startStage.show(); // MENU
EventHandler<ActionEvent> playEasy = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
difficulty = 1; // SET DIFFICULTY
startStage.close(); // CLOSE MENU
play(); // RUN GAME ON EASY
}
};
EventHandler<ActionEvent> playMedium = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
difficulty = 2; // SET DIFFICULTY
startStage.close(); // CLOSE MENU
play(); // RUN GAME ON MEDIUM
}
};
EventHandler<ActionEvent> playHard = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
difficulty = 3; // SET DIFFICULTY
startStage.close(); // CLOSE MENU
play(); // RUN GAME ON HARD
}
};
easyButton.setOnAction(playEasy);
mediumButton.setOnAction(playMedium);
hardButton.setOnAction(playHard);
}
public void play() {
// WRITE GAME CODE HERE
}
To solve your specific problem, you could probably pass the startStage into the play method and then just update the scene there...but regardless I do hope this helps someone whos having trouble on how to use buttons! :)

TranslateTransition in JavaFX does nothing

I'm trying to use a TranslateTransition object in JavaFX to move an onscreen object in a LOGO program I am building. I have an onscreen TurtleDisplay object, which extends ImageView, and this is what I'm trying to move. The code to move it is here:
public void drawTurtle(TurtleData currentData) {
TurtleImage inList = getTurtleImage(currentData);
if (inList==null) {
TurtleImage temp = new TurtleImage(currentData.getX(),
currentData.getY(), currentData.getHeading(), turtleImage);
myTurtlesGroup.getChildren().add(temp);
myTurtlesList.add(temp);
}
else {
TranslateTransition tt = new TranslateTransition(Duration.seconds(3),inList);
tt.setFromX(inList.getX());
tt.setFromY(inList.getY());
tt.setToX(inList.getX()+currentData.getX());
tt.setToY(inList.getY()+currentData.getY());
tt.setCycleCount(Timeline.INDEFINITE);
tt.play();
}
}
This code, which is part of the front end, is called from the back end via a Listener on an ObservableList. The backend contains this ObservableList of TurtleData objects that contain the information necessary to move a turtle on screen -- which turtle to move, the coordinate to move to, and the rotation of the turtle. The code to call this is here:
ObservableList<TurtleData> myTurtles = FXCollections
.observableArrayList();
myTurtles.addListener(new ListChangeListener<TurtleData>() {
#Override
public void onChanged(Change<? extends TurtleData> c) {
myDisplay.getSelectedWorkspace().getTV().clearTurtles();
while (c.next()) {
for (TurtleData addItem : c.getAddedSubList()) {
myDisplay.getSelectedWorkspace().getTV().drawTurtle(addItem);
}
}
}
});
I have stepped through with a debugger and ensured that this code is called -- specifically, the tt.play() line is run. Nothing moves on screen. Does anyone have any idea what is wrong? Do I need to setup an Animation Timeline? Thank you for any help!

JavaFX 2.X - Animated background and animated controls

A few days ago I started studying JavaFX, and came across the desire to perform 2 experiments. Firstly, I would like to know if it is possible to put an animated background behind an user interface. I've succeeded in creating an animated background, and now I'm having great difficulties to position some controls in the middle of my interface.
I'd like to introduce you 2 pictures of my program. The first demonstrates the undesirable result that I'm getting:
I believe this is my nodes tree:
This is the code of my application:
public class AnimatedBackground extends Application
{
// #########################################################################################################
// MAIN
// #########################################################################################################
public static void main(String[] args)
{
Application.launch(args);
}
// #########################################################################################################
// INSTÂNCIAS
// #########################################################################################################
private Group root;
private Group grp_hexagons;
private Rectangle rect_background;
private Scene cenario;
// UI
private VBox lay_box_controls;
private Label lab_test;
private TextArea texA_test;
private Button bot_test;
// #########################################################################################################
// INÍCIO FX
// #########################################################################################################
#Override public void start(Stage stage) throws Exception
{
this.confFX();
cenario = new Scene(this.root , 640 , 480);
this.rect_background.widthProperty().bind(this.cenario.widthProperty());
this.rect_background.heightProperty().bind(this.cenario.heightProperty());
stage.setScene(cenario);
stage.setTitle("Meu programa JavaFX - R.D.S.");
stage.show();
}
protected void confFX()
{
this.root = new Group();
this.grp_hexagons = new Group();
// Initiate the circles and all animation stuff.
for(int cont = 0 ; cont < 15 ; cont++)
{
Circle circle = new Circle();
circle.setFill(Color.WHITE);
circle.setEffect(new GaussianBlur(Math.random() * 8 + 2));
circle.setOpacity(Math.random());
circle.setRadius(20);
this.grp_hexagons.getChildren().add(circle);
double randScale = (Math.random() * 4) + 1;
KeyValue kValueX = new KeyValue(circle.scaleXProperty() , randScale);
KeyValue kValueY = new KeyValue(circle.scaleYProperty() , randScale);
KeyFrame kFrame = new KeyFrame(Duration.millis(5000 + (Math.random() * 5000)) , kValueX , kValueY);
Timeline linhaT = new Timeline();
linhaT.getKeyFrames().add(kFrame);
linhaT.setAutoReverse(true);
linhaT.setCycleCount(Animation.INDEFINITE);
linhaT.play();
}
this.rect_background = new Rectangle();
this.root.getChildren().add(this.rect_background);
this.root.getChildren().add(this.grp_hexagons);
// UI
this.lay_box_controls = new VBox();
this.lay_box_controls.setSpacing(20);
this.lay_box_controls.setAlignment(Pos.CENTER);
this.bot_test = new Button("CHANGE POSITIONS");
this.bot_test.setAlignment(Pos.CENTER);
this.bot_test.setOnAction(new EventHandler<ActionEvent>()
{
#Override public void handle(ActionEvent e)
{
for(Node hexagono : grp_hexagons.getChildren())
{
hexagono.setTranslateX(Math.random() * cenario.getWidth());
hexagono.setTranslateY(Math.random() * cenario.getHeight());
}
}
});
this.texA_test = new TextArea();
this.texA_test.setText("This is just a test.");
this.lab_test = new Label("This is just a label.");
this.lab_test.setTextFill(Color.WHITE);
this.lab_test.setFont(new Font(32));
this.lay_box_controls.getChildren().add(this.lab_test);
this.lay_box_controls.getChildren().add(this.texA_test);
this.lay_box_controls.getChildren().add(this.bot_test);
this.root.getChildren().add(this.lay_box_controls);
}
}
I've tried to make the use of a StackPane as the root of my scene graph, but also found an undesired result. Despite the controls have stayed in the center of the window, the circles begin to move in as they grow and shrink, making it appear that everything is weird.
The second thing I would like to know is if it is possible to customize the controls so they perform some animation when some event happens. Although we can change the appearance of controls using CSS, it's harder to create something complex. For example, when a control changes its appearance due to a change of state, the transition state change is not made in an animated way, but in an abrupt and static way. Is there a way to animate, for example, a button between its states? This would be done using the JavaFX API? Or would that be using CSS? Or would not be possible in any way?
Thank you for your attention.
after much struggle, I and some users of the Oracle community could resolve this issue. I see no need to repeat here all the resolution made ​​by us, so I'll post the link so you can access the solution of the problem. I hope this benefits us all. Thanks for your attention anyway.
https://community.oracle.com/thread/2620500

How can I animate a circle with PlayN?

This is a follow up to my last question:
How can I draw a circle to the screen with PlayN?
For my simple case, I want to programmatically create a single colored circle and move it across a 2-D plain (doesn't need to use box2d lib).
A real-world example would likely involve animating several circles. Two real-world examples for this case (sorry, I had to remove the links -- not enough karma!):
Browsmos for Chrome
Ants AI Challenge
It was suggested in response to my last question that I would want to use the ImmediateLayer class, so I am looking to understand how to properly incorporate this into my game loop.
Here's is my code sample:
public class SimpleCircleAnimation implements Game {
// Surface
private GroupLayer rootLayer;
private ImmediateLayer surface;
private Canvas canvas;
private Circle circle;
private CanvasImage circleImage;
#Override
public void init() {
// create root layer
rootLayer = graphics().rootLayer();
// a simple circle object
int circleX = 0; int circleY = 0;
int circleRadius = 20;
circle = new Circle(circleX, circleY, circleRadius);
// create an immediate layer and add to root layer
ImmediateLayer circleLayer = graphics().createImmediateLayer(new ImmediateLayer.Renderer() {
public void render (Surface surf) {
circleImage = graphics().createImage(circle.radius*2, circle.radius*2);
canvas = circleImage.canvas();
canvas.setFillColor(0xff0000eb);
canvas.fillCircle(circle.radius, circle.radius, circle.radius);
surf.drawImage(circleImage, circle.x, circle.y);
}
});
rootLayer.add(circleLayer);
}
#Override
public void paint(float alpha) {
}
#Override
public void update(float delta) {
// move circle
int newX = circle.x + 4; int newY = circle.y + 4;
circle.setPoint(newX, newY);
}
#Override
public int updateRate() {
return 25;
}
}
This successfully moves the circle diagonally down the screen from left to right. A couple questions:
Is this implemented properly?
In the case of multiple animated circles, is the idea with ImmediateLayer that you would create a circle image for each circle within the Renderer callback? Or would you perhaps create an Immediate Layer for each circle and add those to the root layer?
I would not use ImmediateLayer wiht render (Surface surf) adapter. Here u have, inside the render method creation of an image
circleImage = graphics().createImage(circle.radius*2, circle.radius*2);
just put this in the paint method
surf.drawImage(circleImage, circle.x, circle.y);
using the normal layer and u should be fine
Painting is done in paint method, and do not put calculations there
Update is for calculations, and physics oriented stuff
I discovered a detailed practical example of ImmediateLayer usage in the Cute Game source within the PlayN Samples:
CuteGame.java (code.google.com)

Resources