This site requires JavaScript, please enable it in your browser!
Greenfoot back
Sfven
Sfven wrote ...

2021/2/25

Help with NullPointer on exercise 9.70

Sfven Sfven

2021/2/25

#
Hello all. I have been working on exercises 9.69 and 9.70 for homework. A brief explanation of exercise 9.70 and what I'm trying to do: We modify the Asteroids source code to make it to where whenever all of the asteroids have been destroyed, more than were present in the first "level" will appear for the second "level." So I currently have it where one asteroid will start in the beginning, and my goal is to have 2 asteroids spawn after you've destroyed the entire first asteroid. Then, after you've destroyed the second two asteroids, three more will spawn, so on and so forth. My issue: I am having trouble triggering asteroidsRemaining() without errors. For some reason it keeps resulting in a NullPointerException and I can't figure out why. I have an ArrayList named "asteroidsRemaining" which is supposed to be populated every time an asteroid is created, and every time that asteroid is blown up, it is removed. Once asteroidsRemaining is empty (meaning all asteroids have been blown up), then it should trigger the next "level." Homework constraints: My professor has required that we use List<> or ArrayList<> (I have used the ladder) and that we can only modify Space.java and Asteroid.java. So, here's my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Space class
 
import greenfoot.*;
 
/**
 * Space. Something for rockets to fly in.
 *
 * @author Michael Kölling
 * @version 1.2
 */
public class Space extends World
{
    private Counter scoreCounter;
    private int startAsteroids = 1;
 
    /**
     * Create the space and all objects within it.
     */
    public Space()
    {
        super(600, 500, 1);
        GreenfootImage background = getBackground();
        background.setColor(Color.BLACK);
        background.fill();
        createStars(300);
         
        Rocket rocket = new Rocket();
        addObject(rocket, getWidth()/2 + 100, getHeight()/2);
         
        addAsteroids(startAsteroids);
         
        scoreCounter = new Counter("Score: ");
        addObject(scoreCounter, 60, 480);
 
        Explosion.initializeImages();
        ProtonWave.initializeImages();
    }
     
    /**
     * Add a given number of asteroids to our world. Asteroids are only added into
     * the left half of the world.
     */
    private void addAsteroids(int count)
    {
        for(int i = 0; i < count; i++)
        {
            int x = Greenfoot.getRandomNumber(getWidth()/2);
            int y = Greenfoot.getRandomNumber(getHeight()/2);
            addObject(new Asteroid(), x, y);
        }
    }
 
    /**
     * Crete a given number of stars in space.
     */
    private void createStars(int number)
    {
        GreenfootImage background = getBackground();            
        for(int i=0; i < number; i++)
        {
             int x = Greenfoot.getRandomNumber( getWidth() );
             int y = Greenfoot.getRandomNumber( getHeight() );
             int color = 120 - Greenfoot.getRandomNumber(100);
             background.setColor(new Color(color,color,color));
             background.fillOval(x, y, 2, 2);
        }
    }
     
    /**
     * This method is called when the game is over to display the final score.
     */
    public void gameOver()
    {
        addObject(new ScoreBoard(scoreCounter.getValue()), getWidth()/2, getHeight()/2);
    }
     
    public void countScore(int score)
    {
        scoreCounter.add(score);
    }
     
    public void increaseAsteroids()
    {
        startAsteroids++; // Increase amount of asteroids by 1
        addAsteroids(startAsteroids); // Add more asteroids
    }
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Asteroid class
 
import greenfoot.*;
import java.util.ArrayList;
 
/**
 * A rock in space.
 *
 * @author Poul Henriksen
 * @author Michael Kölling
 */
public class Asteroid extends SmoothMover
{
    ArrayList asteroidsRemaining = new ArrayList(); // Create asteroidsRemaining ArrayList
     
    /** Size of this asteroid */
    private int size;
 
    /** When the stability reaches 0 the asteroid will explode */
    private int stability;
 
    /**
     * Create an asteroid with default size and random direction of movement.
     */
    public Asteroid()
    {
        this(50);
        asteroidsRemaining.add(this);
    }
     
    /**
     * Create an asteroid with a given size and random direction of movement.
     */
    public Asteroid(int size)
    {
        super(new Vector(Greenfoot.getRandomNumber(360), 2));
        setSize(size);
    }
     
