This site requires JavaScript, please enable it in your browser!
Greenfoot back
Çðæyœn
Çðæyœn wrote ...

2019/5/28

Prevent overlap of objects

1
2
Çðæyœn Çðæyœn

2019/6/1

#
I just have one more quick question. Currently I spawn eight asteroids in a random location between the right edge of the world and a location that is twice the length of the world. When the program is run, the asteroids move to the left towards the spaceship on the screen, prompting the user to dodge these asteroids. I want the game to be infinite, so what must I do to edit my code so that the FOR loop generating the eight asteroids is repeated once the asteroid with the highest x-value comes into sight (passes left over right edge)? I suspect I will somehow need to find out how to find the asteroid with the highest x-value, then create an IF block that states if the asteroid with the highest x-value (the last asteroid since all moving left) becomes less than the width of the screen, then to do the loop again to repeat the process. I originally thought I would need a timer for this, but since all the asteroids are moving a random speed (between -1 and -20), a static time delay in spawning a group of eight asteroids would not be accurate every time. Can someone please help? Here is a link to what the game looks like currently: https://drive.google.com/file/d/1nwxdlKqwgwIc97LfT-M7SmcYxBR2_TT1/view Asteroid Class:
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
/**
 * Write a description of class Asteroid here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class Asteroid extends Actor
{
 
    int aSpeed;
    GreenfootSound closeCall = new GreenfootSound("closeCallSound.wav");  
    public Asteroid(int speedIn) {
        aSpeed = speedIn;
        move(aSpeed);
         
        GreenfootImage img = new GreenfootImage(20, 20);
        img = getImage();
        img.scale(25 + Greenfoot.getRandomNumber(200), 25 + Greenfoot.getRandomNumber(200));        //random size between 25 and 200 (both for width and height, not proportional)
        setImage(img);
 
        setRotation(Greenfoot.getRandomNumber(60)-30);           //random orientation
    }
     
    protected void addedToWorld(World w)            //built-in method, no call needed
    {
        while (getX() == 0 || isTouching(Asteroid.class))          //check if any overlap occurs
        {
            setLocation(Greenfoot.getRandomNumber(w.getWidth()) + (w.getWidth() + (this.getImage().getWidth() / 2)), Greenfoot.getRandomNumber(w.getHeight()));     //random location for asteroids  
        }
    }
 
    void removeAsteroids() {                //remove asteroids if asteroids are no longer needed to save space
        if (this.isAtEdge()) {
            if (this.getY() + (this.getImage().getHeight() / 2) < 0) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            } else if (this.getY() - (this.getImage().getHeight() / 2) > getWorld().getHeight()) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            } else if (this.getX() + (this.getImage().getHeight() / 2) < 0) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            }
        }
    }
     
    void CollisionDetection() {                               //prevents overlap of asteroids when moving
        Actor a = getOneIntersectingObject(Asteroid.class);    
        if(a != null)
        {
            if (this.getX() < a.getX()) {
                this.setLocation(this.getX() - 4, this.getY());
            
            if (this.getX() > a.getX()) {
                this.setLocation(this.getX() + 4, this.getY());
            }
            if (this.getY() < a.getY()) {
                this.setLocation(this.getX(), this.getY() - 4);
            }
            if (this.getY() > a.getY()) {
                this.setLocation(this.getX(), this.getY() + 4);
            }
        }
    }
     
    /**
     * Act - do whatever the Asteroid wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act()
    {
        //random movement
        move(this.aSpeed);
        CollisionDetection();
        removeAsteroids();
    }   
}
World Class:
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.lang.*;
/**
 * Write a description of class MyWorld here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class MyWorld extends World
{
    private SpaceBackground Image1, Image2;
    /**
     * Constructor for objects of class MyWorld.
     *
     */
    public MyWorld()
    {   
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(1200, 800, 1, false);
         
        Image1 = new SpaceBackground();
        addObject(Image1, getWidth()/2, getHeight()/2);
        Image2 = new SpaceBackground();
        addObject(Image2, getWidth() + getWidth()/2, getHeight()/2);
         
        Spaceship ship = new Spaceship();
        addObject(ship, 100, getHeight()/2);
         
        spawn();
    }
     
    void spawn() {
        for(int i = 0; i <= 7;i++) {                       //spawning only 6 asteroids for testing
            Asteroid test = new Asteroid((Greenfoot.getRandomNumber(19)-20));
            addObject(test, 0, 0);
        }
    }
     
    public void act() {
        Image1.ActivateScroll();
        Image2.ActivateScroll();
    }
}
This is the last problem with my game I am trying to fix.
danpost danpost

