Change contents of BufferedImage, then update JFrame to reflect it - java

I am trying to make a Mandelbrot Set renderer with a GUI where you can click and drag to zoom into a specific area. When run, it will do the initial calculations and rendering fine, but when you try to click and drag to zoom in, the console says it is doing the calculations, but the content of the JFrame is not updated.
However, I'm not even positive that it is recalculating, because the initial calculation takes about 8 seconds but when you click/drag to zoom it takes about 6 ms.
I have posted my code below.
Complex Numbers Class
public class Complex {
private double real, imag;
// Constructors
public Complex(){
real=0.0;
imag=0.0;
}
public Complex(double real, double imag) {
this.real=real;
this.imag=imag;
}
// add given complex number to this one, returning the Complex result
public Complex add(Complex other) {
return new Complex(this.real+other.real, this.imag+other.imag);
}
// multiply given complex number by this one, returning the Complex result
public Complex multiply(Complex other) {
return new Complex((this.real*other.real)-(this.imag*other.imag), (this.imag*other.real)+(this.real*other.imag));
}
// get the magnitude of this complex number
public double getMagnitude() {
return Math.sqrt((real*real)+(imag*imag));
}
}
Runnable MandelbrotTask Class
public class MandelbrotTask implements Runnable {
private double x1, y1, x2, y2;
private int startCol, endCol, startRow, endRow, maxIters;
private int[][] iterCounts;
public MandelbrotTask(int maxIters, double x1, double y1, double x2, double y2, int startCol, int endCol, int startRow, int endRow, int[][] iterCounts) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.startCol = startCol;
this.endCol = endCol;
this.startRow = startRow;
this.endRow = endRow;
this.iterCounts = iterCounts;
this.maxIters=maxIters;
}
#Override
public void run() {
for (int i = startRow; i < endRow; i++) {
for (int j = startCol; j < endCol; j++) {
Complex c = getComplex(i, j);
int iterCount = countIters(c);
iterCounts[i][j] = iterCount;
}
}
}
public Complex getComplex(int i, int j){
//output image is 600 X 600 pixels
double incrementX;
double incrementY;
if(x2!=x1){
incrementX=(Math.abs(x2-x1)/600);
}
else{
throw new ArithmeticException("Error: area=0");
}
if(y2!=y1){
incrementY=(Math.abs(y2-y1)/600);
}
else{
throw new ArithmeticException("Error: area=0");
}
return new Complex(x1+((double)i*incrementX), y1+((double)j*incrementY));
}
public int countIters(Complex c){
Complex z=new Complex(0, 0);
int iters=0;
while(z.getMagnitude()<2 && iters<=maxIters){
z=z.multiply(z).add(c);
iters++;
}
return iters;
}
}
Main Mandelbrot Class
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Scanner;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Mandelbrot {
private static final int HEIGHT = 600;
private static final int WIDTH = 600;
private static final int maxIters=50000;
private static Rectangle zoomBox;
private static Point initialClick;
private static JLabel content; //bufferedImage will be put into this JLabel
private static int[][] iterCounts;
private static BufferedImage bufferedImage; //rendering will be written to this bufferedImage
private static JFrame frame;
public static void main(String[] args) throws IOException {
zoomBox=null;
Scanner keyboard = new Scanner(System.in);
double x1 = -2;
double y1 = -2;
double x2 = 2;
double y2 = 2;
/*System.out.print("Max iterations (16,581,375 supported): ");
int maxIters=50000;
if(maxIters>16581375){
throw new UnsupportedOperationException("Error: Max Iterations: Overflow.");
}
System.out.print("Output filename: ");
String fileName = keyboard.next();
if(!fileName.endsWith(".png") && !fileName.endsWith(".PNG")){
fileName=fileName + ".png";
}*/
// TODO: create the rendering, save it to a file
iterCounts=new int[WIDTH][HEIGHT];
recalculate(x1, y1, x2, y2, iterCounts);
bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
MouseAdapter listener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
handleMousePressed(e);
}
#Override
public void mouseDragged(MouseEvent e) {
handleMouseDragged(e);
}
#Override
public void mouseReleased(MouseEvent e) {
handleMouseReleased(e);
}
};
content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, true)));
content.addMouseListener(listener);
content.addMouseMotionListener(listener);
/*OutputStream os = new BufferedOutputStream(new FileOutputStream(fileName));
try {
ImageIO.write(bufferedImage, "PNG", os);
} finally {
os.close();
}*/
frame = new JFrame("Mandelbrot Viewer");
frame.getContentPane().add(content);
frame.pack();
frame.setSize(new Dimension(WIDTH, HEIGHT));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static BufferedImage render(int[][] iterCounts, BufferedImage bufferedImage, Rectangle zoomBox, boolean updated){
bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics g = bufferedImage.getGraphics();
Graphics2D g2=(Graphics2D) g;
if(updated){
for(int i=0; i<WIDTH; i++){
for(int j=0; j<HEIGHT; j++){
if(iterCounts[i][j]<maxIters){
String hexCode= String.format("#%06x", (0xFFFFFF & (32*iterCounts[i][j])));
g.setColor(Color.decode(hexCode));
}
else{
g.setColor(Color.CYAN);
}
g.drawLine(i, j, i, j);
}
}
}
else{
if(zoomBox!=null){
g2.setStroke(new BasicStroke(7));
g2.draw(zoomBox);
}
}
return bufferedImage;
}
public static int[][] recalculate(double x1, double y1, double x2, double y2, int[][] iterCounts){
MandelbrotTask[] tasks=new MandelbrotTask[4];
tasks[0]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 0, HEIGHT/4, iterCounts);
tasks[1]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, HEIGHT/4, 2*(HEIGHT/4), iterCounts);
tasks[2]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 2*(HEIGHT/4), 3*(HEIGHT/4), iterCounts);
tasks[3]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 3*(HEIGHT/4), 4*(HEIGHT/4), iterCounts);
//parallelize computation
Thread[] threads=new Thread[4];
for(int i=0; i<4; i++){
threads[i]=new Thread(tasks[i]);
}
System.out.println("Working...");
//start timer, start computation
long start=System.currentTimeMillis();
for(int i=0; i<4; i++){
threads[i].start();
}
for(int i=0; i<4; i++){
try {
threads[i].join();
} catch (InterruptedException e) {
System.err.println("A thread was interrupted.");
}
}
//end timer
long end=System.currentTimeMillis();
long elapsed=end-start;
System.out.println("Done.");
System.out.println("Took " + elapsed + " ms.");
return iterCounts;
}
protected static void handleMousePressed(MouseEvent e) {
initialClick=e.getPoint();
}
protected static void handleMouseDragged(MouseEvent e) {
if(e.getX()>e.getY()){
zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getX()), (int)(e.getY()-initialClick.getX()));
}
else if(e.getY()>e.getX()){
zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getY()), (int)(e.getY()-initialClick.getY()));
}
else{
zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getX()), (int)(e.getY()-initialClick.getY()));
}
content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false)));
content.repaint();
}
protected static void handleMouseReleased(MouseEvent e) {
recalculate(initialClick.getX(), initialClick.getY(), e.getX(), e.getY(), iterCounts);
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
zoomBox=null;
content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false)));
content.repaint();
}
});
}
}

