Java Awt & swing program working on like 2 fps, why? - java

When I run this code, the square is responding so slow, like the program was running on 2 fps. Can you tell me why is it so slow and how can i make it run faster? Additionally I'll say that I used to code in pygame (python) and there was a system that allowed me to set FPS, but i can't find anything like this in java if you could show me how to set FPS I would be grateful.
Thanks for answering.
package World;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
public class Map {
private final String title = "The Lord of the Dungeon";
private final int width = 1600;
private final int height = width / 16 * 9;
private Player player = new Player(width / 2, height / 2, 50);
//class Player contains only: int x, int y, int width, int height
public Map () {
//generate map
}
public void draw () {
boolean running = true;
//Creating the frame.
JFrame frame = new JFrame(title);
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
//Creating the canvas.
Canvas canvas = new Canvas();
canvas.setSize(width, height);
canvas.setBackground(Color.BLACK);
canvas.setVisible(true);
frame.add(canvas);
canvas.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent keyEvent) {
}
#Override
public void keyPressed(KeyEvent keyEvent) {
int keyCode = keyEvent.getKeyCode();
if (keyCode == KeyEvent.VK_W) {
player.setY(player.getY() - 10);
}
else if (keyCode == KeyEvent.VK_A) {
player.setX(player.getX() - 10);
}
else if (keyCode == KeyEvent.VK_S) {
player.setY(player.getY() + 10);
}
else if (keyCode == KeyEvent.VK_D) {
player.setX(player.getX() + 10);
}
}
#Override
public void keyReleased(KeyEvent keyEvent) {
}
});
canvas.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent mouseEvent) {
}
#Override
public void mousePressed(MouseEvent mouseEvent) {
int mouseCode = mouseEvent.getButton();
if (mouseCode == 1) {
System.out.println("1");
}
}
#Override
public void mouseReleased(MouseEvent mouseEvent) {
}
#Override
public void mouseEntered(MouseEvent mouseEvent) {
}
#Override
public void mouseExited(MouseEvent mouseEvent) {
}
});
canvas.createBufferStrategy(3);
BufferStrategy bufferStrategy;
Graphics graphics;
while (running) {
bufferStrategy = canvas.getBufferStrategy();
graphics = bufferStrategy.getDrawGraphics();
graphics.clearRect(0, 0, width, height);
graphics.fillRect(player.getX(), player.getY(), player.getWidth(), player.getHeight());
bufferStrategy.show();
graphics.dispose();
}
}
}
here is Player class:
public class Player {
private int x;
private int y;
private int width;
private int height;
public Player (int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
this.height = width / 9 * 16;
}
public int getX () {
return this.x;
}
public int getY () {
return this.y;
}
public int getWidth () {
return this.width;
}
public int getHeight () {
return this.height;
}
public void setX (int newX) {
this.x = newX;
}
public void setY (int newY) {
this.y = newY;
}
public void setWidth (int newWidth) {
this.width = newWidth;
this.height = newWidth / 9 * 16;
}
}

Related

Swing animation optimization

