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

2014/4/24

Rain drop game, removing objects that fall to the ground

1
2
Akimb Akimb

2014/4/24

#
Hello! I am creating a game, where raindrops fall from the sky. At the ground there is a bucket, if the bucket collects the raindrop, player gets points, otherwise players loses a life. :) I have two problems and both are related to removing objects. The first problem is removing objects when they fall to the ground. I wrote code for it:
 if ( this.getY() == 590)  {
            getWorld().removeObject(this);
            Greenfoot.playSound("RainDrop.wav");
        }
If every raindrop falls to the ground - everything is fine. But if any of the raindrops are collected after a bit I get an error message: Actor not in world. An attempt was made to use the actor's location while it is not in the world. I think I realize the cause of problem - the actor gets picked up(removed) by the bucket and my code checks for every raindrop, including the removed one. How should I change my code so that it only checks those which are still in the World? The other problem is with the bucket, the code is here:
if (isTouching(Bucket.class)) {
           getWorld().removeObject(this);
           
           
        }   
The problem is, that bucket should only collect raindrops when the raindrops get picked by the ]upper part of the bucket, but with this code I can pick the raindrop even if they are to the side of the bucket. :) Thank you for help!
danpost danpost

2014/4/24

#
The problem with the error is not what you think. The execution of one act method is specific to one raindrop and has nothing to do with the acting of other raindrops. You are getting the error because when the bucket removed a raindrop from the world, that same raindrop no longer has a location within the world and therefore 'getX' and 'getY' ('getY()' being called when checking if this same raindrop is at the bottom edge of the world) cannot complete its assigned task of returning a valid coordinate for your actor. You need to add the condition that the actor be in the world before checking its location in the world:
if (getWorld() != null && this.getY() == 590)
For the raindrops being picked up from the side of the bucket, probably the easiest thing to do is check the location of the raindrop with respect to that of the bucket. If the raindrop is within raindrop speed of the top edge of the bucket and within raindrop width of the edges of the bucket, then it is good to pick up (something like that).
Akimb Akimb

2014/4/25

#
Thanks, now I dont get the error. :) I would like to ask one more thing. How should I make my raindrops pop our randomly from the sky ? I have this code:
for (int i = 0; i < 4; i++) {
        
        addObject(new Rain(), Greenfoot.getRandomNumber(getWidth()), 10  );
            
     if (getObjects(Rain.class).equals(2)) {                
         addObject(new Rain(), Greenfoot.getRandomNumber(getWidth()), 10  );
            }
        }
The code gets me four raindrops that appear at the same time, and when they fall down, no more raindrops fall down. How should I make it so that raindrops falls as many times as I want(for example they should fall as long as the player has some lives left). and not everyone at the same time ? while loop ? But if I make it a while loop, my game doesn't even start, I assume that the loop is infinite then. Thanks.
danpost danpost

2014/4/25

#
I get a strong suspicion that you are putting this code in the world constructor where it will only be executed once (when the world is created). For the code to execute repeatedly (during the running of the scenario), you need to move it inside an 'act' method. The 'act' method is what creates the loop you want. Using 'for' or 'while' will run to their completion at the time they are called and you will not notice any of the changes made during their execution (normally); you will only see the end result. In a sense, the 'act' method is similar, in that you only see the end result of what changes are made during the execution of the 'act' method; however, you are not specifying the loop and therefore each execution is actually a step in the 'bigger', 'hidden' loop, and you will see the results after each of these steps. One more thing, which leads to another. Your line 5 uses 'getObjects(Rain.class).equals(2)' for the 'if' condition to add another raindrop into the world. 'getObjects' returns a List object, not a numeric value. If you want the number of raindrops in the world, use 'getObjects(Rain.class).size() == 2'. The other thing is your 'for' loop. What are you trying to accomplish with it? If you are adding 4 (maybe 5 -- which will probably never happen) raindrops into the world each 'act' cycle, you will have a slew of them (probably resulting in massive lag) in a very short amount of time. See if you can explain in a post what you want as far as when and how many raindrops there should be in the world at any time. You will need to regulate the spawning of raindrops using a timer set to a random value; when the timer reaches zero, spawn a raindrop and reset the timer. The timer will need to be an instance int field in the world class.
Akimb Akimb

