Problems with moving in a 2d game using the Slick library - controls

For some reason you just keep moving left and I can't figure out why. The keyboard input reading isn't the problem because moving in any other direction works perfectly. Ignore the isClipped() method as I'm not done with it yet. Thanks for the help.
main class
package Genisis;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import org.lwjgl.input.Mouse;
import org.newdawn.slick.*;
import org.newdawn.slick.state.*;
import org.newdawn.slick.tiled.TiledMap;
public class Play extends BasicGameState
{
int mouseX;
int mouseY;
Level testLevel;
boolean[][] blocked;
MainCharacter mc;
public Play(int state)
{
}
public void init(GameContainer gc, StateBasedGame sbg) throws SlickException
{
gc.setMouseCursor("res/sprites/Cursor.png", 0, 0);
try {
testLevel = new Level(0, 0, "res/maps/testmap.tmx");
} catch (FileNotFoundException e) {
System.out.println("Failed to load map.");
e.printStackTrace();
}
mouseX = Mouse.getX();
mouseY = gc.getHeight() - Mouse.getY();
mc = new MainCharacter(50, 50, new Image("res/sprites/mcg.png"));
}
public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws
SlickException
{
testLevel.render(mc.x, mc.y);
mc.render(mouseX, mouseY);
}
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws
SlickException
{
mc.move(gc, testLevel);
mc.render(gc.getWidth()/2, gc.getHeight()/2);
testLevel.render(mc.x, mc.y);
}
public int getID()
{
return 1;
}
public void spawnMC(float spwnMCX, float spwnMCY)
{
testLevel.render(spwnMCX, spwnMCY);
testLevel.render(spwnMCX, spwnMCY);
}
public void spawnOC()
{
//todo
}
}
Player class
package Genisis;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
public class MainCharacter extends Player
{
public MainCharacter(float newX, float newY, Image newSprite) throws SlickException
{
sprite = newSprite;
x = newX;
y = newY;
}
public void move(GameContainer gc, Level map)
{
Input in = gc.getInput();
if(in.isKeyDown(Input.KEY_W))
if(map.isClipped(x, y + 1))
y += 1;
if(in.isKeyDown(Input.KEY_D))
if(map.isClipped(x + 1, y))
x -= 1;
if(in.isKeyDown(Input.KEY_S))
if(map.isClipped(x, y - 1))
y -= 1;
if(in.isKeyDown(Input.KEY_A));
if(map.isClipped(x - 1, y))
x += 1;
}
public void render(float mX, float mY)
{
float xDist = mX - (500 + (sprite.getWidth() / 2));
float yDist = mY - (250 + (sprite.getHeight() / 2));
double angleToTurn = Math.toDegrees(Math.atan2(yDist, xDist));
sprite.setRotation((float)angleToTurn);
sprite.draw(500, 250);
}
}
level class
package Genisis;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;
import org.newdawn.slick.tiled.TiledMap;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Scanner;
public class Level
{
//height of background (in tiles)
public int height;
//width of background (in tiles)
public int width;
public boolean[][] clipped;
public TiledMap map;
public Level(int newHeight, int newWidth, String path) throws
FileNotFoundException, SlickException
{
map = new TiledMap(path);
clipped = new boolean[map.getHeight()][map.getWidth()];
for(int i = 0; i < map.getHeight(); i++)
for(int j = 0; i < map.getWidth(); i++)
{
if("true".equals(map.getTileProperty(map.getTileId(i, j,
0), "blocked", "false")))
clipped[i][j] = false;
}
}
//render map
public void render(float mcX, float mcY)
{
map.render((int)(mcX), (int)(mcY));
}
//return height of level (in pixels)
public int getHeight()
{
return map.getHeight();
}
//return width of level (in pixels)
public int getWidth()
{
return map.getWidth();
}
public boolean isClipped(float charX, float charY)
{
//return clipped[(int)(charX / 50)][(int)(charY / 50)];
return true;
}
}