I have been working on a simple animation using a Timer on a JComponent. However, I experience incredibly choppy motion when I view the animation. What steps should I take to optimize this code?
MyAnimationFrame
import javax.swing.*;
public class MyAnimationFrame extends JFrame {
public MyAnimationFrame() {
super("My animation frame!");
setSize(300,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new AnimationComponent(0,0,50,50));
setVisible(true);
}
public static void main(String[] args) {
MyAnimationFrame f = new MyAnimationFrame();
}
}
AnimationComponent
import javax.swing.*;
import java.awt.*;
public class AnimationComponent extends JComponent implements ActionListener {
private Timer animTimer;
private int x;
private int y;
private int xVel;
private int yVel;
private int width;
private int height;
private int oldX;
private int oldY;
public AnimationComponent(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
animTimer = new Timer(25, this);
xVel = 5;
yVel = 5;
animTimer.start();
}
#Override
public void paintComponent(Graphics g) {
g.fillOval(x,y,width,height);
}
#Override
public void actionPerformed(ActionEvent e) {
oldX = x;
oldY = y;
if(x + width > getParent().getWidth() || x < 0) {
xVel *= -1;
}
if(y + height > getParent().getHeight() || y < 0) {
yVel *= -1;
}
x += xVel;
y += yVel;
repaint();
}
}
Not sure if this matters, but I am using OpenJDK version 1.8.0_121.
Any help is appreciated.
After a wonderful discussion with Yago it occurred to me that the issue revolves around number of areas, alot comes down to the ability for Java to sync the updates with the OS and the hardware, some things you can control, some you can't.
Inspired by Yago's example and my "memory" of how the Timing Framework works, I tested you code by increasing the framerate (to 5 milliseconds, ~= 200fps) and decreasing the change delta, which gave the same results as using the Timing Framework, but which leaves you with the flexibility of your original design.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.add(new AnimationComponent(0, 0, 50, 50));
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationComponent extends JComponent implements ActionListener {
private Timer animTimer;
private int x;
private int y;
private int xVel;
private int yVel;
private int width;
private int height;
private int oldX;
private int oldY;
public AnimationComponent(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
animTimer = new Timer(5, this);
xVel = 1;
yVel = 1;
animTimer.start();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
);
g2d.setRenderingHints(hints);
g2d.fillOval(x, y, width, height);
}
#Override
public void actionPerformed(ActionEvent e) {
oldX = x;
oldY = y;
if (x + width > getParent().getWidth() || x < 0) {
xVel *= -1;
}
if (y + height > getParent().getHeight() || y < 0) {
yVel *= -1;
}
x += xVel;
y += yVel;
repaint();
}
}
}
If you need to slow down the speed more, then decrease the change delta more, this will mean you have to use doubles instead, which will lead into the Shape's API which supports double values
Which should you use? That's up to you. The Timing Framework is really great for linear animations over a period of time, where you know you want to go from one state to another. It's not so good for things like games, where the state of the object can change from my cycle to another. I'm sure you could do it, but it'd be a lot easier with a simple "main loop" concept - IMHO
Timing Framework offers a way to provide animations highly optimized which may help in this case.
MyAnimationFrame
import javax.swing.*;
public class MyAnimationFrame extends JFrame {
public MyAnimationFrame() {
super("My animation frame!");
setSize(300,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new AnimationComponent(0,0,50,50));
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MyAnimationFrame f = new MyAnimationFrame();
}
});
}
}
AnimationComponent
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.TimeUnit;
import org.jdesktop.core.animation.rendering.*;
import org.jdesktop.core.animation.timing.*;
import org.jdesktop.core.animation.timing.interpolators.*;
import org.jdesktop.swing.animation.rendering.*;
import org.jdesktop.swing.animation.timing.sources.*;
#SuppressWarnings("serial")
public class AnimationComponent extends JRendererPanel {
protected int x;
protected int y;
protected int width;
protected int height;
protected Animator xAnimator;
protected Animator yAnimator;
public AnimationComponent(int x, int y, int width, int height) {
setOpaque(true);
this.x = x;
this.y = y;
this.height = height;
this.width = width;
JRendererFactory.getDefaultRenderer(this,
new JRendererTarget<GraphicsConfiguration, Graphics2D>() {
#Override
public void renderSetup(GraphicsConfiguration gc) {
// Nothing to do
}
#Override
public void renderUpdate() {
// Nothing to do
}
#Override
public void render(Graphics2D g, int w, int h) {
Color c = g.getColor();
g.setColor(g.getBackground());
g.fillRect(0, 0, w, h);
g.setColor(c);
g.fillOval(AnimationComponent.this.x, AnimationComponent.this.y,
AnimationComponent.this.width, AnimationComponent.this.height);
}
#Override
public void renderShutdown() {
// Nothing to do
}
}, false);
this.xAnimator = new Animator.Builder(new SwingTimerTimingSource())
.addTargets(new TimingTargetAdapter() {
#Override
public void timingEvent(Animator source, double fraction) {
AnimationComponent.this.x = (int) ((getWidth() - AnimationComponent.this.width) * fraction);
}})
.setRepeatCount(Animator.INFINITE)
.setRepeatBehavior(Animator.RepeatBehavior.REVERSE)
.setInterpolator(LinearInterpolator.getInstance()).build();
this.yAnimator = new Animator.Builder(new SwingTimerTimingSource())
.addTargets(new TimingTargetAdapter() {
#Override
public void timingEvent(Animator source, double fraction) {
AnimationComponent.this.y = (int) ((getHeight() - AnimationComponent.this.height) * fraction);
}})
.setRepeatCount(Animator.INFINITE)
.setRepeatBehavior(Animator.RepeatBehavior.REVERSE)
.setInterpolator(LinearInterpolator.getInstance()).build();
addComponentListener(new ComponentAdapter() {
private int oldWidth = 0;
private int oldHeight = 0;
#Override
public void componentResized(ComponentEvent event) {
Component c = event.getComponent();
int w = c.getWidth();
int h = c.getHeight();
if (w != this.oldWidth) {
AnimationComponent.this.xAnimator.stop();
AnimationComponent.this.xAnimator = new Animator.Builder()
.copy(AnimationComponent.this.xAnimator)
.setDuration(w * 5, TimeUnit.MILLISECONDS) // Original speed was 200 px/s
.build();
AnimationComponent.this.xAnimator.start();
}
if (h != this.oldHeight) {
AnimationComponent.this.yAnimator.stop();
AnimationComponent.this.yAnimator = new Animator.Builder()
.copy(AnimationComponent.this.yAnimator)
.setDuration(h * 5, TimeUnit.MILLISECONDS) // Original speed was 200 px/s
.build();
AnimationComponent.this.yAnimator.start();
}
this.oldWidth = w;
this.oldHeight = h;
}
});
}
}
I'm getting good results but has one issue: any item you resize, the animation is reset.

