6. Scoring and Boss Practice

What We Will Cover


Illuminations

Questions from last class or the Reading?

Event Questions?

6.1: Scoring the Scroller

Learner Outcomes

At the end of the lesson the student will be able to:

  • Add more interesting scoring rules to a scenario
  • Add parameters to methods
  • Reference user defined methods in the world subclass
  • Concatenate strings
  • Add timers to scenarios

6.1.1: More Flexible Scoring in the World

Scenario file: scroller3.gfar.

  • So far our scenarios have kept score in one of the actor classes
  • For example, in our bug scenario we kept score in the Bug class (see lessons 4.3.1 and 4.3.2)

Keeping Score in an Actor

  • We could keep score in our player class as we have done before
  • First we declare an instance variable in the player:
    private int score;
    
  • Then whenever we collect an astronaut we add a value to the score
    if (isTouching(Astronaut.class))
    {
        score = score + 20;
        // ... other code here
    }
    

More Interesting Scoring Rules

  • We want to add scores for other game events like:
    • Collecting an astronaut (see above)
    • Losing points if an astronaut reaches the left side of the world
    • Being hit by an asteroid does not immediately end the games but instead we loose points
    • If our score falls below zero, we lose and the game ends
  • The second scoring rule is problematic because an astronaut detects if they have left the world
  • An actor like astronaut cannot directly call methods of other actors like our player
  • Thus an astronaut cannot directly access to the score variable in Player to adjust the score
  • We can write code to access the Player object, but it is easier if we keep the score in the world

Check Yourself

  1. True or false: some scoring actions may happen outside the player character.
  2. True or false: an actor cannot directly access the private fields of objects from another class.
  3. True or false: for more varied scoring rules, we need to store the score in a central location like the world.

6.1.2: How to Keep Score in the World

  • We keep score in the world in a similar way to keeping the score in an actor
  • First we declare an instance variable in the world:
    private int score;
    
  • Next we add a method for an actor to call that adjusts the score
    public void addScore() // like in the textbook
    {
        score = score + 20;
    }
    
  • To access a private variable in an object of another class we need use a method call
  • However the above method does not allow us to subtract 15 points for rule 2
  • We could write multiple methods, one for each scoring rule
  • However a better approach is to add a parameter to the method that allows us to both add and subtract values

Adding Parameters

  • Recall from lesson 2.4.3 that every method has a parameter list

    Parts of a method

  • For methods we have written so far, most parameter lists have been empty
  • For addScore(), we add a parameter so we have a single method to call for all scoring rules
    public void addScore(int points)
    {
        score = score + points;
    }
    
  • Our addScore() method now receives the number of points in the points parameter
  • We add the received points to our score variable, allowing us to add or subtract any value

Activity: Scoring in the World (4m)

  1. Start Greenfoot and open the scrolling world scenario from last week.

    If you do not have the scenario then use scroller3.gfar and add to your scenario as homework.

  2. Open the editor for ScrollWorld and declare an instance variable in the world:
    private int score;
    
  3. Next add the following method to the world:
    public void addScore(int points)
    {
        score = score + points;
    }
    
  4. Compile your scenario to make sure the changes were made correctly.

    If you have problems, ask a classmate or the instructor for help as needed.

  5. Test the changes by:
    1. Moving a tile, so you can access the world
    2. Right-clicking the exposed world and calling the method addScore()
    3. Right-clicking the exposed world and selecting Inspect

    You should see the score variable displayed with the value you entered in step b. If you have problems, ask a classmate or the instructor for help as needed.

  6. Save your scenario so you can update it in future exercises.

Check Yourself

  1. True or false: To access a private variable in an object of another class, the other class must provide access via a method.
  2. True or false: adding parameters to a method can make the method more flexible and useful.
  3. When calling a method with a parameter, you must supply the necessary ________.

6.1.3: Accessing a Method in the World

  • Recall that we discussed in lesson 5.3.2 how to reference the world by calling getWorld()
  • In our Player class we can add code to call our addScore() method like:
    if (isTouching(Astronaut.class))
    {
        World world = getWorld();
        world.addScore(20);
        // ... other code here
    }
    
  • However, we get an error if we do

    cannot find symbol - method addScore(int)

  • The error is due to a tricky problem that we must solve with casting

Casting Needed

  • The problem with the above code is that World does not have an addScore() method
  • Instead, our ScrollWorld subclass has an addScore() method
  • The Actor method getWorld() correctly returns a ScrollWorld but says it is returning a World
    public World getWorld()
    
  • The compiler simply accepts that getWorld() returns a World class instead of ScrollWorld
  • We solve the problem with a cast, which tells the compiler we are working with a different object: ScrollWorld
  • We write a cast by writing the correct type in parenthesis in front of the method call:
    if (isTouching(Astronaut.class))
    {
        ScrollWorld world = (ScrollWorld) getWorld();
        world.addScore(20);
        // ... other code here
    }
    
  • Casting is a tricky concept which many people will not understand at this stage
  • We will return to casting once we know more about typing and subtyping