2014/4/26

#
Yes, apparently my for loop was in populate() method. :) I put the code into the act() method, discarded the loop as well as the 'if' statement - getting gazillion raindrops. :D At the start of the game, it would be good to have about 3 raindrops in the World. When one of the raindrops gets picked up by the bucket, or falls down to the ground, immediately another one comes down from the sky. After the Players gets certain amount of points, I would like to increase to number of falling raindrops to maybe five, and later increase the speed of which they are falling. :) I would like to know more about how to construct this timer, please. I checked the documentation and didn't find a similar method to work with that kind of thing. Do I need to create it myself ? :) EDIT: Found a timer in the discussions, and modified it a bit to be good for my game:
 private long timeStarted = System.currentTimeMillis();  
      
    public void act() {  
    long currentTime = System.currentTimeMillis() ; 
    long elapsedTime = currentTime - timeStarted;  
      
    if (elapsedTime / 100 > 10) { 
        timeStarted = currentTime;  
        addObject(new Rain(), Greenfoot.getRandomNumber(getWidth()), 10  );  
    }  
    }  
So that problem is solved I think. :) I'll think about the way to make the game harder as the game progresses.
danpost danpost

2014/4/26

#
Obviously, you will have to come up with some code to do what you want to do. A timer may not be what you need; especially if you are going to use the score of the player to regulate the difficulty (number of drops and speed). Also, there are a couple of ways to regulate the current number of drops. The world act method could be used to determine when a drop needs added to the world:
if (getObjects(Rain.class).size() < /* current number of drops */ )addObject(new Rain(), ... );
However, you probably do not want all three drops falling down in total synchronization from the start or two new one added at about the same time when the difficulty increases. What you might want is a little randomness:
if (getObjects(Rain.class)size() < /* current number of drops */ && Greenfoot.getRandomNumber(100) == 0) addObject( ...
You will not need to add any raindrops into the world to start. You only need the 'current number of drops' field to hold the correct value. The raindrops will come when the scenario starts. For the speed and number of drops, if 'speed' was you field for the speed of the raindrops (initially set to 3) and 'drops' was the number of drops that currently should be in the world (also, initially set to 3), then you could use something like the following:
speed = 3+score/30;
drops = 3+((score%30)/10);
With this, for every 10 point, the number of drops will increase until there are 5 drops; for every 30 points the number will go back down to 3 drops, but the speed will increase. The cycle will then begin again making increasingly difficult. This is similar to what I did in my Paintball Dodge scenario with the number and speed of the paintball pellets shot from the 44 guns. The code for it will be available within the scenario soon.
Akimb Akimb

2014/4/26

#
Thanks :) About the points though, there is a slight problem. I mentioned earlier that I had problem with the way the Game's bucket needs to to 'pick up' the rain drops - only the top of the bucket needs to 'register' the rain, not sides. But before fixing that I thought that I should create the counter. The counter kinda works, but has one weird quirk - only the sides of the bucket register the points adding up to the counter. :D If I pick the raindrop with upper part of bucket, the counter does not give any points. :) What's up with that ? I am still using isTouching(Bucket.class) btw. :)
danpost danpost

2014/4/26

#
I would have to see what code you are using for detecting and determining when a score is to be given. Also, specify which class that code is in. Though, I believe I already know it is in the Rain class in which the Bucket is detected. BTW, the source of my Paintball Dodge scenario if viewable now. Log in, play the game (whether trying to get the best score or running into as many pellets as possible quickly to end it quick), and click on the 'Arena', 'Player', or 'Gun' button to view the source.
Akimb Akimb

2014/4/26

#
I played the game and I will try to see what I can implement from it to my game, it was pretty interesting. I would suggest considering to add one pellet, which is slower than others, but actually follows the player for a short time. :) My counter class is made up of:
    private int score = 0;
    
    
    public void addScore() {
        score += 5;
    }
    
    public void act() {
        setImage(new GreenfootImage("Score: " + score, 24, Color.WHITE, Color.BLACK));
    }
