Home > Back-end >  How do you make, that your Player in an Java2d Top Down Game alwayls looks towards the mouse
How do you make, that your Player in an Java2d Top Down Game alwayls looks towards the mouse

Time:06-06

So I'm trying to awnser this question for like 2-3 hours now, but I can't quite find an fix or resolution for my problem. Like there are no Video tutorials. And because I am new to programming, especially with Java, I just don't know, how to rewrite code, that it matches my code and perfectly works. Here is what I have right now:

Also my project is all based on 2D and you only see your player for right above. So I dont really need Animation for the Player and Entity models, just in case you wondered.

Class: GamePanel

package main;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

import entity.Player;

public class GamePanel extends JPanel implements Runnable{
    
    // SCREEN SETTINGS
    final int originalTitleSize = 16; // 16x16 title
    final int scale = 3; //16x3(scale) = 48
    
    public final int tileSize = originalTitleSize * scale; //48x48 title
    final int maxScreenCol = 16;
    final int maxScreenRow = 12;
    final int screenWidth = tileSize * maxScreenCol; // 768 pixels
    final int screenHeight = tileSize * maxScreenRow; // 576 pixels
    
    //FPS
    int FPS = 60;
    
    KeyHandler keyH = new KeyHandler();
    Thread gameThread;
    Player player = new Player(this,keyH);
    
    
    // Set player's default position
    int playerX = 100;
    int playerY = 100;
    int playerSpeed = 4;
    
    
    public GamePanel() {
        
        this.setPreferredSize(new Dimension(screenWidth, screenHeight));
        this.setBackground(Color.BLACK);
        this.setDoubleBuffered(true);
        this.addKeyListener(keyH);
        this.setFocusable(true);
    }
    
    public void startGameThread() {
        
        gameThread = new Thread(this);
        gameThread.start();
    }


    @Override
//  public void run() {
//      
//      double drawInterval = 1000000000/FPS; // 0.0166666... seconds
//      double nextDrawTime = System.nanoTime()   drawInterval;
//      
//      
//  
//      while(gameThread != null) {
//          
//          // System.out.println("The game loop is running");
//          
//          // 1 UPDATE: update information such as character positions
//          update();
//          
//          
//          
//          // 2 DRAW: draw the screen with the updated information
//          repaint();
//          
//          try {
//              double remainingTime = nextDrawTime - System.nanoTime();
//              remainingTime = remainingTime/1000000;
//              
//              if(remainingTime < 0) {
//                  remainingTime  = 0;
//              }
//              
//              Thread.sleep((long) remainingTime);
//              
//              nextDrawTime  = drawInterval;
//              
//          } catch (InterruptedException e) {
//              e.printStackTrace();
//          }
//      }
//  }
    public void run() {
        
        double drawInterval = 1000000000/FPS;
        double delta = 0;
        long lastTime = System.nanoTime();
        long currentTime;
        long timer = 0;
        int drawCount = 0;
        
        while(gameThread != null) {
            currentTime =System.nanoTime();
                    
            delta  = (currentTime - lastTime) / drawInterval;
            timer  = (currentTime -lastTime);
            lastTime = currentTime;
            
            if(delta >=1) {
                update();
                repaint();
                delta--;
                drawCount  ;
            }
            
            if(timer >= 1000000000) {
                System.out.println("FPS:"   drawCount);
                drawCount = 0;
                timer = 0;
            }
            
            
        }
    }
    public void update() {
        
        player.update();
        
    }
    public void paintComponent(Graphics g) {
        
        super.paintComponent(g);
        
        Graphics2D g2 = (Graphics2D)g;
        
        player.draw(g2);
        
        g2.dispose();
    }

}

Class: KeyHandler