2019/6/1

#
You could add a boolean field to the Asteroid class
1
private boolean passedEdge;
to indicate passed right edge of the world. Add code into act method to check for edge crossing:
1
2
3
4
if (!passedEdge && getX() < getWorld().getWidth())
{
    ...
}
We will fill in the ellipses shortly. In the MyWorld class, add an int field to count asteroids entering world:
1
private int entryCount;
and a method for asteroids to call to bump the count when they enter the world:
1
2
3
4
public void bumpEntryCount()
{
    if (++entryCount % 8 == 0) spawn();
}
For the ellipses, you can now put:
1
2
((MyWorld)getWorld()).bumpEntryCount();
passedEdge = true;
Çðæyœn Çðæyœn

2019/6/2

#
Tried this, and this error comes up: Here is my current code after adding the changes: Asteroid Class:
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
/**
 * Write a description of class Asteroid here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class Asteroid extends Actor
{
    private boolean passedEdge;
    int aSpeed;
    //GreenfootSound closeCall = new GreenfootSound("closeCallSound.wav");  
    public Asteroid(int speedIn) {
        aSpeed = speedIn;
        move(aSpeed);
         
        GreenfootImage img = new GreenfootImage(20, 20);
        img = getImage();
        img.scale(25 + Greenfoot.getRandomNumber(200), 25 + Greenfoot.getRandomNumber(200));        //random size between 25 and 200 (both for width and height, not proportional)
        setImage(img);
 
        setRotation(Greenfoot.getRandomNumber(60)-30);           //random orientation
    }
     
    protected void addedToWorld(World w)            //built-in method, no call needed
    {
        while (getX() == 0 || isTouching(Asteroid.class))          //check if any overlap occurs
        {
            setLocation(Greenfoot.getRandomNumber(w.getWidth()) + (w.getWidth() + (this.getImage().getWidth() / 2)), Greenfoot.getRandomNumber(w.getHeight()));     //random location for asteroids  
        }
    }
 
    void removeAsteroids() {                //remove asteroids if asteroids are no longer needed to save space
        if (this.isAtEdge()) {
            if (this.getY() + (this.getImage().getHeight() / 2) < 0) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            } else if (this.getY() - (this.getImage().getHeight() / 2) > getWorld().getHeight()) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            } else if (this.getX() + (this.getImage().getHeight() / 2) < 0) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            }
        }
    }
     
    void CollisionDetection() {                               //prevents overlap of asteroids when moving
        Actor a = getOneIntersectingObject(Asteroid.class);    
        if(a != null)
        {
            if (this.getX() < a.getX()) {
                this.setLocation(this.getX() - 4, this.getY());
            
            if (this.getX() > a.getX()) {
                this.setLocation(this.getX() + 4, this.getY());
            }
            if (this.getY() < a.getY()) {
                this.setLocation(this.getX(), this.getY() - 4);
            }
            if (this.getY() > a.getY()) {
                this.setLocation(this.getX(), this.getY() + 4);
            }
        }
    }
     
    /**
     * Act - do whatever the Asteroid wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act()
    {
        //random movement
        move(this.aSpeed);
        CollisionDetection();
        removeAsteroids();
         
        if (!passedEdge && getX() < getWorld().getWidth())
        {
            ((MyWorld)getWorld()).bumpEntryCount();
            passedEdge = true;
        }
    }   
}
World Class:
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
/**
 * Write a description of class MyWorld here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class MyWorld extends World
{
    private SpaceBackground Image1, Image2;
    private int entryCount;
    /**
     * Constructor for objects of class MyWorld.
     *
     */
    public MyWorld()
    {   
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(1200, 800, 1, false);
         
        Image1 = new SpaceBackground();
        addObject(Image1, getWidth()/2, getHeight()/2);
        Image2 = new SpaceBackground();
        addObject(Image2, getWidth() + getWidth()/2, getHeight()/2);
         
        Spaceship ship = new Spaceship();
        addObject(ship, 100, getHeight()/2);
         
        spawn();
    }
     
    void spawn() {
        for(int i = 0; i <= 7;i++) {                       //spawning only 6 asteroids for testing
            Asteroid test = new Asteroid((Greenfoot.getRandomNumber(19)-20));
            addObject(test, 0, 0);
        }
    }
     
    public void bumpEntryCount()
    {
        if (++entryCount % 8 == 0) spawn();
    }
     
    public void act() {
        Image1.ActivateScroll();
        Image2.ActivateScroll();
    }
}
danpost danpost