In the world class I have getter:
public Counter getCounter() {
       return counter;
    }
And instantiated Counter object:
Counter counter = new Counter();
Later I add it to the populate() in the World. :) The movements are done by the code in Bucket class act() method:
 if (Greenfoot.mouseMoved(null)) {
          MouseInfo mouse = Greenfoot.getMouseInfo();
          setLocation (50 + mouse.getX() - 50, 800);
    }    
    if (Greenfoot.isKeyDown("right")) {
        setLocation(getX() + 5, getY());
    }
    if ( Greenfoot.isKeyDown("left")) {
        setLocation(getX() - 5, getY());
    }
danpost danpost

2014/4/26

#
I did not ask for the Counter class and related codes. I ask for the code that starts:
if (isTouching(Bucket.class))
which is probably in your Rain class.
Akimb Akimb

2014/4/26

#
if (isTouching(Bucket.class)) {
           getWorld().removeObject(this);
          
           
        }   
But I have
if (isTouching(Rain.class)) { World word = (World) getWorld(); Counter counter = world.getCounter(); counter.addScore(); }
In the bucket class. I would write counter.addScore() to Rain.class but it doesn't work. Get nullPointException. Have to see where's the problem. :)
danpost danpost

2014/4/26

#
Does not this:
if (isTouching(Rain.class)) { World word = (World) getWorld(); Counter counter = world.getCounter(); counter.addScore(); }
cause an error message to come up? I know this cannot be right. What is the name of your subclass of World? EDIT: I will have to depart for a little while. Will return in a bit.
Akimb Akimb

2014/4/26

#
danpost wrote...
Does not this:
if (isTouching(Rain.class)) { BigWorld world = (BigWorld) getWorld(); Counter counter = world.getCounter(); counter.addScore(); }
cause an error message to come up? I know this cannot be right. What is the name of your subclass of World? EDIT: I will have to depart for a little while. Will return in a bit.
Since English is not my native language, I write my methods and variables etc. with my native. And then rewrite them here with english names, so someone who is checking the code, could understand. :) I see now that I wrote the same name for my subclass as the name for the original World class. :) The original code gets nullPointerException error.
if (isTouching(Rain.class)) { BigWorld world = (BigWorld) getWorld(); Counter counter = world.getCounter(); counter.addScore(); } 
Here's properly edited code.
Akimb Akimb

2014/4/26

#
Fixed the code and now the counter works - it doesn't matter what place was 'touched' by the raindrop - it still adds the points up. :) The error about nullPointerException was gone, after I put the removeObject() at the very bottom of the if statement. Now all that left is to think about how should I let bucket understand, that it only needs to pick up the raindrops at front. :)
  
if (isTouching(Bucket.class)) {
          
          BigWorld world = (BigWorld) getWorld();
          Counter counter = world.getCounter();
          counter.addScore();
          getWorld().removeObject(this);
           
        }   
danpost danpost

2014/4/26

#
You need to formulate a 'hit-box' -- determine the valid coordinates of the raindrop with respect to the bucket. Something like this:
Actor bucket = getOneIntersectingObject(Bucket.class); // get reference to bucket
int xDiff = bucket.getX()-getX(), yDiff = bucket.getY()-getY(); // gets offsets of location coordinates
if (yDiff > bucket.getImage().getHeight()/2-10 && Math.abs(xDiff) < bucket.getImage().getWidth()/2-3) {
    ((Counter)((BigWorld)getWorld()).getCounter()).addScore(); // bump score counter
}
getWorld().removeObject(this); // remove raindrop
Line 1 only needs to get the bucket as an Actor object since no Bucket class fields or methods are being used, only Actor class methods). Line 2 gives us the location of the raindrop with respect to the bucket. Line 3 checks to see if the raindrop is within the 'hit-box' (the area that will count as a legal catch). The '10' is the height of the 'hit-box' and the '3' is the amount of width on one side or the other that will not be in the 'hit-box' (the width of the edge of the bucket). You can adjust these values as needed. Line 4 scores the counter only if the catch was a valid one. Line 6 removes the raindrop. Whether a valid catch or not, touching the bucket removes the raindrop.
There are more replies on the next page.
1
2