For one, you're creating a new JLabel with each re-iteration, and this JLabel is being added to nothing.
Instead use the same JLabel but rather create a new ImageIcon and set the viewed JLabel's Icon.
ImageIcon icon = new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false));
content.setIcon(icon);
You also don't seem to be doing anything with the int arrays returned from recalculate.
Shouldn't your handleMouseReleased method have:
iterCounts = recalculate(initialClick.getX(), initialClick.getY(),
e.getX(), e.getY(), iterCounts);
Also, you still have bad threading -- you're calling join on your Threads from within the Swing event thread, something almost sure to freeze your GUI. Use a SwingWorker and then after getting notified from the worker when it's done, use its data. Also you're grossly over using static variables in your GUI. Make your GUI components instance fields, not static fields.
There are more logical errors that I've yet to find I'm afraid...
Second iteration of program -- With the Mandelbrot calculations:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class Mandel2 extends JPanel {
private static final int GUI_HEIGHT = 600;
private static final int GUI_WIDTH = 600;
private static final int MAX_ITERS = 50000;
private BufferedImage image = new BufferedImage(GUI_WIDTH, GUI_HEIGHT,
BufferedImage.TYPE_INT_ARGB);
private Rectangle zoomRect;
private double myX0 = -2.5;
private double myY0 = -2.0;
private double myX1 = 1.5;
private double myY1 = 2.0;
private JDialog waitDialog;
public Mandel2() {
final MyMouse myMouse = new MyMouse();
int delayStartingCalc = 2 * 1000; // 2 second delay
Timer timer = new Timer(delayStartingCalc, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
Rectangle myRect = new Rectangle(0, 0, GUI_WIDTH, GUI_HEIGHT);
createMandel(myRect);
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(GUI_WIDTH, GUI_HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, this);
}
Graphics2D g2 = (Graphics2D) g;
if (zoomRect == null) {
return;
}
g2.setXORMode(Color.gray);
g2.draw(zoomRect);
}
private double screenToLogicalX(double screenX) {
return myX0 + (screenX * (myX1 - myX0)) / GUI_WIDTH;
}
private double screenToLogicalY(double screenY) {
return myY0 + ((GUI_HEIGHT - screenY) * (myY1 - myY0)) / GUI_HEIGHT;
}
private void createMandel(Rectangle myRect) {
double x0 = screenToLogicalX(myRect.x);
double y0 = screenToLogicalY(myRect.y + myRect.height);
double x1 = screenToLogicalX(myRect.x + myRect.width);
double y1 = screenToLogicalY(myRect.y);
myX0 = x0;
myY0 = y0;
myX1 = x1;
myY1 = y1;
MandelWorker mandelWorker = new MandelWorker(MAX_ITERS, x0, y0, x1, y1);
mandelWorker.addPropertyChangeListener(new MandelWorkerListener());
mandelWorker.execute();
if (waitDialog == null) {
Window win = SwingUtilities.getWindowAncestor(Mandel2.this);
JProgressBar jProgressBar = new JProgressBar();
jProgressBar.setIndeterminate(true);
waitDialog = new JDialog(win, "Please Wait", ModalityType.APPLICATION_MODAL);
waitDialog.add(jProgressBar);
waitDialog.pack();
waitDialog.setLocationRelativeTo(win);
}
waitDialog.setVisible(true);
}
private class MyMouse extends MouseAdapter {
private Point p;
#Override
public void mousePressed(MouseEvent e) {
p = e.getPoint();
}
public void mouseDragged(MouseEvent e) {
zoomRect = createRect(e);
repaint();
};
#Override
public void mouseReleased(MouseEvent e) {
zoomRect = createRect(e);
repaint();
createMandel(zoomRect);
}
private Rectangle createRect(MouseEvent e) {
int x = Math.min(p.x, e.getX());
int y = Math.min(p.y, e.getY());
int width = Math.abs(p.x - e.getX());
int height = Math.abs(p.y - e.getY());
return new Rectangle(x, y, width, height);
}
}
private class MandelWorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
waitDialog.setVisible(false);
waitDialog.dispose();
MandelWorker worker = (MandelWorker) evt.getSource();
try {
image = worker.get();
zoomRect = null;
repaint();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
private class MandelWorker extends SwingWorker<BufferedImage, Void> {
private int maxIters;
private double x1;
private double y1;
private double x2;
private double y2;
public MandelWorker(int maxIters, double x1, double y1, double x2, double y2) {
this.maxIters = maxIters;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
#Override
protected BufferedImage doInBackground() throws Exception {
int[][] iterGrid = new int[GUI_HEIGHT][GUI_WIDTH];
for (int i = 0; i < GUI_HEIGHT; i++) {
double y = y1 + i * (y2 - y1) / GUI_HEIGHT;
for (int j = 0; j < GUI_WIDTH; j++) {
double x = x1 + j * (x2 - x1) / GUI_WIDTH;
int iIndex = GUI_HEIGHT - i - 1;
iterGrid[iIndex][j] = calcMandel(x, y);
}
}
return render(iterGrid);
}
private BufferedImage render(int[][] iterGrid) {
int w = GUI_WIDTH;
int h = GUI_HEIGHT;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
if (iterGrid[i][j] < maxIters) {
String hexCode = String.format("#%06x", (0xFFFFFF & (32 * iterGrid[i][j])));
g2.setColor(Color.decode(hexCode));
} else {
g2.setColor(Color.CYAN);
}
g2.drawLine(j, i, j, i);
}
}
g2.dispose();
return img;
}
private int calcMandel(double x, double y) {
Complex c = new Complex(x, y);
Complex z = new Complex();
int iters = 0;
while (z.getMagnitude() < 2 && iters <= maxIters) {
z = z.multiply(z).add(c);
iters++;
}
return iters;
}
}
private class Complex {
private double real, imag;
// Constructors
public Complex() {
real = 0.0;
imag = 0.0;
}
public Complex(double real, double imag) {
this.real = real;
this.imag = imag;
}
// add given complex number to this one, returning the Complex result
public Complex add(Complex other) {
return new Complex(this.real + other.real, this.imag + other.imag);
}
// multiply given complex number by this one, returning the Complex
// result
public Complex multiply(Complex other) {
return new Complex((this.real * other.real) - (this.imag * other.imag),
(this.imag * other.real) + (this.real * other.imag));
}
// get the magnitude of this complex number
public double getMagnitude() {
return Math.sqrt((real * real) + (imag * imag));
}
}
private static void createAndShowGui() {
Mandel2 mainPanel = new Mandel2();
JFrame frame = new JFrame("Mandel2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Second iteration -- it does the calculations, but is not very efficient at it.

Related

How to make Java game more efficient?

I have been working on a flappy bird clone so I can get more practice programming. Everything in the game works, however the game has frame skips and lag drops, and I do not know how to make Java programs run more smoothly. Am I supposed to measure the amount of time a method takes and try to shorten that, or do I do something else? I have seen people explain how to program Java games, but there is hardly anything on improving the performance. Any advice would be helpful. Thank you.
Hazards class
package entity;
import java.util.ArrayList;
public class Hazards {
public ArrayList<Horizontal> hors;
public ArrayList<Vertical> verts;
public Hazards(int width, int height, int thickness) {
hors = new ArrayList<Horizontal>();
hors.add(new Horizontal(0, 0, width, thickness));
hors.add(new Horizontal(0, height-thickness, width, thickness));
verts = new ArrayList<Vertical>();
}
}
Horizontal class
package entity;
import java.awt.Rectangle;
public class Horizontal {
public int xPos, yPos, width, height;
public Rectangle bounds;
public Horizontal(int x, int y, int w, int h) {
this.xPos = x;
this.yPos = y;
this.width = w;
this.height = h;
this.bounds = new Rectangle(x, y, w, h);
}
public void updateBounds(int x, int y, int w, int h) {
this.xPos = x;
this.yPos = y;
this.width = w;
this.height = h;
this.bounds = new Rectangle(x, y, w, h);
}
}
Vertical class
package entity;
import java.awt.Rectangle;
import java.util.Random;
public class Vertical {
public int xPos, width, gapSize, gapPos;
public boolean scoredOn = false;
public Rectangle top, bottom;
public Vertical(int xPos, int width, int roofHeight, int floorHeight, int gapSize) {
this.xPos = xPos;
this.width = width;
this.gapPos = new Random().nextInt(floorHeight - gapSize) + roofHeight;
this.top = new Rectangle();
this.bottom = new Rectangle();
this.top.setBounds(xPos, 0, width, gapPos);
this.bottom.setBounds(xPos, gapPos + gapSize, width, floorHeight - roofHeight + top.height + gapSize);
}
}
Content class
package main;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JPanel;
public class Content extends JPanel {
private static final long serialVersionUID = 1L;
private Engine e;
private Rectangle bounds;
public Content(Engine engine) {
e = engine;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.clearRect(0, 0, this.getWidth(), this.getHeight());
// Roof and floor
g.setColor(Color.black);
for (int x = 0; x < e.e.hors.size(); x++) {
bounds = e.e.hors.get(x).bounds;
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
// Pipes
g.setColor(Color.black);
for(int x = 0; x < e.e.verts.size(); x++) {
bounds = e.e.verts.get(x).top;
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
bounds = e.e.verts.get(x).bottom;
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
// Player
g.setColor(Color.black);
g.fillRect((int) e.p.xPos, (int) e.p.yPos, e.p.size, e.p.size);
// Score
g.setColor(Color.blue);
g.setFont(new Font("Monospaced", Font.PLAIN, 40));
g.drawString(Integer.toString(e.p.score), e.width/2, 80);
}
}
Engine class
package main;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import entity.Hazards;
import entity.Vertical;
import screen.TitleScreen;
public class Engine implements Runnable {
public JFrame f;
public String title = "Flappy Bird";
public int width = 500, height = 500;
public Content c;
public boolean running = false;
public boolean playing = false;
public Thread t;
public Player p;
public Hazards e;
public TitleScreen ts;
public JPanel mainPanel;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Engine e = new Engine();
e.execute();
}
});
}
public void execute() {
ts = new TitleScreen(width, height);
e = new Hazards(width, height, 30);
p = new Player(this);
c = new Content(this);
c.setPreferredSize(new Dimension(width, height));
c.setLayout(null);
c.addKeyListener(p);
mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(width, height));
mainPanel.setLayout(null);
f = new JFrame();
f.setTitle(title);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setResizable(false);
f.add(c);
// f.add(mainPanel);
f.pack();
f.createBufferStrategy(2);
f.setLayout(null);
f.setLocationRelativeTo(null);
f.setVisible(true);
c.requestFocus();
//ts.setScreen(mainPanel);
start();
}
public synchronized void start() {
if (running)
return;
running = true;
t = new Thread(this);
t.start();
}
public synchronized void stop() {
if (!running)
return;
running = false;
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
long lastime = System.nanoTime();
double AmountOfTicks = 60;
double ns = 1000000000 / AmountOfTicks;
double delta = 0;
int tick = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastime) / ns;
lastime = now;
if (delta >= 1) {
// Call all updates here
if (playing) {
p.updatePos();
tick++;
if (tick == 60) {
tick = 0;
p.distance += p.speed;
System.out.println(p.distance);
if ((p.distance % 4) == 0) {
System.out.println("Making new pipes-----------------------------------------------------");
e.verts.add(new Vertical(600, 10, 30, height - 30, 100));
}
}
for (int x = 0; x < e.verts.size(); x++) {
e.verts.get(x).top.x -= p.speed;
e.verts.get(x).bottom.x -= p.speed;
if(e.verts.get(x).top.x<-50) {
e.verts.remove(x);
System.out.println("removed a pipe");
}
if(p.xPos>e.verts.get(x).top.x && !e.verts.get(x).scoredOn) {
e.verts.get(x).scoredOn = true;
p.score++;
}
}
}
mainPanel.revalidate();
mainPanel.repaint();
f.revalidate();
f.repaint();
delta--;
}
}
}
}
Player class
package main;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Player implements KeyListener {
public int size = 10;
public double xPos = 50;
public double yPos = 240;
public double gravity = 3.4;
public double jumpForce = 16.6;
public double weight = 1;
public int speed = 2;
public int score = 0;
public int distance = 0;
public boolean jumping = false;
public double jumpTime = 10;
public int timed = 0;
public Rectangle bounds, temp, top, bottom;
public Engine en;
public Player(Engine engine) {
en = engine;
bounds = new Rectangle((int)xPos, (int)yPos, size, size);
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W && en.playing) {
jumping = true;
} else if (e.getKeyCode() == KeyEvent.VK_SPACE) {
en.playing = !en.playing;
}
if(jumping) {
timed = 0;
jumpForce = 16.6;
}
}
public void keyReleased(KeyEvent e) {
}
public void updatePos() {
// collide with floor or ceiling
for(int x = 0; x < en.e.hors.size(); x++) {
temp = en.e.hors.get(x).bounds;
if(bounds.intersects(temp)) {
en.playing = false;
jumping = false;
timed = 0;
jumpForce = 0;
yPos = 240;
score = 0;
en.e.verts.clear();
distance = 0;
gravity = 3.8;
}
}
// collide with pipe
for(int x =0; x <en.e.verts.size();x++) {
top = en.e.verts.get(x).top;
bottom = en.e.verts.get(x).bottom;
if(bounds.intersects(top)||bounds.intersects(bottom)) {
en.playing = false;
jumping = false;
timed = 0;
jumpForce = 0;
yPos = 240;
score = 0;
gravity =3.4;
en.e.verts.clear();
distance = 0;
}
}
if (jumping && en.playing) {
gravity = 3.4;
yPos -= jumpForce;
jumpForce -= weight;
if (jumpForce == 0) {
jumping = false;
jumpForce = 16.6;
}
}
//if(!jumping && en.playing) {
gravity += 0.1;
//}
System.out.println(gravity);
yPos += gravity;
bounds.setBounds((int)xPos, (int)yPos, size, size);
}
}

