Reputation: 29
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!
Upvotes: 0
Views: 181
Reputation: 347332
You will want to have a look at:
KeyListener
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(startY - endY, startX - endX)
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();
}
}
}
Oh, and also the rotation point is not right in the middle of the player (rectangle). Some dont need accurancy, I really do
Just beware, you're probably never going to find the "exact" solutions to your problems and you're going to need to take the time to experiment ;)
Okay, admittedly, that seemed to work for me, until I started debugging it. The problem was, the original code was calculating the angle from the mouse point and the players current x/y point. It should be using the players "rotation" point, which, in this example, is the players mid point.
So, I added...
public Point midPoint() {
return new Point(x + (gp.tileSize / 2), y + (gp.tileSize / 2));
}
to Player
, so we can easily get the player's mid point (and not have to retype that a lot)
I then updated the angle calculation to make use of it, for example..
Point playerMidPoint = player.midPoint();
double angle = Math.toDegrees(Math.atan2(lastKnownMousePoint.y - playerMidPoint.y, lastKnownMousePoint.x - playerMidPoint.x)) + 90d;
player.angleInDegrees = angle;
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.awt.geom.AffineTransform;
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 = new Point(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
Point playerMidPoint = player.midPoint();
double angle = Math.toDegrees(Math.atan2(lastKnownMousePoint.y - playerMidPoint.y, lastKnownMousePoint.x - playerMidPoint.x)) + 90d;
player.angleInDegrees = angle;
repaint();
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
player.draw(g2);
g2.dispose();
if (lastKnownMousePoint != null) {
g2 = (Graphics2D) g;
g2.setColor(Color.GREEN);
int midX = player.x + (tileSize / 2);
int midY = player.y + (tileSize / 2);
g2.drawLine(midX, midY, lastKnownMousePoint.x, lastKnownMousePoint.y);
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 Point midPoint() {
return new Point(x + (gp.tileSize / 2), y + (gp.tileSize / 2));
}
public void draw(Graphics2D g2) {
g2 = (Graphics2D) g2.create();
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
at.rotate(Math.toRadians(angleInDegrees), (gp.tileSize / 2d), (gp.tileSize / 2d));
g2.transform(at);
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();
}
}
}
Upvotes: 1