i'm trying to implement a function to calculate the area of the polygon. this code allows you yo draw a polygon but when it comes to display the area, i am not sure how to do that. i tried in a couple ways but i am still a beginner at programming, so i'd appreciate any help. here is the code:
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
import java.awt.Color;
import java.awt.Polygon;
public class DrawPolygons extends JApplet implements ActionListener, MouseListener
{
private static final int NUMPOINTS = 500; //Up to 500 points can be chosen
private JButton finish; //Button to indicate user is done entering points
private Polygon shape; //polygon object to be drawn
private boolean isDrawn; //boolean flag for when the user is finished drawing
private int count; //how many points the user has clicked
private Color color; //color of the polygon after user finalizes points
private int[] x; //x coordinates of each point user picks
private int[] y; //y coordinates of each point user picks
private float sum;
private double area;
public void init() //set up GUI
{
setLayout(new FlowLayout());
addMouseListener(this); //adds MouseListener for mouse clicks
isDrawn = false; //isDrawn is initially false
count = 0; //count starts as 0
x = new int[NUMPOINTS]; //allows for up to 500 points to be chosen
y = new int[NUMPOINTS]; //allows for up to 500 points to be chosen
finish = new JButton("Finalize points"); //creates finish button
finish.addActionListener(this); //adds finish button to ActionListener
add(finish); //adds finish button to GUI
color = Color.BLACK; //color is intially black, and will remain
// so if user cancels the color chooser
//JOptionPane.showMessageDialog(null, "Click points that will make up the polygon. After each" +
// "point is entered, press the Finalize Points button");
shape = new Polygon(); //creates the Polygon shape
}
public void paint(Graphics g) //draws the Polylines, Polygons, and sets the color
{
super.paint(g);
g.drawPolyline(x, y, count); //draws the polyline specified by user
// mouseclick
g.setColor(color); //sets the color to the user chosen color
if(isDrawn) //if finalize button is clicked
{
g.fillPolygon(x, y, count); //finalizes polylines into
} // polygon and fills shape
}
public void actionPerformed(ActionEvent a) //decides what to do when finalize button is pressed
{
if(a.getSource() == finish) //if the finalize button is pressed
{
isDrawn = true; //isDrawn is set to true, ending the users ability
// to add more points, and fills the polygon
color = Color.red;
//JColorChooser.showDialog(this, "Choose a color", color); //color is set to users choice
repaint();
JOptionPane.showMessageDialog(null, "The area is ");
}
}
public void mouseClicked(MouseEvent e) //save coordinates of clicks
{
if(isDrawn == false && count < NUMPOINTS) //if the finalize button is not pressed
{ // the user can add additional points
x[count] = e.getX(); //adds the x point at the current mouse x coordinate
y[count] = e.getY(); //adds the y point at the current mouse y coordinate
count++; //count increases with each mouse click
repaint();
}
else if (e.isShiftDown()) {
// Clear the applet. (This only requires a repaint.)
// Also, set count to zero to start a new polygon.
count = 0;
isDrawn = false;
repaint();
}
}
private float getPolygonArea(int[] x, int[] y, int count)
{
float sum_but_no_result=0;
for(int i=0;i<(count-1);i++) // count is point number of polygon
{
sum_but_no_result+=x[i]*y[i+1] + y[i]*x[i+1];
}
sum_but_no_result+=x[count-1]*y[0] + y[count-1]*x[0];
float sum = (float)Math.abs(sum_but_no_result) / 2.0f;
return sum;
}
//Empty Implementation provided here so we can implement MouseListener (needed because we must
// provide concrete forms of all methods of an interface to implement it
public void mousePressed(MouseEvent e){};
public void mouseReleased(MouseEvent e){};
public void mouseEntered(MouseEvent e){};
public void mouseExited(MouseEvent e){};
}
float x[N],y[N]; // point coordinates as x1,y1 x2,y2 ....
...
...
...
...
float sum_but_no_result=0;
for(int i=0;i<(N-1);i++) // N is point number of polygon
{
sum_but_no_result+=x[i]*y[i+1] + y[i]*x[i+1];
}
sum_but_no_result+=x[N-1]*y[0] + y[N-1]*x[0];
float sum= (float)Math.abs(sum_but_no_result) / 2.0f;
http://www.mathopenref.com/coordpolygonarea.html
For self-intersecting polygons, you may add an intersection-coord-finder and polygon-adder algorithm to find all sub-polygons produced by intersecting, recursively.
In Kotlin, using an extension on java.awt.Polygon :
import java.awt.Polygon
fun Polygon.area(): Float {
var sum = 0f
for (i in 0 until npoints - 1) {
sum += xpoints[i] * ypoints[i + 1] + ypoints[i] * xpoints[i + 1]
}
sum += xpoints[npoints - 1] * ypoints[0] + ypoints[npoints - 1] * xpoints[0]
return sum / 2.0f
}
Related
I am currently working on a 3 cushion billiards game project. I have added two balls on the table so far. I am trying to move one of the balls but I am having a hard time doing that. Should I use a timer? If so then could you tell me an effective way to use the timer on my code so I can move my balls?
Your help would be much appreciated.
Thanks in advance.
Farhan Hasan
I have tried to create a move function for the class balls. But I am not sure what I should put inside the function, I have added the xSpeed and ySpeed. The xLocation and the yLocation changes depending on the xSpeed and ySpeed.
public class Balls
{
private Color ballFillColor;
private Color ballBorderColor;
private int ballX = 0;
private int ballY = 0;
private int xSpeed = 5;
private int ySpeed = 0;
private int ballWidth = 0;
private int ballHeight = 0;
Timer t;
public boolean fillBall = false;
private static Balls ballArray[]; //Required for drawMultipleBalls
Balls(){ //Constructor
ballBorderColor = Color.black;
}
Balls(int ballX, int ballY, int ballWidth, int ballHeight, Color ballBorderColor, JFrame window){ //Constructor
// X , Y , Width, Height, Border Colour, container
this.setBallBorderColor(ballBorderColor);
this.setBallWidth(ballWidth);
this.setBallHeight(ballHeight);
this.setBallX(ballX);
this.setBallY(ballY);
this.drawBall(window);
}
//Here is the move function. I am not really sure what to do here.
public void move()
{
if(this.ballX < 1000 - this.ballWidth)
{
this.ballX += this.xSpeed;
}
try
{
Thread.sleep(1);
}
catch(Exception e)
{
}
}
//GET AND SET FUNCTIONS HERE
//HERE ARE THE FUNCTIONS WHICH ARE RESPONSIBLE FOR DRAWING MY BALLS IN JFRAME
public void drawBall(JFrame frame)
{
frame.getContentPane().add(new MyComponent());
}
public void drawMultipleBalls(JFrame frame, Balls[] balls)
{
ballArray = balls;
frame.getContentPane().add(new MyComponent2());
}
private class MyComponent extends JComponent{
public void paintComponent(Graphics g){
if (fillBall) //Fill first, and then draw outline.
{
g.setColor(ballFillColor);
g.fillOval(getBallX(),getBallY(), getBallHeight(),getBallWidth());
}
g.setColor(getBallBorderColor());
g.drawOval(getBallX(),getBallY(), getBallHeight(),getBallWidth());
}
}
private class MyComponent2 extends JComponent{
public void paintComponent(Graphics g){
for (int i = 0; i < ballArray.length; i++)
{
if (ballArray[i].fillBall) //Fill first, and then draw outline.
{
g.setColor(ballArray[i].ballFillColor);
g.fillOval(ballArray[i].getBallX(),ballArray[i].getBallY(), ballArray[i].getBallHeight(),ballArray[i].getBallWidth());
}
g.setColor(ballArray[i].getBallBorderColor());
g.drawOval(ballArray[i].getBallX(),ballArray[i].getBallY(), ballArray[i].getBallHeight(),ballArray[i].getBallWidth());
}
}
}
Hopefully, I can have two movable balls for the game, the should bounce back as the hit the edge of the screen and they should be able to slow down over time. For that, I am thinking to use a damper (I will multiply the xSpeed and ySpeed with a number less than 1, eventually it will slow down the ball)
Here is a simple example I came up with to show a ball moving and bouncing off the edges.
The direction changes based on the boundary. Left and top edges just check for 0. Bottom and right edges need to include the diameter of the ball.
The x and y increments are independent. And these amounts in conjunction with the timer can change the movement. Notice however, that to have objects bounce off of each other (as in a pool game) is more complicated due to angle of trajectories, etc. And the distances bounced will vary and slow with time based on frictional values. Everything else is documented in the Java API.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MovementDemo extends JPanel implements ActionListener {
JFrame frame = new JFrame("Movement Demo");
int size = 500;
int x = 50;
int y = 200;
int diameter = 50;
int yinc = 2;
int xinc = 2;
int xdirection = 1;
int ydirection = 1;
public MovementDemo() {
setPreferredSize(new Dimension(size, size));
frame.add(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MovementDemo().start());
}
public void start() {
Timer timer = new Timer(100, this);
timer.setDelay(5);
timer.start();
}
public void actionPerformed(ActionEvent ae) {
if (x < 0) {
xdirection = 1;
}
else if (x > size - diameter) {
xdirection = -1;
}
if (y < 0) {
ydirection = 1;
}
else if (y > size - diameter) {
ydirection = -1;
}
x = x + xdirection * xinc;
y = y + ydirection * yinc;
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLUE);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillOval(x, y, diameter, diameter);
}
}
It seems in general there are a few things you need to figure out:
has the ball collided with another ball
has the ball collided with a wall
otherwise just figure out what is the ball's new position based on its velocity
Below is some sample code that stubs some of this out. You can first compare the current ball's position to all others (not including the current ball of course). If there are any equal positions, process a collision with a ball. If the ball is at the window border i.e it hit a wall, process a collision with a wall. Otherwise just calculate its new position based on its current velocity.
The process collision part is just to apply physics mechanics to whatever degree of complexity you require. One general suggested change would be to update the velocity of the balls then apply it to the position after. The specific calculations for velocity changes you could apply as needed and as you can imagine it can get pretty involved which is why I suggest using a separate method and possibly a sub class for velocity instead of managing each part of the velocity vector in the ball itself. I used the wall as an object because of this. The composition, weights, velocities etc of the object's colliding can affect the resulting collision, but how complex you want that processing to be is up to you.
Sorry I'm no physics expert but I hope this sends you in the right direction in terms of code! Also this might help with the specific calculations you might want to use:
https://www.khanacademy.org/science/physics/one-dimensional-motion/displacement-velocity-time/v/calculating-average-velocity-or-speed
public void move()
{
// check if balls are on same position not including this ball
for(Ball b: ballArray){
if (this.position == b.position && this != b){
processCollision(this, b, null);
} else{
// if the ball hasn't collided with anything process its movement based on speed
// this assumes a 1000 x 1000 window for keeping objects inside it
if(this.ballX < 1000 - this.ballWidth && this.ballY < 1000 - this.ballHeight){
this.ballX += this.xSpeed;
this.ballY += this.ySpeed;
}else {
processCollision(this, null, new Wall());
}
}
}
try
{
Thread.sleep(1);
}
catch(Exception e)
{
}
}
public void processCollision(Ball b1, Ball b2, Wall w){
// if ball hasn't collided with a wall, process a ball - ball collision
if(w == null){
// apply physics mechanics according the complexity desired for ball collisions
b1.xSpeed -= b2.xSpeed;
b1.ySpeed -= b2.ySpeed;
// ball 2 would end up slowing down
b2.xSpeed -= b1.xSpeed;
b2.ySpeed -= b1.ySpeed;
}
// if ball hasn't collided with a ball, process a ball - wall collision
if(b2 == null){
// apply physics mechanics for hitting a wall
// e.g as below: just send ball in opposite direction
b1.xSpeed = b1.xSpeed * -1;
b1.ySpeed = b1.ySpeed * -1;
}
// either way, process ball's new position based on its new speed
b1.ballX += b1.xSpeed;
b1.ballY += b1.ySpeed;
b2.ballX += b2.xSpeed;
b2.ballY += b2.ySpeed;
}
I am making a program which draws circles in the clicked place.
For the first click it draws circle which is not exactly in the same place I have clicked.
But furthermore, for other clicks it just draws circles one on the other. as i find out it is because the coordinates of click won't change.
my main:
public static void main(String[] args)
{
JFrame frame = new JFrame();
//set window size
frame.setSize(1000, 1000);
//set the title
frame.setTitle("Oval Draw");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//add panel to frame and make it visible
MouseComponent component = new MouseComponent();
frame.add(component);
frame.setVisible(true);
}
and the MouseComponent class:
public class MouseComponent extends JPanel implements MouseListener
{
boolean drawPoint = true;
boolean drawLine = false;
boolean drawOval = false;
public MouseComponent()
{
super();
pointX = 0;
pointY = 0;
lineX = 0;
lineY = 0;
addMouseListener(this);
}
int pointX, pointY, lineX, lineY;
int[] ArrayX,ArrayY;
#Override
public void paintComponent(Graphics g)
{
for(int i=0; i<10; i++)
{
super.paintComponent(g);
if(drawLine)
{
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color startrandomColor = new Color(red, green, blue);
red = (int) (Math.random() * 255);
green = (int) (Math.random() * 255);
blue = (int) (Math.random() * 255);
Color endrandomColor = new Color(red, green, blue);
Graphics2D g2d = (Graphics2D) g;
// this.addMouseListener(this);
GradientPaint gradient = new GradientPaint(70, 70, startrandomColor,
150, 150, endrandomColor);
g2d.setPaint(gradient);
g2d.translate( lineX, lineY);
g2d.fillOval(70, 70, 100, 100);
System.out.print(lineX);
System.out.print(" ");
System.out.print(lineY);
System.out.print(" ");
System.out.print(pointX);
System.out.print(" ");
System.out.print(pointY);
System.out.print(" ");
// repaint();
}
else if(drawPoint)
{
// g.drawOval(pointX-5,pointY-5,10,10);
}
}
}
public void mouseClicked(MouseEvent mouse)
{
if(!drawPoint)
{
pointX = mouse.getX();
pointY = mouse.getY();
drawPoint = true;
}
else if(!drawLine)
{
lineX = mouse.getX();
lineY = mouse.getY();
drawLine = true;
}
repaint();
}
public void mouseEntered(MouseEvent mouse){ }
public void mouseExited(MouseEvent mouse){ }
public void mousePressed(MouseEvent mouse){ }
public void mouseReleased(MouseEvent mouse){ }
}
This here:
if(!drawPoint)
{
pointX = mouse.getX();
pointY = mouse.getY();
drawPoint = true;
}
simply doesn't make too much sense. You init drawPoint to true ... so you will never enter the if block and collect the click coordinates.
And beyond that: when you look into your other method --- the code to draw points is commented out.
Suggestion: step back; don't try to do 5 things at the same time. Write the code it takes to
fetch mouse coordinates after a click
draw circles around that coordinate
and get that to work. Forget about drawing lines, shapes, whatever for now.
And when you have a class that does nothing but that click+draw circles; then create a new class, where you add more features. Your "real" problem right now is that you started working on various features; and that your attempts to "integrate" them into a single class left you with something that is confusing, and not surprising ... not working!
Long story short: forgot about that strange logic that you have in your code right now to "toggle" between drawing points and lines. Draw points. And then, when that works ... add some radio buttons; or a drop down menu and use that to control if you want to draw lines or points. Don't make that "implicit" by toggling booleans in your event handler code!
boolean drawPoint = true;
boolean drawLine = false; cause it to print the first time. After that both are set to true so it will not change coordinates.
I need to stream a video from an IP cam over a java applet form preferably and then draw a rectangle over it and get the four coordinates. I can stream a video over an applet and I can draw a polygon, but separately. What I want to do is I need to draw the polygon while the video is streaming and the polygon should be translucent.
This is the code which I used to draw a polygon.
package IntelligentCameraApp;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class SimplePolygons extends Applet implements MouseListener {
/* Variables for implementing polygon input. */
private int[] xCoord, yCoord; // Arrays containing the points of
// the polygon. Up to 500 points
// are allowed.
private int pointCt; // The number of points that have been input.
private final static int polygonColor = Color.TRANSLUCENT;
// Color that is used to draw the polygons.
public void init() {
// Initialize the applet. The applet listens for mouse events.
// Arrays are created to hold the points.
setBackground(Color.white);
addMouseListener(this);
xCoord = new int[500];
yCoord = new int[500];
pointCt = 0;
}
public void paint(Graphics g) {
// The paint() routine does nothing but draw a 1-pixel black
// border around the applet. Polygons drawn on the applet
// are not permanent.
g.setColor(Color.black);
g.drawRect(0, 0, getSize().width - 1, getSize().height - 1);
} // end paint()
private void putLine(int x1, int y1, int x2, int y2) {
// Draw a line from (x1,y1) to (x2,y2) directly onto the
// applet, without going through the paint() method.
Graphics g = getGraphics();
g.drawLine(x1,y1,x2,y2);
g.dispose();
}
private void putPolygon() {
// Draw the polygon described by the arrays xCoord and yCoord
// and the integer pointCt. A filled polygon with a black
// outline is drawn. If pointCt is 0 or 1, nothing is drawn.
// If pointCt is 2, only a black line is drawn.
if (pointCt < 2)
return;
Graphics g = getGraphics();
if (pointCt == 2) {
g.drawLine(xCoord[0], yCoord[0], xCoord[1], yCoord[1]);
}
else {
//g.setColor(Color.red);
g.fillPolygon(xCoord, yCoord, pointCt);
g.drawPolygon(xCoord, yCoord, pointCt);
}
g.dispose();
}
public void mousePressed(MouseEvent evt) {
// Process a user mouse-click.
if (evt.isShiftDown()) {
// Clear the applet. (This only requires a repaint.)
// Also, set pointCt to zero to start a new polygon.
pointCt = 0;
repaint();
}
else if ( pointCt > 0 && (Math.abs(xCoord[0] - evt.getX()) <= 2)
&& (Math.abs(yCoord[0] - evt.getY()) <= 2) ) {
// User has clicked near the starting point.
// Draw the polygon and reset pointCt so that the
// user can start a new polygon.
putPolygon();
pointCt = 0;
}
else if (evt.isMetaDown() || pointCt == 500) {
// Draw the polygon and reset pointCt so that the
// user can start a new polygon.
putPolygon();
pointCt = 0;
}
else {
// Add the point where the user clicked to the list of
// points in the polygon, and draw a line between the
// previous point and the current point.
xCoord[pointCt] = evt.getX();
yCoord[pointCt] = evt.getY();
pointCt++;
if (pointCt >= 2) {
putLine(xCoord[pointCt-2], yCoord[pointCt-2],
xCoord[pointCt-1], yCoord[pointCt-1]);
}
}
} // end mousePressed()
public void mouseReleased(MouseEvent evt) { }
public void mouseClicked(MouseEvent evt) { }
public void mouseEntered(MouseEvent evt) { }
public void mouseExited(MouseEvent evt) { }
}
Oh boy... I had to many problems when I had mixed the paint with logic... My boss/client must explicitly ask for that to do logic in paint method.
I would like to suggest at least a LayeredPanel. And do stuff with IP camera in layer 1 and do rectangle, whatever you want in layer2.
I want to be able to create new particles from wherever I click on stage. I'm just stuck on adding the mouse portion and I've tried to add/pass parameters and I always get errors while I try setting the parameters. Any suggestions?
This is my current code:
float parSpeed = 1; //speed of particles
int nParticles = 1000; //# of particles
Particle[] particles;
void setup() {
size(700,700); //size of image (8.5 x 11 # 300px) is 3300,2550)
frameRate(60); //framerate of stage
background(0); //color of background
particles = new Particle[nParticles];
//start particle array
for(int i=0; i<nParticles; i++) {
particles[i] = new Particle();
}
}
void draw() {
fill(0,0,0,5); //5 is Alpha
rect(0,0,width,height); //color of rectangle on top?
translate(width/2, height/2); //starting point of particles
//start particle array
for(int i=0; i<nParticles; i++) {
particles[i].update();
particles[i].show();
}
}
//Particle Class
class Particle {
PVector pos; //position
float angle; //angle
float dRange; //diameter range
float dAngle; //beginning angle?
color c; //color
Particle() {
pos = new PVector(0,0);//new position for the particles.
angle = 1; //controls randomization of direction in position when multiplied
dRange = 0.01; // how big of a circle shold the particles rotate on
dAngle = 0.15; // -- maximum angle when starting
c = color(0,0,random(100, 255)); //set color to random blue
}
void update() {
float cor = .25*dRange*atan(angle)/PI;
float randNum = (random(2)-1)*dRange-cor; //Random number from (-dRange, dRange)
dAngle+=randNum; //We don't change the angle directly
//but its differential - source of the smoothness!
angle+=dAngle; //new angle is angle+dAngle -- change angle each frame
pos.x+=parSpeed*cos(angle);//random direction for X axis multiplied by speed
pos.y+=parSpeed*sin(angle);//rabdin durectuib for y axis multiplied by speed
}
void show() {
fill(c); //fill in the random color
noStroke(); //no stroke
ellipse(pos.x,pos.y,10,10); //make the shape
smooth(); //smooth out the animation
}
}
void keyPressed() {
print("pressed " + int(key) + " " + keyCode);
if (key == 's' || key == 'S'){
saveFrame("image-##.png");
}
}
void mouseReleased() {
print("mouse has been clicked!");
}
Overwrite the mouseReleased() method:
In there you'll need to:
capture the position of the mouse
create the new particle
update the position of the newly created particle.
add it to the array (the particle system)
This may look simple but you'll have to keep in mind an array cannot change sizes. I would advise that you create a ParticleSystem class that takes care of adding and removing particles from the system.
Edit: You might want to consider using an ArrayList instead of an Array of Particles. Have a look at this
In pseudo code this would look like this:
void mouseReleased() {
int particleX = mouseX;
int particleY = mouseY;
Particle P = new Particle();
P.setPos ( new PVector ( particleX, particleY ) ); // this needs to be implemented
ParticleSystem.add ( P ); // this needs to be implemented
}
I hope this will be a good start.
AH
I keep getting this error when I compile and run my Java program using the ACM library
Exception in thread "Thread-3" java.lang.NullPointerException
at SpaceTravel.getBlackHoleDistance(SpaceTravel.java:148)
at SpaceTravel.gameOverBlackHole(SpaceTravel.java:132)
at BlackHole.oneTimeStep(BlackHole.java:84)
at BlackHole.run(BlackHole.java:45)
at java.lang.Thread.run(Unknown Source)
This is the game class:
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.awt.*;
public class SpaceTravel extends GraphicsProgram {
// specify the size of the window
public static int APPLICATION_WIDTH = 1280;
public static int APPLICATION_HEIGHT = 600;
// class constants
private static final double
PLANET_SIZE = 80,
BLACK_HOLE_SIZE = 100,
STAR_SIZE = 2;
// instance variables
private GOval ice, fire, iron;
private GPoint lastPoint;
private boolean isDragging = false;
private GLabel gameOverText, goal, win;
private BlackHole blackhole1, blackhole2;
private RandomGenerator rand = new RandomGenerator();
// init method, draw the graphics objects
public void init() {
setBackground(Color.BLACK);
// call the randomly colored stars method
drawStars();
// call 1 instance of BlackHole class
blackhole1 = new BlackHole(BLACK_HOLE_SIZE, 5, 2, this);
add(blackhole1, APPLICATION_WIDTH-400, APPLICATION_HEIGHT/2 );
new Thread(blackhole1).start();
// call 1 instance of BlackHole class, but name it differently
blackhole2 = new BlackHole(BLACK_HOLE_SIZE, 3, -4, this);
add(blackhole2, APPLICATION_WIDTH-200, APPLICATION_HEIGHT/2 );
new Thread(blackhole2).start();
// draw fire planet
fire = drawCircleCentered(100, 400, PLANET_SIZE);
add(fire);
fire.setFilled(true);
fire.setColor(Color.RED);
// draw ice planet
ice = drawCircleCentered(100, 100, PLANET_SIZE);
add(ice);
ice.setFilled(true);
ice.setColor(Color.BLUE);
// draw iron planet
iron = drawCircleCentered(100, 250, PLANET_SIZE);
add(iron);
iron.setFilled(true);
Color grey = new Color(34, 34, 34);
iron.setColor(grey);
// game over text
gameOverText = new GLabel ("GAME OVER", APPLICATION_WIDTH/2 - 250, APPLICATION_HEIGHT/2);
gameOverText.setColor(Color.RED);
gameOverText.setFont(new Font("DEFAULT_FONT", Font.BOLD, 90));
// goal text
goal = new GLabel ("GOAL", APPLICATION_WIDTH-150, APPLICATION_HEIGHT/2);
goal.setColor(Color.RED);
goal.setFont(new Font("DEFAULT_FONT", Font.BOLD, 20));
add(goal);
// win text
win = new GLabel ("WINRAR IS YOU!", APPLICATION_WIDTH/2 - 350, APPLICATION_HEIGHT/2);
win.setColor(Color.RED);
win.setFont(new Font("DEFAULT_FONT", Font.BOLD, 90));
}
// checker method if the ice and fire plantes touch, call the game over method below.
private void checkFireIce(GOval fire, GOval ice) {
if(getDistance(fire, ice) < PLANET_SIZE ) {
gameOver(fire);
}
}
// checker method for when fire planet gets to the goal text, call the game winning method below
private void checkPlanetsGoal() {
if(fire.getBounds().intersects(goal.getBounds())) {
winGame();
}
}
// start dragging if the ball is pressed
public void mousePressed(GPoint point) {
if (ice.contains(point)) {
isDragging = true;
lastPoint = point;
}
}
// move the ball when it is dragged, and call checking methods for game winning or game over conditions
public void mouseDragged(GPoint point) {
checkFireIce(fire, ice);
checkPlanetsGoal();
if (isDragging) {
ice.move(point.getX()-lastPoint.getX(),
point.getY()-lastPoint.getY());
lastPoint = point;
// bump the planets
bump(ice, iron);
bump(iron, fire);
}
}
// checking method for if any of the planets have touched an instance of black hole
public void gameOverBlackHole(BlackHole blackhole) {
double a = getBlackHoleDistance(fire, blackhole);
double b = getBlackHoleDistance(ice, blackhole);
double c = getBlackHoleDistance(iron, blackhole);
if(a < BLACK_HOLE_SIZE/2 + PLANET_SIZE/2) {
gameOver(fire);
}
if(b < BLACK_HOLE_SIZE/2 + PLANET_SIZE/2) {
gameOver(ice);
}
if(c < BLACK_HOLE_SIZE/2 + PLANET_SIZE/2) {
gameOver(iron);
}
}
// get distance between a black hole instance and a planet
private double getBlackHoleDistance(GOval planet, BlackHole blackhole) {
return GMath.distance(planet.getX()+PLANET_SIZE/2, planet.getY()+PLANET_SIZE/2,
blackhole.getX(), blackhole.getY());
}
// bump helper method, calculates how much to move a tangent planet by when it is being bumped by another
private void bump(GOval planet1, GOval planet2) {
double offset = PLANET_SIZE+1.5 - getDistance(planet1, planet2);
if (offset > 0) {
planet2.move(offset*(planet2.getX()-planet1.getX())/PLANET_SIZE,
offset*(planet2.getY()-planet1.getY())/PLANET_SIZE);
}
}
// a helper method, compute the distance between the centers of the balls
private double getDistance(GOval planet1, GOval planet2) {
return GMath.distance(planet1.getX()+PLANET_SIZE/2, planet1.getY()+PLANET_SIZE/2,
planet2.getX()+PLANET_SIZE/2, planet2.getY()+PLANET_SIZE/2);
}
// a helper method, draw a circle centered at the given location
private GOval drawCircleCentered(double centerX, double centerY, double size) {
return new GOval(centerX-size/2, centerY-size/2, size, size);
}
// a helper method, draw randomly colored stars
private void drawStars() {
for (int i = 0; i < 1000; i++) {
GOval star = drawCircleCentered(rand.nextDouble(0, APPLICATION_WIDTH), rand.nextDouble(0, APPLICATION_HEIGHT), STAR_SIZE);
add(star);
star.setFilled(true);
star.setColor(rand.nextColor());
}
}
// helper method to switch dragging off when mouse is released from window
public void mouseReleased(GPoint point) {
isDragging = false;
}
// helper method to switch dragging off, remove the planet that touched the goal, and display game over text
public void gameOver(GOval planet) {
isDragging = false;
remove(planet);
add(gameOverText);
}
// helper method to switch dragging off, remove planets, and display win text
private void winGame() {
isDragging = false;
add(win);
remove(fire);
remove(ice);
remove(iron);
remove(goal);
}
}
And this is the class that creates blackholes which can end the game.
// import libraries
import acm.program.*;
import acm.graphics.*;
import acm.util.*;
import java.awt.*;
import java.util.*;
public class BlackHole extends GCompound implements Runnable {
// instance variables
private double size, xSpeed, ySpeed;
private SpaceTravel game;
private boolean stopHammerTime = false;
// constructor for BlackHole
public BlackHole(double size, double xSpeed, double ySpeed, SpaceTravel game) {
// save the parameters size, xSpeed, ySpeed, centerX, centerY, and game
this.size = size;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.game = game;
// call method drawBlackhole
drawBlackhole(0, 0, size, 3, 40);
}
// run method, move the black hole until it hits a planet || stopHammerTime = true
public void run() {
while(!stopHammerTime) {
oneTimeStep();
}
}
// helper method, creates a black hole
private void drawBlackhole(double centerX, double centerY, double size, double gap, int layers) {
for (int i = 0; i < layers; i++) {
// this gradient color will lighten each time the for loop completes
Color gradient = new Color(0 + 5*i, 0 + 5*i, 0 + 5*i);
GOval ring = drawCircleCentered(centerX, centerY, size-gap*2*i);
add(ring);
ring.setFilled(true);
ring.setColor(gradient);
}
}
// a helper method, draw a circle centered at the given location
private GOval drawCircleCentered(double centerX, double centerY, double size) {
return new GOval(centerX-size/2, centerY-size/2, size, size);
}
// a helper method, move the blackHole in oneTimeStep
private void oneTimeStep() {
double x = getX();
double y = getY();
// if the black hole hits the left or the right wall, reverse the x-speed
if (x < size/2 || x+size/2 > game.getWidth()) xSpeed = -xSpeed;
// if the black hole hits the top or the bottom wall, reverse the y-speed
if (y < size/2 || y+size/2 > game.getHeight()) ySpeed = -ySpeed;
// move the black hole by a small interval, incorporating changes from if statements
move(xSpeed, ySpeed);
// check if a planet has touched a blackhole
game.gameOverBlackHole(this);
// delay
pause(20);
}
}
I'm pretty sure that I have the calling of the classes correct, but for some reason, the blackhole2 that is called just crashes.
Exception in thread "Thread-3" java.lang.NullPointerException
It is NullPointerException happened while executing thread "Thread-3"
At line 148 in SpaceTravel.java file.
It seems while Thread-3 executing, line 148 is trying to do some operation on null reference, which results in NullPointerException.
The code that apparently crashes looks like this:
return GMath.distance(planet.getX()+PLANET_SIZE/2, planet.getY()+PLANET_SIZE/2,
blackhole.getX(), blackhole.getY());
Most likely either planet or blackhole parameter is null. Use debugger or println() to figure out which one. This piece of code is called from three places:
double a = getBlackHoleDistance(fire, blackhole);
double b = getBlackHoleDistance(ice, blackhole);
double c = getBlackHoleDistance(iron, blackhole);
Your line numbers are a bit off but I dare to say it's the first line, so either fire or blackhole is null.
A Quick Analysis:
gameOverBlackHole method gets called form BlackHole class this is causing problems because remove happens inside that method. If remove happens while one thread is executing then there will NPE
This a can be a example of what might go wrong if you share state of object between multiple threads