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);
}
Related
I'm generating a hexagon grid and am able to do so but when I add a MouseListener to the individual hexagons (when they're created) it's almost as if they're behind something because hovering/clicking on a hexagon will not register or do anything for that matter. I want to be able to eventually interact with the hexagons but can't do that if I can't get this to work.
My main GUI elements:
import java.awt.*;
import javax.swing.*;
public class Game2
{
public Game2(int radius,int num_hexes)
{
if(num_hexes%2==0) throw new AssertionError("Can't generate map with
an even number of hexagons.");
JFrame frame=new JFrame();
JPanel panel=new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.RED));
panel.setLayout(new BoxLayout(panel,1));
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setTitle("HexGame");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
Rectangle r=frame.getBounds();
int screen_height=r.height;
int screen_width=r.width;
Hexes2 hexes2=new Hexes2(num_hexes,radius,screen_width,screen_height);
panel.add(hexes2);
JScrollPane scroll_pane=new JScrollPane(panel);
frame.getContentPane().add(scroll_pane);
panel.setOpaque(false);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args)
{
Runnable r=new Runnable()
{
#Override
public void run()
{
new Game2(100,11);
}
};
SwingUtilities.invokeLater(r);
}
}
My multiple hexagons:
import java.awt.*;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JPanel;
public class Hexes2 extends JPanel
{
private static final long serialVersionUID=1L;
private static List<Polygon> hexagons;
private static int[] rows;
private int radius;
public Hexes2(int num_columns,int radius,int screen_width,int screen_height)
{
super();
this.radius=radius;
hexagons=new LinkedList<Polygon>();
rows=Functions.columns(num_columns);
int x=screen_width/6;
int y=screen_height/2;
double height=radius*Math.sqrt(3);
double range=num_columns-rows[0];
//build by columns, first
for(int j=0;j<num_columns;j++)
{
x+=((3/2)*radius)*1.5015;
if(j<=Math.floor(num_columns/2)) y=(int) (100-(j*(height/2)));
else y=(int) ((100-(height*(range/2))+(num_columns-rows[j])*(height/2)));
for(int i=0;i<rows[j];i++)
{
y+=height;
Hex2 hex=new Hex2(i,radius,x,y);
hexagons.add(hex.getHex());
}
}
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D) g;
setOpaque(false);
for(int i=0;i<hexagons.size();i++)
{
Stroke stroke=new BasicStroke(radius/20, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
g2.setStroke(stroke);
g2.setColor(Color.BLACK);
g2.drawPolygon(hexagons.get(i));
}
}
};
My singular hexagon class:
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JLabel;
public class Hex2 extends JLabel implements MouseListener
{
private static final long serialVersionUID = 1L;
private int ID;
private Polygon hexagon;
public Hex2(int ID,int r,int x,int y)
{
super();
this.ID=ID;
hexagon=generateHex(r,x,y);
addMouseListener(this);
}
public Polygon generateHex(int r, int x, int y)
{
Polygon hexagon=new Polygon();
for(int i=0;i<6;i++)
{
/*int _x=(int) (x + r*Math.cos(Math.PI / 3.0 * i));
int _y=(int) (y + r*Math.sin(Math.PI / 3.0 * i));*/
int _x=(int) (x + r*Math.cos(i*2*Math.PI/6));
int _y=(int) (y + r*Math.sin(i*2*Math.PI/6));
hexagon.addPoint(_x,_y);
}
return hexagon;
}
public int getID()
{
return ID;
}
public Polygon getHex()
{
return hexagon;
}
#Override
public void mouseClicked(MouseEvent arg0) {
System.out.println("Clicked on hexagon "+ID);
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
};
Functions:
import java.awt.Dimension;
public class Functions
{
//takes in the max width, n (# hexagons), of the largest row (in the middle)
public static int[] columns(int n)
{
int[] columns=new int[n];
int deviation=(int) java.lang.Math.floor(n/2);
for(int i=0;i<n;i++)
{
columns[i]=n-(java.lang.Math.abs(i-deviation));
}
return columns;
}
public static Dimension getScreenSize()
{
return java.awt.Toolkit.getDefaultToolkit().getScreenSize();
}
}
I apologize for the long code, just wanted to be thorough. Any help greatly appreciated, thanks in advance.
You're adding your MouseListener to your Hex2 JLabels:
public Hex2(int ID, int r, int x, int y) {
super();
this.ID = ID;
hexagon = generateHex(r, x, y);
addMouseListener(this);
}
These are JLabels that you never add to the GUI, since you create them here in line A:
for (int j = 0; j < num_columns; j++) {
x += ((3 / 2) * radius) * 1.5015;
if (j <= Math.floor(num_columns / 2))
y = (int) (100 - (j * (height / 2)));
else
y = (int) ((100 - (height * (range / 2)) + (num_columns - rows[j]) * (height / 2)));
for (int i = 0; i < rows[j]; i++) {
y += height;
Hex2 hex = new Hex2(i, radius, x, y); // ****** [A] *****
hexagons.add(hex.getHex()); // ****** [B] *****
}
}
But hex never is added to the GUI. Instead something else, returned by getHex() is added, so the MouseListener won't work. A MouseListener needs to be added to a component that is visualized within the GUI for its actions to do anything.
I think that you're using too many components here. Only one component, a JPanel, should do all the drawings and should have the MouseLIstener added to it. Everything else should be logical classes that don't extend Swing component classes.
For example, run the code below. It shows that the hex as a non-component class, one that responds to a MouseListener since the listener is added only to the single drawing JLabel. Polygons are shapes and have a contains(Point p) method that can be used inside of the mouse listener to allow them to "know" when they've been pressed:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.swing.*;
public class HexPanel extends JPanel {
private static final long serialVersionUID = 1L;
private List<Hex2b> hex2bs = new ArrayList<>();
private int radius;
private int[] rows;
public HexPanel(int num_columns, int radius, int screen_width, int screen_height) {
super();
setBackground(Color.WHITE);
this.radius = radius;
hex2bs = new LinkedList<Hex2b>();
rows = Functions.columns(num_columns);
int x = screen_width / 6;
int y = screen_height / 2;
double height = radius * Math.sqrt(3);
double range = num_columns - rows[0];
// build by columns, first
for (int j = 0; j < num_columns; j++) {
x += ((3 / 2) * radius) * 1.5015;
if (j <= Math.floor(num_columns / 2))
y = (int) (100 - (j * (height / 2)));
else
y = (int) ((100 - (height * (range / 2)) + (num_columns - rows[j]) * (height / 2)));
for (int i = 0; i < rows[j]; i++) {
y += height;
Hex2b hex = new Hex2b(i, radius, x, y);
hex2bs.add(hex);
}
}
addMouseListener(new MyMouse());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // smooth graphics
// setOpaque(false); // doesn't belong in here
for (int i = 0; i < hex2bs.size(); i++) {
Stroke stroke = new BasicStroke(radius / 20, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
Hex2b hex2b = hex2bs.get(i);
Color color = hex2b.getColor();
g2.setColor(color);
g2.fill(hex2b.getHex());
g2.setStroke(stroke);
g2.setColor(Color.BLACK);
g2.draw(hex2b.getHex());
}
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
for (Hex2b hex2b : hex2bs) {
if (hex2b.getHex().contains(e.getPoint())) {
hex2b.changeColor();
repaint();
break;
}
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Game2b");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Toolkit toolKit = Toolkit.getDefaultToolkit();
Dimension screen = toolKit.getScreenSize();
int width = screen.width;
int height = screen.height;
frame.getContentPane().add(new HexPanel(11, 100, width, height));
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class Hex2b {
private static final Color INIT_COLOR = Color.white;
private static final Color SELECTED_COLOR = Color.red;
private int ID;
private Polygon hexagon;
private Color color = INIT_COLOR;
public Hex2b(int ID, int r, int x, int y) {
super();
this.ID = ID;
hexagon = generateHex(r, x, y);
}
public Color getColor() {
return color;
}
public void changeColor() {
color = color == INIT_COLOR ? SELECTED_COLOR : INIT_COLOR;
}
public Polygon generateHex(int r, int x, int y) {
Polygon hexagon = new Polygon();
for (int i = 0; i < 6; i++) {
int _x = (int) (x + r * Math.cos(i * 2 * Math.PI / 6));
int _y = (int) (y + r * Math.sin(i * 2 * Math.PI / 6));
hexagon.addPoint(_x, _y);
}
return hexagon;
}
public int getID() {
return ID;
}
public Polygon getHex() {
return hexagon;
}
}
I am trying to make a Mandelbrot program that allows zooming, but the zoom doesn't seem to be working, and i don't see what is wrong with the way i have implemented the zoom.I am using eclipse and the program doesn't return any errors. Here is my code:
import java.awt.Graphics;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class Mandelbrot extends JFrame {
private final int MAX_ITER = 570;
private static double ZOOM = 200;
private BufferedImage I;
private double zx, zy, cX, cY, tmp;
public static boolean zooming = false;
public Mandelbrot()
{
super("MandelbrotSet");
setBounds(100, 100, 800, 600);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
I = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
zx = zy = 0;
cX = (x - 400) / ZOOM;
cY = (y - 300) / ZOOM;
int iter = MAX_ITER;
while (zx * zx + zy * zy < 4 && iter > 0) {
tmp = zx * zx - zy * zy + cX;
zy = 2.0 * zx * zy + cY;
zx = tmp;
iter--;
}
I.setRGB(x, y, iter | (iter << 8));
}
}
setVisible(true);
while(1>0)
{
if(zooming)
{
revalidate();
repaint();
System.out.println("zooming");
zooming = false;
}
} }
#Override
public void paint(Graphics g) {
g.drawImage(I, 0, 0, this);
}
public static void main(String[] args) {
new Mandelbrot().addMouseWheelListener(new MouseWheelListener(){ public void mouseWheelMoved(MouseWheelEvent
e) {
if (e.getWheelRotation() < 0) {
ZOOM=ZOOM+100;
zooming = true;
} else
{
ZOOM=ZOOM-100;
zooming = true;
} } });
} }
Your constructor contains an endless loop. It therefore never returns and your MouseWheelListener is never added to the frame.
You calculate the BufferedImage exactly once (before the endless loop), so even if you would attach the MouseWheelListener before the loop it would have no effect.
I would move the calculation of the picture into its own method, call this method once from the constructor and once from your MouseWheelListener and remove the endless loop from the constructor.
public Mandelbrot()
{
super("MandelbrotSet");
//...
I = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
calculatePicture();
setVisible(true);
}
public void calculatePicture() {
for (int y = 0; y < getHeight(); y++) {
//...
}
repaint();
}
public static void main(String[] args) {
new Mandelbrot().addMouseWheelListener(new MouseWheelListener(){
public void mouseWheelMoved(MouseWheelEvent
e) {
//...
calculatePicture();
}
});
}
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();
}
});
}
}
Right now, I am trying to make a Hex Grid GUI that highlights whatever hex that the mouse is hovering over. However, the JPanel which is supposed to display the hexagons are not showing up.
I seriously have no idea what is happening between repaint() and paintComponents() in this case. I tried to look up solutions, which involve setting the JPanel into a visible hierachy, which I made sure I did, and it still is not working.
Code is slightly long
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
public class HexGridTester implements ActionListener, MouseListener, MouseMotionListener
{
private JFrame frame;
private TestPanel panel;
private double radius;
private double width;
private double side;
private double height;
private Location mouseover;
private class TestPanel extends JPanel
{
#Override
public void paintComponents(Graphics g)
{
System.out.println("CALLED");
super.paintComponents(g);
int[] xpoints = new int[6];
int[] ypoints = new int[6];
for(int c = 0; c < 15; c++)
{
for(int r = 0; r < 21; r++)
{
int dx = c * (int) width;
int dy = r * (int) height;
if(new Location(c, r).equals(mouseover))
{
g.setColor(Color.blue);
}
else
{
g.setColor(Color.black);
}
if(c % 2 != 0)
{
dy += height / 2;
}
for(int i = 0; i < 6; i++)
{
xpoints[i] = (int) ((int) 10 * Math.cos(i * 2 * Math.PI / 6) + dx);
ypoints[i] = (int) ((int) 10 * Math.sin(i * 2 * Math.PI / 6) + dy);
}
Polygon hex = new Polygon (xpoints, ypoints, 6);
g.fillPolygon(hex);
System.out.print("Drawing " + r + "," + c);
}
}
}
}
public HexGridTester(int r)
{
radius = r;
side = radius * 3/2;
width = radius * 2;
height = Math.sqrt(3) * radius;
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(0, 1));
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
panel = new TestPanel();
panel.setLayout(new GridLayout(0,1));
panel.addMouseListener(this);
panel.addMouseMotionListener(this);
frame.add(panel);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
panel.repaint();
}
#Override
public void actionPerformed(ActionEvent ae)
{
}
#Override
public void mouseClicked(MouseEvent me)
{
}
#Override
public void mousePressed(MouseEvent me)
{
}
#Override
public void mouseReleased(MouseEvent me)
{
}
#Override
public void mouseEntered(MouseEvent me)
{
}
#Override
public void mouseExited(MouseEvent me)
{
}
#Override
public void mouseDragged(MouseEvent me)
{
}
#Override
public void mouseMoved(MouseEvent me)
{
//System.out.println(me.getPoint().x + " " + me.getPoint().y + " " + mouseover);
mouseover = convert(me.getPoint().x, me.getPoint().y);
panel.repaint();
}
private Location convert(double x, double y)
{
double xt;
double yt;
double r;
double c;
double rt;
double ct;
double dr;
ct = x / side;
if(ct % 2 == 0)
{
rt = y / height;
}
else
{
rt = (y - ( height) / 2) / (height);
}
xt = x - ct * side;
yt = y - rt * height;
if(yt > (height /2))
{
dr = 1;
}
else
{
dr = 0;
}
if(xt > (radius * Math.abs(.5 - yt/height)))
{
c = ct;
r = rt;
}
else
{
c = ct - 1;
r = rt - c%2 + dr;
}
return new Location((int) c, (int) r);
}
private static void runTestWindow()
{
HexGridTester tester = new HexGridTester(25);
}
public static void main(String[] args)
{
Runnable run = new Runnable()
{
#Override
public void run()
{
runTestWindow();
}
};
javax.swing.SwingUtilities.invokeLater(run);
}
}
#Override
public void paintComponents(Graphics g)
{
System.out.println("CALLED");
super.paintComponents(g);
Should be:
#Override
public void paintComponent(Graphics g)
{
System.out.println("CALLED");
super.paintComponent(g);
Respect the paint chain. Don't override any method but paintComponent(Graphics) for any JComponent.
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.