Activity: Calling addScore() in the World (4m)

  1. Start Greenfoot and open the scrolling world scenario from the last Activity.
  2. Open the editor for the Player class and locate the checkCollision() code. Inside the method, add a call to addScore() method in the world like:
    if (isTouching(Astronaut.class))
    {
        ScrollWorld world = (ScrollWorld) getWorld();
        world.addScore(20);
        removeTouching(Astronaut.class);
        Greenfoot.playSound("hooray.wav");
    }
    
  3. Compile your scenario to make sure the changes were made correctly.

    If you have problems, ask a classmate or the instructor for help as needed.

  4. Test your code by running into an object to collect and verifying the score changes.
  5. In addition, for the object to avoid, add a call to the addScore() method with a negative score like:
    if (isTouching(Asteroid.class))
    {
        ScrollWorld world = (ScrollWorld) getWorld();
        world.addScore(-100);
        Greenfoot.playSound("crash.wav");
        //getWorld().showText("Game Over", 300, 200);
        //Greenfoot.stop();
    }
    

    In addition, remove or comment out any calls to showText() or Greenfoot.stop() as shown above.

  6. Again, compile your scenario to make sure the changes were made correctly.

    If you have problems, ask a classmate or the instructor for help as needed.

  7. Test your code by running into an object to avoid and verifying the score changes.
  8. Save your scenario so you can update it in future exercises.

Check Yourself

  1. True or false: unless we tell it differently with a cast, the compiler thinks that getWorld() returns a World object instead of a subclass of World.
  2. Changing the data type of a value is known as a ________.
  3. We write a cast by writing the correct type inside ________.
    1. curly braces: { }
    2. square brackets: [ ]
    3. parenthesis: ( )
    4. slashes: / /

6.1.4: Strings and Concatenation

  • Once we can keep the score we want to display it in the scenario
  • We can easily display the score using the World method showText()
  • The method showText() requires a String argument
  • However, our score variable has an int data type
  • Thus we will need to convert the int value from score into a String

Reviewing Strings

  • Remember from lesson 3.5.1 that a string is a sequence of characters (letters, digits, other symbols)
  • To indicate a string in a program we enclose the characters within double quotes
    "This is a string"
    "b"
    "$3.95"
    "My name is Ed"
    
  • We create variables for strings using the String data type:
    String firstName;           // declaration
    firstName = "Edward";       // assignment
    String lastName = "Parrish" // declaration + assignment
    
  • When a method parameter expects a String, we must provide either characters in double quotes or a variable of type String
    Greenfoot.isKeyDown("right")
    

Joining Strings (Concatenation)

  • We may join two strings together using the '+' operator
  • The join operation is known as concatenation
  • For example:
    String cool = "Java" + " rules!"
    System.out.println(cool);
    

String Conversions

  • We can easily convert numerical data types, like int or double, to strings using the + operator
  • If an expression starts with a String type then the compiler converts other types to String automatically
  • For example:
    System.out.println("The result is: " + 7 + 2);
    
  • Produces the output:
    The result is: 72
    
  • If you want to add 7 + 2, you need to use parenthesis:
    System.out.println("The result is: " + (7 + 2));
    

Displaying the Score with showText()

  • We can update our addScore() method in ScrollWorld class to display the score
    public void addScore(int points)
    {
        score = score + points;
        showText("Score: " + score, 80, 25);
    }
    
  • The message to showText() starts with a String
  • Thus the int score is converted to a String data type by the concatenation operation (+)

Activity: Showing the Score (3m)

  1. Start Greenfoot and open the scrolling world scenario from the last Activity.
  2. Open the editor for the world subclass and locate the addScore() method. Inside the method, add a call to showText() like:
    public void addScore(int points)
    {
        score = score + points;
        showText("Score: " + score, 80, 25);
    }
    
  3. Compile your scenario to make sure the changes were made correctly.

    If you have problems, ask a classmate or the instructor for help as needed.

  4. In addition, add a call to addScore() from the constructor to display the initial score, usually 0.
    public ScrollWorld()
    {
        super(WIDTH, HEIGHT, 1, false); // unbounded world
        prepare();
        addScore(0); // show initial score
    }
    
  5. Test your code by running the game and making sure the score is displayed.
  6. Save your scenario so you can update it in future exercises.

Check Yourself

  1. A sequence of characters is known as a(n) ________.
  2. The operator used to join two strings is ________.
  3. The contents of s3 is ________ after the following code executes.
    String s1 = "Hi ", s2 = "Mom!";
    String s3 = s1 + s2;
    

