I am trying to create a planet (blue circle), and have it move when i update the x-position. Here's the main class.
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
public class Main extends Canvas implements Runnable{
public int width = 1400;
public int height = (width/16)* 9;
Dimension dim = new Dimension(width, height);
JFrame frame;
boolean running;
NewBody earth;
public Main(){
this.setPreferredSize(dim);
this.setBackground(Color.BLACK);
}
public void start(){
running = true;
Thread thread = new Thread(this, "display");
thread.start();
}
public void run(){
long startTime = System.currentTimeMillis();
double conv = Math.pow(10, 3);
while(running){
long now = System.currentTimeMillis();
if((now-startTime)/conv >= 1){
earth.incXPos();
startTime = now;
return;
}
update();
}
}
public void update(){
repaint();
}
public void stop(){
running = false;
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLUE);
g2d.fillOval(earth.xPos,earth.yPos, earth.radius*2, earth.radius*2);
}
public static void main(String args[]){
Main main = new Main();
main.frame = new JFrame();
main.frame.setResizable(false);
main.frame.add(main);
main.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.frame.pack();
main.frame.setVisible(true);
main.earth = new NewBody(0, 0,0, 50);
main.start();
}
}
And here is the NewBody blueprint, from which I am creating "earth"
public class NewBody {
Main main = new Main();
public int xOrigo = 1400/2;
public int yOrigo = 800/2;
public double mass;
public double velocity;
public int xPos;
public int yPos;
public double force;
public double vectorAngle;
public double fx;
public double fy;
public double acceleration;
public int radius;
public NewBody(double mass, int xPos, int yPos, int radius){
this.mass = mass;
this.xPos = xOrigo + xPos - radius;
this.yPos = yOrigo + yPos - radius;
this.radius = radius;
}
public void incXPos(){
this.xPos++;
}
The problem is that when I run the program, the blue circle just stays in the same position, where it was initialized. It just flickers extremely fast, and nothing else happens. I am quite new to coding, and I do not seem to get any error message and therefore I do not know how to proceed. I have been stuck on this for some hours now.
Do you have any ideas?
The return; statement in your run() method causes the method short circuits exit and thus quit immediately after calling incXPos() just once. This occurs even before update() is called and so repaint() is never called.
I'd do things a bit differently though:
I'd draw in a JPanel
I'd draw in its paintComponent method.
I'd use a Swing Timer instead of a Thread to do my animation loop.
I'd be sure to call the super's paintComponent(g) inside of my override.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SimpleAnimation extends JPanel {
private static final int PREF_W = 1400;
private static final int PREF_H = (PREF_W * 9) / 16; // do int mult **first**
private static final int TIMER_DELAY = 13;
private NewBody earth = new NewBody(0, 0, 0, 50);
public SimpleAnimation() {
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// to allow for smooth graphics
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLUE);
g2d.fillOval(earth.xPos, earth.yPos, earth.radius * 2, earth.radius * 2);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
earth.incXPos();
repaint();
}
}
private static void createAndShowGui() {
SimpleAnimation mainPanel = new SimpleAnimation();
JFrame frame = new JFrame("SimpleAnimation");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class NewBody {
// !! Main main = new Main();
public int xOrigo = 1400 / 2;
public int yOrigo = 800 / 2;
public double mass;
public double velocity;
public int xPos;
public int yPos;
public double force;
public double vectorAngle;
public double fx;
public double fy;
public double acceleration;
public int radius;
public NewBody(double mass, int xPos, int yPos, int radius) {
this.mass = mass;
this.xPos = xOrigo + xPos - radius;
this.yPos = yOrigo + yPos - radius;
this.radius = radius;
}
public void incXPos() {
this.xPos++;
}
}
Related
I'm making a simple Java program to bounce a ball up and down. The problem is that the ball bounces up higher than its starting point with each bounce. I expect the ball to bounce back up exactly to the height that it started from.
The ball physics can be found in the circle class in the doPhysics() method where I suspect the problem can be found
import java.awt.*;
import java.util.*;
public class Main{
public static Frame frame = new Frame();
public static Physics physics = new Physics();
public static ArrayList<Circle> circles = new ArrayList<Circle>(); //array for the points
public static void main(String args[]) {
Circle circle = new Circle(100, 300, 50, Color.BLACK);
circles.add(circle);
run();
}
public static void run() {
physics.timer.start();
}
}
import java.awt.*;
public class Circle {
private int x;
private int y;
private double xAccel= 0;
private double yAccel = 0;
private double xVel= 0;
private double yVel = 0;
private Color colour;
private int radius;
public Circle(int x, int y, int radius, Color colour) {
setX(x);
setY(y);
setRadius(radius);
setColour(colour);
}
public void draw(Graphics2D g2d) {
g2d.setColor(colour);
g2d.fillOval(x, y, radius*2, radius*2);
}
public void doPhysics() {
hitGround();
System.out.println(yVel);
yVel += Physics.getGravity();
y -= yVel;
}
public void hitGround() {
if(y + radius*2 > Frame.panel.h ) {
yVel = -yVel;
}
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setColour(Color colour) {
this.colour = colour;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColour() {
return colour;
}
public int getRadius() {
return radius;
}
}
import java.awt.*;
import javax.swing.*;
class Frame extends JFrame {
public static Panel panel;
public Frame() {
panel = new Panel();
this.setTitle("Fun");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
class Panel extends JPanel {
public int w = 500;
public int h = 500;
public Panel() {
this.setPreferredSize(new Dimension(w, h));
this.setBackground(Color.red);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
for(Circle circle : Main.circles) {
circle.draw(g2d);
}
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Physics implements ActionListener {
private static double gravity = -.1;
public Timer timer;
public Physics() {
timer = new Timer(1, this);
}
public static double getGravity() {
return gravity;
}
#Override
public void actionPerformed(ActionEvent e) {
for(Circle circle : Main.circles) {
circle.doPhysics();
}
Main.frame.repaint();
}
}
The problem is mainly caused by using integer values for position (x and y). On each iteration the values are rounded and the errors get accumulated.
Solution: declare double x and double y and only use the rounded integer values for drawing.
Above should reduce the problem, but not completely solve it. The code is doing a rough integration over timeĀ¹ by using the velocity calculated after the time interval (see Numerical Integration). This can be improved by doing an average of the velocities before and after it was changed. Roughly:
double preVel = yVel;
yVel += Physics.getGravity();
y -= (preVel + yVel)/2;
which can be simplified (pure math) to:
yVel += Physics.getGravity();
y -= yVel - Physics.getGravity()/2;
This should work fine since the acceleration is constant. Not the case if the acceleration is also changing. And it is also susceptible to precision errors being accumulated over time.
1 - see Numerical integration and Temporal discretization
I have been working on a simple animation using a Timer on a JComponent. However, I experience incredibly choppy motion when I view the animation. What steps should I take to optimize this code?
MyAnimationFrame
import javax.swing.*;
public class MyAnimationFrame extends JFrame {
public MyAnimationFrame() {
super("My animation frame!");
setSize(300,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new AnimationComponent(0,0,50,50));
setVisible(true);
}
public static void main(String[] args) {
MyAnimationFrame f = new MyAnimationFrame();
}
}
AnimationComponent
import javax.swing.*;
import java.awt.*;
public class AnimationComponent extends JComponent implements ActionListener {
private Timer animTimer;
private int x;
private int y;
private int xVel;
private int yVel;
private int width;
private int height;
private int oldX;
private int oldY;
public AnimationComponent(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
animTimer = new Timer(25, this);
xVel = 5;
yVel = 5;
animTimer.start();
}
#Override
public void paintComponent(Graphics g) {
g.fillOval(x,y,width,height);
}
#Override
public void actionPerformed(ActionEvent e) {
oldX = x;
oldY = y;
if(x + width > getParent().getWidth() || x < 0) {
xVel *= -1;
}
if(y + height > getParent().getHeight() || y < 0) {
yVel *= -1;
}
x += xVel;
y += yVel;
repaint();
}
}
Not sure if this matters, but I am using OpenJDK version 1.8.0_121.
Any help is appreciated.
After a wonderful discussion with Yago it occurred to me that the issue revolves around number of areas, alot comes down to the ability for Java to sync the updates with the OS and the hardware, some things you can control, some you can't.
Inspired by Yago's example and my "memory" of how the Timing Framework works, I tested you code by increasing the framerate (to 5 milliseconds, ~= 200fps) and decreasing the change delta, which gave the same results as using the Timing Framework, but which leaves you with the flexibility of your original design.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.add(new AnimationComponent(0, 0, 50, 50));
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationComponent extends JComponent implements ActionListener {
private Timer animTimer;
private int x;
private int y;
private int xVel;
private int yVel;
private int width;
private int height;
private int oldX;
private int oldY;
public AnimationComponent(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
animTimer = new Timer(5, this);
xVel = 1;
yVel = 1;
animTimer.start();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
);
g2d.setRenderingHints(hints);
g2d.fillOval(x, y, width, height);
}
#Override
public void actionPerformed(ActionEvent e) {
oldX = x;
oldY = y;
if (x + width > getParent().getWidth() || x < 0) {
xVel *= -1;
}
if (y + height > getParent().getHeight() || y < 0) {
yVel *= -1;
}
x += xVel;
y += yVel;
repaint();
}
}
}
If you need to slow down the speed more, then decrease the change delta more, this will mean you have to use doubles instead, which will lead into the Shape's API which supports double values
Which should you use? That's up to you. The Timing Framework is really great for linear animations over a period of time, where you know you want to go from one state to another. It's not so good for things like games, where the state of the object can change from my cycle to another. I'm sure you could do it, but it'd be a lot easier with a simple "main loop" concept - IMHO
Timing Framework offers a way to provide animations highly optimized which may help in this case.
MyAnimationFrame
import javax.swing.*;
public class MyAnimationFrame extends JFrame {
public MyAnimationFrame() {
super("My animation frame!");
setSize(300,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new AnimationComponent(0,0,50,50));
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MyAnimationFrame f = new MyAnimationFrame();
}
});
}
}
AnimationComponent
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.TimeUnit;
import org.jdesktop.core.animation.rendering.*;
import org.jdesktop.core.animation.timing.*;
import org.jdesktop.core.animation.timing.interpolators.*;
import org.jdesktop.swing.animation.rendering.*;
import org.jdesktop.swing.animation.timing.sources.*;
#SuppressWarnings("serial")
public class AnimationComponent extends JRendererPanel {
protected int x;
protected int y;
protected int width;
protected int height;
protected Animator xAnimator;
protected Animator yAnimator;
public AnimationComponent(int x, int y, int width, int height) {
setOpaque(true);
this.x = x;
this.y = y;
this.height = height;
this.width = width;
JRendererFactory.getDefaultRenderer(this,
new JRendererTarget<GraphicsConfiguration, Graphics2D>() {
#Override
public void renderSetup(GraphicsConfiguration gc) {
// Nothing to do
}
#Override
public void renderUpdate() {
// Nothing to do
}
#Override
public void render(Graphics2D g, int w, int h) {
Color c = g.getColor();
g.setColor(g.getBackground());
g.fillRect(0, 0, w, h);
g.setColor(c);
g.fillOval(AnimationComponent.this.x, AnimationComponent.this.y,
AnimationComponent.this.width, AnimationComponent.this.height);
}
#Override
public void renderShutdown() {
// Nothing to do
}
}, false);
this.xAnimator = new Animator.Builder(new SwingTimerTimingSource())
.addTargets(new TimingTargetAdapter() {
#Override
public void timingEvent(Animator source, double fraction) {
AnimationComponent.this.x = (int) ((getWidth() - AnimationComponent.this.width) * fraction);
}})
.setRepeatCount(Animator.INFINITE)
.setRepeatBehavior(Animator.RepeatBehavior.REVERSE)
.setInterpolator(LinearInterpolator.getInstance()).build();
this.yAnimator = new Animator.Builder(new SwingTimerTimingSource())
.addTargets(new TimingTargetAdapter() {
#Override
public void timingEvent(Animator source, double fraction) {
AnimationComponent.this.y = (int) ((getHeight() - AnimationComponent.this.height) * fraction);
}})
.setRepeatCount(Animator.INFINITE)
.setRepeatBehavior(Animator.RepeatBehavior.REVERSE)
.setInterpolator(LinearInterpolator.getInstance()).build();
addComponentListener(new ComponentAdapter() {
private int oldWidth = 0;
private int oldHeight = 0;
#Override
public void componentResized(ComponentEvent event) {
Component c = event.getComponent();
int w = c.getWidth();
int h = c.getHeight();
if (w != this.oldWidth) {
AnimationComponent.this.xAnimator.stop();
AnimationComponent.this.xAnimator = new Animator.Builder()
.copy(AnimationComponent.this.xAnimator)
.setDuration(w * 5, TimeUnit.MILLISECONDS) // Original speed was 200 px/s
.build();
AnimationComponent.this.xAnimator.start();
}
if (h != this.oldHeight) {
AnimationComponent.this.yAnimator.stop();
AnimationComponent.this.yAnimator = new Animator.Builder()
.copy(AnimationComponent.this.yAnimator)
.setDuration(h * 5, TimeUnit.MILLISECONDS) // Original speed was 200 px/s
.build();
AnimationComponent.this.yAnimator.start();
}
this.oldWidth = w;
this.oldHeight = h;
}
});
}
}
I'm getting good results but has one issue: any item you resize, the animation is reset.
I put in a particle system but when i run the program, when I spawn some particles, they don't render. I looked at the ArrayList and its value would always be 0 even when i added a particle to it.
heres the code for main class:
package Main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import me.mango.rendering.Particle;
//do double buffering
public class Game extends JPanel {
private static final long serialVersionUID = 1L;
public static final int height = 400;
public static final int width = height * 16 / 9;
JPanel p;
Game game;
Graphics g;
JFrame frame;
KeyListener kl;
MouseListener ml;
public boolean running = true;
private ArrayList<Particle> particles = new ArrayList<Particle>(500);
public Game(){
kl = new KeyListener(){
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
};
ml = new MouseListener(){
public void mousePressed(MouseEvent e) {
addParticle(true);addParticle(false);addParticle(true);
addParticle(false);addParticle(true);addParticle(false);
}
public void mouseReleased(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
};
}
public void addParticle(boolean b){
int dx,dy;
int x = 100;
int y = 100;
if(b){
dx = (int) (Math.random()*5);
dy = (int) (Math.random()*5);
}else{
dx = (int) (Math.random()*-5);
dy = (int) (Math.random()*-5);
}
int size = (int) (Math.random()*12);
int life = (int) Math.random()*(120)+380;
particles.add(new Particle(x,y,dx,dy,size,life,Color.blue));
}
public void update(double delta){
for(int i = 0; i<= particles.size() - 1;i++){
if(particles.get(i).update()) particles.remove(i);
}
System.out.println(particles.size());
}
#Override
public void paint(Graphics g){
g.clearRect(0, 0, getWidth(), getHeight());
//render here
renderParticles(g);
g.dispose();
}
public void renderParticles(Graphics g){
for(int i =0;i <= particles.size() - 1;i++){
particles.get(i).render(g);
System.out.println("spawned");
}
}
public void run(){
//initialize time loop variables
long lastLoopTime = System.nanoTime();
final int TARGET_FPS = 60;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
double lastFpsTime = 0;
//Main game loop
while(running)
{
//Calculate since last update
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double)OPTIMAL_TIME);
//update frame counter
lastFpsTime += updateLength;
//update FPS counter
if(lastFpsTime >= 1000000000)
{
lastFpsTime = 0;
}
//game updates
game.update(delta);
//graphics (gameState)
game.repaint();
try{
Thread.sleep((Math.abs(lastLoopTime - System.nanoTime() + OPTIMAL_TIME)/1000000));
}catch(Exception e){
System.out.println("Error in sleep");
}
}
}
public void start(){
frame = new JFrame("Game");
game = new Game();
frame.add(game);
frame.pack();
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.addKeyListener(kl);
frame.addMouseListener(ml);
frame.setVisible(true);
run();
}
public static void main(String[] args){
new Game().start();
}
}
and for the particle class:
package me.mango.rendering;
import java.awt.Color;
import java.awt.Graphics;
public class Particle {
private int x;
private int y;
private int dx;
private int dy;
private int size;
private int life;
private Color color;
public Particle(int x, int y, int dx, int dy, int size, int life, Color c){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.size = size;
this.life = life;
this.color = c;
}
public boolean update(){
x += dx;
y += dy;
life--;
if(life <= 0){
return true;
}
return false;
}
public void render(Graphics g){
g.setColor(color);
g.fillRect(x-(size/2), y-(size/2), size, size);
g.dispose();
}
}
Thanks!
You have a thing called game inside the class Game: that's not good design at all. Apparently you dont understand the meaning of creating an object.
In main() you created an object game: that should be enough. That thing you have to manipulate.
Therefore calling game.something() inside the class game is a convolution. Get rid of it.
game = new Game();
Game game;
These things must go.
And any reference to game.someMethod()
should be replaced with just someMethod(), if you are inside Game.
Plus you have things like run() and start() etc: do you think you are creating some threads?? by just using those names for your methods?
No.
I cannot figure out why my graphics program's render function is not displaying my rectangle. Also, if I change the bufferstrategy to '3' I get funky behavior. Currently, my project has two main classes on called Main and the second called UniversalJframe. The UniversalJfame class should be called display, well at least you can think of it as being display. Please keep in mind I am still new to java programming.
public class Main extends Canvas implements Runnable{
public int w = 200;
public int h = 200;
public String t = "Hello";
private boolean running = false;
private UniversalJframe frame;
private Thread thread;
private BufferStrategy bs;
private Graphics g;
private PauseTest pause;
public void run(){
System.out.println("Run Method");
while(running){
render();
tick();
pause.pause();
}
stop();
}
public synchronized void start(){
if(running) { return; }
System.out.println("Starting Main Program");
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop(){
if(!running) { return; }
System.out.println("Stopping");
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void render(){
bs = frame.getCanvas().getBufferStrategy();
if(bs == null){
frame.getCanvas().createBufferStrategy(2);
return;
}
g = bs.getDrawGraphics();
//Draw Here
g.setColor(Color.GREEN);
g.fillRect(10, 10, 10, 10);
//End Draw
bs.show();
g.dispose();
}
public void tick(){
}
public Main(){
frame = new UniversalJframe(h, w, t, this);
pause = new PauseTest();
pause.setDuration(500000);
start();
}
public static void main(String args[]){
new Main();
System.out.println("Running Main Program");
}
}
public class UniversalJframe extends Canvas {
private static final long serialVersionUID = 1L;
private JFrame jFrame;
private Canvas canvas;
int height = 200; int width = 200;
String title = "";
Main obj;
public UniversalJframe(int height, int width, String title, Main obj){
this.height = height;
this.width = width;
this.obj = obj;
this.title = title;
init();
}
public Canvas getCanvas(){
return canvas;
}
private void init(){
jFrame = new JFrame(title);
jFrame.setSize(new Dimension(width, height));
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setResizable(false);
jFrame.add(obj);
jFrame.setLocationRelativeTo(null);
jFrame.setVisible(true);
canvas = new Canvas();
canvas.setPreferredSize(new Dimension(width, height));
canvas.setMaximumSize(new Dimension(width, height));
canvas.setMinimumSize(new Dimension(width, height));
jFrame.add(canvas);
jFrame.pack();
}
}
You're using Canvas, which is an old AWT class.
Here's a fun example of a simple graphics application using Java Swing.
The eye balls follow the cursor around the drawing panel.
Here's the code. You can use the JFrame and drawing JPanel as a base for any graphics application.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MovingEyes implements Runnable {
private static final int drawingWidth = 400;
private static final int drawingHeight = 400;
private static final int eyeballHeight = 150;
private static final int eyeballWidthMargin = 125;
private static final int eyeballOuterRadius = 50;
private static final int eyeballInnerRadius = 20;
private DrawingPanel drawingPanel;
private Eye[] eyes;
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingEyes());
}
public MovingEyes() {
this.eyes = new Eye[2];
this.eyes[0] = new Eye(new Point(eyeballWidthMargin, eyeballHeight));
this.eyes[1] = new Eye(new Point(drawingWidth - eyeballWidthMargin,
eyeballHeight));
}
#Override
public void run() {
frame = new JFrame("Moving Eyes");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel();
frame.add(drawingPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = -2977860217912678180L;
public DrawingPanel() {
this.addMouseMotionListener(new EyeballListener());
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(drawingWidth, drawingHeight));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
for (Eye eye : eyes) {
drawCircle(g, eye.getOrigin(), eyeballOuterRadius);
fillCircle(g, eye.getEyeballOrigin(), eyeballInnerRadius);
}
}
private void drawCircle(Graphics g, Point origin, int radius) {
g.drawOval(origin.x - radius, origin.y - radius, radius + radius,
radius + radius);
}
private void fillCircle(Graphics g, Point origin, int radius) {
g.fillOval(origin.x - radius, origin.y - radius, radius + radius,
radius + radius);
}
}
public class Eye {
private final Point origin;
private Point eyeballOrigin;
public Eye(Point origin) {
this.origin = origin;
this.eyeballOrigin = origin;
}
public Point getEyeballOrigin() {
return eyeballOrigin;
}
public void setEyeballOrigin(Point eyeballOrigin) {
this.eyeballOrigin = eyeballOrigin;
}
public Point getOrigin() {
return origin;
}
}
public class EyeballListener extends MouseMotionAdapter {
private final double eyeballDistance = eyeballOuterRadius
- eyeballInnerRadius - 5;
#Override
public void mouseMoved(MouseEvent event) {
Point p = event.getPoint();
for (Eye eye : eyes) {
Point origin = eye.getOrigin();
double theta = Math.atan2((double) (p.y - origin.y),
(double) (p.x - origin.x));
int x = (int) Math.round(Math.cos(theta) * eyeballDistance)
+ origin.x;
int y = (int) Math.round(Math.sin(theta) * eyeballDistance)
+ origin.y;
eye.setEyeballOrigin(new Point(x, y));
}
drawingPanel.repaint();
}
}
}
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at Snooker.paint(Snooker.java:34)
this is error what I've got.
Here is the code:
Main Class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Snooker extends JPanel {
public static final int WIDTH = 900;
public static final int HEIGHT = 450;
public static final Color c = Color.black;
public static final int SIZE_ball = 10;
private Table table;
private Ball ball;
private Cue cue;
public Snooker() {
table = new Table(0,0,c,WIDTH,HEIGHT);
ball = new Ball(150,150,Color.RED,SIZE_ball);
}
public void paint(Graphics g) {
super.paint(g);
table.drawTableOn(g);
ball.drawBallOn(g);
cue.drawCueOn(g);
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Snooker");
frame.setLayout(new BorderLayout());
frame.setSize(WIDTH, HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Snooker game = new Snooker();
frame.add(game);
frame.setVisible(true);
game.requestFocusInWindow();
}
}
Graphics Class:
import java.awt.Color;
import java.awt.Graphics;
public class GraphicsItem {
protected int x,y;
protected Color color;
private static final int SIZE_tableX = 900;
private static final int SIZE_Cue = 30;
public static final int R_ball = 5;
public static int CoorY = 150;
public static int CoorX = 150;
public GraphicsItem(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void moveBallBy(int dx, int dy) {
x += dx;
y += dy;
}
public void drawTableOn(Graphics g) {
g.setColor(color.BLACK);
g.fillRect(x, y, SIZE_tableX, SIZE_tableX/2);
}
public void drawBallOn(Graphics g) {
g.setColor(color.BLACK);
g.fillOval(x,y,R_ball,R_ball);
}
public void drawCueOn(Graphics g) {
g.setColor(color.BLACK);
g.drawLine(x,y,SIZE_Cue,SIZE_Cue);
}
}
Also there are 5 more classes. Cue,Ball,Table and CueBall(extends Ball), BroadCloth(extends Table) . There have just attitude of their objects.
advice to solve?
You have to initialize cue in the constructor of the class Snooker.
Your constructor should be:
public Snooker() {
table = new Table(0,0,c,WIDTH,HEIGHT);
ball = new Ball(150,150,Color.RED,SIZE_ball);
cue = new Cue( ... );
}
As it stands, cue has not been instantiated, and throws a NullPointerException when you try to access its methods.