Found the problem. There is an extra semicolon (it's always a semicolon -_-) after the if statement for moving left.
if(in.isKeyDown(Input.KEY_A));
if(map.isClipped(x - 1, y))
x += 1;

Related

How can i stop an element when it get the bounds of the screen in JavaFX?

I need to create an animation where there is a ball in the screen. I need to stop the ball one it gets the bound of the screen.
I've tried to use this solution:
boolean collision =
bullet.getBoundsInParent().getMaxX()>=View.getInstance().getXLimit() ||
bullet.getBoundsInLocal().getMaxY()>=View.getInstance().getYLimit();
In order to detect the collision, but it works only for a time! I've tried to relocate the ball by using the "relocate()" method, but when i class "getBoundsInParent().getMaxX()" it return the same value of the screen limit, and i can't restart the animation.
How can I solve the problem?
Since you did not provide any relevant code, I will use the example from here.
Replace
deltaX *= -1; and deltaY *= -1;
with
deltaX = 0;deltaY = 0;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class GamePractice extends Application
{
public static Circle circle;
public static Pane canvas;
#Override
public void start(final Stage primaryStage)
{
canvas = new Pane();
final Scene scene = new Scene(canvas, 800, 600);
primaryStage.setTitle("Game");
primaryStage.setScene(scene);
primaryStage.show();
circle = new Circle(15, Color.BLUE);
circle.relocate(100, 100);
canvas.getChildren().addAll(circle);
final Timeline loop = new Timeline(new KeyFrame(Duration.millis(10), new EventHandler<ActionEvent>()
{
double deltaX = 3;
double deltaY = 3;
#Override
public void handle(final ActionEvent t)
{
circle.setLayoutX(circle.getLayoutX() + deltaX);
circle.setLayoutY(circle.getLayoutY() + deltaY);
final Bounds bounds = canvas.getBoundsInLocal();
final boolean atRightBorder = circle.getLayoutX() >= (bounds.getMaxX() - circle.getRadius());
final boolean atLeftBorder = circle.getLayoutX() <= (bounds.getMinX() + circle.getRadius());
final boolean atBottomBorder = circle.getLayoutY() >= (bounds.getMaxY() - circle.getRadius());
final boolean atTopBorder = circle.getLayoutY() <= (bounds.getMinY() + circle.getRadius());
if (atRightBorder || atLeftBorder) {
deltaX = 0;
deltaY = 0;
}
if (atBottomBorder || atTopBorder) {
deltaY = 0;
deltaX = 0;
}
}
}));
loop.setCycleCount(Timeline.INDEFINITE);
loop.play();
}
public static void main(final String[] args)
{
launch(args);
}
}

Animation depends on previous animations

I am trying to set node animation, that depends on previous animations of that node as well as other nodes.
To demonstrate the issue I'll use a simple Pane with 4 Label children:
The main class as well as the model and view classes:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public final class Puzzle extends Application{
private Controller controller;
#Override
public void start(Stage stage) throws Exception {
controller = new Controller();
BorderPane root = new BorderPane(controller.getBoardPane());
root.setTop(controller.getControlPane());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch();}
}
class View{
private static final double size = 70;
private static Duration animationDuration = Duration.millis(600);
private int[][] cellModels;
private Node[][] cellNodes;
private CountDownLatch latch;
Button play = new Button("Play");
private Pane board, control = new HBox(play);;
View(Model model) {
cellModels = model.getCellModels();
cellNodes = new Node[cellModels.length][cellModels[0].length];
makeBoardPane();
((HBox) control).setAlignment(Pos.CENTER_RIGHT);
}
private void makeBoardPane() {
board = new Pane();
for (int row = 0; row < cellModels.length ; row ++ ) {
for (int col = 0; col < cellModels[row].length ; col ++ ) {
Label label = new Label(String.valueOf(cellModels[row][col]));
label.setPrefSize(size, size);
Point2D location = getLocationByRowCol(row, col);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
cellNodes[row][col] = label;
board.getChildren().add(label);
}
}
}
synchronized void updateCell(int id, int row, int column) {
if(latch !=null) {
try {
latch.await();
} catch (InterruptedException ex) { ex.printStackTrace();}
}
latch = new CountDownLatch(1);
Node node = getNodesById(id).get(0);
Point2D newLocation = getLocationByRowCol(row, column);
Point2D moveNodeTo = node.parentToLocal(newLocation );
TranslateTransition transition = new TranslateTransition(animationDuration, node);
transition.setFromX(0); transition.setFromY(0);
transition.setToX(moveNodeTo.getX());
transition.setToY(moveNodeTo.getY());
//set animated node layout to the translation co-ordinates:
//https://stackoverflow.com/a/30345420/3992939
transition.setOnFinished(ae -> {
node.setLayoutX(node.getLayoutX() + node.getTranslateX());
node.setLayoutY(node.getLayoutY() + node.getTranslateY());
node.setTranslateX(0);
node.setTranslateY(0);
latch.countDown();
});
transition.play();
}
private List<Node> getNodesById(int...ids) {
List<Node> nodes = new ArrayList<>();
for(Node node : board.getChildren()) {
if(!(node instanceof Label)) { continue; }
for(int id : ids) {
if(((Label)node).getText().equals(String.valueOf(id))) {
nodes.add(node);
break;
}
}
}
return nodes ;
}
private Point2D getLocationByRowCol(int row, int col) {
return new Point2D(size * col, size * row);
}
Pane getBoardPane() { return board; }
Pane getControlPane() { return control;}
Button getPlayBtn() {return play ;}
}
class Model{
private int[][] cellModels = new int[][] { {0,1}, {2,3} };
private SimpleObjectProperty<int[][]> cellModelsProperty =
cellModelsProperty = new SimpleObjectProperty<>(cellModels);
void addChangeListener(ChangeListener<int[][]> listener) {
cellModelsProperty.addListener(listener);
}
int[][] getCellModels() {
return (cellModelsProperty == null) ? null : cellModelsProperty.get();
}
void setCellModels(int[][] cellModels) {
cellModelsProperty.set(cellModels);
}
}
and the controller class:
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Pane;
class Controller {
private View view ;
private Model model;
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> shuffle()); //animation works fine
//view.getPlayBtn().setOnAction( a -> IntStream.
// range(0,4).forEach( (i)-> shuffle())); //messes the animation
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
final int fRow = row, fCol = col;
new Thread( () -> view.updateCell(
newValue[fRow][fCol], fRow, fCol)).start();
}
}
}
};
}
void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
}
Play button handler changes the model data (see shuffle()), the change triggers ChangeListener. The ChangeListener animates each changed label by invoking view.updateCell(..) on a separate thread. This all works as expected.
The problem starts when I try to run a few consecutive model updates (shuffle()). To simulate it I change
view.getPlayBtn().setOnAction( a -> shuffle());
with
view.getPlayBtn().setOnAction( a -> IntStream.range(0,4).forEach( (i)-> shuffle()));
which messes the animation (it plays in wrong order and ends up in wrong positions).
This does not come as a surprise: to work properly animations have to be played in a certain order: a label should be re-animated only after all four labels finished their previous animation.
The code posted runs each update on a thread, so execution order is not guaranteed.
My question is what is the right way to implement the needed sequence of multiple nodes and multiple animations ?
I looked at SequentialTransition but I couldn't figure out how it can be used to overcome the issue in question.
I did come up with a solution which I will post as an answer, because of the length of this post, and because I don't think the solution is good.
Your code does not adhere to the seperation of concerns principle. (Or at least you don't do it well.)
The animation should be done by the view and the view alone instead of collaborating between the controller and the view. Put all the animations for scheduling the animations in the View class.
SequentialTransition could wrap multiple animations in a single animation that plays them sequentially, but it shouldn't be the controller's concern to do this.
public class View {
private static final double SIZE = 70;
private static final Duration ANIMATION_DURATION = Duration.millis(600);
private final Map<Integer, Label> labelsById = new HashMap<>(); // stores labels by id
private final Button play = new Button("Play");
private Pane board;
private final HBox control;
private final Timeline animation;
private final LinkedList<ElementPosition> pendingAnimations = new LinkedList<>(); // stores parameter combination for update calls
private Node animatedNode;
public View(int[][] cellModels) { // we don't really need the whole model here
makeBoardPane(cellModels);
this.control = new HBox(play);
control.setAlignment(Pos.CENTER_RIGHT);
final DoubleProperty interpolatorValue = new SimpleDoubleProperty();
animation = new Timeline(
new KeyFrame(Duration.ZERO, evt -> {
ElementPosition ePos = pendingAnimations.removeFirst();
animatedNode = labelsById.get(ePos.id);
Point2D newLocation = getLocationByRowCol(ePos.row, ePos.column);
// create binding for layout pos
animatedNode.layoutXProperty().bind(
interpolatorValue.multiply(newLocation.getX() - animatedNode.getLayoutX())
.add(animatedNode.getLayoutX()));
animatedNode.layoutYProperty().bind(
interpolatorValue.multiply(newLocation.getY() - animatedNode.getLayoutY())
.add(animatedNode.getLayoutY()));
}, new KeyValue(interpolatorValue, 0d)),
new KeyFrame(ANIMATION_DURATION, evt -> {
interpolatorValue.set(1);
// remove bindings
animatedNode.layoutXProperty().unbind();
animatedNode.layoutYProperty().unbind();
animatedNode = null;
if (pendingAnimations.isEmpty()) {
// abort, if no more animations are pending
View.this.animation.stop();
}
}, new KeyValue(interpolatorValue, 1d)));
animation.setCycleCount(Animation.INDEFINITE);
}
private void makeBoardPane(int[][] cellModels) {
board = new Pane();
for (int row = 0; row < cellModels.length; row++) {
for (int col = 0; col < cellModels[row].length; col++) {
Point2D location = getLocationByRowCol(row, col);
int id = cellModels[row][col];
Label label = new Label(Integer.toString(id));
label.setPrefSize(SIZE, SIZE);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
labelsById.put(id, label);
board.getChildren().add(label);
}
}
}
private static class ElementPosition {
private final int id;
private final int row;
private final int column;
public ElementPosition(int id, int row, int column) {
this.id = id;
this.row = row;
this.column = column;
}
}
public void updateCell(int id, int row, int column) {
pendingAnimations.add(new ElementPosition(id, row, column));
animation.play();
}
private static Point2D getLocationByRowCol(int row, int col) {
return new Point2D(SIZE * col, SIZE * row);
}
public Pane getBoard() {
return board;
}
public Pane getControlPane() {
return control;
}
public Button getPlayBtn() {
return play;
}
}
Usage example with reduced complexity:
private int[][] oldValue;
#Override
public void start(Stage primaryStage) {
List<Integer> values = new ArrayList<>(Arrays.asList(0, 1, 2, 3));
oldValue = new int[][]{{0, 1}, {2, 3}};
View view = new View(oldValue);
Button btn = new Button("Shuffle");
btn.setOnAction((ActionEvent event) -> {
Collections.shuffle(values);
int[][] newValue = new int[2][2];
for (int i = 0; i < 2 * 2; i++) {
newValue[i / 2][i % 2] = values.get(i);
}
System.out.println(values);
for (int row = 0; row < newValue.length; row++) {
for (int col = 0; col < newValue[row].length; col++) {
if (newValue[row][col] != oldValue[row][col]) {
view.updateCell(newValue[row][col], row, col);
}
}
}
oldValue = newValue;
});
Scene scene = new Scene(new BorderPane(view.getBoard(), null, view.getControlPane(), btn, null));
primaryStage.setScene(scene);
primaryStage.show();
}
The solution I came up with involved controlling the execution order of the updating threads.
Todo it I changed the controller class by implementing an inner class based on this
so answer. See UpdateView in the following code.
I also modified getModelCangeListener() to use UpdateView:
import java.util.stream.IntStream;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Pane;
class Controller {
private View view ;
private Model model;
private static int threadNumber = 0, threadAllowedToRun = 0;
private static final Object myLock = new Object();
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> IntStream.
range(0,4).forEach( (i)-> shuffle()));
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
final int fRow = row, fCol = col;
new Thread( new UpdateView(
newValue[fRow][fCol], fRow, fCol)).start();
}
}
}
};
}
private void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
//https://stackoverflow.com/a/23097860/3992939
class UpdateView implements Runnable {
private int id, row, col, threadID;
UpdateView(int id, int row, int col) {
this.id = id; this.row = row; this.col = col;
threadID = threadNumber++;
}
#Override
public void run() {
synchronized (myLock) {
while (threadID != threadAllowedToRun) {
try {
myLock.wait();
} catch (InterruptedException e) {}
}
view.updateCell(id, row, col);
threadAllowedToRun++;
myLock.notifyAll();
}
}
}
}
While it as a possible solution I think a solution based on JavaFx own tools is preferable.
The following answer is based this answer. I refactored it, breaking it into smaller, more verbose "pieces", which makes it (for me) easier to understand.
I am posting it in hope it will help others as well.
import java.util.LinkedList;
import java.util.stream.IntStream;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public final class Puzzle extends Application{
private Controller controller;
#Override
public void start(Stage stage) throws Exception {
controller = new Controller();
BorderPane root = new BorderPane(controller.getBoardPane());
root.setTop(controller.getControlPane());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch();}
}
class Controller {
private View view ;
private Model model;
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> IntStream.
range(0,4).forEach( (i)-> shuffle()));
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
view.updateCell(newValue[row][col], row, col);
}
}
}
};
}
private void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
}
class View{
private final Timeline timeLineAnimation;// private final Timeline timeLineAnimation;
private final LinkedList<ElementPosition> pendingAnimations = new LinkedList<>(); // stores parameter combination for update calls
private static final Duration ANIMATION_DURATION = Duration.millis(600);
private static final double SIZE = 70;
private int[][] cellModels;
private final Button play = new Button("Play");
private Pane board, control = new HBox(play);
Node animatedNode;
View(Model model) {
cellModels = model.getCellModels();
makeBoardPane();
((HBox) control).setAlignment(Pos.CENTER_RIGHT);
timeLineAnimation = new TimeLineAnimation().get();
}
private void makeBoardPane() {
board = new Pane();
for (int row = 0; row < cellModels.length ; row ++ ) {
for (int col = 0; col < cellModels[row].length ; col ++ ) {
int id = cellModels[row][col];
Label label = new Label(String.valueOf(id));
label.setPrefSize(SIZE, SIZE);
Point2D location = getLocationByRowCol(row, col);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
label.setId(String.valueOf(id));
board.getChildren().add(label);
}
}
}
public void updateCell(int id, int row, int column) {
pendingAnimations.add(new ElementPosition(id, row, column));
timeLineAnimation.play();
}
private Node getNodeById(int id) {
return board.getChildren().filtered(n ->
Integer.valueOf(n.getId()) == id).get(0);
}
private Point2D getLocationByRowCol(int row, int col) {
return new Point2D(SIZE * col, SIZE * row);
}
Pane getBoardPane() { return board; }
Pane getControlPane() { return control;}
Button getPlayBtn() {return play ;}
private static class ElementPosition {
private final int id, row, column;
public ElementPosition(int id, int row, int column) {
this.id = id;
this.row = row;
this.column = column;
}
}
class TimeLineAnimation {
private final Timeline timeLineAnimation;
//value to be interpolated by time line
final DoubleProperty interpolatorValue = new SimpleDoubleProperty();
private Node animatedNode;
TimeLineAnimation() {
timeLineAnimation = new Timeline(getSartKeyFrame(), getEndKeyFrame());
timeLineAnimation.setCycleCount(Animation.INDEFINITE);
}
// a 0 duration event, used to do the needed setup for next key frame
private KeyFrame getSartKeyFrame() {
//executed when key frame ends
EventHandler<ActionEvent> onFinished = evt -> {
//protects against "playing" when no pending animations
if(pendingAnimations.isEmpty()) {
timeLineAnimation.stop();
return;
};
ElementPosition ePos = pendingAnimations.removeFirst();
animatedNode = getNodeById(ePos.id);
Point2D newLocation = getLocationByRowCol(ePos.row, ePos.column);
// bind x,y layout properties interpolated property interpolation effects
//both x and y
animatedNode.layoutXProperty().bind(
interpolatorValue.multiply(newLocation.getX() - animatedNode.getLayoutX())
.add(animatedNode.getLayoutX()));
animatedNode.layoutYProperty().bind(
interpolatorValue.multiply(newLocation.getY() - animatedNode.getLayoutY())
.add(animatedNode.getLayoutY()));
};
KeyValue startKeyValue = new KeyValue(interpolatorValue, 0d);
return new KeyFrame(Duration.ZERO, onFinished, startKeyValue);
}
private KeyFrame getEndKeyFrame() {
//executed when key frame ends
EventHandler<ActionEvent> onFinished =evt -> {
// remove bindings
animatedNode.layoutXProperty().unbind();
animatedNode.layoutYProperty().unbind();
};
KeyValue endKeyValue = new KeyValue(interpolatorValue, 1d);
return new KeyFrame(ANIMATION_DURATION, onFinished, endKeyValue);
}
Timeline get() { return timeLineAnimation; }
}
}
class Model{
private int[][] cellModels = new int[][] { {0,1}, {2,3} };
private SimpleObjectProperty<int[][]> cellModelsProperty =
cellModelsProperty = new SimpleObjectProperty<>(cellModels);
void addChangeListener(ChangeListener<int[][]> listener) {
cellModelsProperty.addListener(listener);
}
int[][] getCellModels() {
return (cellModelsProperty == null) ? null : cellModelsProperty.get();
}
void setCellModels(int[][] cellModels) { cellModelsProperty.set(cellModels);}
}
(to run copy paste the entire code into Puzzle.java)

