Java Game: Mouse Control and border(rectangle) issue - java

The 20 x 20 box is not being displayed(including motion listener and no errors). Another class is the window which sets up the JFrame and game start(). Here is the code below(with a package called "javagame9" .
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = -2713820159854096116L;
public static final int WIDTH = 640, HEIGHT = 700;
private Thread thread;
private boolean running = false;
public static boolean paused = false;
public Game() {
this.addMouseMotionListener(new Mouse());
new Window(WIDTH, HEIGHT, "A Game", this);
}
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
try {
thread.join();
running = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
this.requestFocus();
long LastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - LastTime) / ns;
LastTime = now;
while (delta >= 1) {
tick();
delta--;
}
if (running)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
stop();
}
private void tick() {
}
private void render() {
}
public static void main(String args[]) {
new Game();
}
}
public class Mouse extends Canvas implements MouseMotionListener {
private static final long serialVersionUID = 7986961236445581989L;
private Image dbImage; //Mouse - class
private Graphics dbg;
int mx, my;
boolean mouseDragged;
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
if (mouseDragged) {
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.LIGHT_GRAY);
g.fillRect(mx, my, 20, 20);
} else {
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.DARK_GRAY);
g.fillRect(mx, my, 20, 20);
}
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = true;
e.consume();
}
public void mouseMoved(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = false;
e.consume();
}
}

The problem is that you have 2 Canvas: Game and Mouse.
Assuming that the Window class is the following (found it here):
public class Window extends Canvas {
public Window(int width, int height, String title, Game game) {
JFrame frame = new JFrame(title);
frame.setPreferredSize(new Dimension(width, height));
frame.setMaximumSize(new Dimension(width, height));
frame.setMinimumSize(new Dimension(width, height));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.add(game);
frame.setVisible(true);
game.start();
}
}
we see that the Canvas being added to the JFrame is the Game one, so it will be the one visible.
But you are only painting on the Mouse canvas, therefore you are not going to see anything.
You could move the paint logic from Mouse to Game, and use Mouse only for the MouseMotionListener functionality.
Modified Game class (differences highlighted by comments):
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = -2713820159854096116L;
public static final int WIDTH = 640, HEIGHT = 700;
private Thread thread;
private boolean running = false;
public static boolean paused = false;
// fields previously in Mouse moved here:
private Image dbImage;
private Graphics dbg;
// mouse field so we can reuse it
private Mouse mouse;
public Game() {
// we create an instance of mouse and use it as MouseMotionListener
mouse = new Mouse();
this.addMouseMotionListener(mouse);
new Window(WIDTH, HEIGHT, "A Game", this);
}
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
try {
thread.join();
running = false;
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void run() {
this.requestFocus();
long LastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - LastTime) / ns;
LastTime = now;
while (delta >= 1) {
tick();
delta--;
}
if (running)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
stop();
}
private void tick() {
}
private void render() {
}
public static void main(String args[]) {
new Game();
}
// paint methods previously in Mouse moved here:
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
if (mouse.mouseDragged) {
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.LIGHT_GRAY);
g.fillRect(mouse.mx, mouse.my, 20, 20);
}
else {
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.DARK_GRAY);
g.fillRect(mouse.mx, mouse.my, 20, 20);
}
repaint();
}
}
Now from the Mouse class you can remove all the methods/fields used for the painting, also it no longer extends Canvas:
public class Mouse implements MouseMotionListener {
private static final long serialVersionUID = 7986961236445581989L;
int mx, my;
boolean mouseDragged;
#Override
public void mouseDragged(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = true;
e.consume();
}
#Override
public void mouseMoved(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = false;
e.consume();
}
}
Using these classes you should now be able to see what you are painting.

Related

Why does paintComponent not erase past Objects?

