I'm trying to make a Conway's game of life game in java swing, and I need to operate a game loop in SwingWorker, and end the thread when The user decides to stop the loop from running. However, when I run the program, The UI freezes up, and I am forced to force quit the program. Could someone tell me what I'm doing wrong? Here is my code.
import java.awt.*;
import java.awt.Canvas;
import javax.swing.*;
import java.*;
import java.awt.event.*;
import java.util.*;
import java.awt.image.BufferedImage;
import javax.swing.Timer;
// use swing timer for thread stuff
/**
* Write a description of class GameLife here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class GameLife extends JPanel
{
// instance variables - replace the example below with your own
private int x;
private static final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
private static final int w = (int)screen.getWidth();
private static final int h = (int)screen.getHeight();
private Color co1 = new Color(221,62,76);
private Color co2 = new Color(0,200,235);
private Color co3 = new Color(229,126,215);
private int square = 20;
private boolean flag = false;
private double mousex;
private double mousey;
private ArrayList<ArrayList<Integer>> selected;
private boolean running = false;
private GameWorker game;
/**
* Constructor for objects of class GameLife
*/
public GameLife()
{
game = new GameWorker();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
if(!running)
{
flag = true;
mousex = me.getX();
mousey = me.getY();
repaint();
}
}
});
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
int id = e.getID();
int keyCode = e.getKeyCode();
char space = ' ';
if(id == KeyEvent.KEY_PRESSED)
{
if(keyCode == KeyEvent.VK_SPACE)
{
running = !running;
if(running)
{
game.doInBackground();
}
else
{
game.cancel(true);
}
}
if(keyCode == KeyEvent.VK_Q)
{
square+=5;
repaint();
}
if(keyCode == KeyEvent.VK_E)
{
square-=5;
repaint();
}
}
}
});
setFocusable(true);
selected = new ArrayList<ArrayList<Integer>>();
mousex = 0;
mousey = 0;
}
/**
* An example of a method - replace this comment with your own
*
* #param y a sample parameter for a method
* #return the sum of x and y
*/
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(co1);
g2.setStroke(new BasicStroke(2));
for(int i = 0; i < square;i++)
{
for(int j = 0; j< square; j++)
{
g2.setColor(co1);
g2.drawRect((w/square) * j, (h/square) * i,(w/square),(h/square));
if(!(selected.contains(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square))))))
{
g2.setColor(co2);
g2.fillRect((w/square) * j,(h/square) * i,(w/square),(h/square));
}
else
{
g2.setColor(co3);
g2.fillRect((w/square) * j,(h/square) * i,(w/square),(h/square));
}
}
}
if(flag)
{
draw(g2, mousex, mousey);
flag = false;
}
}
public void draw(Graphics2D g, double x , double y)
{
int mx = (int) x;
int my = (int) y;
GameLife aiden = new GameLife();
for(int i = 0; i < square;i++)
{
for(int j = 0; j< square; j++)
{
if( x > (w/square) * j && x < (w/square) * j + (w/square) && y > (h/square) * i && y < (h/square) * i + (h/square) && !(selected.contains(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square))))))
{
selected.add(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square))));
}
else if( x > (w/square) * j && x < (w/square) * j + (w/square) && y > (h/square) * i && y < (h/square) * i + (h/square) && selected.contains(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square)))))
{
selected.remove(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square))));
}
}
}
repaint();
}
public static void main()
{
JFrame frame = new JFrame("Conley's Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GameLife canvas = new GameLife();
frame.getContentPane().add(canvas);
canvas.setSize(w,h);
frame.pack();
frame.setVisible(true);
}
}
Here is my SwingWorker Class
import java.util.*;
import javax.swing.*;
/**
* Write a description of class GameWorker here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class GameWorker extends SwingWorker<Void,Void>
{
/**
* Constructor for objects of class GameWorker
*/
public GameWorker()
{
}
public Void doInBackground()
{
while(true)
{
System.out.println("hi");
try{
Thread.sleep(100);
}
catch(InterruptedException e)
{
return null;
}
}
}
}
Related
I am trying to adapt a program I have created for bouncing a ball in Java. I am very new so apologies if the solution is obvious. I have used the following code to create an array of bouncing balls, and then subsequently create two threads with the balls bouncing.
I am trying to achieve this without the array. So, that each thread has only 1 ball.
I feel like the answer is staring me in the face but I just cannot solve the issue. Any help would be greatly appreciated.
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AClass implements Runnable {
private JFrame mainFrame;
private DrawPanel drawPanel;
// private java.util.List<Ball> balls;
private int windowWidth = 640;
private int windowHeight = 480;
private String windowLabel = "Multi-threaded ball application";
public void run() {
//balls = new ArrayList<>();
//Scanner sc = new Scanner(System.in);
//System.out.print("Enter the number of balls you would like to create:");
//int n = sc.nextInt();
//sc.close();
/* Generate balls */
//for (int i = 0; i < n; i++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * windowWidth),
(int) Math.floor(Math.random() * windowHeight),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5)
);
// balls.add(ball);
// }
/* Initialize program */
mainFrame = new JFrame();
drawPanel = new DrawPanel();
mainFrame.getContentPane().add(drawPanel);
mainFrame.setTitle(windowLabel);
mainFrame.setSize(windowWidth, windowHeight);
mainFrame.setVisible(true);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
//for (Ball b: balls) {
ball.update();
// }
/* Give Swing 10 milliseconds to see the update! */
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainFrame.repaint();
}
}
public class DrawPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
// for (Ball b: balls) {
ball.draw(graphics);
// }
}
}
class Ball {//ball class
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
public Ball(int posX, int posY, int size, Color color, int vx, int vy) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
}
void update() {
if (posX > mainFrame.getWidth() || posX < 0) {
vx *= -1;
}
if (posY > mainFrame.getHeight() || posY < 0) {
vy *= -1;
}
if (posX > mainFrame.getWidth()) {
posX = mainFrame.getWidth();
}
if (posX < 0) {
posX = 0;
}
if (posY > mainFrame.getHeight()) {
posY = mainFrame.getHeight();
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
}
void draw(Graphics g) {
g.setColor(color);
g.fillOval(posX, posY, size, size);
}
}
public static void main(String[] args) {
AClass ex = new AClass();
Thread t1= new Thread(ex);
Thread t2 = new Thread(ex);
t1.start();
t2.start();
//System.out.println("Hi");
}
}
First, Swing is not thread safe. You should not be updating the UI (or any state the UI relies on) from outside the context of the Event Dispatching Thread.
See Concurrency in Swing for more details.
I don't think your intention is the correct approach (trying to have each ball be it's own Thread), you're going to quickly end up with all sorts of issues trying to do collision detection, as the state of each ball is always changing independent of each other and it won't scale well. The array and a Swing Timer would be a more suitable solution.
This is the probably the closes I can get to what you want, the problem is, in order to paint it, you'd need a reference to the Ball, so I extended Ball from JPanel instead.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new SurfacePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Surface {
public Dimension getSize();
public void repaint();
}
public class SurfacePane extends JPanel implements Surface {
public SurfacePane() {
setLayout(null);
for (int index = 0; index < 10; index++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * 400),
(int) Math.floor(Math.random() * 400),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5),
this
);
add(ball);
ball.start();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public class Ball extends JPanel {
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
private Surface surface;
private Timer timer;
public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
this.surface = surface;
setBackground(color);
setSize(size, size);
setOpaque(false);
}
public void start() {
if (timer != null) {
timer.stop();
}
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
update();
surface.repaint();
}
});
timer.start();
}
public void stop() {
if (timer == null) {
return;
}
timer.stop();
}
protected void update() {
int width = surface.getSize().width;
int height = surface.getSize().height;
if (posX > width || posX < 0) {
vx *= -1;
}
if (posY > height || posY < 0) {
vy *= -1;
}
if (posX > width) {
posX = width;
}
if (posX < 0) {
posX = 0;
}
if (posY > height) {
posY = height;
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
setLocation(posX, posY);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
g.setColor(color);
g.fillOval(0, 0, size, size);
}
}
}
The problem with this approach (and even the Threaded approach) is, it's not going to scale well. For example, during my experimentation, I only got to about 5, 000 balls before I started have responsiveness issues (resizing the window lagged, alot), compared to roughly 20, 000 balls using an ArrayList (and a single Timer) - I'll be honest, the frame rate was terrible, but the UI remained relatively responsive - I could resize the window without lag
Single Timer, array based example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new SurfacePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Surface {
public Dimension getSize();
public void repaint();
}
public class SurfacePane extends JPanel implements Surface {
private List<Ball> balls = new ArrayList<>(32);
public SurfacePane() {
for (int index = 0; index < 20_000; index++) {
Ball ball = new Ball(
/* Random positions from 0 to windowWidth or windowHeight */
(int) Math.floor(Math.random() * 400),
(int) Math.floor(Math.random() * 400),
/* Random size from 10 to 30 */
(int) Math.floor(Math.random() * 20) + 10,
/* Random RGB colors*/
new Color(
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256)),
(int) Math.floor((Math.random() * 256))
),
/* Random velocities from -5 to 5 */
(int) Math.floor((Math.random() * 10) - 5),
(int) Math.floor((Math.random() * 10) - 5),
this
);
balls.add(ball);
}
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.update();
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
}
public class Ball {
private int posX, posY, size;
private Color color;
private int vx = 5;
private int vy = 5;
private Surface surface;
private Timer timer;
public Ball(int posX, int posY, int size, Color color, int vx, int vy, Surface surface) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
this.vx = vx;
this.vy = vy;
this.surface = surface;
}
protected void update() {
int width = surface.getSize().width;
int height = surface.getSize().height;
if (posX > width || posX < 0) {
vx *= -1;
}
if (posY > height || posY < 0) {
vy *= -1;
}
if (posX > width) {
posX = width;
}
if (posX < 0) {
posX = 0;
}
if (posY > height) {
posY = height;
}
if (posY < 0) {
posY = 0;
}
this.posX += vx;
this.posY += vy;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillOval(posX, posY, size, size);
}
}
}
I'm coding a game for my final project in Java, our teacher provided us with a Board class that is a component that allows us to place and remove pegs on a virtual game board instead of having to code one ourselves. I'm trying to add Key Binding to the Board component but the action I want performed on key press is happening when I run the program but It won't run when I type a Key.
The board class already has a method for getting the position clicked on the component and I think this might be interfering with my Code but I'm not sure.
This is my game class where I tried to add keybinding
package rpgGame;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
public class RPGGame
{
public static final GameWorld WORLD_MAP = new GameWorld();
public static Board LOCAL_MAP = new Board(20,50);
public static List<Mobile> allMobs = new ArrayList<Mobile>();
public static final Player PLAYER = new Player();
public static int xIndex = ((GameWorld.WORLD_SIZE-1)/2) - (50/2);
public static int yIndex = ((GameWorld.WORLD_SIZE-1)/2) - (20/2);
public static boolean boardUpdate = true;
public enum Direction {RIGHT,LEFT,UP,DOWN}
private static final String MOVE_PLAYER_UP = "move up";
private static final String MOVE_PLAYER_LEFT = "move left";
private static final String MOVE_PLAYER_RIGHT = "move right";
private static final String MOVE_PLAYER_DOWN = "move down";
public static final Thread SYNC_BOARD = new Thread()
{
public synchronized void run()
{
while (boardUpdate)
{
for (int i = 0; i < 50; i++)
{
for (int j = 0; j < 20; j++)
{
if (WORLD_MAP.isOccupied(i+xIndex, j+yIndex))
{
LOCAL_MAP.putPeg(Color.RED, j, i);
System.out.println("Successfully Updated");
}
else
{
LOCAL_MAP.putPeg(Color.GRAY, j,i);
}
}
}
boardUpdate = false;
}
}
};
public RPGGame()
{
generateMobs(200);
placeMobs();
placePlayer();
SYNC_BOARD.run();
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), MOVE_PLAYER_UP);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), MOVE_PLAYER_UP);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), MOVE_PLAYER_LEFT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), MOVE_PLAYER_LEFT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), MOVE_PLAYER_RIGHT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), MOVE_PLAYER_RIGHT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), MOVE_PLAYER_DOWN);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), MOVE_PLAYER_DOWN);
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_UP, new MoveAction(Direction.UP));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_LEFT, new MoveAction(Direction.LEFT));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_RIGHT, new MoveAction(Direction.RIGHT));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_DOWN, new MoveAction(Direction.DOWN));
}
public static void main(String[] args)
{
new RPGGame();
}
public static void generateMobs(int numOfMobs)
{
for (int i=0; i<numOfMobs; i++)
{
allMobs.add(new Mobile());
}
}
public static void generateMobs()
{
int numOfMobs = (int)(Math.random()*500);
for (int i=0;i<numOfMobs; i++)
{
allMobs.add(new Mobile());
}
}
public static void placeMobs()
{
for (int i=0; i<allMobs.size(); i++)
{
//i is used as a placeholder value for points until I create a random number generator.
WORLD_MAP.placeCharacter(i, i,allMobs.get(i));
allMobs.get(i).setLocation(i, i);
}
}
public static void placePlayer()
{
WORLD_MAP.placeCharacter(249, 249, PLAYER);
PLAYER.setLocation(249, 249);
}
#SuppressWarnings("serial")
public class MoveAction extends AbstractAction
{
Direction direction;
public MoveAction(Direction direction)
{
if (direction.equals(Direction.RIGHT))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x+1, y, x, y);
PLAYER.move(1, 0);
boardUpdate = true;
System.out.println("MOVE RIGHT");
}
if (direction.equals(Direction.LEFT))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x-1, y);
PLAYER.move(-1, 0);
boardUpdate = true;
System.out.println("MOVE LEFT");
}
if (direction.equals(Direction.UP))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x, y+1);
PLAYER.move(0, 1);
boardUpdate = true;
System.out.println("MOVE UP");
}
if (direction.equals(Direction.DOWN))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x, y-1);
PLAYER.move(0, -1);
boardUpdate = true;
System.out.println("MOVE DOWN");
}
}
#Override
public void actionPerformed(ActionEvent e)
{
}
}
}
This is the Board class
package rpgGame;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/** Board GUI for implementation with various games
* Author: Kirill Levin, Troy Vasiga, Chris Ingram
*/
#SuppressWarnings("serial")
public class Board extends JPanel
{
private static final int X_DIM = 60;
private static final int Y_DIM = 60;
private static final int X_OFFSET = 30;
private static final int Y_OFFSET = 30;
private static final double MIN_SCALE = 0.25;
private static final int GAP = 10;
private static final int FONT_SIZE = 16;
// Grid colours
private static final Color GRID_COLOR_A = new Color(153,255,102);
private static final Color GRID_COLOR_B = new Color(136,255,77);
private Color[][] grid;
private Point lastClick; // How the mouse handling thread communicates
// to the board where the last click occurred
private String message = "";
private int numLines = 0;
private double[][] line = new double[4][100]; // maximum number of lines is 100
private int columns, rows;
private int originalWidth;
private int originalHeight;
private double scale;
/** A constructor to build a 2D board.
*/
public Board (int rows, int columns)
{
super( true );
JFrame boardFrame = new JFrame( "Board game" );
this.columns = columns;
this.rows = rows;
originalWidth = 2*X_OFFSET+X_DIM*columns;
originalHeight = 2*Y_OFFSET+Y_DIM*rows+GAP+FONT_SIZE;
this.setPreferredSize( new Dimension( originalWidth, originalHeight ) );
boardFrame.setResizable(true);
this.grid = new Color[columns][rows];
this.addMouseListener(
new MouseInputAdapter()
{
/** A method that is called when the mouse is clicked
*/
public void mouseClicked(MouseEvent e)
{
int x = (int)e.getPoint().getX();
int y = (int)e.getPoint().getY();
// We need to by synchronized to the parent class so we can wake
// up any threads that might be waiting for us
synchronized(Board.this)
{
int curX = (int)Math.round(X_OFFSET*scale);
int curY = (int)Math.round(Y_OFFSET*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*grid.length)*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale);
// Subtract one from high end so clicks on the black edge
// don't yield a row or column outside of board because of
// the way the coordinate is calculated.
if (x >= curX && y >= curY && x < nextX && y < nextY)
{
lastClick = new Point(y,x);
// Notify any threads that would be waiting for a mouse click
Board.this.notifyAll() ;
} /* if */
} /* synchronized */
} /* mouseClicked */
} /* anonymous MouseInputAdapater */
);
boardFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
boardFrame.setContentPane( this );
boardFrame.pack();
boardFrame.setVisible(true);
}
/** A constructor to build a 1D board.
*/
public Board (int cols)
{
this(1, cols);
}
private void paintText(Graphics g)
{
g.setColor( this.getBackground() );
g.setFont(new Font(g.getFont().getFontName(), Font.ITALIC+Font.BOLD, (int)(Math.round(FONT_SIZE*scale))));
int x = (int)Math.round(X_OFFSET*scale);
int y = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale + GAP ) ;
g.fillRect(x,y, this.getSize().width, (int)Math.round(GAP+FONT_SIZE*scale) );
g.setColor( Color.black );
g.drawString(message, x, y + (int)Math.round(FONT_SIZE*scale));
}
private void paintGrid(Graphics g)
{
for (int i = 0; i < this.grid.length; i++)
{
for (int j = 0; j < this.grid[i].length; j++)
{
if ((i%2 == 0 && j%2 != 0) || (i%2 != 0 && j%2 == 0))
g.setColor(GRID_COLOR_A);
else
g.setColor(GRID_COLOR_B);
int curX = (int)Math.round((X_OFFSET+X_DIM*i)*scale);
int curY = (int)Math.round((Y_OFFSET+Y_DIM*j)*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*(i+1))*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*(j+1))*scale);
int deltaX = nextX-curX;
int deltaY = nextY-curY;
g.fillRect( curX, curY, deltaX, deltaY );
Color curColour = this.grid[i][j];
if (curColour != null) // Draw pegs if they exist
{
g.setColor(curColour);
g.fillOval(curX+deltaX/4, curY+deltaY/4, deltaX/2, deltaY/2);
}
}
}
((Graphics2D) g).setStroke( new BasicStroke(0.5f) );
g.setColor(Color.BLACK);
int curX = (int)Math.round(X_OFFSET*scale);
int curY = (int)Math.round(Y_OFFSET*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*grid.length)*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale);
g.drawRect(curX, curY, nextX-curX, nextY-curY);
}
private void drawLine(Graphics g)
{
for (int i =0; i < numLines; i++ )
{
((Graphics2D) g).setStroke( new BasicStroke( 5.0f*(float)scale) );
g.drawLine( (int)Math.round((X_OFFSET+X_DIM/2.0+line[0][i]*X_DIM)*scale),
(int)Math.round((Y_OFFSET+Y_DIM/2.0+line[1][i]*Y_DIM)*scale),
(int)Math.round((X_OFFSET+X_DIM/2.0+line[2][i]*X_DIM)*scale),
(int)Math.round((Y_OFFSET+Y_DIM/2.0+line[3][i]*Y_DIM)*scale) );
}
}
/**
* Convert a String to the corresponding Color defaulting to Black
* with an invald input
*/
/*private Color convertColour( String theColour )
{
for( int i=0; i<COLOUR_NAMES.length; i++ )
{
if( COLOUR_NAMES[i].equalsIgnoreCase( theColour ) )
return COLOURS[i];
}
return DEFAULT_COLOUR;
}*/
/** The method that draws everything
*/
public void paintComponent( Graphics g )
{
this.setScale();
this.paintGrid(g);
this.drawLine(g);
this.paintText(g);
}
public void setScale()
{
double width = (0.0+this.getSize().width) / this.originalWidth;
double height = (0.0+this.getSize().height) / this.originalHeight;
this.scale = Math.max( Math.min(width,height), MIN_SCALE );
}
/** Sets the message to be displayed under the board
*/
public void displayMessage(String theMessage)
{
message = theMessage;
this.repaint();
}
/** This method will save the value of the colour of the peg in a specific
* spot. theColour is restricted to
* "yellow", "blue", "cyan", "green", "pink", "white", "red", "orange"
* Otherwise the colour black will be used.
*/
public void putPeg(Color colour, int row, int col)
{
this.grid[col][row] = colour;
this.repaint();
}
/** Same as putPeg above but for 1D boards
*/
public void putPeg(Color colour, int col)
{
this.putPeg(colour, 0, col );
}
/** Remove a peg from the gameboard.
*/
public void removePeg(int row, int col)
{
this.grid[col][row] = null;
repaint();
}
/** Same as removePeg above but for 1D boards
*/
public void removePeg(int col)
{
this.grid[col][0] = null;
repaint();
}
/** Draws a line on the board using the given co-ordinates as endpoints
*/
public void drawLine(double row1, double col1, double row2, double col2)
{
this.line[0][numLines]=col1;
this.line[1][numLines]=row1;
this.line[2][numLines]=col2;
this.line[3][numLines]=row2;
this.numLines++;
repaint();
}
/** Removes one line from a board given the co-ordinates as endpoints
* If there is no such line, nothing happens
* If multiple lines, all copies are removed
*/
public void removeLine(int row1, int col1, int row2, int col2)
{
int curLine = 0;
while (curLine < this.numLines)
{
// Check for either endpoint being specified first in our line table
if ( (line[0][curLine] == col1 && line[1][curLine] == row1 &&
line[2][curLine] == col2 && line[3][curLine] == row2) ||
(line[2][curLine] == col1 && line[3][curLine] == row1 &&
line[0][curLine] == col2 && line[1][curLine] == row2) )
{
// found a matching line: overwrite with the last one
numLines--;
line[0][curLine] = line[0][numLines];
line[1][curLine] = line[1][numLines];
line[2][curLine] = line[2][numLines];
line[3][curLine] = line[3][numLines];
curLine--; // perhaps the one we copied is also a match
}
curLine++;
}
repaint();
}
/** Waits for user to click somewhere and then returns the click.
*/
public Point getClick()
{
Point returnedClick = null;
synchronized(this) {
lastClick = null;
while (lastClick == null)
{
try {
this.wait();
} catch(Exception e) {
// We'll never call Thread.interrupt(), so just consider
// this an error.
e.printStackTrace();
System.exit(-1) ;
} /* try */
}
int x = (int)Math.floor((lastClick.getY()-X_OFFSET*scale)/X_DIM/scale);
int y = (int)Math.floor((lastClick.getX()-Y_OFFSET*scale)/Y_DIM/scale);
// Put this into a new object to avoid a possible race.
returnedClick = new Point(x,y);
}
return returnedClick;
}
/** Same as getClick above but for 1D boards
*/
public double getPosition()
{
return this.getClick().getY();
}
public int getColumns()
{
return this.columns;
}
public int getRows()
{
return this.rows;
}
}
You're shooting yourself in the foot with that thread code -- you're calling run() not start() on it
SYNC_BOARD.run();
This will run on the Swing event thread and risks completely freezing your GUI.
As a general rule, you should almost never extend Thread but rather implement Runnable, but regardless, don't use that Thread code -- Instead use a Swing Timer since your code has no breaks in it, no Thread.sleeps and it will make your CPU awfully busy, and the Swing Timer will help make sure that your code obeys Swing threading rules.
Also your MoveAction is wrong. Most of that code should go in the actionPerformed method. The constructor should just set the direction field and that's it.
Something like:
#SuppressWarnings("serial")
public class MoveAction extends AbstractAction {
Direction direction;
public MoveAction(Direction direction) {
// this is the only code the constructor should have!
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
// use direction to help make move in here
}
}
Understand that this is likely causing some major problems, since the constructor is called on program creation (hence your key bindings "work" when the program starts), but it's the actionPerformed that actually gets called when the right key is pressed.
When my animation is in progress figures get stuck together. I think it's because if Figure get velocity x=5 y=5 i move them and then check if they hit anything and my figure can be already inside 2nd figure.
I want to check if they hit anything more often but im not sure how to put my methods in actionPerformed method.
Velocity of figures is not constant.
Do you have any ideas, examples or suggestions?
public class PaintFigures extends JPanel implements ActionListener {
static List<Figure> figuresList = new ArrayList<Figure>();
Timer t = new Timer(5, this);
public PaintFigures(List<Figure> figuresList) {
PaintFigures.figuresList = figuresList;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
t.start();
for (Figure figure : figuresList) {
figure.drawItself(g2d);
}
}
#Override
public void actionPerformed(ActionEvent e) {
FiguresUpdate.update(figuresList); // Check if they hit anything (other figure or frame)
FiguresUpdate.move(figuresList); // move them
repaint();
}
}
Runnable Example Here
Class main
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test extends JPanel implements ActionListener {
static private List<Square> figuresList = new ArrayList<Square>();
Timer t = new Timer(5, this);
public static void main(String[] args) {
Square s1 = new Square(40);
Square s2 = new Square(60);
Square s3 = new Square(20);
figuresList.add(s1);
figuresList.add(s3);
figuresList.add(s2);
JFrame frame = new JFrame("Figures Animation");
frame.setSize(700, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Test();
panel.setBackground(Color.GRAY);
frame.getContentPane().add(BorderLayout.CENTER, panel);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
t.start();
for (Square figure : figuresList) {
figure.drawItself(g2d);
}
}
#Override
public void actionPerformed(ActionEvent e) {
Test.update(figuresList); // Check if they bounce
// FiguresUpdate.move(figuresList); // move them
repaint();
}
public static void update(List<Square> list) {
updateFlags(list);
for (int i = 0; i < list.size(); i++) {
list.get(i).setLocationX(
list.get(i).getLocationX() + (list.get(i).getVelocityX()));
list.get(i).setLocationY(
list.get(i).getLocationY() + (list.get(i).getVelocityY()));
if (list.get(i).getLocationX() < 0
|| list.get(i).getLocationX() > 680 - (list.get(i)
.getWidth())) {
WallXBounceDetected(list.get(i));
}
if (list.get(i).getLocationY() < 0
|| list.get(i).getLocationY() > 360 - (list.get(i)
.getHeight())) {
WallYBounceDetected(list.get(i));
}
for (int j = i + 1; j < list.size(); j++) {
if (list.get(i).getBounds().intersects(list.get(j).getBounds())
&& (!list.get(i).getDidHeBounce())
&& (!list.get(j).getDidHeBounce())) {
System.out.println(list.get(i).getClass().getSimpleName());
FigureBounceDetected(list.get(i), list.get(j));
}
}
}
}
public static void updateFlags(List<Square> list) {
for (int i = 0; i < list.size(); i++) {
list.get(i).setDidHeBounce(false);
}
}
public static void WallXBounceDetected(Square f) {
f.setVelocityX(-f.getVelocityX());
}
public static void WallYBounceDetected(Square f) {
f.setVelocityY(-f.getVelocityY());
}
public static void FigureBounceDetected(Square f1, Square f2) {
// Elastic Collision
// Figure 1
double newSpeedF1X = (f1.getVelocityX() * (f1.getMass() - f2.getMass()) + (2 * f2
.getMass() * f2.getVelocityX()))
/ (f1.getMass() + f2.getMass());
double newSpeedF1Y = (f1.getVelocityY() * (f1.getMass() - f2.getMass()) + (2 * f2
.getMass() * f2.getVelocityY()))
/ (f1.getMass() + f2.getMass());
// Figure 2
double newSpeedF2X = (f2.getVelocityX() * (f2.getMass() - f1.getMass()) + (2 * f1
.getMass() * f1.getVelocityX()))
/ (f1.getMass() + f2.getMass());
double newSpeedF2Y = (f2.getVelocityY() * (f2.getMass() - f1.getMass()) + (2 * f1
.getMass() * f1.getVelocityX()))
/ (f1.getMass() + f2.getMass());
f1.setLocationX(f1.getLocationX() + (newSpeedF1X));
f1.setLocationY(f1.getLocationY() + (newSpeedF1Y));
f2.setLocationX(f2.getLocationX() + (newSpeedF2X));
f2.setLocationY(f2.getLocationY() + (newSpeedF2Y));
// new velocity
f1.setVelocityX(newSpeedF1X);
f1.setVelocityY(newSpeedF1Y);
f2.setVelocityX(newSpeedF2X);
f2.setVelocityY(newSpeedF2Y);
// flag true
f1.setDidHeBounce(true);
f2.setDidHeBounce(true);
}
}
Class Square.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.Random;
public class Square {
Rectangle2D.Double square;
private double locationX = 120;
private double locationY = 120;
private double velocityX =1;
private double velocityY =1;
private double width;
private double height = width;
private double mass = width;
private boolean didHeBounce=false;
Color color;
public Square(int width) {
this.width = width;
height = this.width;
mass = height;
Random r = new Random();
if(r.nextInt(2)>0){
velocityX=-1;
} else {
velocityX=1;
}
if(r.nextInt(2)>0){
velocityY=-1;
} else {
velocityY=1;
}
locationX =r.nextInt(540);
locationY= r.nextInt(220);
}
public void drawItself(Graphics g){
Graphics2D g2d = (Graphics2D) g;
square = new Rectangle2D.Double(locationX,locationY,height,width);
g2d.fill(square);
g.setColor(Color.BLUE);
}
public boolean getDidHeBounce() {
return didHeBounce;
}
public void setDidHeBounce(boolean didHeBounce){
this.didHeBounce = didHeBounce;
}
public double getLocationX() {
return locationX;
}
public void setLocationX(double locationX) {
this.locationX = locationX;
}
public double getLocationY() {
return locationY;
}
public void setLocationY(double locationY) {
this.locationY = locationY;
}
public double getVelocityX() {
return velocityX;
}
public void setVelocityX(double velocityX) {
this.velocityX = velocityX;
}
public double getVelocityY() {
return velocityY;
}
public void setVelocityY(double velocityY) {
this.velocityY = velocityY;
}
public double getMass() {
return mass;
}
public double getHeight() {
return height;
}
public double getWidth() {
return width;
}
public Rectangle2D getBounds() {
return square.getBounds2D();
}
}
Hello first of all you moved them here:
f1.setLocationX(f1.getLocationX() + (newSpeedF1X));
f1.setLocationY(f1.getLocationY() + (newSpeedF1Y));
f2.setLocationX(f2.getLocationX() + (newSpeedF2X));
f2.setLocationY(f2.getLocationY() + (newSpeedF2Y));
So it's not frequency problem.
Problem has to be in formula and it is.
You got:
//figure 1
newSpeedF1X = velocityXF1*(MassF1-MassF2)+(2*MassF2*VelocityXF2)/(MassF1+MassF2)
newSpeedF1Y = velocityYF1*(MassF1-MassF2)+(2*MassF2*VelocityXF2)/(MassF1+MassF2)
// Figure 2
newSpeedF2X = velocityXF2*(MassF2-MassF1)+(2*MassF1*VelocityXF1)/(MassF1+MassF2)
newSpeedF2Y = velocityYF2*(MassF2-MassF1)+(2*MassF1*VelocityXF1)/(MassF1+MassF2)
And in newSpeedF2Y should be VelocityYF1 not X
newSpeedF2Y = velocityYF2*(MassF2-MassF1)+(2*MassF1*VelocityYF1)/(MassF1+MassF2)
Additional remarks
Wall bounce detect:
I noticed your figures getting stuck in walls and you change their velocity to -velocity when they are out of bunds so they cant get out.
To avoid figures getting stuck in wall you should do something like this:
public void wallXBounceDetect(Figure f) {
f.setVelocityX(wallBounceDetect(f.getLocationX(), f.getWidth(), canvas.getWidth(), f.getVelocityX()));
}
public void wallYBounceDetect(Figure f) {
f.setVelocityY(wallBounceDetect(f.getLocationY(), f.getHeight(), canvas.getHeight(), f.getVelocityY()));
}
public double wallBounceDetect(double location, double size, double maxValue, double velocity) {
if ((location < 0 && velocity < 0) || (location + size > maxValue && velocity > 0)) {
return -velocity;
}
return velocity;
}
Where canvas is your class with method PaintComponent which extends JPanel.
It has to do with the laws you are applying. If the bouncing is not long enough they will probably stick forever. A simple rule: if the two figures collide and figure 1 is lower than figure 2 (f1.xf2.x) f1 is bounced a bit back otherwise (f1.x>f2.x) it's bounced a bit forward. It seems to work for me right now. You need to check the laws and what values they give (newSpeedF1X etc)
public static void FigureBounceDetected(Square f1, Square f2) {
// Elastic Collision
// Figure 1
double newSpeedF1X = (f1.getVelocityX() * (f1.getMass() - f2.getMass()) + (2 * f2
.getMass() * f2.getVelocityX()))
/ (f1.getMass() + f2.getMass());
double newSpeedF1Y = (f1.getVelocityY() * (f1.getMass() - f2.getMass()) + (2 * f2
.getMass() * f2.getVelocityY()))
/ (f1.getMass() + f2.getMass());
// Figure 2
double newSpeedF2X = (f2.getVelocityX() * (f2.getMass() - f1.getMass()) + (2 * f1
.getMass() * f1.getVelocityX()))
/ (f1.getMass() + f2.getMass());
double newSpeedF2Y = (f2.getVelocityY() * (f2.getMass() - f1.getMass()) + (2 * f1
.getMass() * f1.getVelocityX()))
/ (f1.getMass() + f2.getMass());
System.out.println("prev "+f1.getprevx()+" "+newSpeedF1X+" "+newSpeedF1Y+" "+newSpeedF2X+" "+newSpeedF2Y);
// f1.setLocationX(f1.getLocationX() + (newSpeedF1X));
// f1.setLocationY(f1.getLocationY() + (newSpeedF1Y));
// f2.setLocationX(f2.getLocationX() + (newSpeedF2X));
// f2.setLocationY(f2.getLocationY() + (newSpeedF2Y));
if(f1.getLocationX()<f2.getLocationX()) f1.setLocationX(Math.max(0, f1.getLocationX()-f1.getWidth()));
else f1.setLocationX(Math.min(700-f1.getWidth(), f1.getLocationX()+f1.getWidth()));
// new velocity
// f1.setVelocityX(newSpeedF1X);
// f1.setVelocityY(newSpeedF1Y);
// f2.setVelocityX(newSpeedF2X);
// f2.setVelocityY(newSpeedF2Y);
// flag true
f1.setDidHeBounce(true);
f2.setDidHeBounce(true);
}
}
Some other changes are minor
for (int j = i + 1; j < list.size(); j++) {
int ij=j%list.size();
if (list.get(i).getBounds().intersects(list.get(j).getBounds())
&& (!list.get(i).getDidHeBounce())
&& (!list.get(ij).getDidHeBounce())
) {
System.out.println(list.get(i).getClass().getSimpleName());
FigureBounceDetected(list.get(i), list.get(ij));
}
public void drawItself(Graphics2D g){
Color c=g.getColor();
Graphics2D g2d = (Graphics2D) g;
square = new Rectangle2D.Double(locationX,locationY,height,width);
g.setColor(Color.BLUE);
g.fill(square);
g.setColor(c);
}
I am trying to learn Java, coming from a C/assembly embedded systems background. After a few weeks of learning, I thought it would be fun to try and make a game, but I am having some problems with a JPanel being repainted at an inconsistent rate.
My "game" GUI consists of a single JFrame which contains a JPanel. As you can see, the main thread for the JFrame sleeps for 30 milliseconds and then updates "game" logic and redraws the JFrame and JPanel. Each time the JPanel is redrawn, I check that it took about 30 milliseconds. As you would expect, it never takes more than about 32 milliseconds between frame redraws.
In spite of the fact that the JPanel is definitely repainted every 30 milliseconds or so, The animation in it can be very jerky. On Ubuntu 14.10, it is extremely obvious, even though the time between calls to my JPanel's repaint() are still never more than 32ms apart.
The most vexing thing is that if I uncomment the lines
//this.resize(300 + a, 300);
//a = (a == 1)?(0):1;
in SimFrame.java, everything is perfectly smooth, which implies that my computer has the capability to update the JPanel at the rate I want.
Is there some way I can force the JFrame to update at the rate I want it to without doing the absurd .resize call?
Main.java
package jdemo;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main
{
public static void main(String[] args)
{
SimFrame s = new SimFrame();
Thread t = new Thread(s);
t.start();
}
}
Sim.java
package jdemo;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.Timer;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class Sim extends JPanel implements KeyListener
{
/**
*
*/
private static final long serialVersionUID = 1L;
private int keys[] = new int[1024];
double x = 100;
double y = 100;
double dx = 0;
double dy = 0;
double theta = 0.0;
private int size = 0;
private int dsize = 1;
public Sim()
{
this.addKeyListener(this);
this.setFocusable(true);
}
long prevmillis;
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D drawing = (Graphics2D)g;
//clear the background.
drawing.setColor(Color.WHITE);
drawing.fillRect(0, 0, this.getWidth() - 1, this.getHeight() - 1);
//System.out.printf("dt = %d\n", System.currentTimeMillis() - prevmillis);
prevmillis = System.currentTimeMillis();
drawing.setColor(Color.BLACK);
drawing.drawRect(0, 0, size, size);
drawing.setColor(Color.BLACK);
int[] xpoints = {(int)(x + 10 * Math.cos(Math.toRadians(theta))),
(int)(x + 5 * Math.cos(Math.toRadians(theta - 150))),
(int)(x + 5 * Math.cos(Math.toRadians(theta + 150)))};
int[] ypoints = {(int)(y + 10 * Math.sin(Math.toRadians(theta))),
(int)(y + 5 * Math.sin(Math.toRadians(theta - 150))),
(int)(y + 5 * Math.sin(Math.toRadians(theta + 150)))};
drawing.drawPolygon(xpoints, ypoints, 3);
}
public void updateLogic()
{
if(keys[KeyEvent.VK_UP] == 1)
{
size++;
}
else if(keys[KeyEvent.VK_DOWN] == 1)
{
size--;
}
//update theta.
if(keys[KeyEvent.VK_LEFT] == 1)
{
theta += 5;
}
if(keys[KeyEvent.VK_RIGHT] == 1)
{
theta -= 5;
}
if(theta > 360.1)
{
theta -= 360;
}
if(theta < -0.1)
{
theta += 360;
}
//update acceleration
if(keys[KeyEvent.VK_SPACE] == 1)
{
dx += 0.08* Math.cos(Math.toRadians(theta));
dy += 0.08 * Math.sin(Math.toRadians(theta));
}
dx *= 0.99;
dy *= 0.99;
//update position
x = x + dx;
y = y + dy;
System.out.printf("%f, %f\n", dx, dy);
//update size
if(size > 150)
{
dsize = -1;
}
if(size < 10)
{
dsize = 1;
}
size += dsize;
}
#Override
public void keyPressed(KeyEvent arg0)
{
// TODO Auto-generated method stub
keys[arg0.getKeyCode()] = 1;
System.out.printf("%d\n", arg0.getKeyCode());
}
#Override
public void keyReleased(KeyEvent arg0)
{
// TODO Auto-generated method stub
keys[arg0.getKeyCode()] = 0;
}
#Override
public void keyTyped(KeyEvent arg0)
{
// TODO Auto-generated method stub
}
}
SimFrame.java
package jdemo;
import jdemo.Sim;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class SimFrame extends JFrame implements Runnable
{
private Sim s;
public SimFrame()
{
this.s = new Sim();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(this.s);
this.pack();
this.setLocationRelativeTo(null);
this.setSize(200, 200);
}
#Override
public void run()
{
int a = 0;
this.setVisible(true);
while(true)
{
//repaint
s.updateLogic();
this.getContentPane().revalidate();
this.repaint();
//this.resize(300 + a, 300);
//a = (a == 1)?(0):1;
try
{
Thread.sleep(30);
}
catch(InterruptedException e)
{
System.out.printf("failed");
break;
}
}
}
}
Thank you very much.
Try this and if it works I'll transform it into a true answer (I just can't know if it will work on your system better than your current code):
public class SimFrame extends JFrame {
public SimFrame() {
setContentPane(new Sim());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private class Sim extends JPanel {
double x = 100;
double y = 100;
double dx = 0;
double dy = 0;
double theta = 0.0;
private int size = 0;
private int dsize = 1;
public Sim() {
addKeyListener(new Controller());
setFocusable(true);
setBackground(Color.WHITE);
new Timer(30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dx *= 0.99;
dy *= 0.99;
// update position
x = x + dx;
y = y + dy;
// update size
if (size > 150)
dsize = -1;
if (size < 10)
dsize = 1;
size += dsize;
// update theta.
if (theta > 360.1) {
theta -= 360;
}
if (theta < -0.1) {
theta += 360;
}
repaint();
}
}).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
};
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D drawing = (Graphics2D) g;
drawing.setColor(Color.BLACK);
drawing.drawRect(0, 0, size, size);
drawing.setColor(Color.BLACK);
int[] xpoints = {(int) (x + 10 * Math.cos(Math.toRadians(theta))), (int) (x + 5 * Math.cos(Math.toRadians(theta - 150))),
(int) (x + 5 * Math.cos(Math.toRadians(theta + 150)))};
int[] ypoints = {(int) (y + 10 * Math.sin(Math.toRadians(theta))), (int) (y + 5 * Math.sin(Math.toRadians(theta - 150))),
(int) (y + 5 * Math.sin(Math.toRadians(theta + 150)))};
drawing.drawPolygon(xpoints, ypoints, 3);
}
private class Controller extends KeyAdapter {
#Override
public void keyPressed(KeyEvent evt) {
switch (evt.getKeyCode()) {
case KeyEvent.VK_UP:
size++;
break;
case KeyEvent.VK_DOWN:
size--;
break;
case KeyEvent.VK_LEFT:
theta += 5;
break;
case KeyEvent.VK_RIGHT:
theta -= 5;
break;
case KeyEvent.VK_SPACE:
dx += 0.08 * Math.cos(Math.toRadians(theta));
dy += 0.08 * Math.sin(Math.toRadians(theta));
break;
}
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SimFrame();
}
});
}
}
And yes, I know there's the OS's delay on holding a key, we'll get to it if it works.
Edit:
Simpler animation:
public class CopyOfSimFrame extends JFrame {
public CopyOfSimFrame() {
setContentPane(new Sim());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private class Sim extends JPanel {
private int size = 0;
private int dsize = 1;
public Sim() {
setBackground(Color.WHITE);
new Timer(30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// update size
if (size >= 150)
dsize = -1;
if (size <= 0)
dsize = 1;
size += dsize;
repaint();
}
}).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(150, 150);
};
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.drawRect(0, 0, size, size);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new CopyOfSimFrame();
}
});
}
}
I recently was coding but encountered an null pointer exception
the stack trace says
Exception in thread "main" java.lang.NullPointerException
at com.masterkgames.twisteddream.level.SpawnLevel.generateLevel(SpawnLevel.java:34)
at com.masterkgames.twisteddream.level.Level.<init>(Level.java:22)
at com.masterkgames.twisteddream.level.SpawnLevel.<init>(SpawnLevel.java:16)
at com.masterkgames.twisteddream.Game.<init>(Game.java:49)
at com.masterkgames.twisteddream.Game.main(Game.java:138)
below are the 3 mentioned classes
Spawnlevel.java:
package com.masterkgames.twisteddream.level;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.masterkgames.twisteddream.level.tile.Tile;
public class SpawnLevel extends Level {
private Tile[] tiles;
private int[] levelPixels;
public SpawnLevel(String path) {
super(path);
}
protected void loadLevel(String path){
try{
BufferedImage image = ImageIO.read(SpawnLevel.class.getResource(path));
int w = image.getWidth();
int h = image.getHeight();
tiles = new Tile[w * h];
levelPixels = new int[w * h];
image.getRGB(0,0,w,h,levelPixels,0,w);
}catch(IOException e){
e.printStackTrace();
}
}
protected void generateLevel(){
for(int i = 0; i < levelPixels.length; i++){
if(levelPixels[i] == 0xff00) tiles[i] = Tile.Grass;
if(levelPixels[i] == 0xffff00) tiles[i] = Tile.Rose;
if(levelPixels[i] == 0x7f7f00) tiles[i] = Tile.Stone;
}
}
}
level.java:
package com.masterkgames.twisteddream.level;
import com.masterkgames.twisteddream.graphics.Screen;
import com.masterkgames.twisteddream.level.tile.Tile;
public class Level {
public Screen screen;
protected int width, height;
protected Tile[] tiles;
protected int[] tilesInt;
public Level(int width, int height) {
this.width = width;
this.height = height;
tilesInt = new int[width * height];
generateLevel();
}
public Level(String path) {
loadLevel(path);
generateLevel();
}
protected void generateLevel() {
}
private void loadLevel(String path) {
}
public void update() {
}
private void time() {
}
public void render(int xScroll, int yScroll, Screen screen) {
screen.setOffset(xScroll, yScroll);
int x0 = xScroll >> 4;
int x1 = (xScroll + screen.width + 16) >> 4;
int y0 = yScroll >> 4;
int y1 = (yScroll + screen.height + 16) >> 4;
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
// getTile(x, y).render(x, y, screen);
if (x + y * 16 < 0 || x + y * 16 >= 256) {
Tile.Void.render(x, y, screen);
continue;
}
tiles[x + y * 16].render(x, y, screen);
}
}
}
public Tile getTile(int x, int y) {
if (x < 0 || y < 0)
return Tile.Void;
if (x >= width || y >= height)
return Tile.Void;
if (tilesInt[x + y * width] == 0)
return Tile.Grass;
if (tilesInt[x + y * width] == 1)
return Tile.Rose;
if (tilesInt[x + y * width] == 2)
return Tile.Stone;
return Tile.Void;
}
}
and lastly
game.java:
package com.masterkgames.twisteddream;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
import com.masterkgames.twisteddream.entity.mob.Player;
import com.masterkgames.twisteddream.graphics.Screen;
import com.masterkgames.twisteddream.input.Keyboard;
import com.masterkgames.twisteddream.level.Level;
import com.masterkgames.twisteddream.level.SpawnLevel;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
public static int width = 300;
public static int height = 168;
public static int scale = 3;
public String Title = "Twisted Dream";
private Thread thread;
private boolean running = false;
private Screen screen;
private Keyboard key;
private Level level;
private Player player;
private JFrame frame;
private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
screen = new Screen(width,height);
key = new Keyboard();
level = new SpawnLevel("/textures/level.png");
player = new Player(key);
frame = new JFrame();
addKeyListener(key);
}
public synchronized void start() {
thread = new Thread(this, "Display");
thread.start();
running = true;
}
public synchronized void stop() {
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
final double ns = 1000000000.0 / 60.0;
double delta = 0;
int frames = 0;
int updates = 0;
requestFocus();
while (running) {
long nowTime = System.nanoTime();
delta += (nowTime - lastTime) / ns;
lastTime = nowTime;
while (delta >= 1){
update();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000){
timer += 1000;
System.out.println(updates + " ups, " + frames + " fps");
frame.setTitle(Title + " | " + updates + " ups, " + frames + " fps");
updates = 0;
frames = 0;
}
}
stop();
}
public void update() {
key.update();
player.update();
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
screen.clear();
int xScroll = player.x - screen.width / 2;
int yScroll = player.y - screen.height / 2;
level.render(xScroll, yScroll, screen);
player.render(screen);
for(int i = 0; i < pixels.length; i++){
pixels[i] = screen.pixels[i];
}
Graphics g = bs.getDrawGraphics();
g.drawImage(image,0,0,getWidth(),getHeight(),null);
g.setFont(new Font("arial", 0, 15));
g.setColor(Color.white);
g.dispose();
bs.show();
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setResizable(false);
game.frame.setTitle(game.Title);
game.frame.add(game);
game.frame.pack();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null);
game.frame.setVisible(true);
game.start();
}
}
p.s this issue according to the stack trace is in the line for(int i = 0; i < levelPixels.length; i++) in SpawnLevel.java
in Level contructor you have generateLevel(); where you are using levelPixels, which is not initialized
contructor call
private void loadLevel(String path) {
}
from class Level, you have to call method loadLevel in SpawnLevel constructor
You are accessing the null array levelPixels.
Here is where your error is:
In your main you call a new SpawnLevel(path)
But SpawnLevel(path) calls the super constructor, which calls its blank yet defined loadLevel and generateLevel. The loadLevel and generateLevel in SpawnLevel are never called, and the member variables are never initialized.
So basically, you tried to use the Level class as an abstract class but you didnt do it correctly. The member variables should be in the Level class if they are to be shared by children of that class. Im guessing this is what you want to do.
you should really get familiar with the debugger, it will be a lifesaver for little problems like this.