6.1.5: Keeping Time

  • One problem with our scenario is that hitting an asteroid occurs multiple times
  • This means that we will lose even for a glancing blow from an asteroid
  • What we need is some time for our player to get out of the way before another penalty is applied

Adding a Timer

  • The usual way to track time is to count game cycles
  • By counting game cycles, the timing is the same no matter the speed of the game
  • To track game cycles, we first declare an instance variable in Player
    private int hitTimer;
    
  • When we are hit by an object to avoid, we will give player some time to make an escape

Planning the Time

  • We want to allow some number of game cycles to move away from an object to avoid
  • To allow time, we add a second test condition to our if-statement for the collision
    if (isTouching(Asteroid.class) && hitTimer == 0)
    {
        // other code omitted
        hitTimer = 25;
    }
    
  • Inside the if-statement we set the amount of time we want to allow
  • Since a scenario runs about 50-60 cycles per second, we are allowing about ½ second to move

Counting Down the Time

  • In addition to the above, we need to count down our timer every game cycle
  • To count down the time, we add an else-if clause to our if-statement
    else if (hitTimer > 0)
    {
        hitTimer--;
    }
    
  • After we hit an asteroid, this code keeps the timer running until it reaches zero
  • Once the timer reaches zero we can get hit again
  • The complete if-else-if code is listed below

Adding a Hit Timer to the Asteroid Collision

if (isTouching(Asteroid.class) && hitTimer == 0)
{
    ScrollWorld world = (ScrollWorld) getWorld();
    world.addScore(-100);
    Greenfoot.playSound("crash.wav");
    //getWorld().showText("Game Over", 300, 200);
    //Greenfoot.stop();
    hitTimer = 25;
}
else if (hitTimer > 0)
{
    hitTimer--;
}

Exercise 6.1: Finishing the Scoring (10m)

In this exercise, we finish off the scoring for our scroller scenario. Make sure to compile whenever you add a line of code to verify you added the code correctly.

Specifications

  1. Start Greenfoot and open the scroller scenario from the previous Activities:
    1. Activity: Scoring in the World
    2. Activity: Calling addScore() in the World
    3. Activity: Showing the Score
  2. Open the Player class and add an instance variable for the hit timer, like:
    private int hitTimer;
    

    Again, compile after adding each line of code to verify you added the code correctly.

  3. Add a second test condition to your if-statement for the collision, like:
    if (isTouching(Asteroid.class) && hitTimer == 0)
    {
        // other code omitted
        hitTimer = 25;
    }
    
  4. In addition, set the amount of time to allow on the hit timer as shown above.
  5. To count down the time, add an else-if clause to your if-statement
    else if (hitTimer > 0)
    {
        hitTimer--;
    }
    

    For more information see section: 6.1.5: Keeping Time.

  6. Compile and run your code, verifying that hitting an object to avoid has a delay before another score penalty is applied.

    If you have problems, ask a classmate or the instructor for help as needed.

  7. As another scoring step, in Astronaut add a call to ScrollWorld that reduces the score when disappearing off the edge of the world, like:
    if (getX() <= 0)
    {
        ScrollWorld world = (ScrollWorld) getWorld();
        world.addScore(-15);
        getWorld().removeObject(this);
    }
    
  8. As a "final" step, open the editor for the ScrollWorld class and update the addScore() method with code to test if the score is less than zero, like:
    if (score < 0)
    {
        Greenfoot.playSound("game-over.wav");
        showText("Game Over", 300, 200);
        Greenfoot.stop();
    }
    

    You can use the following game over sound: game-over.wav.

  9. Compile and run your scenario to verify all the changes work well.

    If you have problems, ask a classmate or the instructor for help as needed.

  10. Save a copy of your completed lesson scenario to upload to Canvas as part of Lab 5.

When finished, please help those around you.

6.1.6: Referencing the Player

  • One common problem in a scenario is obtaining a reference to a particular object
  • For example, when an NPC runs into a player some action should occur
  • By careful placement of collision detection code, we can often avoid having to call methods of other actors
  • However, sometimes an NPC actor needs a reference to the player

Saving a Reference in the World

  • One easy way to get a reference to the player is to track the player in the world subclass
  • For example, we can store a reference to the player in an instance variable
  • private Player player;
    
  • When the ScrollWorld adds a player to the scenario, the player object is assigned to the instance variable
    player = new Player();
    addObject(player, 150, getHeight() / 2);
    
  • In our example, the Player variable is assigned in the prepare() method

Accessing the Player Reference

  • To access the player instance variable, we add a getPlayer() method to ScrollWorld
    public Player getPlayer()
    {
        return player;
    }
    
  • Now when an NPC needs a reference to the player, it can get it from the world
    ScrollWorld world = (ScrollWorld) getWorld();
    Player p = world.getPlayer();
    

