Home > Enterprise >  How to make smoother movement in my java code
How to make smoother movement in my java code

Time:09-29

Im trying to get my movement smoother. At the moment when I press the up key it will go up and then when I press the left key it stops and then turns left. I want to make it so when i press right it will do it straight away and have no delay. How could I with my code

                
        import java.awt.event.*;
        import javax.swing.*;
        import java.awt.*;
        import java.awt.Image;
        import javax.swing.*;
        
        
        class MyFrame extends JFrame implements KeyListener {
            JLabel label;
            
            MyFrame(){ 
             this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             addKeyListener(this);
             ImageIcon imageIcon = new ImageIcon("C:\\Users\\jacob\\Downloads\\player.png");
             
             Image image = imageIcon.getImage();
             Image newimg = image.getScaledInstance(150, 150,  java.awt.Image.SCALE_SMOOTH);
             imageIcon = new ImageIcon(newimg);
             label = new JLabel(imageIcon);
             this.add(label);
             this.setSize(500, 500);
             this.setVisible(true);
         }
            @Override
            public void keyTyped(KeyEvent e) {
                // TODO Auto-generated method stub
                
            }
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_UP)
                    label.setLocation(label.getX(), label.getY()-10);
                else if (e.getKeyCode() == KeyEvent.VK_DOWN)
                    label.setLocation(label.getX(), label.getY() 10);
                else if (e.getKeyCode() == KeyEvent.VK_LEFT) 
                    label.setLocation(label.getX()-10, label.getY());
                else if (e.getKeyCode() == KeyEvent.VK_RIGHT) 
                    label.setLocation(label.getX() 10, label.getY());
                
            }
            @Override
            public void keyReleased(KeyEvent e) {
                // TODO Auto-generated method stub
                
            }
        }

CodePudding user response:

The main problem here is that different operating systems have different times for key-repeat functionality (how often, and how much to accelerate, repeated key press deliveries).

The way to solve this is to maintain state for which keys are currently down or not, and in some timer thread, perform actions if the state is true or not.

For example, (in very rough Java, this won't compile without fixing I think) you want something like this

// mark that the key is down
public void keyPressed(KeyEvent e) {
    keyboardState[e.getKeyCode()] = true;
}

// mark that the key has been released
public void keyReleased(KeyEvent e) {
    keyboardState[e.getKeyCode()] = false;
}

// this function should be called at some repeatable interval, fast enough where it looks "smooth"
public void calledAtFixedInterval(int delta) {
    // we can query definitively if the key is currently down or up, rather than waiting on some event to be delivered at an arbitrary interval
    if (keyboardState[KeyEvent.VK_LEFT] == true) {
        // your logic here
    }
}

This architecture is roughly how most game engines work - they will handle user input using an event-driven architecture, but just maintain a giant map of which keys are currently down or not - that way it's easy and fast to query if a key is pressed deep within the render loop.

CodePudding user response:

My guess would be to probably make a velocityX and velocityY. And also, if you want smooth movements. You need a tick method.

Basic tick method for Java Game Development:

public synchronized void start() {
    thread = new Thread(this);
    thread.start();
    running = true;
}

public synchronized void stop() {
    try {
        thread.join();
    }catch(Exception e){
        e.printStackTrace();
    }
    }

public void run() {
    long lastTime = System.nanoTime();
    double ns =1000000000 / 60.0;
    double delta = 0;
    long timer = System.currentTimeMillis();
    int ticks = 0;
    int frames = 0;
    while(running) {
        long now = System.nanoTime();
        delta  = (now - lastTime) / ns;
        lastTime = now;
        while(delta >= 1) {
            tick();
            ticks  ;
            delta--;
        }
        if(running) {
            render();
            frames  ;
        }
        
        if(System.currentTimeMillis() - timer > 1000) {
            timer  = 1000;
            System.out.println(ticks   " tps"   " | "   frames   " fps");
            frame.setTitle(name   " | "   ticks   " tps"   "  "   frames   " fps");
            frames = 0;
            ticks = 0;
            
            //meanings
            //ns = nanoseconds
            //amountOfTicks = ticks per second
            
        }
    }
    stop();
}

The start and stop methods are for the Thread. You can find easy tutorials on how to do the tick method and fully understand it.

When you have your ticks working properly, you can make your x and y = the velX and velY, just like this:

public void tick() {
        y  = velY;
        x  = velX;
}

Making the player update the x and y each tick

And the velX and velY need to react to your KeyInputs so they, y'know, have a value, something like this:

if(key == KeyEvent.VK_W) { tempObject.setVelY(-10); keyDown[0] = true; }
                if(key == KeyEvent.VK_S) { tempObject.setVelY(10); keyDown[1] = 
true; }
                if(key == KeyEvent.VK_D) { tempObject.setVelX(10); keyDown[2] = 
true; }
                if(key == KeyEvent.VK_A) { tempObject.setVelX(-10); keyDown[3] = 
true; }

This code, don't copy and paste it. It has different methods because I am basing this code off a game I made a while ago. So methods are different.

And so when you get that, you are basically done.

setVelY and setVelX look something like this:

public void setVelY(int velY){
    this.velY = velY;
}

public void setVelY(int velY){
    this.velX = velX;
}

So, conclusion. If you want smoother animations, you need a tick method, so the x and y update every second.

My code will basically serve nothing if you copy and paste it, but it's just the concept of how to make the movements smooth.

So x and y need to update every second. Making a Runnable tick method is your only way.

I really hope this helps you just a little bit.

Good luck! :D

CodePudding user response:

A solution I commonly use these days is to separate the game animation thread from the input events (mouse and keyboard), employing loose coupling to communicate between the two.

The game loop can be driven by a java.util.Timer operating at 60 fps. I use this rate as it is pretty common for monitors to refresh at 60 fps, so there are diminishing returns for going at a faster rate.

Some prefer to use a javax.swing.Timer but IDK if that is such a good idea. The EDT (event dispatch thread) can become something of a bottle neck. (The classic Java game programming book: Killer Game Programming in Java compares the two timers and the util Timer comes out the winner.) Arguably, a ScheduledThreadPoolExecutor is more robust than either, though it requires a bit of a learning curve if you haven't used Executors for multi-threading yet. (I prefer using JavaFX, so I use its AnimationTimer.)

The class holding the game loop should have public methods where the keyboard or mouse events that are triggered can simply set a variable or toggle a boolean (or add a variable to the top of a queue). The public input method for the keyboard can have a bit of extra processing to compare the incoming key event with current key state (e.g., a maintained map of key states as described elsewhere on this thread) and set a field for the game-loop thread to read and act upon.

This sort of architecture for a game program adds a great deal of predictability and control, as well as clarifying the logic of the various layers. It also takes good advantage of the multi-threading capabilities of today's processors.

  •  Tags:  
  • java
  • Related