Im trying to make a rectangle move on certain Input. Although the Input works, the rectangle is not being redrawn in the way I want it to. The former rectangles are not being erased.
`
public class MiniGame_Panel extends JPanel implements Runnable{
/**
*
*/
private static final long serialVersionUID = 1L;
Thread gameThread;
public KeyHandler2 keyH = new KeyHandler2();
public MGPlayer spieler = new MGPlayer(keyH, this);
public MiniGame_Panel() {
setBackground(new Color(222, 228, 128, 0));
setBounds(0, 0, 16*16*3 + 300 + 3, 16*13*3);
setVisible(true);
setFocusable(true);
requestFocus();
this.addKeyListener(keyH);
}
public void startGameThread() {
gameThread = new Thread((Runnable) this);
gameThread.start();
}
public void run() {
double drawInterval = 1000000000/60;
double delta = 0;
long lastTime = System.nanoTime();
long currentTime;
// GAME LOOP
while(gameThread != null) {
currentTime = System.nanoTime();
delta += (currentTime - lastTime) / drawInterval;
lastTime = currentTime;
if(delta >= 1) {
update();
repaint();
delta--;
}
}
}
public void update() {
spieler.update();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
spieler.draw(g2);
g2.dispose();
}
}
public class MGPlayer {
private int x, y;
private Rectangle rect;
private int width, height;
private int leben;
private KeyHandler2 keyH;
private MiniGame_Panel mGp;
public MGPlayer(KeyHandler2 keyH, MiniGame_Panel miniGame_Panel) {
x = 10;
y = 100;
width = 50;
height = 100;
leben = 3;
this.keyH = keyH;
this.mGp = miniGame_Panel;
}
boolean allowJump;
public void update() {
if(keyH.stats) {
if(!allowJump) {
this.y -= 8;
}
//System.out.println("Leertaste");
}
if(keyH.left == true) {
this.x--;
//System.out.println("links");
}
if(keyH.right == true) {
this.x++;
//System.out.println("rechts");
}
if(y <= 200) {
this.y++;
allowJump = true;
}else {
allowJump = false;
}
}
public void draw(Graphics2D g2) {
g2.setColor(Color.BLACK);
g2.fillRect(x, y, width, height);
}
}
`
I just want to draw a rectangle that moves on certain input. This works but instead of redrawing the rectangle, they all stay in the panel.

Problems with thread and bufferstrategy: IllegalStateException: Component must have a valid peer

