Search code examples
javajavafx

JavaFX Refreshing To repaint a picture in new Location


I'm actually learning JavaFX and trying to make a simple application To just move a picture of a character with arrow keys.

I did onKeyPressed part which is working and I'm able to modify the variable locationX and locationY of the object, but the image isn't moving, is there something like a "repaint()" method to do it simply? or do I have to use Animation? I'd prefer to not use Animation for the moment.

Fenetre.java

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Fenetre extends Application{
        
    public Charac c1;
    
    public void init(){
        c1 = new Charac(110,110);
    }

    public void start(Stage stage){
        ImageView ivChar = new ImageView();
        ivChar.setImage(c1.getImg());
        ivChar.relocate((double)c1.getLocationX(),(double)c1.getLocationY());
        
        HBox box = new HBox();
        box.setTranslateX((double)c1.getLocationX());
        box.setTranslateY((double)c1.getLocationY());
        box.getChildren().add(ivChar);
        box.setFocusTraversable(true);
        box.setOnKeyPressed(new EventHandler<KeyEvent>(){
            @Override
            public void handle(KeyEvent e) {
                switch(e.getCode()){
                    case UP:
                        c1.goUp();
                        break;
                    case DOWN:
                        c1.goDown();
                        break;
                    case LEFT:
                        c1.goLeft();
                        break;
                    case RIGHT:
                        c1.goRight();
                        break;
                default:
                    break;
                }   
            }
        });
        
        Group root = new Group();
        root.getChildren().add(box);
        Scene scene = new Scene(root);
        scene.setFill(Color.BLACK);
        
        
        stage.setWidth(600);
        stage.setHeight(600);   
        stage.setTitle("BBMAN");
        stage.setScene(scene);
        stage.show();
    
        
    }
}

Charac.java

import javafx.scene.Parent;
import javafx.scene.image.Image;

public class Charac extends Parent{
    private int locationX=0;
    private int locationY=0;
    private Image img = new Image("/Images/bbfront.png");

    public Charac(int locationX, int locationY){
        this.locationY=locationY;
        this.locationX=locationX;

        
    }


    public void setLocationX(int locationX){
        this.locationX=locationX;
    }

    public int getLocationX(){
        return this.locationX;
    }

    public int getLocationY(){
        return this.locationY;
    }
    public void setLocationY(int locationY){
        this.locationY=locationY;
    }

    public void goRight(){
        this.locationX+=1;
        this.img = new Image("/Images/bbright.png");
    }

    public void goLeft() {
        this.locationX-=1;
        this.img = new Image("/Images/bbleft.png");
    }

    public void goUp(){
        this.locationY-=1;
        this.img = new Image("/Images/bbup.png");
    }

    public void goDown(){
        this.locationY+=1;
        this.img = new Image("/Images/bbfront.png");
        
    }

    public Image getImg(){
        return this.img;
    }
}

I don't realy know if I'm posting in the right section, this is my first post.

Thank You all!


Solution

  • The following code was tested and works, though with different graphics. (I had to redo the loading to work with my Eclipse file structure.)

    It's okay to use the "root" node as your Keyboard Event Handler. Main thing is to remember to both make it FocusTraversable and to give it the Focus.

    I stripped out some code in your Charac that wasn't needed, and preloaded the graphics into an array so the structure would be more like a sprite, and so you wouldn't be loading a new graphic (relatively costly) every time you change direction.

    There's a bit of redundant code (three lines) that initializes the ImageView and then updates it in the KeyEvent Handling routine. Probably can and should be eliminated.

    The main thing is that you can set the X and Y of the ImageView directly, as well as update the image that the ImageView displays. I've found ImageView to be a very handy class and am using it more often as a result. The ability to vary opacity and scaling are really nice to have, too.

    import javafx.application.Application;
    import javafx.event.EventHandler;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.input.KeyEvent;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    
    public class CharacMove extends Application
    {
        public static void main(String[] args) 
        {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) throws Exception 
        {
            Group root = new Group();
            Scene scene = new Scene(root);
    
            scene.setFill(Color.BLACK);
            stage.setWidth(600);
            stage.setHeight(600);   
            stage.setTitle("BBMAN");
            stage.setScene(scene);
    
            Charac c1 = new Charac();
            ImageView ivChar = new ImageView();
            ivChar.setImage(c1.getImage());
            ivChar.setX(c1.getX());
            ivChar.setY(c1.getY());
    
            root.setFocusTraversable(true);
            root.requestFocus();
            root.setOnKeyPressed(new EventHandler<KeyEvent>()
            {
                @Override
                public void handle(KeyEvent e) 
                {
                    switch(e.getCode())
                    {
                        case UP:
                            c1.goUp();
                            break;
                        case DOWN:
                            c1.goDown();
                            break;
                        case LEFT:
                            c1.goLeft();
                            break;
                        case RIGHT:
                            c1.goRight();
                            break;
                        default:
                            break;
                    }
                    ivChar.setImage(c1.getImage());
                    ivChar.setX(c1.getX());
                    ivChar.setY(c1.getY());
                }
            });
    
            root.getChildren().add(ivChar);
            stage.show();
        }
    
        class Charac
        {
            private Image[] img;
            private double xLoc, yLoc;
            private int currentImg;    
    
            public Image getImage() { return img[currentImg]; }
            public double getX() { return xLoc; }
            public double getY() { return yLoc; }
    
            public Charac()
            {
                img = new Image[4];
                img[0] = new Image(getClass().getResource("Images/bbright.png").toString());
                img[1] = new Image(getClass().getResource("Images/bbleft.png").toString());
                img[2] = new Image(getClass().getResource("Images/bbup.png").toString());
                img[3] = new Image(getClass().getResource("Images/bbfront.png").toString());
            }
    
            public void goRight()
            {
                xLoc += 1;
                currentImg = 0;
            }
    
            public void goLeft() 
            {
                xLoc -= 1;
                currentImg = 1;
            }
    
            public void goUp()
            {
                yLoc -= 1;
                currentImg = 2;
            }
    
            public void goDown()
            {
                yLoc += 1;
                currentImg = 3;
            }
        }
    }
    

    When I was starting out with JavaFX, I wrote and posted a simple game animation tutorial at java-gaming.org, that takes things another couple steps, e.g., using AnimationTimer, if/when you are ready to explore that. You can check it out here. But at that time I still hadn't figured out I could simply use the Group node root for keyboard handling.