Image Does Not Animate and is Stuck in one Frame

I'm having a problem in my code where the animation from another class does not animate and is stuck in one frame. The image moves across the screen in the right manner though. I know that the problem is somehow related to the UPDATE method of the animation. I have tried every possible solutions in my knowledge to find what causes the error and the solutions I found online were not quite helpful. Any help will be appreciated.
Here's my Code:
LevelOneScreen.java
public class LevelOneScreen implements Screen {
private final MyGame app;
WalkAnimate walkAnimate;
private Stage stage;
private Image levelOneImage;
private Image holdStartImage;
public Image walkRightImage;
public Image walkLeftImage;
public float deltaTime = Gdx.graphics.getDeltaTime();
public LevelOneScreen(final ThumbChase app){
this.app = app;
this.stage = new Stage(new StretchViewport(app.screenWidth,app.screenHeight , app.camera));
}
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
walkAnimate = new WalkAnimate();
walkAnimate.update(deltaTime);
levelOneBackground();
holdStart();
ninjaWalk();
}
public void holdStart(){
Texture holdStartTexture = new Texture("HoldStart.png");
holdStartImage = new Image(holdStartTexture);
float holdStartImageW = holdStartImage.getWidth();
float holdStartImageH = holdStartImage.getHeight();
float holdStartImgWidth = app.screenWidth*0.8f;
float holdStartImgHeight = holdStartImgWidth *(holdStartImageH/holdStartImageW);
holdStartImage.isTouchable();
holdStartImage.setSize(holdStartImgWidth,holdStartImgHeight);
holdStartImage.setPosition(stage.getWidth()/2-holdStartImgWidth/2,stage.getHeight()/2-holdStartImgHeight/2);
stage.addActor(holdStartImage);
holdStartImage.addListener(new ActorGestureListener(){
/* public void touchDown (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};*/
public void fling(InputEvent event, float velocityX, float velocityY, int button) {
holdStartImage.setVisible(false);
}
public void touchDown (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};
public void touchDrag (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};
});
}
public void levelOneBackground(){
Texture levelOneTexture = new Texture("BGBlue Resize.png");
levelOneImage = new Image(levelOneTexture);
levelOneImage.setSize(app.screenWidth,app.screenHeight);
levelOneImage.setPosition(0,0);
stage.addActor(levelOneImage);
/*levelOneImage.addListener(new ActorGestureListener(){
public void touchDown (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};
});*/
}
public void ninjaWalk(){
TextureRegion ninjaWalkRight = new TextureRegion(walkAnimate.getCurrentFrameRight());
TextureRegion ninjaWalkLeft = new TextureRegion(walkAnimate.getCurrentFrameLeft());
//Texture ninjaWalkRight = new Texture("badlogic.jpg");
//Texture ninjaWalkLeft = new Texture("badlogic.jpg");
walkRightImage = new Image(ninjaWalkRight);
walkLeftImage = new Image(ninjaWalkLeft);
float walkImageW = walkRightImage.getWidth();
float walkImageH = walkRightImage.getHeight();
float walkImageWidth = app.screenWidth*0.25f;
float walkImageHeight = walkImageWidth*(walkImageH/walkImageW);
walkRightImage.isTouchable();
walkLeftImage.isTouchable();
walkRightImage.setSize(walkImageWidth,walkImageHeight);
walkLeftImage.setSize(walkImageWidth,walkImageHeight);
walkRightImage.setPosition(stage.getWidth()/2-walkImageWidth/2,0);
walkLeftImage.setPosition(stage.getWidth()/2-walkImageWidth/2,0);
walkRightImage.addAction(moveBy(app.screenWidth*0.2f,0,1f));
stage.addActor(walkRightImage);
walkRightImage.addListener(new ActorGestureListener(){
public void pan(InputEvent event, float x, float y, float deltaX, float deltaY) {
holdStartImage.setVisible(true);
}
});
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//walkAnimate.update(deltaTime);
update(delta);
}
public void update(float deltaTime){
stage.act(deltaTime);
stage.draw();
app.batch.begin();
app.batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
stage.dispose();
}
}
WalkAnimate.java
public class WalkAnimate {
public MyGame app;
public Stage stage;
private Animation walkAnimationRight;
private Animation walkAnimationLeft;
private Texture walkSheetRight;
private Texture walkSheetLeft;
private TextureRegion[] walkFramesRight;
private TextureRegion[] walkFramesLeft;
private TextureRegion currentFrameRight;
private TextureRegion currentFrameLeft;
private float stateTime;
private Rectangle bound; //used for positioning and collision detection
private static final int FRAME_COLS_WALK = 3;
private static final int FRAME_ROWS_WALK= 2;
private float screenWidth = Gdx.graphics.getWidth();
private float screenHeight = Gdx.graphics.getHeight();
public float currentFrameWidth = (float)(screenHeight*0.15);
public float currentFrameHeight = (float)(screenHeight*0.15);
public float walkSheetWidth;
public float walkSheetHeight;
public WalkAnimate () {
walkSheetRight = new Texture("ninjaWalkRight.png");
walkSheetWidth = walkSheetRight.getWidth();
walkSheetHeight = walkSheetRight.getWidth();
TextureRegion[][] tmp = TextureRegion.split(walkSheetRight, (int) walkSheetRight.getWidth() / FRAME_COLS_WALK, (int) walkSheetRight.getHeight() / FRAME_ROWS_WALK);
walkFramesRight = new TextureRegion[FRAME_COLS_WALK * FRAME_ROWS_WALK];
int index = 0 ;
for (int i = 0; i < FRAME_ROWS_WALK; i++) {
for (int j = 0; j < FRAME_COLS_WALK; j++) {
walkFramesRight[index++] = tmp[i][j];
}
}
walkAnimationRight = new Animation(0.044f, walkFramesRight);
stateTime = 0f;
walkSheetLeft = new Texture("ninjaWalkLeft.png");
walkSheetWidth = walkSheetLeft.getWidth();
walkSheetHeight = walkSheetLeft.getWidth();
TextureRegion[][] tmp1 = TextureRegion.split(walkSheetLeft, (int) walkSheetRight.getWidth() / FRAME_COLS_WALK, (int)walkSheetLeft.getHeight() / FRAME_ROWS_WALK);
walkFramesLeft = new TextureRegion[FRAME_COLS_WALK * FRAME_ROWS_WALK];
int index1 = 0;
for (int i = 0; i < FRAME_ROWS_WALK; i++) {
for (int j = 0; j < FRAME_COLS_WALK; j++) {
walkFramesLeft[index1++] = tmp1 [i][j];
}
}
walkAnimationLeft = new Animation(0.044f, walkFramesLeft);
stateTime = 0f;
currentFrameRight = walkAnimationRight.getKeyFrame(stateTime, true);
currentFrameLeft = walkAnimationLeft.getKeyFrame(stateTime, true);
}
public Rectangle getBound(){
return bound;
}
public void update(float delta){
stateTime += delta;
}
public TextureRegion getCurrentFrameRight(){
return currentFrameRight;
}
public TextureRegion getCurrentFrameLeft(){
return currentFrameLeft;
}
}
Like the earlier comment by Eames. Just by glancing at your code, I can say that you should begin by moving
walkAnimate.update(delta);
To the render section. The show section only runs once (when the class is started). Your walkAnimate.update(delta) should be running constantly to change the frame when is time. Otherwise it would only update once.
You can also do it the way I do it.
The class where you are going to use it. The 16 represents the number of images. The 1F means it is done in one second.
public void show() {
texture = new Texture("item/coin_animation.png");
animation = new Animation(new TextureRegion(texture), 16, 1f);
}
public void render(float delta) {
animation.update(delta);
}
Animation Class
public Array<TextureRegion> frames;
private float maxFrameTime;
private float currentFrameTime;
private int frameCount;
private int frame;
private static TextureRegion textureRegion;
public Animation(TextureRegion region, int frameCount, float cycleTime){
textureRegion = region;
frames = new Array<TextureRegion>();
int frameWidth = textureRegion.getRegionWidth() / frameCount;
for (int i = 0; i < frameCount; i++){
frames.add(new TextureRegion(textureRegion, i * frameWidth, 0, frameWidth, textureRegion.getRegionHeight()));
}
this.frameCount = frameCount;
maxFrameTime = cycleTime / frameCount;
frame = 0;
}
public void update(float delta){
currentFrameTime += delta;
if (currentFrameTime > maxFrameTime){
frame++;
currentFrameTime = 0;
}
if (frame >= frameCount)
frame = 0;
}
public TextureRegion getFrame(){
return frames.get(frame);
}
public void restart(){
frame = 0;
currentFrameTime = 0;
}
I will answer my question, I've tried using a separate stage for the animation and it works. I also used inputmultiplexor to set two stages both as inputprocessors. I know that this is not the best solution and I am open for more proper solutions. This is open for editing.