package main;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class KeyHandler implements KeyListener{
    
    public boolean upPressed, downPressed, leftPressed, rightPressed;

    @Override
    public void keyTyped(KeyEvent e) {
        
    }

    @Override
    public void keyPressed(KeyEvent e) {
        
        int code = e.getKeyCode();
        
        if(code == KeyEvent.VK_W) {
            upPressed = true;
        }
        if(code == KeyEvent.VK_S) {
            downPressed = true;
        }
        if(code == KeyEvent.VK_A) {
            leftPressed = true;
        }
        if(code == KeyEvent.VK_D) {
            rightPressed = true;
        }
        
    }

    @Override
    public void keyReleased(KeyEvent e) {
        
        int code = e.getKeyCode();
        
        if(code == KeyEvent.VK_W) {
            upPressed = false;
        }
        if(code == KeyEvent.VK_S) {
            downPressed = false;
        }
        if(code == KeyEvent.VK_A) {
            leftPressed = false;
        }
        if(code == KeyEvent.VK_D) {
            rightPressed = false;
        }
    }

}

Class: Main

package main;

import javax.swing.JFrame;

public class Main {

    public static void main(String[] args) {
        
        JFrame window = new JFrame();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setResizable(false);
        window.setTitle("Zombio 0.0.0.01");
        
        GamePanel gamePanel = new GamePanel();
        window.add(gamePanel);
        
        window.pack();
        
        window.setLocationRelativeTo(null);
        window.setVisible(true);
        
        gamePanel.startGameThread();

    }

}

I also have an Player:

package entity;

import java.awt.Color;
import java.awt.Graphics2D;

import main.GamePanel;
import main.KeyHandler;

public class Player extends Entity{
    
    GamePanel gp;
    KeyHandler keyH;
    
    public Player(GamePanel gp, KeyHandler keyH) {
        
        this.gp = gp;
        this.keyH = keyH;
        
        setDefaultValues();
    }
    
    public void setDefaultValues() {
        
        x = 100;
        y = 100;
        speed = 4;
    }
    public void update() {
        if(keyH.upPressed == true) {
            y -= speed;
        }
        else if(keyH.downPressed == true) {
            y  = speed;
        }
        else if(keyH.leftPressed == true) {
            x -= speed;
        }
        else if(keyH.rightPressed == true) {
            x  = speed;
        }
    }
    
    public void draw(Graphics2D g2) {
        g2.setColor(Color.yellow);
        
        g2.fillRect(x,  y,  gp.tileSize,  gp.tileSize); //(x,  y,  width,  height)
    }
}

And this Player is based on the normal-entity:

package entity;

public class Entity {
    
    public int x, y;
    public int speed;
}

I know, this is a lot you need to look through, but I really don't know, what exaclty you need. I would like to implement the rotation in the Player Class, if this is possible for you. Also please not only write Code and set it as awnser, I really have no expirience, so take my by the hand :)

Thanks for your help, appreciate it!

CodePudding user response:

You will want to have a look at:

  • Key bindings - these will solve all the focus related issues with KeyListener
  • Concurrency in Swing and probably switch over to using a Swing Timer. Swing is NOT thread safe, it's not a good idea to use Thread as your "main loop".

