This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I'm working on a pong game with Processing. The game works just fine with a single class, however, I have to add multiple classes. Every time I get a Null Pointer Exception. This is part of the main class which extends PApplet
PApplet app = new PApplet();
void MAINMENU() {
// Start the Menu Song
if (musicStartMenu) {
MENUsong.loop();
musicStartMenu = false;
}
// Stop the Level song if needed
if (!musicStart) {
BGsong.stop();
musicStart = true;
}
// Resetting player scores
ScoreP1 = ScoreP2 = 0;
// Creating the Background for this scene
image(menuBG, 0, 0);
textFont(SC);
// Setting the title
text("PONG", width / 2 - 100, 150);
// Creating the buttons for this scene
Button Play= new Button(width / 2 - 150, height / 2 - 70, 300, 100, "PLAY", width / 2 - 100, height / 2 + 10, 1, 1,MAIN,app);
Button Exit= new Button(width / 2 - 150, height / 2 + 70, 300, 100, "EXIT", width / 2 - 100, height / 2 + 150, 3, 2,MAIN,app);
Play.Create();
Exit.Create();
}
and this is the button class:
import processing.core.PApplet;
public class Button {
int Bx,By, width, height;
String label;
int labelW, labelH, action, style, MAIN;
PApplet app;
public Button(int Bx, int By, int width, int height, String label, int labelW, int labelH, int action, int style, int MAIN, PApplet app) {
this.Bx = Bx;
this.By = By;
this.width = width;
this.height = height;
this.label = label;
this.labelW = labelW;
this.labelH = labelH;
this.action = action;
this.style = style;
this.MAIN = MAIN;
this.app = app;
}
void Create(){
// Check if we hover the mouse over and select a style
if (ButtonBorder(Bx, By, height, width)) {
if (style == 1)
// light green
app.fill(100, 155, 100);
else if (style == 2)
// light red
app.fill(255, 100, 100);
} else app.fill(0, 50); // black transparent
// Nobody likes borders
app.stroke(0, 0);
// Create the button box
app.rect(Bx, By, width, height);
// CHeck if the mouse is pressed and is hovering above the button
if (app.mousePressed && ButtonBorder(Bx, By, height, width)) {
// Select scene on click
MAIN = action;
}
// SET the fill of the label and create the label
app.fill(255);
app.text(label, labelW, labelH);
}
boolean ButtonBorder(int xB, int yB, int ButtonHeight, int ButtonWidth) {
// returns true if the mouse pointer is located inside the button
if (!(app.mouseX >= xB && app.mouseX <= xB + ButtonWidth))
return false;
if (!(app.mouseY >= yB && app.mouseY <= yB + ButtonHeight))
return false;
return true;
}
If anyone has any clue it would be of much help. I want to say that the code is working just fine in a method inside the main class but for some reason that I can't seem to find it doesn't work in another class.
Trace:
java.lang.NullPointerException
at processing.core.PApplet.fill(PApplet.java:14521)
at ButtonB.Create(ButtonB.java:41)
at Pong.MAINMENU(Pong.java:205)
at Pong.draw(Pong.java:161)
at processing.core.PApplet.handleDraw(PApplet.java:2429)
at processing.awt.PSurfaceAWT$12.callDraw(PSurfaceAWT.java:1557)
at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:313)
Your code has a few problems, because you aren't using Processing the way it was designed to be used.
First, it doesn't make sense to create an instance of PApplet directly like this:
PApplet app = new PApplet();
Instead, you want to extend the PApplet class to create your own sketch, like this:
import processing.core.PApplet;
public class MySketch extends PApplet{
public void settings(){
size(500, 500);
}
public void draw(){
ellipse(mouseX, mouseY, 50, 50);
}
public void mousePressed(){
background(64);
}
public static void main(String[] args){
String[] processingArgs = {"MySketch"};
MySketch mySketch = new MySketch();
PApplet.runSketch(processingArgs, mySketch);
}
}
Secondly, you have to make sure you don't call Processing's functions (like fill() and rect()) until after the setup() function of your sketch class has been called.
Also, some feedback: please try to use standard naming conventions in your code. Classes and constructors should start with an upper-case letter, and functions and variables should start with a lower-case letter. Following this convention will make your code much easier to read.
Shameless self-promotion: here is a tutorial on using Processing as a Java library.
Related
I am trying to teach myself more about graphics in Java. To do this I'm trying to build a chess game. I've hit my first roadblock at making the board. My thought here is that I would have a extension of JComponent called "Square" that would be my container for both the color of the board square and the piece on that square (if any). To start with I haven't attempted to include any representation of the piece yet, just the square colors. Later on I hope to have an abstract "Pieces" class that is extended by multiple subclasses representing all the different types of pieces, and add those to each Square as applicable.
When I execute the following, I only get one black square in the upper left hand corner.
ChessBoardTest.java
public class ChessBoardTest {
public static void main(String[] args) {
ChessBoard Board = new ChessBoard();
Board.Display();
}
}
ChessBoard.java
public class ChessBoard extends JFrame {
public static final int FRAME_WIDTH = 500;
public static final int FRAME_HEIGHT = 500;
// Declare instance variables
private Square[][] square = new Square[rows][cols];
private final static int rows = 8;
private final static int cols = 8;
public ChessBoard() {
}
public void Display() {
JPanel Board_Layout = new JPanel();
Board_Layout.setLayout(new GridLayout(8,8));
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
if((i+j) % 2 == 0) {
square[i][j] = new Square(1);
Board_Layout.add(square[i][j]);
} else {
square[i][j] = new Square(0);
Board_Layout.add(square[i][j]);
}
}
}
setTitle("Chess Mod");
setSize(FRAME_WIDTH, FRAME_HEIGHT);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(Board_Layout);
setVisible(true);
}
public void messageBox(String pMessage) {
JOptionPane.showMessageDialog(this, pMessage, "Message", JOptionPane.PLAIN_MESSAGE);
}
}
Square.java
public class Square extends JComponent {
private int color;
public Square(int c) {
this.color=c;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (this.color == 1) {
g.setColor(new Color(0,0,0));
} else {
g.setColor(new Color(255,255,255));
}
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
}
I only get one black square in the upper left hand corner.
That's mainly because of the following call:
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
getX() returns the horizontal pixel offset/location of the Component which is invoked upon, relative to the Container that contains that Component. getY() accordingly returns the vertical pixel offset/location of the Component which is invoked upon, relative to the Container that contains the Component.
getWidth() and getHeight() return the size of the Component.
So imagine that the Component at row with index 2 and column with index 3, will have its coordinates at about x == 3 * w / 8 and y == 2 * h / 8 where w and h is the size (width and height respectively) of the parent Container (ie the Board_Layout panel). Let's assume that Board_Layout has a size of 300x300 when you show the graphical user interface... This means that the Square at the location I mentioned will only paint the region which starts at x == 112 and y == 75 and expands at one 8th of the width (and height) of Board_Layout (because there are 8 rows and 8 columns in the grid). But the size of the Square itself is also at one 8th of the width (and height) of Board_Layout, ie about 37x37. So the painted region which starts and expands from the location 112,75 will not be shown at all (because it lies completely outside the Square's size).
Only the top left Square will have some paint on it because its bounds in the parent happen to intersect the drawn region.
To fix this, the location given at the Graphics object should be relative to each Square and not its parent Board_Layout. For example:
g.fillRect(0, 0, getWidth(), getHeight());
I'm making an Ultimate Tic-Tac-Toe game. If you aren't familiar with the rules, that's fine. But the board layout is just a 3x3 of Tic-Tac-Toe boards. I need an algorithm, for lack of a better term, to make it so I can make x and y wherever I want and it will draw it there correctly.
int width = 67; // Note: I've determined this variable means a lot to this "algorithm" because changing it changed the lines dramatically.
g.drawRect(0, 0, x, y); // Changing this made the boxes go to the center, so this works
g.drawLine(width, 0, width, y);
g.drawLine(width*2, 0, width*2, y);
g.drawLine(0, width, x, width);
g.drawLine(0, width*2, x, width*2);
The Class Constructor calls for the x and y to be input, so those are varying.
Specifically, I want this to work no matter what I make the x and y coordinates be.
The last 4 lines in the code make the 4 intersection bars (shaped like a very large #).
Making 1: This works fine.
Making 2: The Horizontal(--) lines work, not the Vertical(|).
Making 3: Third box doesn't even show up.
Also note that these above 3 tests are just 1 row of Tic-Tac-Toe boards.
I also understand that width can not be a single value (like it is now) but changing it makes the lines go far from where they're supposed to be so I don't know what to change it to.
Please refer to the Java Platform documentation for the Graphics class. Specifically the documentation for the drawLine() method, and also the drawRect() method (although you don't strictly need drawRect() to accomplish the task).
Based on your description, it sounds like you want to reuse the four drawLine() statements from your code sample to create each of the nine small grids as well as the one big grid.
Assuming your calling code looks something like this:
int totalWidth = 200;
// draw big grid
drawGrid(g, 0, 0, totalWidth);
// draw small grids
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
drawGrid(g, totalWidth / 3 * i, totalWidth / 3 * j, totalWidth / 3);
}
}
Here's what I would do:
private void drawGrid(Graphics g, int x, int y, int width) {
// draw box around grid (not necessary)
//g.drawRect(x, y, width, width);
// draw vertical grid lines
g.drawLine(width / 3 + x, y, width / 3 + x, y + width);
g.drawLine(width / 3 * 2 + x, y, width / 3 * 2 + x, y + width);
// draw horizontal grid lines
g.drawLine(x, width / 3 + y, x + width, width / 3 + y);
g.drawLine(x, width / 3 * 2 + y, x + width, width / 3 * 2 + y);
}
Note this doesn't put any padding around the small grids, so it looks like one huge grid. I imagine you'll want to add some padding to make it look right. You'd do that from the calling code where it loops to create the small grids.
Um. Besides the fact that it appears your asking us to do the work for you...
I would suggest not even using the Java Graphics to draw the shape of an x and to draw the shape of an o (it'll make your program all dirty, but you know.. whatever this is a tutorial). Rather, I would a series of panels JTextAreas. Give them a giant font, and make their 'isFocusable()' boolean to false. Then, I would add mouse listeners to each of them, so whenever the mouse clicks the JTextArea, it runs a method. I will write a method for you here, it's very simple. It will take one parameter, a JTextArea. This will be the area that was clicked, which can be easily be found out, because you'll be calling this method inside the mouseListener's mousePressed() method. Bare in mind, this will require a mouseListener for all 9 of your JTextAreas. This method also assumes you have created two booleans, one for the first player and one for the second. If it is Player1's turn, this method will find out, then change the turn to Player2's and vise versa. ****NOTE**** make sure you set the value of Player1 to true when you create it, or at least before this method is called.
public void makingMove(JTextArea jta) {
if (player1) {
jta.setText("x");
player1 = false;
player2 = true;
}
if (player2) {
jta.setText("o");
player1 = true;
player2 = false;
}
}
Hmm will this work? NO! what if you click a JTextArea that already has a letter in it? it'll replace it! We can't have that! so, now we need to change up our makingMove method, so we know we don't break any rules:
public void makingMove(JTextArea jta) {
if (jta.getText() == null) {
if (player1) {
jta.setText("x");
player1 = false;
player2 = true;
}
if (player2) {
jta.setText("o");
player1 = true;
player2 = false;
}
}
else {
JOptionPane.showMessageDialog(null, "You can't move there, someone already has!");
}
}
Done right? WRONG! Now we need to create a method that will check to see if anyone has won yet!
We will call this after our else statement in our makingMove() method. Like so:
.....
}
else {
JOptionPane.showMessageDialog(null, "You can't move there, someone already has!");
}
checkWinnerX(1, 2, 3, 4, 5, 6, 7, 8, 9);
checkWinnerY(1, 2, 3, 4, 5, 6, 7, 8, 9);
}
The checkWinnerX method will have to take 9 parameters, same with our checkWinnerO method. Each input object will be a JTextArea, so we can compare their input. This method assumes, your JTextAreas are named 1-9, counting like it would be a phone's dialpad. In addition, we have our checkWinnerX and checkWinnerO methods calling a method to clear everything, and reset the game, which you can assign to a button if you'd like, but I would definitely recommend having one.
*****NOTE******
I will not write the checkWinnerO method because it is the same as the checkWinnerX, but with all o's instead. and I'm sure you get the idea. AND this is an EXTREMELY inefficient way of handling this, but oh well.
public static void checkWinnerX(JTextArea 1, JTextArea 2, etc.. up to 9) {
if (1.equals("x") && 2.equals("x") && 3.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
if (4.equals("x") && 5.equals("x") && 6.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
if (7.equals("x") && 8.equals("x") && 9.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
if (1.equals("x") && 2.equals("x") && 3.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
if (1.equals("x") && 5.equals("x") && 9.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
if (7.equals("x") && 5.equals("x") && 3.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
if (1.equals("x") && 4.equals("x") && 7.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
if (2.equals("x") && 5.equals("x") && 8.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
if (3.equals("x") && 6.equals("x") && 9.equals("x")) {
JOptionPane.showMessageDialogue(null, "The winner is X!");
clear();
}
}
public static void clear() {
1.setText(null);
2.setText(null);
3.setText(null);
4.setText(null);
5.setText(null);
6.setText(null);
7.setText(null);
8.setText(null);
9.setText(null);
}
Now if you've got your mouseListeners set up correctly, and you use GridBagConstraints to correctly format your playing board. You pretty much have it done.
I could make this a thousand ways, using a thousand different methods and objects and blah, I chose this way because you specified you wanted an 'algorithm' for it. Now, you should be able to refer to this VERY broken down tutorial of a not-so-great way of making this game, and write your own code for it.
Hope this helps.
If I understand you correctly, you want a 3 x 3 grid of tic tac toe boards.
Here's a quick Swing application that draws a 3 x 3 grid of tic tac toe boards. 9 tic tac toe boards on 9 panels.
And here's the code. The drawing code is in the DrawingPanel class.
package com.ggl.testing;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TicTacToe implements Runnable {
private JFrame frame;
private List<DrawingPanel> drawingPanels;
public TicTacToe() {
this.drawingPanels = new ArrayList<>();
}
#Override
public void run() {
frame = new JFrame();
frame.setTitle("Ultimate Tic Tac Toe");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
exitProcedure();
}
});
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(3, 3, 10, 10));
for (int i = 0; i < 9; i++) {
DrawingPanel drawingPanel = new DrawingPanel();
mainPanel.add(drawingPanel);
drawingPanels.add(drawingPanel);
}
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private void exitProcedure() {
frame.dispose();
System.exit(0);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new TicTacToe());
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = -3774580797998095321L;
public DrawingPanel() {
this.setPreferredSize(new Dimension(170, 170));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(5.0F));
Rectangle r = new Rectangle(0, 0, getWidth(), getHeight());
int sectionWidth = r.width / 3;
int sectionHeight = r.height / 3;
g2d.setColor(Color.BLACK);
g2d.drawLine(r.x, r.y + sectionHeight,
r.x + r.width, r.y + sectionHeight);
g2d.drawLine(r.x, r.y + sectionHeight + sectionHeight,
r.x + r.width, r.y + sectionHeight + sectionHeight);
g2d.drawLine(r.x + sectionWidth, r.y, r.x + sectionWidth,
r.y + r.height);
g2d.drawLine(r.x + sectionWidth + sectionWidth, r.y,
r.x + sectionWidth + sectionWidth, r.y + r.height);
}
}
}
I have recently started using a mac to develop on and i am having a strange problem.
Take the Program below:
public class Driver {
public static void main(String [ ] args) {
SolarSystem SSpanel = new SolarSystem(600, 600);
SSpanel.drawSolarObject(0, 0, 30, "YELLOW");
}
}
the SolarSystem class extends JFrame and basically when the new SolarSystem is created it makes a panel of that size.
the drawSolarObjects basically draws a circle of a certain colour and size. finishedDrawing actually makes the object appear on the panel.
The example above does work but I have more complex requirements which involve putting this into a while loop.
this is where it gets weird, if i run the below program with cmd on a windows computer it works fine and prints the yellow circle to the screen. On my mac, adding this while loop causes it to just create the panel but not paint the yellow circle.
public class Driver{
public static void main(String [ ] args) {
boolean oMove = true;
SolarSystem SSpanel = new SolarSystem(600, 600);
while(oMove){
SSpanel.drawSolarObject(0, 0, 30, "YELLOW");
SSpanel.finishedDrawing();
}
}
}
I put a print into my loop to check it was running through it and that showed that it was definitely running through the loop.
Does anyone know what could be causing this?
Ive am adding the functions so you can get a better picture
SolarSystem Constructer:
public SolarSystem(int width, int height)
{
this.width = width;
this.height = height;
this.setTitle("The Solar System");
this.setSize(width, height);
this.setBackground(Color.BLACK);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
drawSolarObject Function:
public void drawSolarObject(double distance, double angle, double diameter, String col)
{
Color colour = this.getColourFromString(col);
double centreOfRotationX = ((double) width) / 2.0;
double centreOfRotationY = ((double) height) / 2.0;
double rads = Math.toRadians(angle);
double x = (int) (centreOfRotationX + distance * Math.sin(rads)) - diameter / 2;
double y = (int) (centreOfRotationY + distance * Math.cos(rads)) - diameter / 2;
synchronized (this)
{
if (things.size() > 1000)
{
System.out.println("\n\n");
System.out.println(" ********************************************************* ");
System.out.println(" ***** Only 1000 Entities Supported per Solar System ***** ");
System.out.println(" ********************************************************* ");
System.out.println("\n\n");
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
}
else
{
SolarObject t = new SolarObject((int)x, (int)y, (int)diameter, colour);
things.add(t);
}
}
}
finishedDrawing function:
public void finishedDrawing()
{
try
{
this.repaint();
Thread.sleep(30);
}
catch (Exception e) { }
synchronized (this)
{
things.clear();
}
}
This all works fine on a windows PC
Your code risks tying up the Swing event thread preventing it from drawing on your GUI, and effectively freezing your program. Instead use a Swing Timer, not a while loop to achieve your goal.
e.g.,
final SolarSystem SSpanel = new SolarSystem(600, 600);
int timerDelay = 100;
new Timer(timerDelay, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do repeated action in here
}
}).start();
As an aside, I was going to place,
SSpanel.drawSolarObject(0, 0, 30, "YELLOW");
SSpanel.finishedDrawing();
inside my timer code, but it wouldn't make sense because this code isn't "dynamic" and doesn't change anything or do any animation.
I am making a Java applet for school whose function is to randomly select six numbers for coordinates of three points and connect them to make a triangle. It is only supposed to draw one triangle and find the "length of the sides". However when I put it on my website it will redraw itself multiple times.
I made another applet, simpler, that only selects 4 random numbers for coordinates to draw a line. Same problem.
The redrawing problem seems to happen when the user moves the screen, e.g. when I scroll or when I resize the applet viewer in Eclipse. My source code is posted here.
I appreciate any help! Thanks!
import javax.swing.JApplet;
import java.awt.*;
#SuppressWarnings("serial")
public class LineApplet extends JApplet {
/**
* Create the applet.
*/
static int width;
int height;
public void init() {
width = getSize().width;
height = getSize().height;
}
public static int[] randomLine() {
int[] pointArray = new int[4];
int x;
for (int i = 0; i < 4; i++) {
x = ((int)(Math.random()*(width/10-2)))*20+10;
pointArray[i] = x;
}
return pointArray;
}
public void paint(Graphics g) {
g.setColor(Color.blue);
int[] coords = new int[4];
coords = randomLine();
g.drawLine(coords[0], coords[1], coords[2], coords[3]);
g.drawString(coords[0]/10 + ", " + coords[1]/10, coords[0], coords[1]);
g.drawString(coords[2]/10 + ", " + coords[3]/10, coords[2], coords[3]);
int midpointx = (coords[0] + coords[2])/2;
int midpointy = (coords[1] + coords[3])/2;
g.drawString(midpointx/10 + ", " + midpointy/10, midpointx, midpointy);
}
}
As pointed out by Reimues, you are re-generating your coords each time the applet is repaint.
The other problem with your paint method is actually you're not clear the previous state of the graphics context (this would have being done by paint, but you failed to respect it's functionality when you overrode it).
You have two choices.
call super.paint(g)
call super.paint(g) and call Graphics#clearRect(int, int, int, int) or Graphics#fillRect(int, int, int, int)
You should also, very rarely, need to override the paint method of top level containers. One of the reasons is that they're not double buffered, the other is the paint chain is complex and easily broken...
You're better off using a JPanel (or such) and overriding the paintComponent method instead...
UPDATED
I updated your code to demonstrate the issues.
public class TestBadApplet extends JApplet {
public void init() {
}
#Override
public void start() {
final LinePane linePane = new LinePane();
setLayout(new BorderLayout());
JButton update = new JButton("Update");
add(linePane);
add(update, BorderLayout.SOUTH);
update.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
linePane.regenerate();
}
});
}
protected class LinePane extends JPanel {
private int[] coords = new int[4];
public void regenerate() {
coords = randomLine();
repaint();
}
public int[] randomLine() {
int[] pointArray = new int[4];
int x;
for (int i = 0; i < 4; i++) {
x = ((int) (Math.random() * (Math.min(getWidth(), getHeight()) / 10 - 2))) * 20 + 10;
pointArray[i] = x;
}
return pointArray;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.blue);
g.drawLine(coords[0], coords[1], coords[2], coords[3]);
g.drawString(coords[0] / 10 + ", " + coords[1] / 10, coords[0], coords[1]);
g.drawString(coords[2] / 10 + ", " + coords[3] / 10, coords[2], coords[3]);
int midpointx = (coords[0] + coords[2]) / 2;
int midpointy = (coords[1] + coords[3]) / 2;
g.drawString(midpointx / 10 + ", " + midpointy / 10, midpointx, midpointy);
}
}
}
With super.paintComponent
Without super.paintComponent
You are calculating new co-ordinates every time paint() is called.
paint() is called every time the applet window is resized or regains focus.
To fix, you could make
int[] coords = new int[4];
a class member variable and move
coords = randomLine();
to your init() method, which will only be called once upon initialization.
Addendum:
Always call super.paint(g); when overriding paint().
For custom painting using Swing, the preferred approach is to extends a JComponent component leveraging the enhanced paint functionality offered by using paintComponent.
For more see: Performing Custom Painting.
The trouble seems to be that paint() is called every time the component needs repainting.
paint() methods should be written in a way that doesn't produce side effects and they shouldn't change the internal state of the applet. paint() must be restricted to doing just that: painting.
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