How to turn a String into a Algebraic Expression

So I am trying to write a program that gets a function like 3x^2 + x + 8 and then graphs said function. I am using the eval() method to turn a String into a expression but it keeps throwing a NullPointerException.
package Function;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class FunctionGrapherTest
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
FunctionGrapherComponent comp = new FunctionGrapherComponent();
frame.setSize(600, 600);
frame.setTitle("Function");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(comp);
frame.getContentPane().add(comp.control(), BorderLayout.SOUTH);
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
int x = (int)((dimension.getWidth() - frame.getWidth()) / 2);
int y = (int)((dimension.getHeight() - frame.getHeight()) / 2);
frame.setLocation(x, y);
}
}
package Function;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
public class FunctionGrapherComponent extends JPanel
{
private static final long serialVersionUID = 1L;
public FunctionGrapherComponent()
{
JPanel field = new JPanel();
JLabel y = new JLabel("y = ", SwingConstants.RIGHT);
field.add(y);
equaField = new JTextField(15);
field.add(equaField);
control = new JPanel();
control.setLayout(new GridLayout(1, 3));
control.add(field);
JButton draw = makeButton("Graph");
control.add(draw);
count = 0;
}
public JPanel control()
{
return control;
}
public JButton makeButton(String label)
{
JButton button = new JButton(label);
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String equation = equaField.getText();
if(!equation.equals("") || equation != null)
{
equa = equation;
count = 1;
repaint();
}
equaField.setText("");
}
}
ActionListener listener = new ButtonListener();
button.addActionListener(listener);
return button;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Axes axes = new Axes(xPixel(XMIN), xPixel(XMAX), yPixel(YMIN), yPixel(YMAX),
xPixel(0), yPixel(0), sWidth(1), sHeight(1));
axes.drawAxes(g2);
axes.drawTicks(g2);
if(count == 1)
{
Function func = new Function();
delta = (XMAX - XMIN) / 100;
for(double i = XMIN; i <= (XMAX - delta); i = i + delta)
{
x1 = xPixel(i);
y1 = yPixel(func.functionVal(i, equa));
x2 = xPixel(i + delta);
y2 = yPixel(func.functionVal(i + delta, equa));
func.plot(g2, x1, y1, x2, y2);
}
count = 0;
}
}
public double xPixel(double xuser)
{
return (xuser - XMIN) * (getWidth( ) - 1) / (XMAX - XMIN);
}
public double yPixel(double yuser)
{
return (yuser - YMAX) * (getHeight( ) - 1) / (YMIN - YMAX);
}
public double sHeight(double yuser)
{
return yuser * (getHeight() - 1) / (YMAX - YMIN);
}
public double sWidth(double xuser)
{
return xuser * (getWidth() - 1) / (XMAX - XMIN);
}
private static final double XMIN = -100;
private static final double XMAX = 100;
private static final double YMIN = -100;
private static final double YMAX = 100;
private double delta;
private double x1;
private double y1;
private double x2;
private double y2;
private int count;
private JPanel control;
private JTextField equaField;
private String equa;
}
package Function;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class Function
{
public Function()
{
}
public void plot(Graphics2D g2, double x1, double y1, double x2, double y2)
{
Line2D.Double seg = new Line2D.Double(x1, y1, x2, y2);
g2.draw(seg);
}
public double functionVal(double x, String equa)
{
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("Java");
try
{
funcVal = (double)engine.eval(equa);
}
catch (ScriptException e)
{}
return funcVal;
}
private double funcVal;
}
package Function;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
public class Axes
{
public Axes(double xmin, double xmax, double ymin, double ymax,
double xzero, double yzero, double xunit, double yunit)
{
xMin = xmin;
xMax = xmax;
yMin = ymin;
yMax = ymax;
xZero = xzero;
yZero = yzero;
xUnit = xunit;
yUnit = yunit;
}
public void drawAxes(Graphics2D g2)
{
Line2D.Double xAxis = new Line2D.Double(xMin, yZero, xMax, yZero);
Line2D.Double yAxis = new Line2D.Double(xZero, yMin, xZero, yMax);
g2.draw(xAxis);
g2.draw(yAxis);
}
public void drawTicks(Graphics2D g2)
{
for(double i = xZero + xUnit; i <= xMax; i = i + xUnit)
{
Line2D.Double tick = new Line2D.Double(i, yZero + TICK_LENGTH, i, yZero - TICK_LENGTH);
g2.draw(tick);
}
for(double i = xZero - xUnit; i >= xMin; i = i - xUnit)
{
Line2D.Double tick = new Line2D.Double(i, yZero + TICK_LENGTH, i, yZero - TICK_LENGTH);
g2.draw(tick);
}
for(double i = yZero + yUnit; i <= yMin; i = i + yUnit)
{
Line2D.Double tick = new Line2D.Double(xZero + TICK_LENGTH, i, xZero - TICK_LENGTH, i);
g2.draw(tick);
}
for(double i = yZero - yUnit; i >= yMax; i = i - yUnit)
{
Line2D.Double tick = new Line2D.Double(xZero + TICK_LENGTH, i, xZero - TICK_LENGTH, i);
g2.draw(tick);
}
}
private double xMin;
private double xMax;
private double yMin;
private double yMax;
private double xZero;
private double yZero;
private double xUnit;
private double yUnit;
private static final double TICK_LENGTH = 3;
}
Start by changing ScriptEngine engine = mgr.getEngineByName("Java"); to ScriptEngine engine = mgr.getEngineByName("javascript");, despite what some people think, Java isn't a scripting language and there is a difference between Java and JavaScript
Also, you shouldn't ignoring the exceptions thrown by the engine...
try {
System.out.println(equa);
funcVal = ((Number) engine.eval(equa)).doubleValue();
} catch (ScriptException e) {
e.printStackTrace();
}
You'll also find it faster if you don't re-create the ScriptEngine each time you call functionVal
public static class Function {
private final ScriptEngine engine;
public Function() {
ScriptEngineManager mgr = new ScriptEngineManager();
engine = mgr.getEngineByName("javascript");
}
public void plot(Graphics2D g2, double x1, double y1, double x2, double y2) {
Line2D.Double seg = new Line2D.Double(x1, y1, x2, y2);
g2.draw(seg);
}
public double functionVal(double x, String equa) {
try {
System.out.println(equa);
funcVal = ((Number) engine.eval(equa)).doubleValue();
} catch (ScriptException e) {
}
return funcVal;
}
private double funcVal;
}
Updated
So, assuming you want to solve the equation 3x^2 + x + 8, you need to give x some value...
engine.put("x", 10);
Then you can use 3*2^+x+8 as you equation...
funcVal = ((Number) engine.eval("3*2^+x+8")).doubleValue();
For example...
public double functionVal(double x, String equa) {
try {
System.out.println(equa);
engine.put("x", 10);
funcVal = ((Number) engine.eval(equa)).doubleValue();
System.out.println(equa + " = " + funcVal);
} catch (ScriptException e) {
e.printStackTrace();
}
return funcVal;
}

