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

2013/12/1

Passing Parameters Between Worlds

1
2
Adept_Red Adept_Red

2013/12/1

#
Ok I need help with this... I've looked through tutorials and the forms and am still just lost. In my Battlefield world I have scoreCounter actor. It works just fine, but I need to return the value of the currentScore on to the Highscore world. I gather that in order to do this I need a "reference" but I don't understand the relationship between these things. How can I pass on the value of currentScore to be used by actors in the Highscore world? I am not quite sure what code I should share that would be helpful to answering this question, if any. Regardless here is my code for the scoreCounter actor...
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.awt.Color; // Because black text isn't going to work.
import java.awt.Font; //Because default font is lame.
/**
 * This Actor displays the base score, incrementing while the Player is alive.
 */
public class ScoreCounter extends Actor
{
   //sets inital score
    private int score = 0;
     
    public void act()
     
    {
           
        updateScore();
        giveScore();
         
    }
     
        public boolean detectPlayer()
        {
            return !getObjectsInRange(1000, Player.class).isEmpty();
         
        }
         
        public void updateScore()
        {
            // draws a String with the added score in the image.
             
            //increases timer over time.
            if(detectPlayer())
            {
                score=score+1;
            }
            //creates and updates image
            setImage (new GreenfootImage(200, 40));
            GreenfootImage img = getImage(); //current image
            img.clear(); //erase old score
             
            //customizes font
            float fontSize = 20.0f;
            Font font = img.getFont().deriveFont(fontSize);
            img.setFont(font);
             
            //draws the image
            img.setColor(Color.WHITE);
            img.drawString("Experience: " + score, 0, 20); //display new score
             
             
        }
         
   public int giveScore()
   {
        int currentScore = score;
        return currentScore;
   }
         
}
You can kind of ignore the "giveScore" part... just part of a sad attempt. Thanks in advance...
danpost danpost

2013/12/1

#
Adept_Red wrote...
You can kind of ignore the "giveScore" part... just part of a sad attempt. Thanks in advance...
Actually, it is not a sad attempt. Your 'score' field is 'private' and the 'giveScore' method provides access to the value of 'score' outside the class. What you need now, is a reference to the 'scoreCounter' object in the class you set the 'Highscore' world in and a way to pass the value to the 'Highscore' world. This can be done by adding an argument in the constructor of the 'Highscore' class or by calling a method in the 'Highscore' class that accepts the value as a parameter of the method.
Adept_Red Adept_Red

2013/12/1

#
Thanks for the quick replay danpost. That clears things up a bit, now lets see if I follow. So an example of this method call in the Highscore class might be something like this?
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 
/**
 *
 */
public class Highscore_Screen extends World
{
 
    /**
     * Constructor for objects of class Highscore_Screen.
     *
     */
    public Highscore_Screen()
    {   
        // Create a new world with 800x500 cells with a cell size of 1x1 pixels.
        super(800, 500, 1);
         
        getScore();
         
        addObject(new Cursor_HS(),270,460);
        addObject(new HS_controller(),0,0);
    }
     
        public int getScore(ScoreCounter)
        {
            final int finalScore = currentScore;
        }
}
danpost danpost

2013/12/2

#
The method in your 'Highscore_Screen' world would not be called 'getScore', but 'setScore'. Also, it would not be called from the constructor of its class, but from the class that creates and sets this world as active.
1
2
3
Highscore_Screen hss = new Highscore_Screen();
hss.setScore(scoreCounter.getScore());
Greenfoot.setWorld(hss);
Line 1 creates a new 'Highscore_Screen' world. Line 2 passes, via the method call, the score to the new world. Line 3 sets the new world as active. This is done while your standard game world is active. The 'setScore' method would be more like this:
1
2
3
4
public void setScore(int score)
{
    // use the int 'score' for displaying
}
The problems with your 'getScore' method above are the following: (1) you call 'getScore()' at line 18 using no parameters, yet your declaration statement says the method requires an 'int' value as a parameter; (2) you declare a return type of 'int' from the method, yet you have no 'return' statement within the method; (3) you declare a 'final int' field within the method, yet it is not used later within the method and its value is lost once the method is done executing; (also, I do not think you can declare 'final' fields within a method). In view of these problems, I feel it might be best that you review the Java tutorials on Classes and Objects.
Adept_Red Adept_Red

2013/12/2

#
I understand what your code does, but I'm afraid I'm still very confused at what is going where. I imagine that the first 3 lines you posted would be used in the 'Battlefield' world somewhere, but the second line doesn't make sense to me because you're using setScore and getScore, but you said getScore should setScore? So then is getScore a method in the scoreCounter actor? Or is it somewhere else? Sorry I'm so lost...
danpost danpost

