How can I obtain the coordinates of the cursor on a JPanel? I've tried using this:
MouseInfo.getPointerInfo().getLocation();
But this returns the location on the screen. I tried using the mouseMoved(MouseEvent m) method and then get the coordinates from m.getX() and m.getY(), but that method isn't being called. (I am using MouseListener).
Here's my Panel class:
public class Panel extends JPanel implements Runnable, KeyListener, MouseListener {
// serial
private static final long serialVersionUID = -2066956445832373537L;
// dimensions
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
// game loop
private Thread thread;
private boolean running;
private final int FPS = 30;
private final int TARGET_TIME = 1000 / FPS;
// drawing
private BufferedImage image;
private Graphics2D g;
// status handler
private StatusHandler statusHandler;
// constructor
public Panel() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if(thread == null) {
addKeyListener(this);
addMouseListener(this);
thread = new Thread(this);
thread.start();
}
}
public void run() {
init();
long start;
long elapsed;
long wait;
// game loop
while(running) {
start = System.nanoTime();
update();
render();
renderToScreen();
elapsed = System.nanoTime() - start;
wait = TARGET_TIME - elapsed / 1000000;
if(wait < 0) wait = TARGET_TIME;
try {
Thread.sleep(wait);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
private void init() {
running = true;
image = new BufferedImage(WIDTH, HEIGHT, 1);
g = (Graphics2D) image.getGraphics();
statusHandler = new StatusHandler();
}
private void update() {
statusHandler.update();
}
private void render() {
statusHandler.render(g);
}
private void renderToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH, HEIGHT, null);
}
public void keyTyped(KeyEvent key) {}
public void keyPressed(KeyEvent key) {
KeyInput.setKey(key.getKeyCode(), true);
}
public void keyReleased(KeyEvent key) {
KeyInput.setKey(key.getKeyCode(), false);
}
public void mouseClicked(MouseEvent m) {}
public void mouseReleased(MouseEvent m) {
MouseInput.setButton(m.getButton(), false);
MouseInput.setMouseEvent(m);
}
public void mouseEntered(MouseEvent m) {}
public void mouseExited(MouseEvent m) {}
public void mousePressed(MouseEvent m) {
MouseInput.setButton(m.getButton(), true);
MouseInput.setMouseEvent(m);
}
}
The mouseMoved event is handled by a MouseMotionListener.
Related
I'm trying to make a game with ducks, the ducks are moving on the screen and I can't get a mouse click on them. I'm obviously doing something wrong because despite setting a MouseListener, its methods are not called. This is my code, I omitted getters and the DuckGame class, which is only generating ducks.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class DuckGameScreen extends JPanel {
private int initialDuckNum = 7;
private final int INTERVAL = 25;
private final int INTERVAL_BTWN_DUCKS = 1000;
private Timer duckTimer;
private int ducksStarted = 0;
private DuckGame duckGame;
public DuckGameScreen() {
setBackground(Color.cyan);
askAboutDifficulty();
startTimer();
startGame();
startDifficultyIncrease();
}
private void startGame() {
duckGame = new DuckGame(initialDuckNum);
startDucks();
addDuckListeners();
}
private void addDuckListeners() {
for (Duck duck : duckGame.getDucks()) {
duck.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Duck clicked");}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
});
}
}
#Override
public void paint(Graphics g) {
super.paint(g);
drawDucks(g);
Toolkit.getDefaultToolkit().sync();
}
private void animateDuck(Duck duck) {
new Thread() {
#Override
public void run() {
super.run();
while (!duck.isDuckStopped()) {
try {
Thread.sleep(INTERVAL);
duck.move();
if (duck.getX() - duck.getWidth() >= Toolkit.getDefaultToolkit().getScreenSize().width) {
duck.resetPosition();
}
repaint();
} catch (InterruptedException e) {
e.printStackTrace();
duck.stop();
break;
}
}
}
}
.start();
}
private void drawDucks(Graphics g) {
for (Duck duck : duckGame.getDucks()) {
duck.paintComponent(g);
}
}
}
And my Duck class:
public class Duck extends JComponent {
private final static int INIT_X = -100;
private int x = INIT_X;
private int y = 100;
private int width = 0;
private int height = 0;
private Image image = null;
private boolean isDuckStopped = false;
private final int STEP = 5;
public Duck() {
initRandomPosition();
loadImage();
setVisible(true);
setBounds(x, y, width, height);
}
private void initRandomPosition() {
y = getRandomY();
}
public void move() {
x += STEP;
}
private void loadImage() {
ImageIcon imageIcon = new ImageIcon(getFilePath(Images.DUCK.getFileName()));
Dimension newDimension = Utils.getScaledDimension(100, 100, imageIcon.getIconWidth(), imageIcon.getIconHeight());
image = imageIcon.getImage().getScaledInstance(newDimension.width, newDimension.height, Image.SCALE_DEFAULT);
width = image.getWidth(null);
height = image.getHeight(null);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponents(g);
drawDuck(g);
Toolkit.getDefaultToolkit().sync();
}
private void drawDuck(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
g2D.drawImage(image, x, y, this);
setBounds(x, y, width, height);
}
public void stop() {
isDuckStopped = true;
setVisible(false);
}
public void resetPosition() {
x = INIT_X;
y = getRandomY();
}
}
When i try to set value to BufferedImage called dinoImage in Dino.java in a constructor i just get a blank screen every time (second picture) because repaint() is not being called, but if i set it to null it is working just fine but without this image (first picture).
No exceptions, everything seems fine in this code, this problem appears when i try to set value to this field using static method getImage of Resource.java which uses this line of code ImageIO.read(new File(path)) and it causes that repaint() is not being called, i guess this line causes such weird behavior but i dont know how to solve it.
Main.java
public class Main {
public static void main(String[] args) {
GameWindow gameWindow = new GameWindow();
gameWindow.startGame();
}
}
GameWindow.java
public class GameWindow extends JFrame {
private GameScreen gameScreen;
public GameWindow() {
super("Runner");
setSize(1000, 500);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameScreen = new GameScreen();
add(gameScreen);
}
public void startGame() {
gameScreen.startThread();
}
}
GameScreen.java
public class GameScreen extends JPanel implements Runnable, KeyListener {
private Thread thread;
public static final double GRAVITY = 0.1;
public static final int GROUND_Y = 300;
private Dino dino;
public GameScreen() {
thread = new Thread(this);
dino = new Dino();
}
public void startThread() {
thread.start();
}
#Override
public void run() {
while(true) {
try {
Thread.sleep(20);
dino.updatePosition();
repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// g.clearRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.drawLine(0, GROUND_Y, getWidth(), GROUND_Y);
dino.draw(g);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("Key Pressed");
dino.jump();
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("Key Released");
}
}
Dino.java
public class Dino {
private double x = 100;
private double y = 100;
private double speedY = 0;
private BufferedImage dinoImage;
public Dino() {
dinoImage = getImage("data/dino.png");
}
public void updatePosition() {
if(y + speedY >= GROUND_Y - 100) {
speedY = 0;
y = GROUND_Y - 100;
} else {
speedY += GRAVITY;
y += speedY;
}
}
public void jump() {
if(y == GROUND_Y - 100) {
speedY = -5;
y += speedY;
}
}
public void draw(Graphics g) {
g.setColor(Color.BLACK);
g.drawRect((int)x, (int)y, 100, 100);
g.drawImage(dinoImage, (int)x, (int)y, null);
}
}
Resource.java
public class Resource {
public static BufferedImage getImage(String path) {
BufferedImage image = null;
try {
image = ImageIO.read(new File(path));
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
}
setSize(1000, 500);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameScreen = new GameScreen();
add(gameScreen);
Swing components need to be added to the frame BEFORE the frame is made visible. Otherwise the panel has a size of (0, 0) and there is nothing to paint.
The code should be something like:
gameScreen = new GameScreen();
add(gameScreen);
setSize(1000, 500);
setVisible(true);
I have made simple JFrame that works fine except when I am trying to access it from other classes it returns null. So I made getter for it (public static JFrame getWindow()) it returns null. If I set JFrame to public and try to access it that way it returns null. When I create it becomes null right after the game engine starts.
Main:
public class Main {
private static String title = "2D SquareWorld 0.";
private static String version = "";
private static JFrame window;
private static Container container;
public static void main(String[] args) {
GameEngine game = new GameEngine();
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle(title + Version.newVersion());
window.setResizable(false);
window.add(game);
window.setSize(1000, 720);
window.setLocationRelativeTo(null);
window.setVisible(true);
game.start();
}
public static Container getContainer() {
return container;
}
public static JFrame getWindow() {
return window;
}
}
GameEngine:
package GameEngine;
public class GameEngine extends Canvas implements Runnable, KeyListener, MouseListener, MouseMotionListener {
private static final long serialVersionUID = 1L;
private Thread thread;
private boolean running;
private GameStateManager gsm;
public GameEngine() {
gsm = new GameStateManager();
}
public void start() {
thread = new Thread(this, "Game");
thread.start();
running = true;
init();
}
public void init() {
Data.setValue("ScreenWidth", getWidth());
Data.setValue("ScreenHeight", getHeight());
addKeyListener(this);
}
public void update(double delta) {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if(bs == null) {
createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
g2.fillRect(0, 0, getWidth(), getHeight());
gsm.draw(g2);
g2.dispose();
bs.show();
}
public void run() {
long lastLoopTime = System.nanoTime();
long lastFpsTime = 0;
final int TARGET_FPS = Fps.TargetFPS;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
int CurrentFps = 0;
while(running) {
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double) OPTIMAL_TIME);
lastFpsTime += updateLength;
CurrentFps++;
if (lastFpsTime >= 1000000000) {
Fps.FPS = CurrentFps;
lastFpsTime = 0;
CurrentFps = 0;
}
update(delta);
render();
try {
if(((lastLoopTime-System.nanoTime() + OPTIMAL_TIME) / 1000000) >= 1) {
Thread.sleep((lastLoopTime-System.nanoTime() + OPTIMAL_TIME) / 1000000);
}
}catch(InterruptedException IE) {
}
}
}
public void mouseDragged(MouseEvent me) {
}
public void mouseMoved(MouseEvent me) {
}
public void mouseClicked(MouseEvent me) {
}
public void mouseEntered(MouseEvent me) {
}
public void mouseExited(MouseEvent me) {
}
public void mousePressed(MouseEvent me) {
}
public void mouseReleased(MouseEvent me) {
}
public void keyPressed(KeyEvent ke) {
gsm.keyPressed(ke.getKeyCode());
}
public void keyReleased(KeyEvent ke) {
}
public void keyTyped(KeyEvent ke) {
}
}
I called getWindow from this class:
public class LoginState extends GameState {
private GameStateManager gsm;
public LoginState(GameStateManager gsm) {
this.gsm = gsm;
}
private JTextField username;
private JTextField password;
public void init() {
username = new JTextField(25);
username.setVisible(true);
username.setBounds(20, 20, 50, 50);
Main.getWindow().add(username);
}
public void draw(Graphics2D g) {
g.setColor(Color.gray);
//g.fillRect(0, 0, Data.getIntegerValue("ScreenWidth"), Data.getIntegerValue("ScreenHeight"));
}
GameStateManager:
public class GameStateManager {
private ArrayList<GameState> gameStates;
public static final int LOGINSTATE = 0;
public static final int MENUSTATE = 1;
public static final int PLAYSTATE = 2;
private static int currentState;
public GameStateManager() {
gameStates = new ArrayList<GameState>();
currentState = LOGINSTATE;
gameStates.add(new LoginState(this));
gameStates.add(new MenuState(this));
gameStates.add(new PlayState(this));
gameStates.get(currentState).init();
}
Please help.
Thanks for the update, but... you still haven't shown where LoginWindow is being initialized.
A guess -- you're program is starting from a different main method from the one you're showing, and so the main method which creates and assigns your JFrame to the static window field is never called. I suggest that you avoid using static methods and fields in this way, that depend on a main method to initialize. Java is an OOP language -- so make your code OOP compliant and create needed objects within code guaranteed to be called, and then assign them to non-static fields.
edit:
simply swap the two lines:
GameEngine game = new GameEngine();
window = new JFrame();
to
window = new JFrame();
GameEngine game = new GameEngine();
Your JFrame gets initialized in the main method of Main. You are better off using a static initialization block instead.
static {
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle(title + Version.newVersion());
window.setResizable(false);
window.add(game);
window.setSize(1000, 720);
window.setLocationRelativeTo(null);
window.setVisible(true);
}
I have a KeyListener that works, but when I try to call a method such as getdx(), I'm not seeing any change. goal is to make a map move with arrows.
I've searched google and stackoverflow in various places, chopped my code all up and it's now pretty messy. what am I doing wrong?
public class GuiPanel extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private Timer timer;
private DrawMap drawmap = new DrawMap();
private DrawChar drawchar = new DrawChar();
KeyBoard keyboard = new KeyBoard();
boolean change = false;
public GuiPanel()
{
KeyListener listener = new KeyBoard();
addKeyListener(listener);
initGuiPanel();
}
private void initGuiPanel()
{
setFocusable(true);
setBackground(Color.BLACK);
setDoubleBuffered(true);
timer = new Timer(1000, this);
timer.start();
new GameLogic ("gamelogic").start();
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
draw(g);
Toolkit.getDefaultToolkit().sync();
}
private void draw(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
drawmap.drawmap(g2d);
drawchar.drawchar(g2d);
}
#Override
public void actionPerformed(ActionEvent e)
{
keyboard.getdx();
keyboard.getdy();
drawmap.move(keyboard.getdx(),keyboard.getdy());
repaint();
}
}
public class KeyBoard extends KeyAdapter
{
private int dx;
private int dy;
private int angle=1;
private boolean change = false;
KeyBoardLogic keyboardlogic = new KeyBoardLogic();
public int getdx()//does not return proper value
{
return keyboardlogic.getdx();
}
public void keyPressed(KeyEvent e)
{
keyboardlogic.keypressed(e);
}
public void keyReleased(KeyEvent e)
{
keyboardlogic.keyreleased(e);
}
}
public class KeyBoardLogic
{
private int dx=0;
public int getdx()
{
return dx;
}
public void keypressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT)
{
dx = -1;//does not update when getdx() is called
System.out.println("left");//works
}
}
public void keyreleased(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT)
{
dx = -1;//just to know that something happened...
}
}
}
Initialize dx in the constructor of KeyBoardLogic like this:
public class KeyBoardLogic
{
private int dx;
public keyBoardLogic()
{
this.dx = 0;
}
//Remaining methods....
}
Got it. after a week of troubleshooting. dx and dy needed to be static. also a number of other coding errors masked the main problem.
I have a problem with a simple Java game I am creating right now. I want a dot (a car) to be movable across the game screen, but instead of this all I can see on the screen is the long "snake" created by the dot moved by me:
Other problem is that activity manager on my Mac shows that the game uses huge amount of CPU power - my laptop gets very hot very fast. I suspect that there is something wrong with my game loop, but since now I haven't found any solution:
BoardPanel.java:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class BoardPanel extends JPanel implements KeyListener, Runnable {
public static final int WIDTH = 600;
public static final int HEIGHT = 600;
private Thread thread;
private boolean running;
private BufferedImage image;
private Graphics2D g;
private int FPS = 30;
private int targetTime = 1000/FPS;
private Map map;
private Car car;
public BoardPanel() {
super();
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if(thread == null) {
thread = new Thread(this);
thread.start();
}
addKeyListener(this);
}
public void run() {
init();
long startTime;
long reTime;
long waitTime;
while (running) {
startTime = System.nanoTime();
update();
render();
draw();
reTime = System.nanoTime() - startTime;
waitTime = targetTime - reTime;
try {
Thread.sleep(waitTime);
}
catch(Exception e) {
}
}
}
private void init() {
running = true;
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
map = new Map();
car = new Car(map);
car.setxpos(50);
car.setypos(50);
}
private void update() {
map.update();
car.update();
}
private void render() {
map.draw(g);
car.draw(g);
}
private void draw() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
}
public void keyTyped(KeyEvent key) {
}
public void keyPressed(KeyEvent key) {
int code = key.getKeyCode();
if(code == KeyEvent.VK_LEFT) {
car.setLeft(true);
}
if(code == KeyEvent.VK_RIGHT) {
car.setRight(true);
}
if(code == KeyEvent.VK_UP) {
car.setUp(true);
}
if(code == KeyEvent.VK_DOWN) {
car.setDown(true);
}
}
public void keyReleased(KeyEvent key) {
int code = key.getKeyCode();
if(code == KeyEvent.VK_LEFT) {
car.setLeft(false);
}
if(code == KeyEvent.VK_RIGHT) {
car.setRight(false);
}
if(code == KeyEvent.VK_UP) {
car.setUp(false);
}
if(code == KeyEvent.VK_DOWN) {
car.setDown(false);
}
}
}
Car.java
import java.awt.*;
public class Car {
private double xpos;
private double ypos;
//private int xsize;
//private int ysize;
private boolean left;
private boolean right;
private boolean up;
private boolean down;
private Map map;
public Car(Map m) {
map = m;
}
public void setxpos(int i) {
xpos = i;
}
public void setypos(int i) {
ypos = i;
}
public void setLeft (boolean b) {
left = b;
}
public void setRight (boolean b) {
right = b;
}
public void setUp (boolean b) {
up = b;
}
public void setDown (boolean b) {
down = b;
}
public void update() {
if(left) {
xpos--;
}
if(right) {
xpos++;
}
if(up) {
ypos--;
}
if(down) {
ypos++;
}
}
public void draw(Graphics2D g) {
int mx = map.getx();
int my = map.gety();
g.setColor(Color.BLUE);
g.fillOval((int)(mx+xpos-20/2), (int)(my+ypos-20/2), 20, 20);
}
}
Map.java (I haven't created map yet, right now only want the dot to move properly)
import java.awt.*;
public class Map {
public int x;
public int y;
public int getx() {
return x;
}
public int gety() {
return y;
}
public void setx(int i) {
x = i;
}
public void sety(int i ) {
y = i;
}
public void update() {
}
public void draw(Graphics2D g) {
}
}
RacerMain.java
import javax.swing.JFrame;
public class RacerMain {
public static void main (String[]args) {
//MainFrame mf = new MainFrame();
JFrame mf = new JFrame("Racer");
mf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mf.setContentPane(new BoardPanel());;
mf.pack();
mf.setVisible(true);
}
}
Many thanks for your help!!!
In addition to what camickr said: Be careful with your usage of the Graphics. A rule of thumb:
Never call getGraphics on a Component!
Additionally, you are never disposing the Graphics that you are fetching from the BufferedImage. You are instead disposing the Graphics that you obtained from the Component, which may be even worse than fetching it in the first place!
I wonder why you are overriding the addNotify method. You should not implement any functionality based on averriding this method....
So you should change the respecive parts of your BoardPanel class roughly as follows:
public class BoardPanel extends JPanel implements KeyListener, Runnable {
...
// private Graphics2D g; // Don't store this here
public BoardPanel() {
...
// Create the thread here instead of in the "addNotify" method!
if(thread == null) {
thread = new Thread(this);
thread.start();
}
addKeyListener(this);
}
public void run() {
...
while (running) {
...
//draw(); // Don't call this method
repaint(); // Trigger a repaint instead!
}
}
private void render() {
Graphics2D g = image.createGraphics();
// Clear the background (see camickrs answer)
g.setColor(Color.BLACK);
g.fillRect(0,0,image.getWidth(),image.getHeight());
try
{
map.draw(g);
car.draw(g);
}
finally
{
g.dispose(); // Dispose the Graphics after it has been used
}
}
/** Don't call "getGraphics" on a component!
private void draw() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
}
*/
// Override the paintComponent method instead:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image,0,0,null);
}
In your draw() method you need to clear the BufferedImages background before your invoke the fillOval method. Something like:
g.setColor( Color.BLACK );
g.fillRect(...);
g.setColor( Color.BLUE );
g.fillOval(...);
Print out your "waitTime" to make sure you are waiting a reasonable time.