JPanel animation

Hy there.
I am trying to make a JPanel which reacts to certain events and plays a little animation. For example if I click on a button, it should flash red.(I need this to indicate when a file was successfully saved(green flash), or a error occurred(red flash).
I found some tutorials on animations, but I'm having a hard time changing it to fit my needs. For example most of the tutorials instantiate a Timer at the beginning. But I only need the timer to be active for that short amount of time where the flash is played and than stop. Also I need different animation types.(red flash, green flash...)
This is what I have got so far, which is basically nothing:
package MainPackage;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class StatusBar extends JPanel implements ActionListener{
Timer t = new Timer(10, this);
boolean stop = false;
Color color;
public void paintComponent (Graphics g) {
super.paintComponent(g);
setBackground(color);
}
public void confirm(){
color = new Color(46, 204, 113);
t.start();
}
public void warning(){
color = Color.red;
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}
Thanks in advance!
public class flashclass extends JFrame{
Thread th;
Color defaultColor, flashColor;
int i;
boolean success;
JPanel p;
public flashclass(){
setSize(200, 200);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
success = false;
defaultColor = new Color(214,217,223);
p = new JPanel();
JButton rbtn = new JButton("Red flash");
JButton gbtn = new JButton("Green flash");
rbtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
success = false;
flash(success);
}
});
gbtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
success = true;
flash(success);
}
});
p.add(rbtn);
p.add(gbtn);
getContentPane().add(p);
}
public void flash(boolean success){
i=0;
if(!success){
flashColor = Color.red;
}
else{
flashColor = Color.green;
}
th = new Thread(new Runnable() {
#Override
public void run() {
while(i<10){
p.setBackground(flashColor);
i++;
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
p.setBackground(defaultColor);
}
}
});
th.start();
}
}
public static void main(String args[]){
new flashclass();
}
}
So here is the finished class:
New animations can be added easily. And they also do not interfere with each other. So multiple states can be triggered simultaneously.
The StatusBar.java
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JComponent;
import javax.swing.Timer;
public class StatusBar extends JComponent implements ActionListener{
int width;
int height;
boolean bLoad, bWarn, bConfirm, bError;
Timer timer;
Color bgColor;
int xPosLoad, alphaWarn, alphaConfirm, alphaError;
float cntWarn, cntConfirm, cntError;
int cntLoad;
final int barLength = 200;
public StatusBar(Color bg){
width = getWidth();
height = getHeight();
xPosLoad = -barLength;
alphaWarn = 0;
alphaConfirm = 0;
alphaError = 0;
bgColor = bg;
timer = new Timer(10, this);
this.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent event) {
width = getWidth();
height = getHeight();
}
});
}
#Override
protected void paintComponent(Graphics g) {
// Background
g.setColor(bgColor);
g.fillRect(0, 0, width, height);
// loading
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(xPosLoad,0, new Color(0,0,0,0), xPosLoad+barLength, 0, new Color(200, 200, 255));
g2d.setPaint(gp);
g2d.fillRect(xPosLoad, 0, barLength, height);
// Green
g.setColor(new Color(20, 210, 60, alphaConfirm));
g.fillRect(0, 0, width, height);
// Yellow
g.setColor(new Color(255, 223, 0, alphaWarn));
g.fillRect(0, 0, width, height);
// Red
g.setColor(new Color(255, 0, 0, alphaError));
g.fillRect(0, 0, width, height);
}
#Override
public void actionPerformed(ActionEvent e) {
// step increase for all active components
boolean toggle = false;
if (bConfirm){
if(cntConfirm < 1){
cntConfirm += 0.01f;
alphaConfirm = lerp(cntConfirm, 255, true);
}else{
bConfirm = false;
cntConfirm = 0;
alphaConfirm = 0;
}
toggle = true;
}
if (bWarn){
if(cntWarn < 1){
cntWarn += 0.01f;
alphaWarn = lerp(cntWarn, 255, true);
}else{
bWarn = false;
cntWarn = 0;
alphaWarn = 0;
}
toggle = true;
}
if (bError){
if(cntError < 1){
cntError += 0.01f;
alphaError = lerp(cntError, 255, true);
}else{
bError = false;
cntError = 0;
alphaError = 0;
}
toggle = true;
}
if (bLoad){
if(cntLoad < 100){
cntLoad += 1;
xPosLoad = (cntLoad * (width + barLength)) / 100 - barLength;
}else{
cntLoad = 0;
xPosLoad = -barLength;
}
toggle = true;
}
repaint();
if (!toggle){
timer.stop();
}
}
private void startTimer(){
if (!timer.isRunning())
timer.start();
}
public void setBG(Color bg){
bgColor = bg;
System.out.println("Color: " + bgColor);
repaint();
}
// Green flash
public void confirm(){
// set values
bConfirm = true;
alphaConfirm = 255;
cntConfirm = 0;
startTimer();
}
//Yellow flash
public void warning(){
// restart values
bWarn = true;
alphaWarn = 255;
cntWarn = 0;
startTimer();
}
//Red Flash
public void error(){
// restart values
bError = true;
alphaError = 255;
cntError = 0;
startTimer();
}
//Blue load
public void loadStart(){
// restart values
bLoad = true;
xPosLoad = -barLength;
cntLoad = 0;
startTimer();
}
public void loadEnd(){
bLoad = false;
xPosLoad = -barLength;
}
private int lerp(float progress, int max, boolean inverse){
float x = progress;
float x2 = (float) Math.pow(x, 4);
float g = x + (1 - x);
float y = (float) ((float) x2 / (float)(Math.pow(g, 4)));
y = Math.min(y, 1);
y = Math.max(y, 0);
int res = (int) (y * max);
if (inverse){
res = max - res;
}
return res;
}
}
And the Example.java
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example extends JFrame{
public static void main(String[] args){
new Example("Stat Example");
}
public Example(String title){
super(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
StatusBar stat = new StatusBar(Color.black);
stat.setPreferredSize(new Dimension(0, 10));
JPanel panel = new JPanel();
JButton bConfirm = new JButton("Confirm");
JButton bWarn = new JButton("Warning");
JButton bErr = new JButton("Error");
JButton bLoadS = new JButton("Start Loading");
JButton bLoadE = new JButton("End Loading");
panel.add(bConfirm);
panel.add(bWarn);
panel.add(bErr);
panel.add(bLoadS);
panel.add(bLoadE);
this.getContentPane().add(stat, BorderLayout.CENTER);
this.getContentPane().add(panel, BorderLayout.SOUTH);
this.pack();
this.setVisible(true);
// Listener
bConfirm.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
stat.confirm();
}
});
bWarn.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
stat.warning();
}
});
bErr.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
stat.error();
}
});
bLoadS.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
stat.loadStart();
}
});
bLoadE.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
stat.loadEnd();
}
});
}
}