Java JApplet: Why aren't my components appearing on screen?

So I've got this challenge to make one of those spirograph drawrings in an applet. The problem is, I'm new to Java and have absolutely no idea how to get any components appearing on the screen. I'm hoping someone can just add an extra line of code to fix this, though I'm really grateful for any answers :)
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Dimension;
import java.awt.Toolkit;
public class SpaceCadets3_WB extends JApplet {
int numOfPoints = 1080;
int[] x = new int[numOfPoints];
int[] y = new int[numOfPoints];
int x1, x2, y1, y2, width, height, animationSleep = 0;
int R = 75;
int r = 10;
int O = 75;
JTextField changeNumOfPoints = new JTextField(15);
public SpaceCadets3_WB() {
}
public void start() {
this.setSize(600, 600);
this.setBackground(new Color(100,100,255));
this.getContentPane().setLayout(null);
this.getContentPane().add(changeNumOfPoints);
changeNumOfPoints.setVisible(true);
changeNumOfPoints.setLocation(width - changeNumOfPoints.getSize().width - 25, 25);
}
public void calculatePoints(){
width = SpaceCadets3_WB.this.getWidth();
height = SpaceCadets3_WB.this.getHeight();
for(int t = 0; t<numOfPoints; t++){
x[t] = (int) ((R+r)*Math.cos(t) - (r+O)*Math.cos(((R+r)/r)*t) + width/2);
y[t] = (int) ((R+r)*Math.sin(t) - (r+O)*Math.sin(((R+r)/r)*t) + height/2);
}
}
public void paint(Graphics g){
g.setColor(Color.RED);
calculatePoints();
for(int i = 0; i<numOfPoints;i++){
x1 = x[i];
x2 = x[i+1];
y1 = y[i];
y2 = y[i+1];
g.drawLine(x1, y1, x2, y2);
try {
Thread.sleep(animationSleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Your sleeping the paint method, something you should never do. It's bad to call Thread.sleep(...) on the Swing event thread, but it's a sin to do it in any painting method. So now the applet can't draw. Remember that any painting method must do nothing but painting, and must do it blazingly fast. It shouldn't do any program logic, it shouldn't directly do animation, it should just paint. Use a Swing timer as any similar question will show you how to do.
Also, never draw directly in the applet but instead in the paintComponent method of a JPanel that the applet holds.
So create your JPanel, override its paintComponent method, and draw using fields of the class. Then change the state of those fields in your Timer and call repaint() on the JPanel.
Edit
And yes, you should avoid using null layouts as that's preventing you from easily and adequately adding components to your GUI. Instead in this situation, a FlowLayout, the default layout for JPanel, would work great.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class SpaceCadets extends JApplet {
#Override
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
SpaceCadetsPanel panel = new SpaceCadetsPanel();
getContentPane().add(panel);
setSize(SpaceCadetsPanel.PREF_W, SpaceCadetsPanel.PREF_H);
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
}
#SuppressWarnings("serial")
class SpaceCadetsPanel extends JPanel {
public static final int PREF_W = 600;
public static final int PREF_H = 600;
public final static int TOTAL_POINTS = 1080;
private static final int R = 75;
private static final int R2 = 10;
private static final int O = 75;
private static final Color DRAW_COLOR = Color.RED;
private static final int ANIMATION_DELAY = 20;
private Point[] pts;
private JSpinner pointCountSpinner = new JSpinner(new SpinnerNumberModel(
800, 100, 1080, 1));
private JButton doItButton = new JButton(new DoItBtnAction("Do It!"));
private BufferedImage bufImg = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
private Timer timer;
public int imageIndex;
private CalcWorker calcWorker;
public Graphics2D g2;
public SpaceCadetsPanel() {
System.out.println(pointCountSpinner.getEditor().getClass());
add(pointCountSpinner);
add(doItButton);
setBackground(Color.white);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bufImg != null) {
g.drawImage(bufImg, 0, 0, this);
}
}
class DoItBtnAction extends AbstractAction {
public DoItBtnAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
imageIndex = 0;
if (timer != null && timer.isRunning()) {
timer.stop();
}
if (g2 != null) {
g2.dispose();
}
pts = new Point[0];
bufImg = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
g2 = bufImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(DRAW_COLOR);
int totalPoints = 0;
totalPoints = ((Integer) pointCountSpinner.getValue()).intValue();
timer = new Timer(ANIMATION_DELAY, new TimerListener(totalPoints));
calcWorker = new CalcWorker(totalPoints);
calcWorker.addPropertyChangeListener(new CalcWorkerListener());
calcWorker.execute();
}
}
class CalcWorker extends SwingWorker<Point[], Void> {
private int totalPoints;
public CalcWorker(int totalPoints) {
this.totalPoints = totalPoints;
}
#Override
protected Point[] doInBackground() throws Exception {
Point[] pts2 = new Point[totalPoints];
for (int i = 0; i < pts2.length; i++) {
int x = (int) ((R + R2) * Math.cos(i) - (R2 + O)
* Math.cos(((R + R2) / R2) * i) + PREF_W / 2);
int y = (int) ((R + R2) * Math.sin(i) - (R2 + O)
* Math.sin(((R + R2) / R2) * i) + PREF_H / 2);
pts2[i] = new Point(x, y);
}
return pts2;
}
}
class CalcWorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
try {
pts = calcWorker.get();
timer.start();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
class TimerListener implements ActionListener {
private int totalPoints;
public TimerListener(int totalPoints) {
this.totalPoints = totalPoints;
}
#Override
public void actionPerformed(ActionEvent e) {
int x1 = pts[imageIndex].x;
int y1 = pts[imageIndex].y;
int x2 = pts[imageIndex + 1].x;
int y2 = pts[imageIndex + 1].y;
g2.drawLine(x1, y1, x2, y2);
repaint();
imageIndex++;
if (imageIndex == totalPoints - 1) {
((Timer) e.getSource()).stop();
}
}
}
}
Which displays as,

Make circle and then move circle on mouse dragged event

How to draw circle on mouse dragged event, and then how to move that circle on mouse dragged event in Java?
My code is below.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class r extends JPanel {
public int x1, x2, y1, y2, r, w, h,xDist,yDist;
public static boolean flag = false, pressFlag = false;
public r() {
setBackground(Color.WHITE);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent m) {
// pressFlag = true;
if (r > (int) Math.sqrt(Math.abs(m.getX() - x1) * Math.abs(m.getX() - x1) + Math.abs(m.getY() - y1) * Math.abs(m.getY() - y1))) {
flag = true;
yDist=xDist=x2 = y2 = 0;
} else {
x1 = y1 = 0;
r=x2 = y2 = 0;
x1 = m.getX();
y1 = m.getY();
}
repaint();
}
public void mouseReleased(MouseEvent m) {
w = x2 - x1;
h = y2 - y1;
r = (int) Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
flag = false;
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent m) {
if (flag && (x2!=0 && y2!=0)) {
xDist=(m.getX()-x2);
yDist=(m.getY()-y2);
}
x2 = m.getX();
y2 = m.getY();
repaint();
}
});
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (flag) {
x1=x1+xDist;
y1=y1+yDist;
g.drawOval(x1, y1, w, h);
} else {
g.drawOval(x1, y1, x2 - x1, y2 - y1);
}
}
}
public class q extends JFrame {
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setSize(300, 300);
jFrame.add(new r());
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Something along those lines works nicely for me:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
class DragCircle extends JPanel {
private final class MouseDrag extends MouseAdapter {
private boolean dragging = false;
private Point last;
#Override
public void mousePressed(MouseEvent m) {
last = m.getPoint();
dragging = isInsideEllipse(last);
if (!dragging) {
x = last.x;
y = last.y;
width = 0;
height = 0;
}
repaint();
}
#Override
public void mouseReleased(MouseEvent m) {
last = null;
dragging = false;
repaint();
}
#Override
public void mouseDragged(MouseEvent m) {
int dx = m.getX() - last.x;
int dy = m.getY() - last.y;
if (dragging) {
x += dx;
y += dy;
} else {
width += dx;
height += dy;
}
last = m.getPoint();
repaint();
}
}
private int x;
private int y;
private int width;
private int height;
private MouseDrag mouseDrag;
public DragCircle() {
setBackground(Color.WHITE);
mouseDrag = new MouseDrag();
addMouseListener(mouseDrag);
addMouseMotionListener(mouseDrag);
}
public boolean isInsideEllipse(Point point) {
return new Ellipse2D.Float(x, y, width, height).contains(point);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(x, y, width, height);
}
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setSize(300, 300);
jFrame.add(new DragCircle());
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

How to make an image move while listening to a keypress in Java.

I'm starting to learn java programming and I think it's cool to learn java through game development. I know how to draw image and listen to a keypress then move that image. But is it possible to make the image move back and forth to the window while the window is listening to a keypress? Like for example, while the image or object(like spaceship) is moving left to right in the window, then if I press space key, a laser will fire at the bottom of the screen( cool huh :D ). But basically I just want to know how to make the image move left to right while the window is listening to a keypress.
I'm thinking that I will add a key listener to my window then fire an infinite loop to move the image. Or do I need to learn about threading so that another thread will move the object?
Please advise.
Many thanks.
Yep, a Swing Timer and Key Bindings would work well. Here's another example (mine) :)
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class AnimationWithKeyBinding {
private static void createAndShowUI() {
AnimationPanel panel = new AnimationPanel(); // the drawing JPanel
JFrame frame = new JFrame("Animation With Key Binding");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class AnimationPanel extends JPanel {
public static final int SPRITE_WIDTH = 20;
public static final int PANEL_WIDTH = 400;
public static final int PANEL_HEIGHT = 400;
private static final int MAX_MSTATE = 25;
private static final int SPIN_TIMER_PERIOD = 16;
private static final int SPRITE_STEP = 3;
private int mState = 0;
private int mX = (PANEL_WIDTH - SPRITE_WIDTH) / 2;
private int mY = (PANEL_HEIGHT - SPRITE_WIDTH) / 2;
private int oldMX = mX;
private int oldMY = mY;
private boolean moved = false;
// an array of sprite images that are drawn sequentially
private BufferedImage[] spriteImages = new BufferedImage[MAX_MSTATE];
public AnimationPanel() {
// create and start the main animation timer
new Timer(SPIN_TIMER_PERIOD, new SpinTimerListener()).start();
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setBackground(Color.white);
createSprites(); // create the images
setupKeyBinding();
}
private void setupKeyBinding() {
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inMap = getInputMap(condition);
ActionMap actMap = getActionMap();
// this uses an enum of Direction that holds ints for the arrow keys
for (Direction direction : Direction.values()) {
int key = direction.getKey();
String name = direction.name();
// add the key bindings for arrow key and shift-arrow key
inMap.put(KeyStroke.getKeyStroke(key, 0), name);
inMap.put(KeyStroke.getKeyStroke(key, InputEvent.SHIFT_DOWN_MASK), name);
actMap.put(name, new MyKeyAction(this, direction));
}
}
// create a bunch of buffered images and place into an array,
// to be displayed sequentially
private void createSprites() {
for (int i = 0; i < spriteImages.length; i++) {
spriteImages[i] = new BufferedImage(SPRITE_WIDTH, SPRITE_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = spriteImages[i].createGraphics();
g2.setColor(Color.red);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double theta = i * Math.PI / (2 * spriteImages.length);
double x = SPRITE_WIDTH * Math.abs(Math.cos(theta)) / 2.0;
double y = SPRITE_WIDTH * Math.abs(Math.sin(theta)) / 2.0;
int x1 = (int) ((SPRITE_WIDTH / 2.0) - x);
int y1 = (int) ((SPRITE_WIDTH / 2.0) - y);
int x2 = (int) ((SPRITE_WIDTH / 2.0) + x);
int y2 = (int) ((SPRITE_WIDTH / 2.0) + y);
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(y1, x2, y2, x1);
g2.dispose();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(spriteImages[mState], mX, mY, null);
}
public void incrementX(boolean right) {
oldMX = mX;
if (right) {
mX = Math.min(getWidth() - SPRITE_WIDTH, mX + SPRITE_STEP);
} else {
mX = Math.max(0, mX - SPRITE_STEP);
}
moved = true;
}
public void incrementY(boolean down) {
oldMY = mY;
if (down) {
mY = Math.min(getHeight() - SPRITE_WIDTH, mY + SPRITE_STEP);
} else {
mY = Math.max(0, mY - SPRITE_STEP);
}
moved = true;
}
public void tick() {
mState = (mState + 1) % MAX_MSTATE;
}
private class SpinTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
tick();
int delta = 20;
int width = SPRITE_WIDTH + 2 * delta;
int height = width;
// make sure to erase the old image
if (moved) {
int x = oldMX - delta;
int y = oldMY - delta;
repaint(x, y, width, height);
}
int x = mX - delta;
int y = mY - delta;
// draw the new image
repaint(x, y, width, height);
moved = false;
}
}
}
enum Direction {
UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(KeyEvent.VK_RIGHT);
private int key;
private Direction(int key) {
this.key = key;
}
public int getKey() {
return key;
}
}
// Actions for the key binding
#SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
private AnimationPanel draw;
private Direction direction;
public MyKeyAction(AnimationPanel draw, Direction direction) {
this.draw = draw;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
draw.incrementY(false);
break;
case DOWN:
draw.incrementY(true);
break;
case LEFT:
draw.incrementX(false);
break;
case RIGHT:
draw.incrementX(true);
break;
default:
break;
}
}
}
Here is another example that uses this sprite sheet:
obtained from this site.
Again it's an example of drawing within a JPanel's paintComponent method and using Key Bindings to tell which direction to move.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class Mcve3 extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 640;
private static final int TIMER_DELAY = 50;
private int spriteX = 400;
private int spriteY = 320;
private SpriteDirection spriteDirection = SpriteDirection.RIGHT;
private MySprite sprite = null;
private Timer timer = null;
public Mcve3() {
try {
sprite = new MySprite(spriteDirection, spriteX, spriteY);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
setBackground(Color.WHITE);
setKeyBindings(SpriteDirection.LEFT, KeyEvent.VK_LEFT);
setKeyBindings(SpriteDirection.RIGHT, KeyEvent.VK_RIGHT);
setKeyBindings(SpriteDirection.FORWARD, KeyEvent.VK_DOWN);
setKeyBindings(SpriteDirection.AWAY, KeyEvent.VK_UP);
timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
private void setKeyBindings(SpriteDirection dir, int keyCode) {
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
KeyStroke keyPressed = KeyStroke.getKeyStroke(keyCode, 0, false);
KeyStroke keyReleased = KeyStroke.getKeyStroke(keyCode, 0, true);
inputMap.put(keyPressed, keyPressed.toString());
inputMap.put(keyReleased, keyReleased.toString());
actionMap.put(keyPressed.toString(), new MoveAction(dir, false));
actionMap.put(keyReleased.toString(), new MoveAction(dir, true));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
sprite.draw(g);
}
private class MoveAction extends AbstractAction {
private SpriteDirection dir;
private boolean released;
public MoveAction(SpriteDirection dir, boolean released) {
this.dir = dir;
this.released = released;
}
#Override
public void actionPerformed(ActionEvent e) {
if (released) {
sprite.setMoving(false);
} else {
sprite.setMoving(true);
sprite.setDirection(dir);
}
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (sprite.isMoving()) {
sprite.tick();
}
repaint();
}
}
private static void createAndShowGui() {
Mcve3 mainPanel = new Mcve3();
JFrame frame = new JFrame("MCVE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MySprite {
private static final String SPRITE_SHEET_PATH = "http://"
+ "orig12.deviantart.net/7db3/f/2010/338/3/3/"
+ "animated_sprite_sheet_32x32_by_digibody-d3479l2.gif";
private static final int MAX_MOVING_INDEX = 4;
private static final int DELTA = 4;
private SpriteDirection direction;
private Map<SpriteDirection, Image> standingImgMap = new EnumMap<>(SpriteDirection.class);
private Map<SpriteDirection, List<Image>> movingImgMap = new EnumMap<>(SpriteDirection.class);
private int x;
private int y;
private boolean moving = false;
private int movingIndex = 0;
public MySprite(SpriteDirection direction, int x, int y) throws IOException {
this.direction = direction;
this.x = x;
this.y = y;
createSprites();
}
public void draw(Graphics g) {
Image img = null;
if (!moving) {
img = standingImgMap.get(direction);
} else {
img = movingImgMap.get(direction).get(movingIndex);
}
g.drawImage(img, x, y, null);
}
private void createSprites() throws IOException {
URL spriteSheetUrl = new URL(SPRITE_SHEET_PATH);
BufferedImage img = ImageIO.read(spriteSheetUrl);
// get sub-images (sprites) from the sprite sheet
// magic numbers for getting sprites from sheet, all obtained by trial and error
int x0 = 0;
int y0 = 64;
int rW = 32;
int rH = 32;
for (int row = 0; row < 4; row++) {
SpriteDirection dir = SpriteDirection.values()[row];
List<Image> imgList = new ArrayList<>();
movingImgMap.put(dir, imgList);
int rY = y0 + row * rH;
for (int col = 0; col < 5; col++) {
int rX = x0 + col * rW;
BufferedImage subImg = img.getSubimage(rX, rY, rW, rH);
if (col == 0) {
// first image is standing
standingImgMap.put(dir, subImg);
} else {
// all others are moving
imgList.add(subImg);
}
}
}
}
public SpriteDirection getDirection() {
return direction;
}
public void setDirection(SpriteDirection direction) {
if (this.direction != direction) {
setMoving(false);
}
this.direction = direction;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public boolean isMoving() {
return moving;
}
public void setMoving(boolean moving) {
this.moving = moving;
if (!moving) {
movingIndex = 0;
}
}
public void tick() {
if (moving) {
switch (direction) {
case RIGHT:
x += DELTA;
break;
case LEFT:
x -= DELTA;
break;
case FORWARD:
y += DELTA;
break;
case AWAY:
y -= DELTA;
}
movingIndex++;
movingIndex %= MAX_MOVING_INDEX;
}
}
public int getMovingIndex() {
return movingIndex;
}
public void setMovingIndex(int movingIndex) {
this.movingIndex = movingIndex;
}
}
enum SpriteDirection {
FORWARD, LEFT, AWAY, RIGHT
}
As an alternative to KeyListener, consider using actions and key bindings, discussed here. Derived from this example, the program below moves a line left, down, up or right using either buttons or keys.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* #see https://stackoverflow.com/questions/6991648
* #see https://stackoverflow.com/questions/6887296
* #see https://stackoverflow.com/questions/5797965
*/
public class LinePanel extends JPanel {
private MouseHandler mouseHandler = new MouseHandler();
private Point p1 = new Point(100, 100);
private Point p2 = new Point(540, 380);
private boolean drawing;
public LinePanel() {
this.setPreferredSize(new Dimension(640, 480));
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(8,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
private class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
drawing = true;
p1 = e.getPoint();
p2 = p1;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
drawing = false;
p2 = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (drawing) {
p2 = e.getPoint();
repaint();
}
}
}
private class ControlPanel extends JPanel {
private static final int DELTA = 10;
public ControlPanel() {
this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));
}
private class MoveButton extends JButton {
KeyStroke k;
int dx, dy;
public MoveButton(String name, int code, final int dx, final int dy) {
super(name);
this.k = KeyStroke.getKeyStroke(code, 0);
this.dx = dx;
this.dy = dy;
this.setAction(new AbstractAction(this.getText()) {
#Override
public void actionPerformed(ActionEvent e) {
LinePanel.this.p1.translate(dx, dy);
LinePanel.this.p2.translate(dx, dy);
LinePanel.this.repaint();
}
});
ControlPanel.this.getInputMap(
WHEN_IN_FOCUSED_WINDOW).put(k, k.toString());
ControlPanel.this.getActionMap().put(k.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
MoveButton.this.doClick();
}
});
}
}
}
private void display() {
JFrame f = new JFrame("LinePanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.add(new ControlPanel(), BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new LinePanel().display();
}
});
}
}
But basically I just want to know how to make the image move left to right while the window is listening to a keypress
You can use a Swing Timer to animate an image:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TimerAnimation extends JLabel implements ActionListener
{
int deltaX = 2;
int deltaY = 3;
int directionX = 1;
int directionY = 1;
public TimerAnimation(
int startX, int startY,
int deltaX, int deltaY,
int directionX, int directionY,
int delay)
{
this.deltaX = deltaX;
this.deltaY = deltaY;
this.directionX = directionX;
this.directionY = directionY;
setIcon( new ImageIcon("dukewavered.gif") );
// setIcon( new ImageIcon("copy16.gif") );
setSize( getPreferredSize() );
setLocation(startX, startY);
new javax.swing.Timer(delay, this).start();
}
public void actionPerformed(ActionEvent e)
{
Container parent = getParent();
// Determine next X position
int nextX = getLocation().x + (deltaX * directionX);
if (nextX < 0)
{
nextX = 0;
directionX *= -1;
}
if ( nextX + getSize().width > parent.getSize().width)
{
nextX = parent.getSize().width - getSize().width;
directionX *= -1;
}
// Determine next Y position
int nextY = getLocation().y + (deltaY * directionY);
if (nextY < 0)
{
nextY = 0;
directionY *= -1;
}
if ( nextY + getSize().height > parent.getSize().height)
{
nextY = parent.getSize().height - getSize().height;
directionY *= -1;
}
// Move the label
setLocation(nextX, nextY);
}
public static void main(String[] args)
{
JPanel panel = new JPanel();
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().setLayout(null);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
frame.getContentPane().add( new TimerAnimation(300, 100, 3, 2, -1, 1, 20) );
// frame.getContentPane().add( new TimerAnimation(0, 000, 5, 0, 1, 1, 20) );
frame.getContentPane().add( new TimerAnimation(0, 200, 5, 0, 1, 1, 80) );
frame.setSize(400, 400);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
// frame.getContentPane().add( new TimerAnimation(10, 10, 3, 0, 1, 1, 10) );
}
}
You can add a KeyListener to the panel and it will operate independently of the image animation.

Categories

Resources