How to drop a ball when clicked with mouse?

I'm working on a game project. The aim is clicking the balls and drop them into the basket which is below in the JPanel. I created some ways to do that but I can't achieve it. In my opinion, when the user click the ball's center with a margin of error (because the program can't catch the real point of balls), the program understands the action and runs the function of this issue. After clicking the ball it should be dropped down straight but the other balls should be continued. I use MouseListener#mousePressed method, but it doesn't work or I'm missing some parts. In addition, I made some changes in my source code, but I want to listen your advices so I am writing this topic.
I wrote a method which finds the mouse and ball coordinates. So when I make some changes to it, I can achieve my project goal. You can see the editing in the picture.
This is my source code ;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Game {
public Game() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException
| UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("MultipleBallApp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BallControl());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BallControl extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
this.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent clickEvent) {
// TODO Auto-generated method stub
System.out.println("X coordinate =" + clickEvent.getX());
System.out.println("Y coordinate = " + clickEvent.getY());
double radius1;
int x = 0;
double y = 0;
int radius = 15;
double xM = clickEvent.getX();
double yM = clickEvent.getY();
radius1 = Math.sqrt((xM - x) * (xM - x) + (yM - y)
* (yM - y));
System.out.println("Radius1 =" + radius1);
// ballPanel.list.get(0).setD
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
}
});
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend) {
ballPanel.suspend();
} else if (e.getSource() == Resume) {
ballPanel.resume();
} else if (e.getSource() == Add) {
ballPanel.add();
} else if (e.getSource() == Subtract) {
ballPanel.subtract();
}
}
}
}
class BallPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private int delay = 30;
public ArrayList<AnimatedShape> list = new ArrayList<AnimatedShape>();
private AnimatedRectange rectangle;
public BallPanel() {
this.rectangle = new AnimatedRectange(-25, 373, 50, 25, Color.BLACK);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/**
* Handle the action event
*/
#Override
public void actionPerformed(ActionEvent e) {
for (AnimatedShape ball : list) {
ball.update(getBounds());
}
rectangle.update(getBounds());
repaint();
}
});
public void add() {
int radius = 15;
// Randomised position
int x = (int) (Math.random() * (getWidth() - (radius * 2)))
+ radius;
int y = (int) (Math.random() * (getHeight() - (radius * 2)))
+ radius;
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
AnimatedBall ball = new AnimatedBall(x, y, radius, color);
list.add(ball);
}
// public void formula(MouseEvent clickEvent) {
// double radius1;
// int x = 0;
// double y = 0;
// int radius = 15;
// double xM = clickEvent.getX();
// double yM = clickEvent.getY();
// radius1 = Math.sqrt((xM - x) * (xM - x) + (yM - y) * (yM - y));
// System.out.println("Radius1 =" + radius1);
// }
public void subtract() {
if (list.size() > 0) {
list.remove(list.size() - 1); // Remove the last ball
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (AnimatedShape ball : list) {
ball.paint(this, g2d);
}
rectangle.paint(this, g2d);
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
public interface AnimatedShape {
public void update(Rectangle bounds);
public void paint(JComponent parent, Graphics2D g2d);
}
public abstract class AbstractAnimatedShape implements AnimatedShape {
private Rectangle bounds;
private int dx, dy;
public AbstractAnimatedShape() {
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
public int getDx() {
return dx;
}
public int getDy() {
return dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
#Override
public void update(Rectangle parentBounds) {// ball
Rectangle bounds = getBounds();
int dx = getDx();
int dy = getDy();
bounds.x += dx;
bounds.y += dy;
if (bounds.x < parentBounds.x) {
bounds.x = parentBounds.x;
setDx(dx *= -1);
} else if (bounds.x + bounds.width > parentBounds.x
+ parentBounds.width) {
bounds.x = parentBounds.x + (parentBounds.width - bounds.width);
setDx(dx *= -1);
}
if (bounds.y < parentBounds.y) {
bounds.y = parentBounds.y;
setDy(dy *= -1);
} else if (bounds.y + bounds.height > parentBounds.y
+ parentBounds.height) {
bounds.y = parentBounds.y
+ (parentBounds.height - bounds.height);
setDy(dy *= -1);
}
}
}
public class AnimatedBall extends AbstractAnimatedShape {
private Color color;
public AnimatedBall(int x, int y, int radius, Color color) {
setBounds(new Rectangle(x, y / 2, radius * 2, radius * 2));
this.color = color;
setDx(Math.random() > 0.5 ? 2 : -2);
// setDy(Math.random() > 0.5 ? 2 : -2);
}
public Color getColor() {
return color;
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(getColor());
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
public class AnimatedRectange extends AbstractAnimatedShape {
private Color color;
public AnimatedRectange(int x, int y, int width, int height, Color color) {
setBounds(new Rectangle(x, y, width, height));
this.color = color;
setDx(2);
}
// Don't want to adjust the vertical speed
#Override
public void setDy(int dy) {
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(color);
g2d.fill(bounds);
}
}
/**
* Main method
*/
public static void main(String[] args) {
new Game();
}
}
At the end of your mousePressed method, you can go though all AnimatedShape objects, check whether they are an AnimatedBall. When the object is a ball, you can test whether the mouse click hits the ball. The mouse click hits the ball when the distance between the center of the ball and the mouse position is smaller than the ball radius. When the ball is hit, you can set its horizontal speed to 0, and the vertical speed to 5 or so.
for (AnimatedShape as : ballPanel.list)
{
if (as instanceof AnimatedBall)
{
AnimatedBall ball = (AnimatedBall)as;
Rectangle b = ball.getBounds();
int ballCenterX = b.x + b.width / 2;
int ballCenterY = b.y + b.height / 2;
Point p = new Point(ballCenterX, ballCenterY);
double d = p.distance(clickEvent.getPoint());
if (d < radius)
{
ball.setDx(0);
ball.setDy(5);
}
}
}
Note
In order to make this work properly, you have to attach this listener to the ballPanel. Originally, you had the line
this.addMouseListener(new MouseListener() {
in your code. You have to change this to
ballPanel.addMouseListener(new MouseListener() {
Otherwise, the mouse coordinates will refer to the wrong component!
Concerning the question from the comment
how can I stop and disapper clickedball's when they crash the bound
You may probably insert method to check this, and call this method in the actionPerformed method of your timer. Further explainations are probably beyond the scope of an anser on a Q&A site. Stackoverflow is not a homework-solution-generator.

Problems with KeyBindings

I've switched from KeyListeners to KeyBindings as instructed, however they still seem to do nothing. My keybinds are set up as to allow the left and right arrow keys to call a setDx() method in paddle.java which instructs the move() method to move the paddle.
gamePanel.java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class gamePanel extends JPanel implements ActionListener {
paddle Paddle;
boolean ingame = true;
int delay = 1000;
Timer timer;
JLabel text = new JLabel("stuff here");
InputMap im = this.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = this.getActionMap();
public gamePanel() {
setBackground(Color.WHITE);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "RightArrow");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "LeftArrow");
add(text);
text.setBounds(100, 100, 200, 300);
timer = new Timer(delay, this);
Paddle = new paddle();
timer.start();
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (ingame) {
g.drawImage(Paddle.getImage(), Paddle.getX(), Paddle.getY(),
Paddle.getWidth(), Paddle.getHeight(), this);
}
}
#Override
public void actionPerformed(ActionEvent ae) {
Object obj = ae.getSource();
if (obj == timer) {
Paddle.move();
validate();
repaint();
}
}
public class ArrowAction extends AbstractAction {
private String cmd;
public ArrowAction(String cmd) {
this.cmd = cmd;
}
#Override
public void actionPerformed(ActionEvent e) {
if (cmd.equalsIgnoreCase("LeftArrow")) {
Paddle.setDx(-20);
} else if (cmd.equalsIgnoreCase("RightArrow")) {
Paddle.setDx(20);
}
}
}
/*
#Override
public void keyPressed(KeyEvent ke) {
int KeyCode = ke.getKeyCode();
if (KeyCode == KeyEvent.VK_LEFT) {
text.setText("key pressed");
Paddle.setDx(-20);
}
if (KeyCode == KeyEvent.VK_RIGHT) {
Paddle.setDx(20);
}
}
#Override
public void keyReleased(KeyEvent ke) {
int KeyCode = ke.getKeyCode();
if (KeyCode == KeyEvent.VK_LEFT) {
Paddle.setDx(0);
}
if (KeyCode == KeyEvent.VK_RIGHT) {
Paddle.setDx(0);
}
}
#Override
public void keyTyped(KeyEvent ke) {
}
*/
}
Paddle.java:
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
public class paddle {
int dx = 0;
int x, y;
int height, width;
Image image;
public paddle() {
ImageIcon ii = new ImageIcon("src/Paddle.png");
image = ii.getImage();
width = image.getWidth(null);
height = image.getHeight(null);
//dx = 20;
resetState();
}
public void setDx(int z) {
dx = z;
}
public void move() {
x += dx;
if (x <= 2) {
x = 2;
}
if (x >= (640 - getWidth())) {
x = (640 - getWidth());
}
}
public void resetState() {
x = 250;
y = 375;
}
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setY(int y) {
this.y = y;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
Image getImage() {
return image;
}
Rectangle getRect() {
return new Rectangle(x, y, image.getWidth(null), image.getHeight(null));
}
}
If your KeyListener methods are not being called I suspect it's because the correct component does not have focus. It's difficult sometimes to manage what component has focus especially in a game, so I would suggest switching over to using key bindings which doesn't require components to have focus.
You never add the corresponding actions you the ActionMap
am.put("LeftArrow", new ArrowAction("LeftArrow"));
am.put("RightArrow", new ArrowAction("RightArrow"));
Also, if you don't repaint() in the actioPerformed of the ArrowAction, you won't see it update immediately, until repaint() is called by the Timer, which isn't very long, but still a miniscule delay.

Java Multithreading Mouse Click

After making changes based on a previous post I have taken the following code a few steps further by introducing single/double click recognition. Why are the balls being created in the top left corner and not where the mouse is clicked?
BouncingBalls.java
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BouncingBalls extends JPanel implements MouseListener {
protected List<RandomBall> randomBalls = new ArrayList<RandomBall>(20);
protected List<VerticalBall> verticalBalls = new ArrayList<VerticalBall>(20);
private Container container;
private DrawCanvas canvas;
private Boolean doubleClick = false;
private final Integer waitTime = (Integer) Toolkit.getDefaultToolkit()
.getDesktopProperty("awt.multiClickInterval");
private static int canvasWidth = 500;
private static int canvasHeight = 500;
public static final int UPDATE_RATE = 30;
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height) {
canvasWidth = width;
canvasHeight = height;
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
start();
}
public void start() {
Thread t = new Thread() {
public void run() {
while (true) {
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {
}
}
}
};
t.start();
}
public void update() {
for (RandomBall ball : randomBalls) {
ball.ballBounce(container);
}
for (VerticalBall ball : verticalBalls) {
ball.verticalBounce(container);
}
}
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
container.draw(g);
for (RandomBall ball : randomBalls) {
ball.draw(g);
}
for (VerticalBall ball : verticalBalls) {
ball.draw(g);
}
}
public Dimension getPreferredSize() {
return (new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Stack Answer 2");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(canvasHeight, canvasWidth));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getClickCount() >= 2) {
doubleClick = true;
verticalBalls.add(new VerticalBall(getX(), getY(), canvasWidth, canvasHeight));
System.out.println("double click");
} else {
Timer timer = new Timer(waitTime, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (doubleClick) {
/* we are the first click of a double click */
doubleClick = false;
} else {
count++;
randomBalls.add(new RandomBall(getX(), getY(), canvasWidth, canvasHeight));
/* the second click never happened */
System.out.println("single click");
}
}
});
timer.setRepeats(false);
timer.start();
}
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
RandomBall.java
import java.awt.Color;
import java.awt.Graphics;
public class RandomBall {
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private int x;
private int y;
private int canvasWidth = 500;
private int canvasHeight = 500;
private boolean leftRight;
private boolean upDown;
private int deltaX;
private int deltaY;
private int radius = 20;
private int red = random(255);
private int green = random(255);
private int blue = random(255);
RandomBall(int x, int y, int width, int height) {
this(x, y, width, height, false, false);
}
RandomBall(int x, int y, int width, int height, boolean leftRight, boolean upDown) {
this.x = x;
this.y = y;
this.canvasWidth = width;
this.canvasHeight = height;
this.leftRight = leftRight;
this.upDown = upDown;
updateDelta();
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius),
(int) (2 * radius));
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
deltaX = minimumMovement + (int) (Math.random() * maxExtra);
}
public void ballBounce(Container container) {
// controls horizontal ball motion
if (leftRight) {
x += deltaX;
if (x >= getWidth()) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public void verticalBounce(Container container) {
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return canvasWidth;
}
public int getHeight() {
return canvasHeight;
}
}
VerticalBall.java
import java.awt.Color;
import java.awt.Graphics;
public class VerticalBall {
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private int x;
private int y;
private int canvasWidth = 500;
private int canvasHeight = 500;
private boolean upDown;
private int deltaY;
private int radius = 20;
private int red = random(255);
private int green = random(255);
private int blue = random(255);
VerticalBall(int x, int y, int width, int height) {
this(x, y, width, height, false);
}
VerticalBall(int x, int y, int width, int height, boolean upDown) {
this.x = x;
this.y = y;
this.canvasWidth = width;
this.canvasHeight = height;
this.upDown = upDown;
updateDelta();
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius),
(int) (2 * radius));
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
}
public void verticalBounce(Container container) {
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return canvasWidth;
}
public int getHeight() {
return canvasHeight;
}
}
Container.java
import java.awt.Color;
import java.awt.Graphics;
public class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g) {
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
Basically, because that's where you're telling them to be created...
verticalBalls.add(new VerticalBall(getX(), getY(), canvasWidth, canvasHeight));
getX() and getY() are getting the component's location (which happens to be close enough to 0x0 for arguments sake)...
Instead, you should be using the MouseEvent information...
public void mousePressed(MouseEvent e) {
if (e.getClickCount() >= 2) {
doubleClick = true;
verticalBalls.add(new VerticalBall(e.getX(), e.getY(), canvasWidth, canvasHeight));
System.out.println("double click");
}
}
This will create an issue for your Timer...
Instead you may need to introduce a couple of variables to hold the position information...
final int x = e.getX();
final int y = e.getX();
if (e.getClickCount() >= 2) {
...
} else {
Timer timer = new Timer(waitTime, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (doubleClick) {
/* we are the first click of a double click */
doubleClick = false;
} else {
count++;
randomBalls.add(new RandomBall(x, y, canvasWidth, canvasHeight));
/* the second click never happened */
System.out.println("single click");
}
}
});
timer.setRepeats(false);
timer.start();
}
You need to use the getX and getY from the MouseEvent, not from the JPanel.
As a sidenote, your use of a separate Thread to periodically update the gui is bad because you can't use separate threads to call swing methods. use a swing Timer instead.

how to notify multithread event on swing game

I am supposed to make a little game simulation. in this game there are three button . when user click start tank and car will close each other in 90 degrees when user click shut button tank will throw bullet to car.
i made and a simulation for this. tank throw bullet to car but when bullet crash car i couldn't this. i just need increase score of how many times tank hit car.
here is the source code
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Vehicle extends Thread {
private JPanel box;
private int XSIZE;
private int YSIZE;
private int time;
private int x;
private int y;
private int dx = 5;
private int dy = 5;
private int dim;
public Vehicle(JPanel b, int i) {
box = b;
this.dim = i;
if (i == 0) {
x = 0;
y = 100;
time = 1000;
XSIZE = 9;
YSIZE = 20;
} else {
time = 200;
y = box.getSize().height;
x = box.getSize().width / 2;
XSIZE = 6;
YSIZE = 10;
}
}
public void draw() {
Graphics g = box.getGraphics();
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public void moveHorizontal() {
if (!box.isVisible())
return;
Graphics g = box.getGraphics();
g.setColor(Color.BLUE);
g.setXORMode(box.getBackground());
g.fillOval(x, y, XSIZE, YSIZE);
x += dx;
Dimension d = box.getSize();
if (x < 0) {
x = 0;
dx = -dx;
}
if (x + XSIZE >= d.width) {
x = d.width - XSIZE;
dx = -dx;
}
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public JPanel getBox() {
return box;
}
public void setBox(JPanel box) {
this.box = box;
}
public void moveVertical() {
if (!box.isVisible())
return;
Graphics g = box.getGraphics();
g.setXORMode(box.getBackground());
g.fillOval(x, y, XSIZE, YSIZE);
y += dy;
Dimension d = box.getSize();
if (y < 0) {
y = 0;
dy = -dy;
}
if (y + YSIZE >= d.height) {
y = d.height - YSIZE;
dy = -dy;
}
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public void move(int i) {
if (i == 0) {
moveHorizontal();
} else {
moveVertical();
}
}
public int getYSIZE() {
return YSIZE;
}
public void setYSIZE(int ySIZE) {
YSIZE = ySIZE;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public int getXSIZE() {
return XSIZE;
}
public void setXSIZE(int xSIZE) {
XSIZE = xSIZE;
}
public void setY(int y) {
this.y = y;
}
public void run() {
try {
draw();
for (;;) {
move(dim);
sleep(time);
}
} catch (InterruptedException e) {
}
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Bullet extends Thread {
private JPanel box;
private int XSIZE = 3;
public int getXSIZE() {
return XSIZE;
}
public void setXSIZE(int xSIZE) {
XSIZE = xSIZE;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
private int YSIZE = 1;
private int x;
private int y;
private int dx = 3;
public Bullet(JPanel b, Vehicle tank, Vehicle car) {
box = b;
x = tank.getX() + tank.getXSIZE();
if (x >= tank.getBox().getSize().width / 2)
dx = -dx;
y = tank.getY() + tank.getYSIZE() / 2;
;
}
public void draw() {
Graphics g = box.getGraphics();
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public void move() {
if (!box.isVisible())
return;
Graphics g = box.getGraphics();
g.setColor(Color.RED);
g.setXORMode(box.getBackground());
g.fillOval(x, y, XSIZE, YSIZE);
x += dx;
Dimension d = box.getSize();
if (x < 0) {
x = 0;
}
if (x + XSIZE >= d.width) {
x = d.width - XSIZE;
}
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public void run() {
try {
draw();
for (;;) {
move();
sleep(20);
}
} catch (InterruptedException e) {
}
}
}
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
#SuppressWarnings("serial")
public class Tank_Shut_Car_ThreadFrame extends JFrame {
private JPanel canvas;
private boolean isOn = false;
private Vehicle tank;
private Vehicle car;
private JLabel score;
public static int sc = 0;
public Tank_Shut_Car_ThreadFrame() {
setResizable(false);
setSize(600, 400);
setTitle("Tank Shut Car");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Container contentPane = getContentPane();
canvas = new JPanel();
contentPane.add(canvas, "Center");
canvas.setLayout(null);
score = new JLabel("0");
score.setBounds(527, 11, 36, 14);
canvas.add(score);
JLabel lblScore = new JLabel("score");
lblScore.setBounds(481, 11, 36, 14);
canvas.add(lblScore);
JPanel p = new JPanel();
addButton(p, "Start", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (!isOn) {
tank = new Vehicle(canvas, 0);
tank.start();
car = new Vehicle(canvas, 1);
car.start();
isOn = true;
}
}
});
addButton(p, "Shut", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (isOn) {
Bullet bullet = new Bullet(canvas, tank, car);
bullet.start();
score.setText("" + sc);
}
}
});
addButton(p, "Close", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
canvas.setVisible(false);
System.exit(0);
}
});
contentPane.add(p, "South");
}
public void addButton(Container c, String title, ActionListener a) {
JButton button = new JButton(title);
c.add(button);
button.addActionListener(a);
}
}
import javax.swing.JFrame;
public class Test {
public static void main(String[] args) {
JFrame frame = new Tank_Shut_Car_ThreadFrame();
frame.setVisible(true);
}
}
Okay, so I had a play around with this (just for fun)
Now, this is far from a "proper" or "complete" game engine, but it provides a basic idea of how it might be possible to mix a core thread engine with UI components.
Rather then pasting the entire code, I've uploaded the source it to Tank.zip
But the basic engine looks like this...
public class GameEngine extends Thread {
public static final Object ASSET_LOCK = new Object();
private List<GameAsset> lstAssets;
private GameScreen screen;
public GameEngine(GameScreen screen) {
// Let the thread die when the JVM closes
setDaemon(true);
// Want to be below the UI thread (personal preference)
setPriority(NORM_PRIORITY - 1);
// A list of game assests
lstAssets = new ArrayList<GameAsset>(25);
// A reference to the screen
this.screen = screen;
// Add global key listener, this is simpler to the trying to attach a key listener
// to the screen.
Toolkit.getDefaultToolkit().addAWTEventListener(new EventHandler(), AWTEvent.KEY_EVENT_MASK);
}
public GameAsset[] getAssets() {
synchronized (ASSET_LOCK) {
return lstAssets.toArray(new GameAsset[lstAssets.size()]);
}
}
/*
* Allows for assets to be added
*/
public void addAsset(GameAsset asset) {
synchronized (ASSET_LOCK) {
lstAssets.add(asset);
}
}
#Override
public void run() {
while (true) {
try {
sleep(40);
} catch (InterruptedException ex) {
}
synchronized (ASSET_LOCK) {
GameAsset[] assets = lstAssets.toArray(new GameAsset[lstAssets.size()]);
for (GameAsset asset : assets) {
if (lstAssets.contains(asset)) {
asset.update(this, screen);
}
}
screen.repaint(new ArrayList<GameAsset>(lstAssets));
}
}
}
/**
* Allows the removal of an asset...
*/
public void removeAsset(GameAsset asset) {
synchronized (ASSET_LOCK) {
lstAssets.remove(asset);
}
}
/**
* Key event handling...
*/
protected class EventHandler implements AWTEventListener {
#Override
public void eventDispatched(AWTEvent event) {
KeyEvent keyEvent = (KeyEvent) event;
if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
synchronized (ASSET_LOCK) {
GameAsset[] assets = lstAssets.toArray(new GameAsset[lstAssets.size()]);
for (GameAsset asset : assets) {
if (lstAssets.contains(asset)) {
asset.processKeyPressed(GameEngine.this, screen, keyEvent);
}
}
}
} else if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
synchronized (ASSET_LOCK) {
GameAsset[] assets = lstAssets.toArray(new GameAsset[lstAssets.size()]);
for (GameAsset asset : lstAssets) {
if (lstAssets.contains(asset)) {
asset.processKeyReleased(GameEngine.this, screen, keyEvent);
}
}
}
}
}
}
}
Swing is not thread safe. Events should be fired on the event-dispatching thread.
http://www.javamex.com/tutorials/threads/invokelater.shtml

Categories