    /**
     * Create an asteroid with a given size and direction of movement.
     */
    public Asteroid(int size, Vector velocity)
    {
        super(velocity);
        setSize(size);
    }
     
    public void act()
    {        
        move();
    }
 
    /**
     * Set the size of this asteroid. Note that stability is directly
     * related to size. Smaller asteroids are less stable.
     */
    public void setSize(int size)
    {
        stability = size;
        this.size = size;
        GreenfootImage image = getImage();
        image.scale(size, size);
    }
 
    /**
     * Return the current stability of this asteroid. (If it goes down to
     * zero, it breaks up.)
     */
    public int getStability()
    {
        return stability;
    }
     
    /**
     * Hit this asteroid dealing the given amount of damage.
     */
    public void hit(int damage)
    {
        stability = stability - damage;
        if (stability <= 0)
        {
            breakUp();
        }
    }
     
    /**
     * Break up this asteroid. If we are still big enough, this will create two
     * smaller asteroids. If we are small already, just disappear.
     */
    private void breakUp()
    {
        Greenfoot.playSound("Explosion.wav");
        Space space = (Space)getWorld();
         
        if (size <= 16) {
            space.countScore(3);
            asteroidsRemaining.remove(this); // This should remove this Asteroid from the asteroidsRemaining ArrayList
            getWorld().removeObject(this);
            asteroidsRemaining(); // Calls asteroidsRemaining()
        }
        else
        {
            if (size <= 25)
            {
                space.countScore(2);
            }
            else
            {
                space.countScore(1);
            }
             
            int r = getVelocity().getDirection() + Greenfoot.getRandomNumber(45);
            double l = getVelocity().getLength();
            Vector speed1 = new Vector(r + 60, l * 1.2);
            Vector speed2 = new Vector(r - 60, l * 1.2);       
            Asteroid a1 = new Asteroid(size/2, speed1);
            Asteroid a2 = new Asteroid(size/2, speed2);
            getWorld().addObject(a1, getX(), getY());
            getWorld().addObject(a2, getX(), getY());       
            a1.move();
            a2.move();
         
            getWorld().removeObject(this);
        }
    }
     
    private void asteroidsRemaining()
    {
        Space space = (Space)getWorld();
        if (asteroidsRemaining.isEmpty()) // If asteroidsRemaining ArrayList is empty, which should only be if the last asteroid has been blown up
        {
            space.increaseAsteroids(); // Call increaseAsteroids() from Space.java
        }
    }
}
Any help is appreciated ;)
Sfven Sfven

2021/2/25

#
Forgot to comment that line 28 of the asteroid class adds the asteroid to the asteroidsRemaining ArrayList.
danpost danpost

2021/2/25

#
If this asteroid is not in the world, then getWorld (line 130) for this asteroid will return null and line 133 will error (you cannot call a method on a null value). At any rate, everything regarding what you are currently trying to do is misplaced. You have EACH asteroid tracking ONLY its own state of remaining. That is, each asteroid you create gets its own list and adds itself to it. Actually, and thankfully, there is a much easier way to tell when no asteroids remain, and this can be done in your Space class. Before continuing, remove lines 14, 28, 98, 100 and 127 thru 135 in Asteroid class code above. In act method of Space class, you can use:
1
if (getObjects(Asteroid.class).isEmpty()) ...
Sfven Sfven

2021/2/25

#
In act method of Space class, you can use:
1
if (getObjects(Asteroid.class).isEmpty()) ...
Thank you Dan, that was the code I was missing. I should also mention, my professor also said that we were not allowed to modify the act method, so I replaced line 100 of the Asteroid class with the if statement you posted. It looks like this:
1
2
3
4
5
6
7
8
if (size <= 16) {
            space.countScore(3);
            getWorld().removeObject(this);
            if (space.getObjects(Asteroid.class).isEmpty()) // If the Objects list is empty
            {
                space.increaseAsteroids(); // Increase asteroids (amount is specified in the method)
            }
        }
I think this is also more efficient since it is not getting the objects every act, but only when an asteroid is removed. Anyhow, thank you for the help!
You need to login to post a reply.