Here is the code that I tried to execute, but failed:
public class Game extends Canvas implements Runnable,KeyListener {
private static final long serialVersionUID = 1L;
private static Thread thread ;
private int width = 240;
private int height = 120;
private int scale = 3;
private boolean isRunning = true;
private BufferedImage image;
public static Spritesheet spritesheet;
private Player player;
Game(){
Dimension dimension = new Dimension(width*scale,height*scale);
this.setPreferredSize(dimension);
image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
}
void initframe() {
JFrame f = new JFrame("Game");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setVisible(true);
f.setLocationRelativeTo(null);
f.setResizable(false);
}
public synchronized void start() {
thread = new Thread(this);
isRunning = true;
thread.start();
}
public synchronized void stop() {
isRunning = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Game game = new Game();
game.start();
}
void tick() {
}
void render() {
BufferStrategy bs = this.getBufferStrategy();
if(bs == null) {
this.createBufferStrategy(3);
return;
}
Graphics g = image.getGraphics();
g.setColor(Color.GREEN);
g.fillRect(0, 0, width, height);
g.dispose();
g = bs.getDrawGraphics();
g.drawImage(image, 0, 0, width*scale,height*scale,null);
bs.show();
}
public void run() {
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = (10^9)/amountOfTicks ;
int frames = 0;
double delta;
double timer = System.currentTimeMillis();
while(isRunning) {
long now = System.nanoTime();
delta = (now - lastTime)/ns ;
lastTime = now;
if(delta >= 1) {
tick();
render();
frames++;
delta--;
}
if(System.currentTimeMillis() - timer >= 1000) {
System.out.println(" A taxa de fps e: "+frames);
frames = 0;
timer+=1000;
}
}
stop();
}
And the error that appears to me is:
Exception in thread "Thread-0" java.lang.IllegalStateException: Component must have a valid peer
at java.desktop/java.awt.Component$FlipBufferStrategy.createBuffers(Component.java:4105)
at java.desktop/java.awt.Component$FlipBufferStrategy.<init>(Component.java:4079)
at java.desktop/java.awt.Component$FlipSubRegionBufferStrategy.<init>(Component.java:4611)
at java.desktop/java.awt.Component.createBufferStrategy(Component.java:3942)
at java.desktop/java.awt.Canvas.createBufferStrategy(Canvas.java:195)
at java.desktop/java.awt.Component.createBufferStrategy(Component.java:3866)
at java.desktop/java.awt.Canvas.createBufferStrategy(Canvas.java:170)
at com.main.Game.render(Game.java:83)
at com.main.Game.run(Game.java:115)
at java.base/java.lang.Thread.run(Thread.java:834)
I checked out every line of this code and I don't know why show that the problem is with thread or bufferStrategy, the method render or method run that I used.

MouseEvent canceling

I am creating a MouseEvent and it seems to be cancelling a few ticks after it starts as I can sometimes get a small response before it stops responding. Does anyone see why?
Here is where I initialize in init()
public class Game implements Runnable {
private Display display;
private int width, height;
public String title;
private boolean running = false;
private Thread thread;
private BufferStrategy bs;
private Graphics g;
//Input
Mouse mouse;
//Handler
private Handler handler;
public Game(String title, int width, int height){
this.title = title;
this.width = width;
this.height = height;
mouse = new Mouse();
}
public synchronized void start(){
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop(){
if(!running)
return;
running = false;
try {
thread.join();
}
catch (InterruptedException e){ e.printStackTrace(); }
}
public void run() {
init();
int fps = 60;
double timePerTick = 1000000000 / fps;
double delta = 0;
long now;
long lastTime = System.nanoTime();
long timer = 0;
int ticks = 0;
while(running){
now = System.nanoTime();
delta += (now - lastTime) / timePerTick;
timer += now - lastTime;
lastTime = now;
if(delta >= 1){
tick();
render();
ticks++;
delta--;
}
if(timer >= 1000000000){
ticks = 0;
timer = 0;
}
}
stop();
}
private void init() {
display = new Display(title, width, height);
display.getFrame().addMouseListener(mouse);
Assets.init();
handler = new Handler(this);
}
private void tick() {
Tile tile = new Tile();
tile.tick();
}
private void render() {
bs = display.getCanvas().getBufferStrategy();
if(bs == null){
display.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
g.clearRect(0, 0, width, height); //Clears the Screen
//Start of Drawing
ChessBoard cb = new ChessBoard(64);
cb.render(g);
cb.tick();
//End of Drawing
bs.show();
g.dispose();
}
}
And here is the MouseEvent
public class Mouse implements MouseListener {
public void mousePressed(MouseEvent e) {
System.out.println("1");
}
public void mouseReleased(MouseEvent e) {
System.out.println("2");
}
public void mouseEntered(MouseEvent e) {
System.out.println("3");
}
public void mouseExited(MouseEvent e) {
System.out.println("4");
}
public void mouseClicked(MouseEvent e) {
System.out.println("5");
}
}

How to add a JPanel on top of a canvas placing the mouse on the top of a Graphic element?

I am building the test application to improve later. I have a Java Graphic Element drawn on a canvas using a Game Loop (update,render). It is a red ball that changes its color when the mouse is placed on top of it.
I am trying to figure out a method to create a JPanel when the mouse is on top of the ball,to show some sort of "Hidden Information" inside the ball. My original idea is to display a histogram made with JFreeChart API as the "Hiden information, so I believe that if I create this JPanel I can later add the histogram to the JPanel created. Similar to this http://www.bitjuice.com.au/research/#hierarchicalclassificationexample. In the link, whenever you put the mouse on top of the rectangles, you display extra information.
So far I've got this code:
*Window.java * (The JFrame)
public class Window extends JFrame {
JLabel title_label = new JLabel();
public Window(int width, int height, String title, Animation animation){
setTitle(title);
setPreferredSize(new Dimension(width,height));
setMaximumSize(new Dimension(width,height));
setMinimumSize(new Dimension(width,height));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
add(animation);
add(title_label, BorderLayout.SOUTH);
setVisible(true);
animation.start();
}
public void update(){
title_label.setText(Animation.mouseX + " " + Animation.mouseY);
}
}
Animation.java(The game Loop)
public class Animation extends Canvas implements Runnable {
public static final int WIDTH = 1024, HEIGHT = WIDTH/12*9 ;
private Thread thread;
private boolean running = false;
public static int mouseX,mouseY;
public Window window;
Button button = new Button();
public Animation(){
window = new Window(WIDTH, HEIGHT,"Test", this);
addMouseMotionListener(new Handler(window));
addMouseListener(new Handler(window));
}
public void run() {
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000/amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while(running){
long now = System.nanoTime();
delta += (now-lastTime) / ns;
lastTime = now;
while(delta >= 1){
update();
delta--;
}
if(running)
render();
frames++;
if(System.currentTimeMillis() - timer >1000){
//System.out.println(frames);
timer += 1000;
frames = 0;
}
}
stop();
}
public synchronized void start(){
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop(){
try{
thread.join();
running = false;
}catch(Exception e){
e.printStackTrace();
}
}
public static int getMouseX(){
return mouseX;
}
public static int getMouseY(){
return mouseY;
}
public static void setMouseX(int x){
mouseX = x;
}
public static void setMouseY(int y){
mouseY = y;
}
private void update(){
window.update();
button.update();
}
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(4);
return;
}
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHints(rh);
g2d.setColor(Color.white);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
button.render(g);
g.dispose();
g2d.dispose();
bs.show();
}
public static void main(String args[]){
new Animation();
}
}
Handler.java
public class Handler extends MouseAdapter {
int x,y;
private Window window;
public Handler(Window window){
this.window = window;
}
public void mouseMoved(MouseEvent e){
Animation.setMouseX(e.getX());
Animation.setMouseY(e.getY());
}
}
Button.java
public class Button {
Ellipse2D mask;
boolean mouseIsOn = false;
public Button(){
mask = new Ellipse2D.Double(500,350,50,50);
}
public void update(){
if(mask.contains(Animation.mouseX,Animation.mouseY)){
mouseIsOn = true;
}else
mouseIsOn = false;
}
public void render(Graphics g){
if(mouseIsOn)
g.setColor(Color.green);
else
g.setColor(Color.red);
g.fillOval(500,350, 50, 50);
}
}
I appreciate the help.
Heavy weight and light weight components don't mix. JPanel is a lightweight component and Canvas is heavyweight. Heavyweight components always get drawn on top of lightweight ones.
What you may want to do instead is just draw the mouseover portion directly to your canvas. You can use FontMetrics for drawing Strings if that is what you need.

Jpanel graphic not moving inside runnable

Any ideas why my oval is not moving to the right? I have a game loop but somehow the circle stays where it appears, instead of moving by 2pixels to the right every cycle.
The strange thing is that it enters the loop before actually painting. But then the graphics object show up(at a fixed position).
public class GamePanel extends JPanel implements Runnable {
public static int WIDTH = 1024;
public static int HEIGHT = WIDTH / 16 * 9;
private Thread t1;
boolean running;
private int FPS = 60;
private long optimalTime = 1000 / FPS;
private int heroX = 200;
private int heroY = 200;
public void addNotify(){
Dimension size = new Dimension(WIDTH,HEIGHT);
setPreferredSize(size);
setFocusable(true);
requestFocus();
running = true;
t1 = new Thread(this);
t1.start();
}
public void paintComponent (Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, WIDTH, HEIGHT);
g2.setColor(Color.BLACK);
g2.fillOval(heroX, heroY, 50, 50);
g2.dispose();
}
public void run() {
long startTime;
long passedTime;
long waitTime;
while (running){
startTime = System.nanoTime();
System.out.println("Runs");
update();
draw();
repaint();
passedTime = System.nanoTime() - startTime;
waitTime = optimalTime - passedTime / 1000000;
try {
if (waitTime <= 0){
waitTime = 2;
}
Thread.sleep(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void draw() {
}
private void update() {
heroX += 2;
}
}
You should use a swing Timer instead of trying to use threads. If you need to do something time consuming in the background, use SwingWorkers. It is possible to use your own threading with swing should you really need to (you need a twisted user case to need to do that), but do not try to do that until you have a good grasp of threads and the way to use them with swing.
public class GamePanel extends JPanel {
private static final int DELAY = 1000 / 60;
private final Timer timer;
// ...
private int heroX = 200;
private int heroY = 200;
public GamePanel() {
timer = new Timer(DELAY, new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
update();
repaint();
}
});
// ...
}
// No need to make this public
#Override
protected void paintComponent (Graphics g) {
// ...
}
#Override
public Dimension getPreferredSize() {
// Overriding is cleaner than using set*Size(). Search old questions to see why
}
public void startAnimation() {
timer.start();
}
public void stopAnimation() {
timer.stop();
}
// Remove addNotify() and run()
}
After reading and testing, I just wanted to do this. It works as intended, even if paintComponent is outside the EDT.
public class GamePanel extends JPanel implements Runnable {
public static int WIDTH = 1024;
public static int HEIGHT = WIDTH / 16 * 9;
private int cordX = WIDTH / 2;
private Thread t1;
private boolean running = true;
public void addNotify() {
super.addNotify();
Dimension size = new Dimension (WIDTH, HEIGHT);
setPreferredSize(size);
t1 = new Thread(this);
t1.start();
}
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.BLACK);
g.fillOval(cordX, HEIGHT /2 , 20, 20);
}
public void run() {
while(running) {
cordX += 2;
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Categories

Resources