Scratch for Java

Bouncing Hedgehog

Spike the hedgehog loves to roll up into a spiky ball and bounce on his trampoline, but he is a bit clumsy. Can you move the trampoline so that he never falls to the ground?

In this chapter you build a whole game on your own, using everything from the chapters before: sprites of your own classes, positions, keys, run(), if and touching. At the end you swap in your own pictures.

A few steps are marked Your turn. Try them yourself before you open the solution.

Step 1: The stage

Create a class BouncyHedgehogStage:

import org.openpatch.scratch.*;
 
public class BouncyHedgehogStage extends Stage {
  public BouncyHedgehogStage() {
    this.addBackdrop("background");
  }
 
  public static void main(String[] args) {
    Window myWindow = new Window(480, 360);
    myWindow.setStage(new BouncyHedgehogStage());
  }
}

Run it. You see the backdrop, and nothing else yet.

Step 2: The trampoline

The trampoline is a sprite, so it gets a class of its own:

import org.openpatch.scratch.*;
 
public class TrampolineSprite extends Sprite {
  public TrampolineSprite() {
    this.addCostume("spring");
    this.setSize(70);
  }
}

Line by line:

  • public class TrampolineSprite extends Sprite says that a trampoline is a kind of sprite. It can do everything a sprite can do.
  • public TrampolineSprite() { ... } is the constructor. It runs once, when a trampoline is made, and sets it up: a costume and a size.

A class is only a plan, so nothing has changed on the stage. Make a trampoline with new and add it to the stage:

import org.openpatch.scratch.*;
 
public class BouncyHedgehogStage extends Stage {
  public BouncyHedgehogStage() {
    this.addBackdrop("background");
    this.add(new TrampolineSprite());
  }
 
  public static void main(String[] args) {
    Window myWindow = new Window(480, 360);
    myWindow.setStage(new BouncyHedgehogStage());
  }
}

Run it again. The trampoline is in the middle of the stage.

Step 3: Your turn: the hedgehog

Add Spike the same way: a class HedgehogSprite with the costume spikeBall1 and a size of 40, and a line in the stage that adds him.

Solution
import org.openpatch.scratch.*;
 
public class HedgehogSprite extends Sprite {
  public HedgehogSprite() {
    this.addCostume("spikeBall1");
    this.setSize(40);
  }
}

And in BouncyHedgehogStage, below the trampoline:

this.add(new HedgehogSprite());

Step 4: Starting positions

Both sprites are in the middle, on top of each other. The trampoline belongs at the bottom. Add a line to its constructor:

this.setPosition(0, -120);

x: 0 is the middle from left to right, and y: -120 is 120 steps down from the middle.

Your turn: put Spike at the top left, at x: -180, y: 140.

Solution

In the constructor of HedgehogSprite:

this.setPosition(-180, 140);

Step 5: Move the trampoline

The arrow keys should move the trampoline. whenKeyPressed is called by the library every time a key is pressed, and key says which one it was:

import org.openpatch.scratch.*;
 
public class TrampolineSprite extends Sprite {
  public TrampolineSprite() {
    this.addCostume("spring");
    this.setSize(70);
    this.setPosition(0, -120);
  }
 
  public void whenKeyPressed(KeyCode key) {
    if (key == KeyCode.LEFT) {
      this.changeX(-10);
    } else if (key == KeyCode.RIGHT) {
      this.changeX(10);
    }
  }
}

This is Scratch's when [left arrow] key pressed, but one method handles every key, and the if decides what to do. Run it, click the stage so it gets the keys, and move the trampoline.

Step 6: Make Spike bounce

Spike should keep moving, bounce off the edges, and fly back up when he lands on the trampoline. If he gets past it, he hits the ground:

import org.openpatch.scratch.*;
 
public class HedgehogSprite extends Sprite {
  public HedgehogSprite() {
    this.addCostume("spikeBall1");
    this.setSize(40);
    this.setPosition(-180, 140);
    this.setDirection(160);
  }
 
  public void run() {
    if (this.getY() > -100) {
      this.move(2);
      this.ifOnEdgeBounce();
 
      if (this.isTouchingSprite(TrampolineSprite.class)) {
        this.setDirection(Random.randomInt(-45, 45));
      }
    } else {
      this.say("Ouch!");
    }
  }
}

Before you run it, read the code and predict what will happen. Then run it and check.

  • setDirection(160) points Spike down and a little to the right. 0 is up, 90 is right, 180 is down.
  • run() is called in every frame. As long as Spike is above y: -100 he keeps moving. Below that he is lower than the top of the trampoline, so he has missed it: he stops and says "Ouch!".
  • isTouchingSprite(TrampolineSprite.class) asks: am I touching any trampoline? If so, Spike gets a random direction between -45 and 45, which is always upwards.

The finished game

Here it is running. Click the stage and use the arrow keys.

Step 7: Your own pictures

So far every picture was built in. You can use your own just as well: a drawing of yours, a photo, or a picture from the internet that you are allowed to use.

This needs Studio or VS Code, because the browser editor cannot load files from your computer. Download the project with a hedgehog, a trampoline and a playground as picture files:

Project: Bouncy Hedgehog

The pictures are in the project folder, next to the Java files. Give each one a name of your choice, followed by its file name:

this.addCostume("hedgehog", "hedgehog.png");
this.addCostume("trampoline", "trampoline.png");
this.addBackdrop("playground", "playground.jpg");

The name is what you use later with switchCostume. File names must match exactly, including capital letters and the ending. You may need a different setSize, because these pictures are smaller than the built-in ones. Costumes, Backdrops and Sounds explains more, also about sounds.

The finished game with these pictures:

Project: Bouncy Hedgehog 100%

Things to try

Make Spike faster

Hint

How far Spike moves in each frame is the number in move.

Solution

In run(), change this.move(2); to this.move(4);. The game gets harder, so the trampoline may need to be faster as well: change the 10 in whenKeyPressed.

Space puts the trampoline back in the middle

Hint

Add a third case to the if in whenKeyPressed. KeyCode.SPACE is the space bar, and setX sets the position from left to right.

Solution
public void whenKeyPressed(KeyCode key) {
  if (key == KeyCode.LEFT) {
    this.changeX(-10);
  } else if (key == KeyCode.RIGHT) {
    this.changeX(10);
  } else if (key == KeyCode.SPACE) {
    this.setX(0);
  }
}

Count the bounces

Show how often Spike has landed on the trampoline.

Hint

You need a field bounces in HedgehogSprite, and showVariable shows it on the stage. Careful: Spike touches the trampoline for several frames in a row, so count only in the frame where the touching starts. A second field can remember whether he was touching it in the frame before.

Solution
import org.openpatch.scratch.*;
 
public class HedgehogSprite extends Sprite {
  private int bounces = 0;
  private boolean wasTouching = false;
 
  public HedgehogSprite() {
    this.addCostume("spikeBall1");
    this.setSize(40);
    this.setPosition(-180, 140);
    this.setDirection(160);
    this.showVariable("Bounces", () -> this.bounces);
  }
 
  public void run() {
    if (this.getY() > -100) {
      this.move(2);
      this.ifOnEdgeBounce();
 
      boolean touching = this.isTouchingSprite(TrampolineSprite.class);
      if (touching && !this.wasTouching) {
        this.bounces = this.bounces + 1;
        this.setDirection(Random.randomInt(-45, 45));
      }
      this.wasTouching = touching;
    } else {
      this.say("Ouch!");
    }
  }
}

When you are confident, try changeColorEffect to change Spike's colour every time he bounces.

Bouncing Hedgehog

Create Shareable URL

Select Sections