I have looked into Double Buffering and plan on implementing it eventually but as of right now I can't figure out how to use it or anything like it. I am trying to make pong so I plan on adding three objects total but for now I just want to get one object to work smoothly. I'm fairly new to graphics so I don't know entirely what I'm doing and I'm just trying to learn as I go.
Here is my code:
Pong:
public static void main(String[]args) {
JFrame window= new JFrame();
window.setTitle("Pong Game");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setPreferredSize(new Dimension(800,500));
window.pack();
window.setVisible(true);
Ball ball= new Ball();
Paddle player= new Paddle();
window.getContentPane().add(ball);
for(;;) {
ball.move();
//window.setContentPane(ball);
window.setContentPane(player);
player.move();
}
}
Paddles:
double x, y, ymove;
boolean cpu;
public Paddle() {
x=5;
y=180;
ymove=.1;
}
//passing an integer through to make the computer paddle
public Paddle(int a) {
cpu= true;
x=761;
y=180;
ymove=.1;
}
public void paint(Graphics g) {
g.setColor(Color.blue);
g.fillRect((int)x, (int)y, 18, 120);
}
public void move() {
y+=ymove;
if(y>=500-160||y<=0) {
ymove*=-1;
}
}
Ball:
double x, y, xspeed, yspeed;
public Ball() {
x=200;
y=200;
xspeed=0;
yspeed=.1;
}
public void move() {
x+=xspeed;
y+=yspeed;
if(y>=440||y<=0) {
yspeed*=-1;
}
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillOval((int)x, (int)y, 20, 20);
}
This Answer is a very very simplified explanation! I recommend checking out this linux journal, which explores something similia.
The main issue, which causes the "flickering" is, that the draw is done "to fast".
Take your main loop:
for(;;) {
ball.move();
window.setContentPane(ball);
window.setContentPane(player);
player.move();
}
This loop updates the positions of the ball and afterwards "adds it to the content pane". While it is drawn, the next image is already added and drawn. This is causing the flickering (again, note: this is very simplified).
The simplest solution to fix the "flickering" is, to let the Thread sleep after it has drawn and "wait" until the draw is finished.
boolean running = true;
int delay = 15; // adjust the delay
while(running) {
ball.move();
player.move();
window.setContentPane(ball);
window.setContentPane(player);
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
// We were interrupted while waiting
// Something "woke us up". Stop the loop.
e.printStackTrace();
running = false;
}
}
This Thread.sleep method let's the current Thread "wait" for the specified time.
The delay can be adjusted to something more practical. You could for example calculate how many frames you want and sleep for that amount.
Another way would be to "time" the updates. This could be done with a timer. Since it is more or less deprecated, i implement it using the ScheduledExecutorService
int delay = 15; // adjust the delay
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
ball.move();
player.move();
}
}, delay, TimeUnit.MILLISECONDS)
As of Java8, you might write this using a Lambda like this:
scheduledExecutorService.scheduleAtFixedRate(() -> {
ball.move();
player.move();
}, delay, TimeUnit.MILLISECONDS)
To stop it, you could now call:
scheduledExecutorService.shutdown();
However: There are more sophisticated solutions. One is, as you already noted, the double buffering. But there are also multiple different techniques, that compensate more difficult problems. They use something called page flipping.
The problem you are having is that you have split your paint methods,
if you make one class that is dedicated to doing the painting and you put all of your paints in one paint method it should work without flickering.
I would also recommend looking into making your paint calls run on a timer which lets you decide the refresh rate which usually leads to a smoother experience overall.
Here is an example of my graphics class in my latest game,
class GameGraphics extends JPanel implements ActionListener {
private Timer refreshHZTimer;
private int refreshHZ = 10;
private int frameID = 0;
public GameGraphics(int width, int height) {
setBounds(0,0, width, height);
setVisible(true);
refreshHZTimer = new Timer(refreshHZ, this);
refreshHZTimer.start();
}
#Override
public void actionPerformed(ActionEvent e) {
frameID++;
if (frameID % 100 == 1)
System.out.println("Painting FrameID: " + frameID);
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Init graphics
Graphics2D g2 = (Graphics2D) g;
//Paint stuff here:
}
}
And add this code to your JFrame constructor:
GameGraphics gpu = new GameGraphics(width, height);
frame.add(gpu);
Related
I’m trying to create some animation ( boy who runs whenever key is pressed) using JPanel and painComponent.
So, first of all I declare some images, Image array and some methods for drawing. I created timer and added it to constructor.
Image img1;
Image img2;
Image img3;
Image img4;
int index = 0;
Image currentImage l;
Image[] images = new Image[4]
public class Animation extends JPanel implements ActionListener
{
public Animation()
{
loadImages();
getTimer();
imgAdder();
addFirstImage();
}
public void serCurrentImage(Image currentImage)
{
this.currentImage = currentImage;
}
public void getTimer()
{
Timer timer = new Timer(20,this)
timer.start();
}
public void imgAdder()
{
images[1] = img1;
...
}
public void addFirstImage()
{
currentImage = img1;
}
private void loadImages()
{
try
{
BufferedImage img = ImageIO.read(getClass.getResource(“/resources/images/img1.png”);
img1 = img;
// and so on for every image
}catch(IOexception ioe )
ioe.printStackTrace();
}
}
public void paintComponent (Graphics g)
{
super.paintComponent(g);
g.drewImage(currentImage,0,0,this);
requestsFocus();
}
public class FieldKeyListener extends KeyAdapter
{
public void move()
{
setCurrentImage(image[index]);
index++;
if( index == 4 )
index = 0;
}
public void keyPressed(KeyEvent e)
{
super.keyPressed(e);
int key = e.getKeyCode();
if(key == Key.Event.VK_LEFT)
move();
}
}
}
Then drew all images through paintComponent using loop for my array.
Also I declared class which extends KeyAdapter.
Everything seems to be fine and my animation works, but problem is that it works not as smoothly as I wanted. When I press and hold key, images are changing too fast and process look unnatural. I want , for instance , 3 or 4 images change per second instead of 20.
May I added timer in wrong method ? May be there is something like time delay. I don’t know how exactly it works , and which listener should I mention as argument in timer.
P.s. I’m just beginner and my code may look incorrectly in terms of coding standards. Also I wrote just crucial parts of my project which represent problem. I hope you help me with this. Thanks in advance.
Animation is a complex subject, with lots of boring theory. Basically, animation is the illusion of change over time. This is very important, as everything you do in animation will based around time.
In something like a game, you will have a bunch of entities all playing at a different rates of time. One of the challenges is taking the time to devise a solution which allows a entity to play over a period of time while been decoupled from the refresh cycle (ie frame count), unless you have sprite with the correct number of frames to match you refresh cycle, but even then, I'd be concerned, as the system won't be flexible enough to adapt to situations where the OS and hardware can't keep up.
The following is a simple example which takes a sprite sheet (a series of images stored in a single image), the number of expected images/frames and the time to complete a full cycle.
It calculates the individual frame size and returns a frame based on the amount of time that the sprite has been animated...
public class Sprite {
private BufferedImage source;
private int imageCount;
private int imageWidth;
// How long it takes to play a full cycle
private Duration duration;
// When the last cycle was started
private Instant startedAt;
public Sprite(BufferedImage source, int imageCount, int cycleTimeInSeconds) throws IOException {
this.source = source;
this.imageCount = imageCount;
imageWidth = source.getWidth() / imageCount;
duration = Duration.ofSeconds(cycleTimeInSeconds);
}
public BufferedImage getFrame() {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration timePlayed = Duration.between(startedAt, Instant.now());
double progress = timePlayed.toMillis() / (double)duration.toMillis();
if (progress > 1.0) {
progress = 1.0;
startedAt = Instant.now();
}
int frame = Math.min((int)(imageCount * progress), imageCount - 1);
return getImageAt(frame);
}
protected BufferedImage getImageAt(int index) {
if (index < 0 || index >= imageCount) {
return null;
}
int xOffset = imageWidth * index;
return source.getSubimage(xOffset, 0, imageWidth, source.getHeight());
}
}
nb: It also needs a means to be reset or stopped, so you can force the sprite back to the start, but I'll leave that to you
Next, we need some way to play the animation
public class TestPane extends JPanel {
private Sprite sprite;
public TestPane(Sprite sprite) {
this.sprite = sprite;
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
BufferedImage img = sprite.getFrame();
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}
There's nothing really special here, it's a simple Swing Timer set to a high resolution (5 milliseconds) which constantly updates the UI, requesting the next frame from the sprite and painting it.
The important part here is the sprite and the refresh cycle are independent. Want the character to walk faster, change the sprite duration, want the character walk slower, changed the sprite duration, the refresh cycle doesn't need be altered (or any other entity)
So, starting with...
Same cycle, first over 1 second, second over 5 seconds
You can also have a look at something like How to create a usable KeyReleased method in java, which demonstrates the use of key bindings and a centralised Set as a "action" repository
I have two classes which I use to paint a JFrame (see below).
I am trying to refresh the content so it gives the impression of the points randomly "moving". (Ie: Repainting fast enough)
Ideally, I would then like to pass in some parameters to specify at which coordinates the points should appear. However, all I get is a static image.
Any advice?
package uk.me.dariosdesk.dirtydemo;
import javax.swing.*;
import java.awt.*;
import java.util.Random;
class DrawPanel extends JPanel {
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
for (int i = 0; i <= 1000; i++) {
Dimension size = getSize();
Insets insets = getInsets();
int w = size.width - insets.left - insets.right;
int h = size.height - insets.top - insets.bottom;
Random r = new Random();
int x = Math.abs(r.nextInt()) % w;
int y = Math.abs(r.nextInt()) % h;
g2d.drawLine(x, y, x, y);
}
g2d.fillRect(200, 250, 200, 250);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
And
package uk.me.dariosdesk.dirtydemo;
import javax.swing.*;
public class PointsExample extends JFrame {
public PointsExample() {
initUI();
}
public final void initUI() {
DrawPanel dpnl = new DrawPanel();
add(dpnl);
setSize(500, 500);
setTitle("Points");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
PointsExample ex = new PointsExample();
ex.setVisible(true);
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ex.repaint();
}
}
});
}
}
"all I get is a static image" is very light on details. But I think LuxxMiner is right, Thread.Sleep on your Event Dispatch Thread is a bad idea. What's more, the Runnable never exits for 1000 seconds. So you are blocking the EDT for 1000 seconds.
What repaint Component.repaint does (emphasis mine):
If this component is a lightweight component, this method causes a call to this component's paint method as soon as possible. Otherwise, this method causes a call to this component's update method as soon as possible.
This already signals that this method posts a message to the dispatch thread, which you are blocking with Thread.Sleep. What you can do instead is use a Swing Timer to ask for a repaint every second:
In general, we recommend using Swing timers rather than general-purpose timers for GUI-related tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task automatically executes on the event-dispatch thread.
I cannot figure out why I need to call repaint two times as in the below program. I expected that the first repaint will keep the rectangle as it was when I click in another position, and so now there are two rectangles in the window. But it actually removes the first rectangle. Can someone explain this?
class MyPanel extends JPanel {
private int squareX = 50;
private int squareY = 50;
private int squareW = 20;
private int squareH = 20;
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
}
private void moveSquare(int x, int y) {
int OFFSET = 0;
if ((squareX!=x) || (squareY!=y)) {
//repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
repaint(squareX,squareY,squareW,squareH);
squareX=x;
squareY=y;
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
//repaint(squareX,squareY,squareW,squareH);
}
}
public Dimension getPreferredSize() {
return new Dimension(250,200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);
g.setColor(Color.RED);
g.fillRect(squareX,squareY,squareW,squareH);
g.setColor(Color.BLUE);
g.drawRect(squareX,squareY,squareW,squareH);
}
}
It's all about timing. You are scheduling two different repaint requests, one for the area which the rectangle use to occupy and one for the area that the rectangle now occupies.
The repaint requests are pushed to the Event Queue (via the RepaintManager), which means that they occur some time in the future and are not immediately handled (within the method that is calling them).
This ensures that you are "erasing" the area where the rectangle "use" to be before you paint the area where the rectangle now is.
By using repaint(int, int, int, int) you reduce the amount of area which needs to be painted and can make the paint process more efficient.
Take a look at Painting in AWT and Swing for more details
I'm trying to understand how to implement collision detection between multiple object.
My project detects the collision between the objects but it crashes immediately after.
This is my main class with the JFrame and Main Loop:
public class Window {
public static void main(String[] args){
GamePanel gamepanel = new GamePanel();
JFrame f = new JFrame("Multiple Collision Detection");
f.setSize(400, 400);
f.add(gamepanel);
f.setVisible(true);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
while(true){
gamepanel.repaint();
gamepanel.update();
try{
Thread.sleep(10);
}catch(Exception e){
System.out.println("Main Loop Error");
}
}
}
}
Then i have two classes, one for the player and one for the enemy:
PLAYER:
public class Player {
int x = 175, y = 175, w = 50, h = 50, dx = 0, dy = 0;
Rectangle rect;
public void paint(Graphics g) {
rect = new Rectangle(x, y, w, h);
g.setColor(Color.black);
g.fillRect(rect.x, rect.y, rect.width, rect.height);
g.setColor(Color.CYAN);
g.drawRect(x, y, w, h);
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
public void move() {
x += dx;
y += dy;
}
public void update() {
move();
}
}
ENEMY:
public class Enemy {
int x, y, w = 35, h = 35;
Rectangle rect;
public Enemy(int x, int y) {
this.x = x;
this.y = y;
}
public void paint(Graphics g) {
rect = new Rectangle(x, y, w, h);
g.setColor(Color.red);
g.fillRect(rect.x, rect.y, rect.width, rect.height);
}
public void update() {
}
}
I've the EnemyManager class that draws more than one enemy (in this case 3) through a List:
public class EnemyManager {
Player player = new Player();
Rectangle playerrect;
Rectangle enemyrect;
List<Enemy> enemies = new ArrayList<Enemy>();
public void paint(Graphics g) {
enemies.add(new Enemy(20, 20));
enemies.add(new Enemy(320, 20));
enemies.add(new Enemy(20, 320));
for (Enemy e : enemies) {
e.paint(g);
}
}
public void update() {
}
}
Finally i've the GamePanel class that draws the Graphics from the other classes (Player and EnemyManager):
public class GamePanel extends JPanel implements KeyListener{
Player player = new Player();
EnemyManager enemymanager = new EnemyManager();
public void paint(Graphics g){
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, 400, 400);
player.paint(g);
enemymanager.paint(g);
}
public void checkPlayerEnemyCollision(){
for(Enemy e : enemymanager.enemies){
if(e.rect.intersects(player.rect)){
System.out.println("Collision");
}
}
}
public void update(){
addKeyListener(this);
setFocusable(true);
player.update();
enemymanager.update();
checkPlayerEnemyCollision();
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
player.setDy(-2);
}
if(e.getKeyCode() == KeyEvent.VK_S){
player.setDy(2);
}
if(e.getKeyCode() == KeyEvent.VK_A){
player.setDx(-2);
}
if(e.getKeyCode() == KeyEvent.VK_D){
player.setDx(2);
}
if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
System.exit(0);
}
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
player.setDy(0);
}
if(e.getKeyCode() == KeyEvent.VK_S){
player.setDy(0);
}
if(e.getKeyCode() == KeyEvent.VK_A){
player.setDx(0);
}
if(e.getKeyCode() == KeyEvent.VK_D){
player.setDx(0);
}
}
}
When the game detects a collision between the player and an enemy, it prints "Collision" to the console but after that it crashes with this error:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at GamePanel.checkPlayerEnemyCollision(GamePanel.java:22)
at GamePanel.update(GamePanel.java:34)
at Window.main(Window.java:20)
Anyone knows what is the problem and maybe how to solve it?
Thanks in advance.
paint is called from the event dispatcher thread. You modify the list in this method
event dispatcher thread
-> GamePanel.paint
-> EnemyManager.paint
-> enemies.add
However you use a iterator of the list through the "foreach" loop in GamePanel.checkPlayerEnemyCollision:
for(Enemy e : enemymanager.enemies)
But the iterator of ArrayList fails, if you modify the list after the iterator is created with the exception you got.
Since the list modification and the iterator can are used from different threads they can easyly interfere, e.g.:
main thread creates iterator
main thread reads a few elements
event dispatcher thread calls paint
event dispatcher thread adds Enemy to list
main thread calls tries to get a element from the iterator -> exception
Your design is a bit flawed:
You have little control, when paint is called and as the method name suggests it's used for painting. Don't change the data in this method, just draw it.
Redesign your program keeping that in mind (maybe read a tutorial first, e.g. this one).
You will still need to read the Enemy list from a different thread than the one that modifies the list. Use the get method of the list instead of the iterator and keep in mind, that the list size can change so you will need to synchronize things a bit. This could be done effectively, if the threads "reserve" a specific index range that they are allowed to access.
I tried your program but couldn't get anything to move, using the keys A, W, S, D. However, I do get a concurrent mod exception like you said, right at the point of a for loop.
Try changing all enhanced loops to normal for loops. For example, in the part
for (Enemy e : enemies) {
e.paint(g);
}
change to:
for (int i = 0; i < enemies.size(); i++)
enemies.get(i).paint(g);
After doing this, I didn't get the error any longer.
Enhanced for loops give you error if you do certain things to an array at certain times. But I'm not sure why in this case, because I'm not too familiar with how graphics work behind the scenes in Java.
You're getting the ConcurrentModificationException, which means you're attempting to modify a collection in one thread while iterating over it in another (this can also happen within a single thread if you attempt to modify the collection while iterating, but that's not what's happening here).
This all stems from the fact you're not paying attention to which thread your events are happening in. You've got two active threads, whether you realize it or not.
Thread #1 is the programs Main thread, which is where the app starts from
Thread #2 is the Swing EDT, where all of your user actions initiate from.
It looks like you're trying to update your domain model from the Main thread while iterating over collections from the Swing EDT. You either need to synchronize access or make all your updates perform on the EDT by wrapping it in an invoke later:
SwingUtilities.invokeLater(new Runnable(){
public void run(){
gamepanel.repaint();
gamepanel.update();
}
});
Note that I don't know that simply using the above snippet in a loop+Thread.sleep is the correct action in this case, as I haven't gone through all your logic. At the very least you should read a tutorial on Swing threading.
I am trying to code a simple animation like a moving circle. I have tried using getGraphics() and work with that but it's not dynamic and it's painted for just one time
So please help me and guide me to code a dynamic graphic program.
I mean for example defining a function and every time when it called, it draws a line on a label.
Here is how to make a growing rectangle:
public class MovingRectangle extends JPanel {
private Timer timer = new Timer(500, new ActionListener() {
public void actionPerformed(ActionEvent event) {
rectWidth += 100;
repaint();
}
};
private int rectWidth = 100;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(0, 0, 100. rectWidth);
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
public void reset() {
rectWidth = 100;
repaint();
}
}
you should override the paintComponent(Graphic g).
This method is called every time the repaint() is called, so you should periodic calling that method.
You should also set DoubleBuffering on true: setDoubleBuffered(true)
It will prevent possible flicker of your animation