2019/6/2

#
Remove line 15.
Çðæyœn Çðæyœn

2019/6/2

#
Removed line 15 from Asteroid class, same error comes up: "java.lang.IllegalStateException: Actor not in world. An attempt was made to use the actor's location while it is not in the world. Either it has not yet been inserted, or it has been removed." It also underlines which line is the problem: edit: I tried this, and I got it working to be infinite:
1
2
3
4
5
6
7
8
9
10
if (getWorld() != null) {
    if (!passedEdge && getX() < getWorld().getWidth())
    {
        ((MyWorld)getWorld()).bumpEntryCount();
        passedEdge = true;
    }
} else if (getWorld().getObjects(Asteroid.class).size() == 0) {
    removeAsteroids();
    ((MyWorld)getWorld()).spawn();
}
Super_Hippo Super_Hippo

2019/6/2

#
You could move the "removeAsteroids" method call to the end of the act method. Checking if no asteroids are in the world in which the asteroid is, doesn't seem right.
Çðæyœn Çðæyœn

2019/6/2

#
My edit above only seems to work, but the asteroids are not removed (tested this). Tried what you suggested Super_Hippo, the same error (illegal state exception) comes up, due to trying to get the x-location of an asteroid that has been removed from the world, which only happens in the cases when the last asteroid with the highest x-value becomes removed (passing top or bottom edge) before coming into sight (x-value less than world width). It might be possible for me to implement collision detection so asteroids cannot pass the top or bottom edge (bounce) before passing left over the right edge of the world, and then once the asteroids' x location is less than the world width, then the asteroids can pass the edges (to be removed).
Çðæyœn Çðæyœn

2019/6/3

#
By the way, if I have a different question about the same game, should I ask over the current discussion, or create a new one?
Super_Hippo Super_Hippo

2019/6/3

#
Oh sorry, I somehow didn't see the notification from your last reply… I think you should show how the code currently looks. You can do both. Creating a new topic might help someone with the same question to find the answer in the future.
Çðæyœn Çðæyœn

2019/6/4

