I want to display many animation at the same time with thread.
But It doesn't work, the second animation start when the first end.
I'm using thread but probably the wrong way because i'm beginner
Here is my code :
public class Board extends JPanel{
Mouse mouse;
ArrayList<Explosion> explosions;
public Board() {
mouse = new Mouse(this);
explosions = new ArrayList();
setDoubleBuffered(true);
this.addMouseListener(mouse);
}
public void addExplosion(Explosion e) {
explosions.add(e);
new Thread(explosions.get(explosions.indexOf(e))).start();
}
public void removeExplosion(Explosion e) {
explosions.remove(e);
}
public void paint(Graphics g) {
super.paint(g);
for(int i=0; i<explosions.size(); i++) {
explosions.get(i).paintComponent(g);
}
Graphics2D g2d = (Graphics2D)g;
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
}
public class Explosion extends JPanel implements Runnable{
private BufferedImage img;
final int width = 320;
final int height = 320;
final int rows = 5;
final int cols = 5;
private int x,y;
private int cursor;
BufferedImage[] sprites = new BufferedImage[rows * cols];
Board board;
public Explosion(Board board,int x, int y) {
this.board = board;
this.x = x;
this.y = y;
try {
try {
this.img = ImageIO.read(new File((this.getClass().getResource("files/explosion2.png")).toURI()));
} catch (URISyntaxException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
cursor = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
sprites[(i * cols) + j] = img.getSubimage(
j * (width/rows),
i * (height/cols),
width/rows,
height/cols
);
}
}
}
public void run() {
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
cursor++;
}
};
Timer timer = new Timer(50, taskPerformer);
while(cursor < ((rows*cols)-1)) {
timer.start();
board.repaint();
}
timer.stop();
board.removeExplosion(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(sprites[cursor], x, y, this);
g.dispose();
}
}
Thanks for helping
Any code that updates the GUI should be run on the thread associated with the GUI. To do so from a different thread, like in your case, you will need to use SwingUtilities.InvokeLater:
SwingUtilities.InvokeLater(new Runnable() {
public void run() {
// code to update the GUI goes here
}
});
Related
I just started learning Java (OOP) and I am trying to develop a simple game (like space invaders). I want to replace the guards (brown rectangles) to an image to make the game more aesthetically pleasing.
I am not sure how to do this as the guards are dependent on a lot of things. I tried using the loadImage() method and others but it did not work.
Relevant codes:
Guard.java
public class Guard {
private List<Square> squares;
public Guard(int x, int y) {
squares=new ArrayList<>();
for(int i=0; i<3; i++) {
for (int j = 0; j < 6; j++) {
squares.add(new Square(x + SQUARE_SIZE * j, y + SQUARE_SIZE * i));
}
}
}
public void collisionWith(MovingObject obj) {
for(Square square : squares) {
if(square.visible && square.intersects(obj.getBoundary())) {
square.setVisible(false);
obj.die();
}
}
}
public void draw(Graphics g) {
for(Square square : squares)
{
if(square.visible) square.draw(g);
}
}
}
Square.java
class Square extends Rectangle {
boolean visible;
Square(int x, int y)
{
super(x, y, SQUARE_SIZE, SQUARE_SIZE);
setVisible(true);
}
void setVisible(boolean visible) {
this.visible = visible;
}
void draw(Graphics g) {
g.setColor(new Color(228, 155, 30));
g.fillRect(x, y, width, height);
}
}
Screenshot for reference: Instead of brown boxes, I want to change it to other things using images
The following mre demonstrates using an image to represent a rectangle. Note that for easier implementation Gurd extends Componenet:
import java.awt.*;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class SwingMain {
SwingMain() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(new Guard(0,0));
frame.pack();
frame.setVisible(true);
}
class Guard extends Component{
private static final int GAP = 3, W = 6, H = 3;
private int squareSize, totalX, totalY;
private final List<Square> squares;
public Guard(int x, int y) {
squares=new ArrayList<>();
totalY = 0;
Square square = null;
for(int i=0; i< H; i++) {
totalX = 0;
for (int j = 0; j < W; j++) {
square = new Square(x + totalX , y + totalY );
squares.add(square);
totalX += square.getWidth() +GAP;
}
totalY += square.getHeight() +GAP;
}
}
#Override
public void paint(Graphics g) {
super.paint(g);
for(Square square : squares) {
square.draw(g);
}
}
#Override
public Dimension getPreferredSize(){
return new Dimension(totalX, totalY);
}
}
class Square{
private static final String BOX =
"https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Orange.png";
private Image image = null;
private final int x,y;
Square(int x, int y) {
this.x=x; this.y = y;
try {
image = new ImageIcon(new URL(BOX)).getImage();
} catch (IOException ex) {
ex.printStackTrace();
}
}
void draw(Graphics g) {
g.drawImage(image, x,y, null);
}
int getWidth(){
return image.getWidth(null);
}
int getHeight(){
return image.getHeight(null);
}
}
public static void main(String[] args) {
new SwingMain();
}
}
Square is not efficient because it reads and constructs image over again. To make it more efficient you can introduce a static block to initialize image only once:
static class Square{
private static final String BOX =
"https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Orange.png";
private static Image image = null;
static{
try {
image = new ImageIcon(new URL(BOX)).getImage();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private final int x,y;
Square(int x, int y) {
this.x=x; this.y = y;
}
void draw(Graphics g) {
g.drawImage(image, x,y, null);
}
int getWidth(){
return image.getWidth(null);
}
int getHeight(){
return image.getHeight(null);
}
}
When I use normal JPanel initialized inside main instead of extended class. The button is added to a panel without problem and after launch is displayed in a center of a frame (default layout).
I would like to be able to add buttons inside an extended class. This problem occurs in Screen class aswell, where I need a Play Again button or Next Level button. The Screen is class extended by a JPanel too and JButtons are initialized inside the constructor aswell.
I'm not sure if the wrong part is in way of adding the components or in writing a code for a JPanel.
Here is the code:
Main:
public static void main(String[] args) {
// window - class JFrame
theFrame = new JFrame("Brick Breaker");
// game panel initialization
GamePanel gamePanel = new GamePanel(1);
theFrame.getContentPane().add(gamePanel);
// base settings
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.setLocationRelativeTo(null);
theFrame.setResizable(false);
theFrame.setSize(WIDTH, HEIGHT);
theFrame.setVisible(true);
}
GamePanel Class:
public class GamePanel extends JPanel {
// Fields
boolean running;
boolean clicked = false;
private int rows = 8, colms = 11, N = rows * colms, level; // numbers of rows and columns
private int colmsC, rowsC;
private BufferedImage image;
private Graphics2D g;
private MyMouseMotionListener theMouseListener;
private MouseListener mouseListener;
private int counter = 0;
// entities
Ball theBall;
Paddle thePaddle;
Bricle[] theBricks;
Screen finalScreen;
public GamePanel(int level) {
// buttons
JButton pause = new JButton(" P ");
add(pause);
this.level = level;
init(level);
}
public void init(int level) {
// level logic
this.rowsC = level + rows;
this.colmsC = level + colms;
int count = rowsC * colmsC;
thePaddle = new Paddle();
theBall = new Ball();
theBall.setY(thePaddle.YPOS - thePaddle.getHeight() + 2);
theBall.setX(thePaddle.getX() + thePaddle.getWidth() / 2 - 5);
theBricks = new Bricle[count];
theMouseListener = new MyMouseMotionListener();
addMouseMotionListener(theMouseListener);
mouseListener = new MyMouseListener();
addMouseListener(mouseListener);
// make a canvas
image = new BufferedImage(BBMain.WIDTH, BBMain.HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// specific Bricks initialized
int k = 0;
for (int row = 0; row < rowsC; row++) {
for (int col = 0; col < colmsC; col++) {
theBricks[k] = new Bricle(row, col);
k++;
}
}
running = true;
}
public void playGame() {
while (running) {
//update
if (clicked) {
update();
}
// draw
draw();
// display
repaint();
// sleep
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// update loop ( like playGame )
public void update() {
// ball moving
checkCollisions();
theBall.update();
}
public void draw() {
// background
g.setColor(Color.WHITE);
g.fillRect(0,0, BBMain.WIDTH, BBMain.HEIGHT-20);
// the bricks
int k = 0;
for (int row = 0; row < rowsC; row++) {
for (int col = 0; col < colmsC; col++) {
theBricks[k].draw(g, row, col);
k++;
}
}
// the ball and the paddle
theBall.draw(g);
thePaddle.draw(g);
// counter
String countString = new Integer(this.counter).toString();
g.drawString(countString, 20, 20);
// WIN / LOOSE SCREEN
if (this.counter == this.N * 20) {
win();
}
if (theBall.getRect().getY() + theBall.getRect().getHeight() >= BBMain.HEIGHT) {
loose();
}
}
public void paintComponent(Graphics g) {
// retype
Graphics2D g2 = (Graphics2D) g;
// draw image
g2.drawImage(image, 0, 0, BBMain.WIDTH, BBMain.HEIGHT, null);
// dispose
g2.dispose();
}
public void pause() {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"GAME PAUSED");
}
public void win() {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"YOU WIN");
}
public void loose () {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"YOU LOOSE");
}
public void addScore() {
this.counter += 20;
theBall.setDY(theBall.getDY() - 0.001);
}
// Mouse Listeners
private class MyMouseListener implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
clicked = true;
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
private class MyMouseMotionListener implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if (clicked)
thePaddle.mouseMoved(e.getX());
}
}
}
outputImage
Everything works fine but when i scroll while the graphics is being generated, the scrolled portion graphics disappears
MainApp.java
public abstract class MainApp implements ActionListener{
//Just Listing the method that triggers Canvas(extends JPanel and starts painting)
public void startGeneration(){
String rule = (String) cb.getSelectedItem();
Jexception.setVisible(false);
Jexception1.setVisible(false);
if(genNum > 0){
cgs = new CAGenerationSet(genNum, rule);
cAGenList = new ArrayList<>();
cAGenList = cgs.run(cgs);
if(sp!= null) {
frame.remove(sp);
if(canvas!=null) sp.remove(canvas);
}
//initializing canvas and adding it to the JScrollPane
canvas = new Canvas(cAGenList);
sp = new JScrollPane();
splitPane.setDividerLocation(80);
splitPane.setRightComponent(sp);
sp.setVisible(true);
EventQueue.invokeLater(new Runnable() {
public void run() {
sp.setViewportView(canvas);
sp.revalidate();
}
});
}
else {
Jexception.setVisible(false);
Jexception1.setVisible(true);
}
}
}
Canvas extends JPanel. Here I am overriding the paintComponent() and calling the draw method that triggers the SwingWorker to draw the graphics.
Canvas.java
public class Canvas extends JPanel {
ArrayList<CAGeneration> cAGenList;
private int y,x;
private Thread t;
public static SwingWorker<Void, Void> worker;
private int count = 0;
private boolean check = false;
public CACanvas(ArrayList<CAGeneration> cAGenList) {
this.cAGenList = cAGenList;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
revalidate();
if(!check){
drawCA(g, this.cAGenList);
}
}
private void drawCA(Graphics g, ArrayList<CAGeneration> cAGenList ) {
worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Graphics2D g2d = (Graphics2D) g;
check = true;
y= 10;
count = 0;
synchronized (cAGenList) {
for(CAGeneration cg: cAGenList){
count++;
y = y+10;
x = 0;
if(!isCancelled()){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
for(int i=0; i<cg.getcACell().length; i++){
x = x+10;
if (cg.getcACell()[i] == 0) {
paintRect(g2d, x, y, 9, Color.WHITE);
}
if (cg.getcACell()[i] == 1) {
paintRect(g2d, x, y, 9, Color.GRAY);
}
if (cg.getcACell()[i] == 2) {
paintRect(g2d, x, y, 9, Color.BLACK);
}
}
}
}
}
check = false;
return null;
}
};
ExecutorService threadPool = Executors.newSingleThreadExecutor();
threadPool.submit(worker);
}
private void paintRect(Graphics2D g2D, int x, int y, int size, Color color){
if(g2D!=null) g2D = (Graphics2D) getGraphics();
g2D.setColor(color);
g2D.fillRect(x, y, size, size);
}
}
So I'm trying to write a tile-grid based game and came up with a quite unusual solution. I filled a 2D JPanel Array with JLabels with an ImageIcon as tile. Everything works so far but I did not find any way to render this activly.
I've tryied some methods for active rendering I found on the Internet, but they did not work on my idea. Do you have some ideas how to realize this without rewrite everything to Canvas or something similar?
Here's my code:
Window
public class Win extends JFrame {
private static final long serialVersionUID = 1L;
private BufferStrategy bs;
public Win(int x, int y) {
this.setSize(x, y);
this.setVisible(true);
this.setResizable(false);
this.setIgnoreRepaint(true);
this.createBufferStrategy(2);
setBs(getBufferStrategy());
}
public BufferStrategy getBs() {
return bs;
}
public void setBs(BufferStrategy bs) {
this.bs = bs;
}
}
"Draw"
public class Field extends JPanel {
private static final long serialVersionUID = 5257799495742189076L;
private int x = 0;
private int y = 0;
private JPanel backPanel[][] = new JPanel[19][19];
private BufferedImage images[] = new BufferedImage[100];
private JLabel image[][] = new JLabel[19][19];
public Field() {
this.setLayout(new GridLayout(20, 20));
this.setIgnoreRepaint(true);
}
// Creates Panel Grid & Draws floor
public void setPanels() {
for (int h = 0; h < 19; h++) {
for (int w = 0; w < 19; w++) {
backPanel[h][w] = new JPanel();
backPanel[h][w].setLayout(new GridLayout(1, 1));
image[h][w] = new JLabel(new ImageIcon(images[0]));
backPanel[h][w].add(image[h][w]);
this.add(backPanel[h][w]);
}
}
}
// Loads the Textures
public void getTextures() throws IOException {
for (int i = 0; i < 1; i++) {
images[i] = ImageIO.read(new File("texture.png"));
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(images[1], 0, 0, null);
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
}
Game Loop
public class GameLoop implements Runnable {
private boolean runFlag = true;
#Override
public void run() {
Field field = new Field();
Win window = new Win(640, 640);
window.add(field);
try {
field.getTextures();
} catch (IOException e) {
e.printStackTrace();
}
while (runFlag) {
try {
field.setPanels();
window.getBs().show();
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop() {
runFlag = false;
}
}
Some alternatives:
Shuffle the components and do removeAll(), add(), and validate() as shown here.
Shuffle the contents and do setIcon(), as shown here.
In either case,
Use javax.swing.Timer to pace the animation, as shown here and here.
Consider TexturePaint to fill the icons, as shown here.
I want to add 2 buttons to the following code that will let me "FREEZE" and "START" the ball move as it bounces on the window. I've been trying to do this for the last hour but I cant figure this out. I did some work but it mostly crap, if anyone wants please let me know to post it(avoided it in order not to extend my coding). Anyone can help me with this?
Open to any suggestions.
Thanks
import java.awt.*;
import javax.swing.*;
public class BallMoves extends JPanel implements Runnable {
Color color = Color.red;
int dia = 30;
long delay = 40;
private int x = 1;
private int y = 1;
private int dx = 3;
private int dy = 7;
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(x,y,30,30); // adds color to circle
g.setColor(Color.red);
g2.drawOval(x,y,30,30); // draws circle
}
public void run() {
while(isVisible()) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
System.out.println("interrupted");
}
move();
repaint();
}
}
public void move() {
if(x + dx < 0 || x + dia + dx > getWidth()) {
dx *= -1;
color = getColor();
}
if(y + dy < 0 || y + dia + dy > getHeight()) {
dy *= -1;
color = getColor();
}
x += dx;
y += dy;
}
private Color getColor() {
int rval = (int)Math.floor(255);
int gval = (int)Math.floor(0);
int bval = (int)Math.floor(0);
return new Color(rval, gval, bval);
}
private void start() {
while(!isVisible()) {
try {
Thread.sleep(25);
} catch(InterruptedException e) {
System.exit(1);
}
}
Thread thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
public static void main(String[] args) {
BallMoves test = new BallMoves();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(test);
f.setSize(400,400);
f.setLocation(200,200);
f.setVisible(true);
test.start();
}
}
Update version
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class BallMoves extends JPanel implements Runnable {
Color color = Color.red;
int dia = 30;
long delay = 40;
private int x = 1;
private int y = 1;
private int dx = 3;
private int dy = 7;
private boolean isRunning;
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(x,y,30,30); // adds color to circle
g.setColor(Color.red);
g2.drawOval(x,y,30,30); // draws circle
}
public void run() {
while(isVisible()) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
System.out.println("interrupted");
}
move();
repaint();
}
}
public void move() {
if (isRunning) {
if(x + dx < 0 || x + dia + dx > getWidth()) {
dx *= -1;
color = getColor();
}
if(y + dy < 0 || y + dia + dy > getHeight()) {
dy *= -1;
color = getColor();
}
x += dx;
y += dy;
}
}
private Color getColor() {
int rval = (int)Math.floor(255);
int gval = (int)Math.floor(0);
int bval = (int)Math.floor(0);
return new Color(rval, gval, bval);
}
private void start() {
while(!isVisible()) {
try {
Thread.sleep(25);
} catch(InterruptedException e) {
System.exit(1);
}
}
Thread thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
final BallMoves test = new BallMoves();
JFrame f = new JFrame();
JButton start = new JButton("start");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
test.isRunning = true;
}
});
JButton stop = new JButton("stop");
stop.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
test.isRunning = false;
}
});
JPanel panel = new JPanel();
panel.add(start);
panel.add(stop);
f.add(panel, java.awt.BorderLayout.NORTH);
f.getContentPane().add(test);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(new Dimension(400, 400));
f.setLocationRelativeTo(null);
f.setVisible(true);
test.start();
}
});
}
}
Create a flag and switch it on button click.
private volatile boolean isRunning;
public void move() {
if (isRunning) {
// your existing code
...
}
}
on start button click
isRunning = true;
on stop button click
isRunning = false;