2013/12/2

#
As far as the name of the method is concerned, I was just making a point that the name of the method should convey what the method does. If it is returning a value, usually one would give the method a name beginning with 'get' and followed by the name of the field whose value is being returned; if it is receiving a value to set a field to, it is usually given a name beginning with 'set' followed by the name of the field. The first 3 lines I posted above was coded for the specific class that has the 'scoreCounter' field (which is a reference to the ScoreCounter object that holds and displays the 'score' value ). Actually I was not quite sure what your coding of the method was supposed to do, 'get' or 'set' the score. It had an 'int' return type (which meant it was to return a value, like a 'get' method would do) AND it had an 'int' parameter (which meant it was to receive a value, like a 'set' method would normally have). Then, within the method, you do not return any value. In fact, the method declaration declares an argument type, but does not give a field name to any object that might be received.
1
public int getScore(ScoreCounter /* missing field name */)
On top of that, your line 18 neither sets a field to any value (or used that possible returned value) that might be received nor passed an argument to the method. I know you are probably still confused. Please explain exactly what your thoughts are, so they can be corrected.
Adept_Red Adept_Red

2013/12/3

#
Well since the confusion is about more concept than syntax, perhaps it might help if I mapped it out. 1. The giveScore method returns the value of currentScore in ScoreCounter object. (first post) 2. The Battlefield class (the world where ScoreCounter exists) then needs this 'getScore' method to get the value returned in ScoreCounter in order to send it HighScore. This is the 'reference'? 3. the 3 lines you posted then initialize the HighScore world and sends 'getScore' to a receiving method called 'setScore' in the HighScore class. 4. Once 'setScore' has received the value of ScoreCounter from getScore, objects in HighScore (like HS_Controller) can then use it do whatever fancy things it feels like doing. How much of this do I understand this much correctly?
danpost danpost

2013/12/3

#
Alright, let me explain. 1. The giveScore method (which I suggested you rename getScore) does return the current score of the ScoreCounter object that it is called on:
1
2
3
// world class code EXAMPLE (demo)
ScoreCounter counter = (ScoreCounter)getObjects(ScoreCounter.class).get(0);
int finalScore = counter.giveScore();
Line 2 above gets a reference to a ScoreCounter object that is in your world (if you only have one ScoreCounter object in your world, it will get a reference to the correct ScoreCounter object). Line 3 sets a local field (finalScore) to the score of that ScoreCounter object. 2. The Battlefield class would either use code similar to that in (1) above (line 2) to get the reference to the ScoreCounter object OR would have an instance field to hold the object:
1
2
3
4
5
6
7
// EXAMPLE CODE
// instance field in world class (in the class, not in a method)
private ScoreCounter scoreCounter;
// in constructor
scoreCounter = new ScoreCounter();
// when game over
int finalScore = scoreCounter.giveScore();
'scoreCounter' is the name of the field that refers to the ScoreCounter object (the reference). 3. I believe you understand correctly (but the way you explained it is unclear). The 3 lines I posted creates a HighScore world and then uses the 'setScore' method in that world to pass the value of 'score' held by the ScoreCounter object to it; then, sets that world as active. 4. The 'setScore' method in the HighScore class should then finalize the preparation of that world (possible finalization processes may include changing/altering the background image of the world and adding/adjusting actors within the world).
8bitcarrotjuice 8bitcarrotjuice

2013/12/4

#
Couldn't you just make the variable static?
danpost danpost

2013/12/4

#
8bitcarrotjuice wrote...
Couldn't you just make the variable static?
Which variable are you referring to? If you are referring to 'score' in the ScoreBoard class, then no. Each instance of the ScoreBoard class should have its own score value. If you are referring to 'scoreBoard' in the Battlefield class, then again, no. By making it 'static', it will not reset with the scenario. Also each Battlefield world instance should have its own ScoreBoard object referred to by 'scoreBoard'.
Adept_Red Adept_Red

2013/12/4

#
Alright! Now we're making progress. I think. Here is the code for Field_Screen (instead of Battlefield), HighScore, and an object in Highscore called hs_controller.
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
public class Field_Screen extends World
{
    //Time for controling Bacon spawns
    private int wait = 0;
    private final int spawnDelay = 500;
    private boolean spawnBacon = false;
     
    //create reference to scoreCounter object
    ScoreCounter counter = (ScoreCounter)getObjects(ScoreCounter.class).get(0); 
    int finalScore = counter.giveScore();
 
    public Field_Screen()
    {   
        // Create a new world with 800x500 cells with a cell size of 1x1 pixels.
        super(800, 500, 1);
        addObject(new PlayerUI(),145,75);
        addObject(new Player(),600,400);
        addObject(new Rocket(),740,300);
        addObject(new CageTear(),300,400);
        addObject(new ScoreCounter(),700,50);
        addObject(new PlayerHealth(),205,58);
        addObject(new PlayerMagic(),205,88);
         
        makinBacon();
         
         
        endGame(finalScore);
    }
     
         
         