#
As I mentioned above, the error that states "you are trying to remove something that doesn't exist" is due to the last asteroid generated by the FOR loop being removed from the world (either by passing the top or bottom edge), before coming into sight (meaning x-location passes left over right edge, getX() < getWorld().getWidth()). I am proposing that if I change my code so that asteroids that are not yet in sight cannot be removed, this will force the line "if (!passedLeftEdge && getX() < getWorld().getWidth())" to not cause any errors. For now, I put "if (getWorld() != null)" over this, to rid of this error, as will been seen in my code below. Currently, I have the removeAsteroids() method commented out because calling it at the end of the act() method doesn't cause any errors, but removes the game's ability to be infinite (spawn asteroids over an infinite amount of time). Asteroid Class:
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
/**
 * Write a description of class Asteroid here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class Asteroid extends Actor
{
    private boolean passedLeftEdge;
    int aSpeed;
    //GreenfootSound closeCall = new GreenfootSound("closeCallSound.wav");  
    public Asteroid(int speedIn) {
        aSpeed = speedIn;
        move(aSpeed);
         
        GreenfootImage img = new GreenfootImage(20, 20);
        img = getImage();
        img.scale(25 + Greenfoot.getRandomNumber(200), 25 + Greenfoot.getRandomNumber(200));        //random size between 25 and 200 (both for width and height, not proportional)
        setImage(img);
 
        setRotation(Greenfoot.getRandomNumber(40)-20);           //random orientation
    }
     
    protected void addedToWorld(World w)            //built-in method, no call needed
    {
        while (getX() == 0 || isTouching(Asteroid.class))          //check if any overlap occurs
        {
            setLocation(Greenfoot.getRandomNumber(w.getWidth()) + (w.getWidth() + (this.getImage().getWidth() / 2)), Greenfoot.getRandomNumber(w.getHeight()));     //random location for asteroids  
        }
    }
 
    void removeAsteroids() {                //remove asteroids if asteroids are no longer needed to save space
        if (this.isAtEdge()) {
            if (this.getY() + (this.getImage().getHeight() / 2) < 0) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            } else if (this.getY() - (this.getImage().getHeight() / 2) > getWorld().getHeight()) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            } else if (this.getX() + (this.getImage().getWidth() / 2) < 0) {
                getWorld().removeObject(this);
                //System.out.println("surpassed edge");
            }
        }
    }
     
    void CollisionDetection() {                               //prevents overlap of asteroids when moving
        Actor a = getOneIntersectingObject(Asteroid.class);    
        if(a != null)
        {
            if (this.getX() < a.getX()) {
                this.setLocation(this.getX() - 4, this.getY());
            
            if (this.getX() > a.getX()) {
                this.setLocation(this.getX() + 4, this.getY());
            }
            if (this.getY() < a.getY()) {
                this.setLocation(this.getX(), this.getY() - 4);
            }
            if (this.getY() > a.getY()) {
                this.setLocation(this.getX(), this.getY() + 4);
            }
        }
    }
     
    public void repeat() {
        if (getWorld() != null) {
            if (!passedLeftEdge && getX() < getWorld().getWidth())
            {
                ((MyWorld)getWorld()).count();
                passedLeftEdge = true;
            }
        }
    }
     
    /**
     * Act - do whatever the Asteroid wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act()
    {
        //random movement
        move(this.aSpeed);
        CollisionDetection();
        repeat();
 
        //removeAsteroids();
    }   
}
World Class:
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
/**
 * Write a description of class MyWorld here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class MyWorld extends World
{
    private SpaceBackground Image1, Image2;
    private int entryCount;
    GreenfootSound backgroundMusic = new GreenfootSound("GameScreenMusic.wav");             //background music assigned to a variable
    /**
     * Constructor for objects of class MyWorld.
     *
     */
    public MyWorld()
    {   
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(1200, 800, 1, false);
         
        Image1 = new SpaceBackground();
        addObject(Image1, getWidth()/2, getHeight()/2);
        Image2 = new SpaceBackground();
        addObject(Image2, getWidth() + getWidth()/2, getHeight()/2);
         
        Spaceship ship = new Spaceship();
        addObject(ship, 100, getHeight()/4);
        Spaceship2 ship2 = new Spaceship2();
        addObject(ship2, 100, (getHeight() / 2) + (getHeight() / 4));
         
        spawn();
    }
     
    public void spawn() {
        for(int i = 0; i <= 7;i++) {                       //spawning only 6 asteroids for testing
            Asteroid test = new Asteroid((Greenfoot.getRandomNumber(19)-20));
            addObject(test, 0, 0);
        }
    }
     
    public void count()
    {
        if (++entryCount % 8 == 0) spawn();
    }
     
    public void act() {
        backgroundMusic.playLoop();         //play background music on run()
        Image1.ActivateScroll();
        Image2.ActivateScroll();
        if (getObjects(Spaceship.class).isEmpty() && getObjects(Spaceship2.class).isEmpty()) {
            Image1.setLocation(this.getWidth() / 2, this.getHeight() / 2);
            Image2.setLocation(this.getWidth() / 2, this.getHeight() / 2);
        }
    }
}
Trying to figure this out, because although this current code cause no errors and works well, it is laggy since no asteroids are being removed, and will continue to become more slow as more asteroids spawn. Thank you for your help so far.
Super_Hippo Super_Hippo

2019/6/4

#
Couldn't you remove the asteroid in line 72? So instead of using this "passedLeftEdge" variable, you could remove the asteroid from the world.
danpost danpost

2019/6/4

#
Instead of spawning an array of 7 or so asteroids at a time, would it not be easier to just spawn one at a time at regular intervals? That would avoid the mess of having to know when to spawn again.
You need to login to post a reply.
1
2