Check Yourself

  1. True or false: sometimes an actor needs to call methods of another actor.
  2. True or false: one convenient way to access an actor is to keep track of the actor in the scenario world.
  3. True or false: getWorld() returns the current subclass of world as a World object.

6.1.7: Review

  • In this section we looked at how to reference user defined methods in the world subclass
  • As examples, we added more insteresting scoring rules to the game
  • To make the scoring more flexible, we added the scoring to the world subclass (ScrollWorld)
  • First we declared an instance variable in the world:
    private int score;
    
  • Next we add a method for an actor to call that adjusts the score
    public void addScore(int points)
    {
        score = score + points;
        showText("Score: " + score, 80, 25);
    }
    
  • The method has a parameter that recieves the number of points to add to the score
  • During the method call, we display the current score on the screen as well
  • To access the world method from an actor, we must use a cast like:
    ScrollWorld world = (ScrollWorld) getWorld();
    world.addScore(20);
    
  • The cast tells the compiler that getWorld() actually returns a world subclass object and not a World object

Timers

  • Another technique we looked at was to add timers to our scenario
  • The usual way to track time is to count game cycles
  • By counting game cycles, the timing is the same no matter the speed of the game
  • For our example, we first declared an instance variable in Player
    private int hitTimer;
    
  • We then added a second test condition to our if-statement testing a collision
    if (isTouching(Asteroid.class) && hitTimer == 0)
    {
        // other code omitted
        hitTimer = 25;
    }
    else if (hitTimer > 0)
    {
        hitTimer--;
    }
    
  • To count down the time, we added an else-if clause to our if-statement
  • The timer code allows us a set number of game cycles to pass until the executing the if-statement again

6.2: Preparing for Boss Basic

Learner Outcomes

At the end of the lesson the student will be able to:

  • Review possible exam questions
  • Know the study aids available for the exam

6.2.1: Reviewing the Exam Topics

  • Remember that we each chose a test topic last class meeting
  • Your homework, due today, was to develop at least 5 possible test questions for your selected topic
  • We will break into groups and review the questions

Test Review Exercise (10m)

  • Within your guild, review each set of test questions for accuracy and testing efficacy
  • Discuss ways to improve test questions and update postings in Canvas
  • Choose one excellent question to review with the entire class.
  • Write the question on the board.

6.2.2: Reviewing with Greenfoot Jeopardy

  • To help you study, we can use this review game: Greenfoot Jeopardy
  • We can work together in our guilds
  • We will start the game without a timer and with the "basics.txt" file

Game Rules

  • We will rotate the question selection so each guild gets a chance to select a question
  • We will compete within each guild -- the first to raise their hand gets to answer
  • Once the guild agrees on who was first, the person selected writes down their answer (a, b, c or d)
  • Everyone keeps track of their own score:
    • If you raise your hand first and get the question right, you get the points for the question
    • If you get the question wrong, you subtract the points
    • If you are not first to raise your hand within a team, you pass
  • The team selecting the question can volunteer the answer after every team has selected their person
Information + Practice are like Yin and Yang

Information + Practice

6.2.3: Practicing for an Exam

  • One of the purposes of an exam is to give us a chance to review the material we have covered
  • Practicing for an exam is important to doing our best
  • We do not want to wait until the night before to prepare
  • To help us practice we will review some problems during class
  • Treat the practice like homework -- because it is!
  • Anything not completed in class must be completed at home and turned in before the test

Midterm 1 Review Supplements

6.2.4: Summary of Test Preparation Materials

  • To help your understanding of how the midterm will operate, I am providing a practice exam in Canvas
  • The questions are intended to help you get a "feel" for taking an exam in Canvas
  • The questions are NOT intended to tell you everything that is on the exam
  • Suggestion: prepare first and then try the practice exam
  • This will give you a better understanding of how much more preparation you need

Summary of Test Preparation Techniques

  • Practice midterm
  • Playing Greenfoot Jeopardy
  • Playing Jumbled Games/Boss Events
  • Reviewing programming projects
  • Reviewing notes
  • Reviewing student-prepared study questions
  • Reviewing CodeLab questions

Top Rated (5) Study Areas from Students (Spring 2015)

  1. Practice midterm (9)
  2. Playing Greenfoot Jeopardy (8)
  3. Jumbled Games/Boss Events (6)
  4. Reviewing programming projects (5)
  5. Reviewing notes (5)
  6. Reviewing student-prepared study questions (3)
  7. Reviewing CodeLab questions (2)

Wrap Up

Due Next:
Q5: Basic Arcane Studies (3/2/17)
Lab 6: Piano Practice (3/7/17)
Q6: Scenario Mashup (3/9/17)
  • When class is over, please shut down your computer
  • You may complete unfinished lesson exercises at any time before the due date.
Home | Canvas | Schedule | Syllabus | Room Policies
Help | FAQ's | HowTo's | Links
Last Updated: March 11 2017 @15:24:33