So your basic problem is a "simple" trigonometry problem (I as say simple, but I'm an idiot). You have two points in space and need to calculate the angle between them, for example...

 // Radians
 -Math.atan2(startX - endX, startY - endY)

Runnable example...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                GamePanel gamePanel = new GamePanel();
                JFrame frame = new JFrame();
                frame.add(gamePanel);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                gamePanel.startGameThread();
            }
        });
    }

    public class GamePanel extends JPanel { //implements Runnable {

        // SCREEN SETTINGS
        final int originalTitleSize = 16; // 16x16 title
        final int scale = 3; //16x3(scale) = 48

        public final int tileSize = originalTitleSize * scale; //48x48 title
        final int maxScreenCol = 16;
        final int maxScreenRow = 12;
        final int screenWidth = tileSize * maxScreenCol; // 768 pixels
        final int screenHeight = tileSize * maxScreenRow; // 576 pixels

        //FPS
        int FPS = 60;

        Player player = new Player(this);

        private Timer timer;
        private Set<KeyAction.Direction> movementState = new HashSet<>();
        private Point lastKnownMousePoint;

        public GamePanel() {
            this.setBackground(Color.BLACK);

            addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    lastKnownMousePoint = e.getPoint();
                }
            });

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Pressed.up");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Released.up");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Pressed.down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Released.down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Pressed.left");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Released.left");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Pressed.right");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Released.right");

            ActionMap am = getActionMap();

            am.put("Pressed.up", new KeyAction(KeyAction.Direction.UP, true, movementState));
            am.put("Released.up", new KeyAction(KeyAction.Direction.UP, false, movementState));
            am.put("Pressed.down", new KeyAction(KeyAction.Direction.DOWN, true, movementState));
            am.put("Released.down", new KeyAction(KeyAction.Direction.DOWN, false, movementState));
            am.put("Pressed.left", new KeyAction(KeyAction.Direction.LEFT, true, movementState));
            am.put("Released.left", new KeyAction(KeyAction.Direction.LEFT, false, movementState));
            am.put("Pressed.right", new KeyAction(KeyAction.Direction.RIGHT, true, movementState));
            am.put("Released.right", new KeyAction(KeyAction.Direction.RIGHT, false, movementState));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(screenWidth, screenHeight);
        }

        public void startGameThread() {
            if (timer == null) {
                timer = new Timer((int) Math.floor(1000f / FPS), new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        update();
                        repaint();
                    }
                });
            }
            timer.start();
        }

        public void update() {
            if (movementState.contains(KeyAction.Direction.UP)) {
                player.y -= player.speed;
            } else if (movementState.contains(KeyAction.Direction.DOWN)) {
                player.y  = player.speed;
            } else if (movementState.contains(KeyAction.Direction.LEFT)) {
                player.x -= player.speed;
            } else if (movementState.contains(KeyAction.Direction.RIGHT)) {
                player.x  = player.speed;
            }

            if (lastKnownMousePoint != null) {
                // This assumes that character is facing "UP" by default
                // That is, 0 has the character entity facing towards to the
                // top of the sceen.  If the character is facing in a different
                // direction, then you will need to offset this calculation
                // to compenstate, but that might be better done in the player
                // entity
                double angle = -Math.toDegrees(Math.atan2(player.x - lastKnownMousePoint.x, player.y - lastKnownMousePoint.y));
                player.angleInDegrees = angle;
            }
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            player.draw(g2);
            g2.dispose();
        }

    }

    public class KeyAction extends AbstractAction {
        enum Direction {
            UP, DOWN, LEFT, RIGHT
        }

        private Direction direction;
        private boolean activate;
        private Set<Direction> inputState;

        public KeyAction(Direction direction, boolean activate, Set<Direction> inputState) {
            this.direction = direction;
            this.activate = activate;
            this.inputState = inputState;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (activate) {
                inputState.add(direction);
            } else {
                inputState.remove(direction);
            }
        }
    }

    public class Entity {
        public int x;
        public int y;
        public int speed;
    }

    public class Player extends Entity {

        GamePanel gp;

        double angleInDegrees = 0;

        public Player(GamePanel gp) {
            this.gp = gp;
            setDefaultValues();
        }

        public void setDefaultValues() {
            x = 100;
            y = 100;
            speed = 4;
        }

        public void draw(Graphics2D g2) {
            g2 = (Graphics2D) g2.create();
            g2.translate(x, y);
            g2.rotate(Math.toRadians(angleInDegrees), (gp.tileSize / 2), (gp.tileSize / 2));
            g2.setColor(Color.yellow);
            g2.fillRect(0, 0, gp.tileSize, gp.tileSize);
            g2.setColor(Color.RED);
            g2.drawLine((gp.tileSize / 2), (gp.tileSize / 2), (gp.tileSize / 2), 0);
            g2.dispose();
        }
    }
}
  • Related