BlackBerry - Redraw my titlebar after orientation change

I am writing a BlackBerry application that uses a custom title bar. Rather than using the textual based title bar, my application uses an image.
I am having trouble redrawing this title bar once the orientation of the device, such as a BlackBerry Storm or Torch, changes from portrait to landscape. See my code for my titleBar Class below.
Any help would be appreciated! Thanks!!
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
/**
* Title Bar
*/
public class TitleBar extends Field implements DrawStyle
{
private int fieldWidth;
private int fieldHeight;
private int fontColour;
private int backgroundColor;
private Bitmap bgImage = Bitmap.getBitmapResource("bgtitle.png");
private Bitmap titleImage = Bitmap.getBitmapResource("logotitle.png");
private static final int BACKGROUND_COLOR = 0x00000000;
public TitleBar()
{
super(Field.NON_FOCUSABLE);
fieldHeight = titleImage.getHeight();
fieldWidth = Display.getWidth();
//background color is black
backgroundColor = BACKGROUND_COLOR;
}
public void setBackgroundColour(int _backgroundColour)
{
backgroundColor = _backgroundColour;
invalidate();
}
protected void layout(int width, int height)
{
setExtent(getPreferredWidth(), getPreferredHeight());
}
public int getPreferredWidth()
{
return fieldWidth;
}
public int getPreferredHeight()
{
return fieldHeight;
}
protected void paint(Graphics graphics)
{
int w = this.getPreferredWidth();
int h = this.getPreferredHeight();
int width_of_bg = 10;
int paint_position = 0;
int screen_width = Display.getWidth();
while(paint_position<screen_width){
graphics.drawBitmap(paint_position, 0, w, h, bgImage, 0, 0);
paint_position += width_of_bg;
}
int marginX = (w- titleImage.getWidth() ) / 2 ;
graphics.drawBitmap(marginX, 0, w, h, titleImage, 0, 0);
}
}
Solved it!
It was because I was getting the width in the constructor. When the device was reoriented, I would retrieve the saved value which was taken in the constructor.
Here is the fixed code:
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
/**
* Title Bar
*/
public class TitleBar extends Field implements DrawStyle
{
private int fieldWidth;
private int fieldHeight;
private int fontColour;
private int backgroundColor;
private Bitmap bgImage = Bitmap.getBitmapResource("bgtitle.png");
private Bitmap titleImage = Bitmap.getBitmapResource("logotitle.png");
private static final int BACKGROUND_COLOR = 0x00000000;
public TitleBar()
{
super(Field.NON_FOCUSABLE);
fieldHeight = titleImage.getHeight();
fieldWidth = Display.getWidth();
//background color is black
backgroundColor = BACKGROUND_COLOR;
}
public void setBackgroundColour(int _backgroundColour)
{
backgroundColor = _backgroundColour;
invalidate();
}
protected void layout(int width, int height)
{
setExtent(getPreferredWidth(), getPreferredHeight());
}
public int getPreferredWidth()
{
return Display.getWidth();
}
public int getPreferredHeight()
{
return fieldHeight;
}
protected void paint(Graphics graphics)
{
int w = this.getPreferredWidth();
int h = this.getPreferredHeight();
int width_of_bg = 10;
int paint_position = 0;
while(paint_position<w){
graphics.drawBitmap(paint_position, 0, w, h, bgImage, 0, 0);
paint_position += width_of_bg;
}
int marginX = (w- titleImage.getWidth() ) / 2 ;
graphics.drawBitmap(marginX, 0, w, h, titleImage, 0, 0);
}
}

Resources