        public void endGame(int finalScore)
        {
            if (Greenfoot.isKeyDown("escape"))
            {
                Highscore_Screen hss = new Highscore_Screen(); 
                hss.setScore(finalScore); 
                Greenfoot.setWorld(hss);
            }
        }
         
         
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Highscore_Screen extends World
{
    /**
     * Constructor for objects of class Highscore_Screen.
     */
    public Highscore_Screen()
    {   
        // Create a new world with 800x500 cells with a cell size of 1x1 pixels.
        super(800, 500, 1);
                         
        addObject(new Cursor_HS(),270,460);
        addObject(new HS_controller(),0,0);
    }
         
        public int setScore(int finalScore)
        {          
            int hsScore = finalScore;
            return hsScore;
             
        }
}
public class HS_controller extends Actor { /** * Act - do whatever the HS_controller wants to do. This method is called whenever * the 'Act' or 'Run' button gets pressed in the environment. */ public void act() { setRanks(); displayCurrent(hsScore, rankArray); //displayHoF(); } So how are we looking now? Assuming the code for the classes are correct, in hs_controller I cannot use hsScore as a parameter yet. Am I just missing something obvious, or do I need yet another reference to pass the score from the Highscore world to the object there?
davmac davmac

2013/12/4

#
I'd suggest three changes:
  • Create an instance variable in Highscore_Screen to keep track of the HS_Controller instance that you create (on line 12). You'll need to create it and add it to the world in two steps instead of the one step you currently use.
  • Add a constructor to HS_controller which takes an 'int' parameter, and saves this to an instance variable. You then need to pass the appropriate value to the constructor when you create the instance from Highscore_Screen.
  • You can then use the value of the instance variable in HS_Controller.
danpost danpost

2013/12/4

#
First of all, Line 9 of the Field_Screen class will not work because you will not as yet have any ScoreCounter objects in the world when the line is executed, so the list will have no elements; trying to get the first element, using 'get(0)', when there are none will cause an IndexOutOfRange exception. Remove lines 8 through 10. Actually, replace line 8 with:
1
ScoreCounter counter = new ScoreCounter();
and change line 20 to:
1
addObject(counter, 700, 50);
Line 8 declares the instance field that holds a ScoreCounter object and initializes it to a new one. Line 20 adds the ScoreCounter object held by that field into the world. I do not see a method called 'makinBacon' in the Field_Screen class code and cannot say whether it should be in the constructor of this class or not; but, I can say, for sure, that line 27 should not be in the world constructor (in the block of code you show it in above). It should be executed from the 'act' method of that class.
Adept_Red Adept_Red

2013/12/6

#
davmac wrote...
Create an instance variable in Highscore_Screen to keep track of the HS_Controller instance that you create (on line 12). You'll need to create it and add it to the world in two steps instead of the one step you currently use.
You're going to have to elaborate on this for me. I thought you only needed to do this if you had multiple of the same object. I will only have one HS_controller. I have gone ahead with your second recommendation and added a constructor to HS_controller to accept setScore and put it in a local field.
danpost wrote...
Line 8 declares the instance field that holds a ScoreCounter object and initializes it to a new one. Line 20 adds the ScoreCounter object held by that field into the world.
I like this better because it's easier to understand, also I realize putting the old code before the constructor even added scoreCounter didn't make much sense, as it had nothing to use. However, Removing lines 8 through 10 gets rid of: int finalScore = counter.giveScore(); So if this line is redundant, then how does endGame use counter as a parameter, if there are no local fields to use?
davmac davmac

2013/12/6

#
You're going to have to elaborate on this for me. I thought you only needed to do this if you had multiple of the same object. I will only have one HS_controller.
It's true that if you only have one object of some particular type in the world, you can find it without needing to track it via a local field. However. adding a field is best from a design point of view (it means you can later safely invalidate the assumption that there is only one, and it makes the singular relation clearer) and is more efficient.
So if this line is redundant, then how does endGame use counter as a parameter, if there are no local fields to use?
I believe the line is not so much redundant as it is in the wrong place. Where you have it, it will execute the moment that the Field_Screen world is created (and obtain the score at that time, which will of course be 0). You want it to execute when game ends, i.e. in the endGame(...) method. I think you also should change the HighscoreSCreen class - remove the setScore method, and instead pass in the score to the constructor (i.e. add a parameter to the constructor for this purpose). This will make it much easier to get the score to the HS_controller.
There are more replies on the next page.
1
2