The Greenfoot Programmers' Manual

 

Version 0.3

for Greenfoot 1.0

 

 

Michael Kölling
University of Kent

 

 

Copyright © M Kölling, 2006

This document may be reproduced in hard copy.

Mirroring of electronic versions on sites other
than greenfoot.org is prohibited.

 

 

1    Introduction

This manual is an introduction to programming in Greenfoot. It starts from the start: We first discuss how to create a new scenario, then how to create worlds and actors, and so on.

This may not be the order in which you approach your own personal Greenfoot programming experience. In fact, probably the most common way for people to start programming in Greenfoot is by modifying an existing scenario. In that case, you already have a scenario, a world, and one or more actor classes.

Feel free to jump right into the middle of this manual and start reading there. You may, for instance, be interested in generating actor images in a certain way, or in dealing with collisions. The secitons in this manual were written with the goal that they can be read independently - there is no strong need to read everything in order.

Every now and then, when it is useful to refer to examples, we will use the 'wombats', 'ants' and 'lunarlander' scenarios as examples. All these scenarios are included in the standard Greenfoot distribution. You can find them in the 'scenarios' folder.

Have fun!

 

2    Creating a new scenario

The first thing to do when you want to create your own program is to create your own scenario. (Scenarios are sometimes called 'projects' - scenario and project are synonyms in our context. Maybe we should decide one day what we want to call them and stick to one word...)

Doing this is easy: choose 'New' from the 'Project' menu, and select a location and name for your scenario. Greenfoot will create a folder with that name that contains all files associated with your scenario.

The scenario will be opened, and you will see a screen similar to the one on the left. That is: you have the 'World' and 'Actor' classes in the class display, and not much else.

Both 'World' and 'Actor' are abstract classes - that is: you cannot create any objects of them. Especially, there is no world object at the moment, because we have no finished world class to make a world object from.

As a result, even if we had an actor object now, we could not place it anywhere, because we cannot create a world.

In order to progress from here, we need to create subclasses (that is: special cases) of World and of Actor. In other words: we have to define our own world, and then we have to define one or more of our own actors.

That's what we'll start in the next section.

 

3    Using the API

When programming with Greenfoot, it is essential to know about the methods available in the standard Greenfoot classes. The available methods are known as the Greenfoot API (for "Application Programming Interface"), and they are available from the Greenfoot web site.

You can also select the 'API Documentation' function from Greenfoot's Help menu (right) to open the API documentation in a web browser.

Greenfoot provides four classes that you shoudl know about. They are World, Actor, Greenfoot and GreenfootImage.

The World and Actor classes serve as superclasses for our own world and actor classes - we have seen them in the class display.

'GreenfootImage' is a class that provides images and image drawing methods for us to use with our worlds and actors.

'Greenfoot' is a class that gives us access to the Greenfoot framework itself, such as pausing the execution or setting the speed.

All of these classes will be mentioned more below. While you are programming in Greenfoot, it is often a good idea to have the API available to us, either printed out or in a web browser window.

 

4    Creating a world

To create a new world, select 'New subclass...' from the World class's popup menu (see right).

After choosing a name for your new world and clicking Ok, you should see the new world in the class display. Your new world class will have a skeleton that is valid and compiles.

You can now compile your scenario, and you will notice that the new world is automatically visible in the world view. This is one of the built-in features of Greenfoot: whenever a valid world class exists, Greenfoot will, after every compilation, create one world object and show it in the world display.

In theory, you could have multiple world subclasses in a single Greenfoot scenario. It is non-deterministic, though, which of those world will then be automatically created. You could manually create a new world using the world's constructor, but in practice we usually have only a single world subclass in every scenario.

World size and resolution

Let us have a closer look at the code inside the new world class. The default skeleton looks something like this:

import greenfoot.*;  // (World, Actor, GreenfootImage, and Greenfoot)
public class MyWorld extends World
{
   /**
    * Create a new world with 20x20 cells
    * with a cell size of 10x10 pixels.
    */
   public MyWorld() {
       super(20, 20, 10);
   }
}

The first things we have to decide are the size of our world and the size of its cells. There are essentially two kinds of worlds: those with large cells, where every actor object occupies only one single cell (that is: the cell is large enough to hold the complete actor), and those with small cells, where actors span multiple cells.

You have probably seen examples of both. 'wombats', for example, is one of the first kind, whereas 'ants' is of the second kind. (The wombats and ants scenarios are distributed as examples together with Greenfoot. If you have not looked at them, and are not sure what we are discussing here, it would be a good idea to look at them now.)

We can specify the world size and cell size in the super call in the constructor. Assume we change it to

    super(10, 8, 60);

In this case, we will get a world that is 10 cells wide, 8 cells high, and where every cell is 60x60 pixels in size. The signature of thr World class constructor (which we are calling here) is

    public World(int worldWidth, int worldHeight, int cellSize)

All positioning of actors in the world is in terms of cells. You cannot place actors between cells (although an actor's image can be larger than a cell, and thus overlap many cells).

Large cell / small cell trade-off

The trade-off to be made when choosing a world's cell size is between smooth motion and ease of collision detection.

One result of the fact that actors can only be placed in cells is that worlds with large cells give you fairly course-grained motion. The wombats scenario, for instance uses a 60-pixel sized cell, and thus, when wombats move one step forward, their image on screen moves by 60 pixels. On the other hand, in worlds with large cells, where the actors are completely contained inside a cell, detecting other actors at one actor's position is a bit simpler - we do not need to check for overlapping images, but simply for the presence of other actors in the same cell. Finding actos in neighbouring cells is also easy.

We will discuss this in more detail in the section 'Detecting other objects (collisions)', below.

If you want smoother motion, you should use a smaller cell size. the 'ants' scenario, for example, uses 1-pixel cells, ginving the ants the ability to move in small steps. Thsi can also be combined with large actor objects. The 'lunarlander' scenario, for instance, uses a large actor (the rocket) on a 1-pixel cell world. This gives the actor very smooth motion, since it's location can be changed in one-pixel intervalls.

World background images

Most of the time, we want our world to have a background image. This relatively easy to do.

First, you have to make of find a suitable background image. (There are some at the end of the Greenfoot Image Collection on the Greenfoot web site.) Place the image file into the 'images' folder inside your scenario folder. Once the image file is there, it is available to your Greenfoot scenario.

Then you can set the world's background image with the line:

    setBackground("myImage.jpg");

where "myImage.jpg" should be replaced with the correct image file name. For example, assume we have placed the image "sand.jpg" into our scenario's 'images' folder, then our world's constructor might look like this:

    public MyWorld() 
    {
        super(20, 20, 10);
        setBackground("sand.jpg");
    }

The background will, by default, be filled with the image by tiling the image across the world. To get  smooth looking background, you should use an image whose right edge fits seamlessly to the left, and the bottom to the top. Alternatively, you can use a single image that is large enough to cover the whole world.

If you want to paint the background programmatically you can easily do so instead of using an image file. The world always has a background image. By default (as long as we do not specify anythign else) it is an image that has the same size as the world and is completely transparent. We can retrieve the image object for the world's background image and perform drawign operations on it. For example:

    GreenfootImage background = getBackground();
    background.setColor(Color.BLUE);
    background.fill();

These instructions will fill the entire bakground image with blue.

A third option is to combine the two: You can load an image file, then draw onto it, and use the modified file as a background for the world:

    GreenfootImage background = new GreenfootImage("water.png");
    background.drawString("WaterWorld", 20, 20);
    setBackground(background);

Background images are typically set only once in the constructor of the world, although there is nothing that stops you from changing the world's background dynamically at other times while your scenario is running.

Showing tiles

Sometimes, when you have worlds with large grid sizes, you want to make the grid visible. The 'wombats' scenario does this - you can see the grid painted on the background of the world.

There is no special function in Greenfoot to do this. This is done simply by having the grid painted on the image that is used for the world background. In the case of 'wombats', the world has 60-pixel cells, and the background image is a 60x60 pixel image (to match the cell size) that has a one pixel line at the left and top edges darkened a bit. The effect of this is a visible grid when the tiles are used to fill the world background.

The grid can also be drawn programatically onto the background image in the world constructor. Here is an example:

    private static final Color OCEAN_BLUE = new Color(75, 75, 255);
private static final int ENV_SIZE = 12; // environment size in numer of cells
private static final int CELL_SIZE = 40; // cell size in pixels ... private void drawBackground() { GreenfootImage bg = getBackground(); bg.setColor(OCEAN_BLUE); bg.fill(); bg.setColor(Color.BLACK); for(int i = 0; i < ENV_SIZE; i++) { bg.drawLine(i * CELL_SIZE, 0, i * CELL_SIZE, ENV_SIZE * CELL_SIZE); bg.drawLine(0, i * CELL_SIZE, ENV_SIZE * CELL_SIZE, i * CELL_SIZE); } }

5    Creating new actors

This section discusses some of the characteristics of actor classes, and what to consider when writing them.

All classes that we want to act as part of our scenario are subclasses of the built-in 'Actor' class. We can create new actor classes by selecting 'New subclass...' from the Actor class's popup menu.

The following dialogue lets us specify a name and an image for our new class. The name must be a valid Java class name (that is: contain only letters and numbers). The class image will be the default image for all objects of that class.

Class images

Every class has an assiciated image that serves as the default image for all objects of that class. Every object can later alter its own image, so that individual objects of the class may look different. This is further discussed in the section 'Dealing with images', below. If objects do not set images explicitly, they will receive the class image.

The image selector in the dialogue shows two groups of images: project images and library images. The library images are included in the Greenfoot distribution, the project images are stored in the 'images' folder inside your scenario (project) folder. If you have your own images that you like to use for your class, you have two options:

  • You can copy you image file into the scenario's 'images' folder. It will then be available for selection in this dialogue; or
  • You can use the 'Browse for more images' button in this dialogue to select your image from anywhere in your file system. The image will then automatically be copied into the images folder of the scenario.

Initialisation

As with most Java objects, the normal initialisation of the object happens in the object's constructor. However, there are some initialisation tasks that cannot be finished here. The reason is that, at the time the object is constructed, it has not been entered into the world yet. The order of events is:

  1. The object is constructed.
  2. The object is entered into the world.

During step 1, the object's constructor is executed. Since the object is not in the world at this time, methods such as getWorld(), getX() and getY() cannot be called in the constructor (when you're not in the world, you do not have a location).

So, if we want to do anything as part of our initialisation that needs access to the world (such as create other objects in the world, or set our image depending on neighbouring objects), it cannot be done in the constructor. Instead, we have a second initialisation method, called 'addedToWorld'. Every actor class inherits this method from class 'Actor'.

The signature of this method is

    public void addedToWorld(World world) 

This method is automatically called when the actor has been added to the world. So, if we have work to do at the time the object has been added, all we have to do is to define an 'addedToWorld' method in our class with this signature and place our code there. For example:

    public class Rabbit extends Actor
    {
        private GreenfootImage normalImage;
        private GreenfootImage scaredImage;

        public Rabbit()
        {
            normalImage = new GreenfootImage("rabbit-normal.png");
            scaredImage = new GreenfootImage("rabbit-scared.png");
        }

        public void addedToWorld(World world)
        {
            if(isNextToFox()) {
                setImage(scaredImage);
            }
             else {
                setImage(normalImage);
            }
        }

        private boolean isNextToFox()
        {
            ... // calls getWorld() to check neighbours in world
        }
    }

In this example, the intention is that a rabbit, when placed into the world, looks 'normal' if it is not next to a fox, but looks scared when placed next to a fox. To do this, we have two image files ("rabbit-normal.png" and "rabbit-scared.png"). We can load the images in the Rabbit's constructor, but we cannot select the image to show, since this involves checking the world, which is not accessible at the time the constructor executes.

When a user places an object into the world, things happen in this order:

  1. The object is created (and the constructor is executed).
  2. The object is placed into the world.
  3. The setLocation method of the object is called with its new location.
  4. The addedToWorld method of the object is called.

In our example above, by the time the addedToWorld method is called, the object is in the world and has a location. So we can now call our own isNextToFox() method (which presumably makes use of the world and our location).

The setLocation method will be called every time the location of the object changes. The addedToWorld method is called only once.

The 'Lander' class in the 'lunarlander' scenario (from the Greenfoot sample scenarios) shows another example of using this method.

 

6    Making things move

Every time a user clicks the 'Act' button on screen, the 'act' method of each object in the world will be called. The 'act' method has the following signature:

   public void act()

Every object that is active (i.e. is expected to do something) should implement this method.

The effect of a user clicking the 'Run' button is nothing more than a repeated (very fast) click on the 'Act' button. In other words, our 'act' method will be called over and over again, as long as the scenario runs.

To make object move on screen, it is enough to modify the object's location. Three attributes of each actor become automatically and immediately visible on screen when you change them. They are:

  • the location (given as x and y coordinates)
  • the rotation
  • the image

If we change any of these attributes, the appearance of the actor on screen will change. The Actor class has methods to get and set any of these.

Changing the location

The first thing to look at is location changes. Consider, for example, the following code:

    public void act()
{
int x = getX(); int y = getY(); setLocation(x + 1, y); }

The effect of this code fragment is to move the actor one cell to the right. It does this by getting the actor's current x and y coordinates, and then setting a new location for the actor with the x-coordinate increased by one.

We can write the same code a little shorter as

    public void act()
{
setLocation(getX() + 1, getY()); }

(Note that this code is incomplete: the actor would eventually run out of the world, and this is an error! Our program would stop and report an "IndexOutOfBoundsException". To fix this, we need to add code that checks whether we can move in that direction before actually moving. The 'wombats' scenario shows an eample of this.)

The location coordinates are the indices of the cells in the world. They must not exceed the world size. The (0,0) location is in the top left of the world, and coordinates increase right (x) and down (y).

Changing the rotation

In a similar manner to the location, we can change the rotation of the object's image. Here is an example:

    public void act()
{ int rot = getRotation() + 1; if(rot == 360) { rot = 0; }
setRotation(rot); }

This method gets the object's current rotation, and then increases it by one. The effect is that the object will slowly rotate clockwise.

The valid range for the rotation is [0..359], and the angle increases clockwise. Thus, in the code example above, we check whether we have reached 360 (that is: left the valid range) and then reset the value to 0.

Changing the image

The last of the actor attributes that is automatically visualised is the actor's image. Changing the image will become immediately visible on screen. Consider this:

    public void act()
{ if(hasEaten()) { setImage("happy.jpg"); }
else { setImage("sad.jpg"); }
}

This code assumes that we have a hasEaten() method in our code, and then sets the image accordingly. There are two versions of the setImage method: one expects a file name of an image file as a parameter, the other one expects an object of type GreenfootImage.

In general, it is a good idea to load image file only once into a GreenfootImage object, and then to reuse the same image object if you need to set the image multiple times. For instance, instead of calling

    setImage("happy.jpg");

repeatedly in the act method, you could initialise an instance field with the image:

    private GreenfootImage happyImage = new GreenfootImage("happy.jpg");

and then set the image using this object:

    setImage(happyImage);

More information about dealing with images is in the section 'Dealing with images' (below).

Restricting movement

The location of an actor cannot only be changed by the actor's source code, it can also be changed by the scenario's user: the object can be dragged over the screen manually.

The actor can receive notification when this happens, and it can even forbid the movement of the object.

When an actor object is dragged by a user, the actor's 'setLocation' method is called to execute the change. The signature of this method is

    public void void setLocation(int x, int y)

When we write an actor, we can override this method to modify the result. For example:

    public void setLocation(int x, int y)
{ super.setLocation(x, y); if(x > 200 && y < 120) { explode(); }
}

In this example, we test whether the location is in a certain area, and then do something (explode!) if it is. To do this, we first call the superclass's setLocation method, so that the location is actually changed as requested. Then we check the location and do the exploding if necessary.

If we modify the parameter to the superclass, we can influence the actual placement. For example:

    public void setLocation(int x, int y)
{ super.setLocation(x, 180); }

In this example, the superclass's 'setLocation' method is also called, but the parameters are modified. We pass on the x-coordinate, but we ignore the y-coordinate and pass a constant (180) to setLocation instead.

The effect is that the object will move freely horizontally, but will stick to a constant height vertically.

The 'plane' scenario from the Greenfoot sample scenarios uses this technique for the plane's control stick.

7    Random behaviour

Random behavior in Greenfoot scenarions is based on random numbers.

Generating random numbers in Greenfoot is fairly easy. Greenfoot has a built-in class called 'Greenfoot' that is part of the framework (see The Greenfoot class, below). This class has a method called getRandomNumber. Its signature is

    public int getRandomNumber(int limit)

In our own code, whenever we need a random number, we can call this method:

    int myNumber = Greenfoot.getRandomNumber(10);

This example will give us a number in the range [0..9]. That is: the number is always between zero (inclusive) and the limit you specify (exclusive). For details, see the description of the Greenfoot class in the Greenfoot API.

Once you have random numbers, using these for random behaviour is only a small step. For example:

    if(Greenfoot.getRandomNumber(2) == 0) {   // 50% chance
        turnLeft();
    }
    else {
        turnRight();
    }

For more examples, see the 'Wombat' class in the 'wombats2' scenario, or the 'Ant' class in 'ants'.

8    Dealing with images

Greenfoot supports various different ways how objects can acquire images.

There are three different ways (and then combinations of the three). Objects can get an image by:

  1. using default images from their class;
  2. loading image files from disk;
  3. containing code to paint an image.

All three method are used by the various objects in the ants scenario that is included in the Greenfoot distribution. It is useful to study this example if you want to learn about images. We will refer to this scenario repeatedly in this section.

We discuss all three methods in turn.

Using default class images

Every class has an associated image. This image is usually assigned when creating the class, but may be changed later using the 'Set Image...' function from the class's popup menu.

For an object to use the class's image, there is nothing we need to do. If we write no special image handling code, this is the image that will be used to display objects of this class.

In the ants project, the AnyHill objects use this technique. A fixed image is assigned to the class, and all AnyHill objects look the same.

Using image files

We can easily alter the image of an individual object by using the Actor's 'setImage(..)' method. In the ants scenario for example, the Ant class uses this method. When an ant finds some food, its takeFood() method is executed, which includes the line

    setImage("ant-with-food.gif");

When the ant drops the food (in the dopFood() method) it uses this line:

    setImage("ant.gif");

This way, the image of each individual ant object can dynamically change.

When this 'setImage' method is used, an image file is loaded from disk. The parameter specifies the file name, and the file should be located in the project's 'images' folder.

If images of objects change frequently, or have to change quickly, this can be optimised by loading the image from disk only once, and storing them in an image object (of type GreenfootImage). Here is a code snippet to illustrate this:

    public class Ant extends Actor
    {
        private GreenfootImage foodImage;
        private GreenfootImage noFoodImage;

        public Ant()
        {
            foodImage = new GreenfootImage("ant-with-food.gif");
            noFoodImage = new GreenfootImage("ant.gif");
        }

        private boolean takeFood()
        {
            ... 
            setImage(foodImage);
        }

        private boolean dropFood()
        {
            ... 
            setImage(noFoodImage);
        }
    }

This example illustrates a second version of the setImage method: setImage can also be called with a GreenfootImage as a parameter, instead of a file name. The GreenfootImage can be constructed using the image file name, and the resulting object can then be reused.

This version saves resources and executes quicker. It is preferrable whenever the images change frequently.

Using generated images

(to be written)

9    Detecting other objects (collisions)

One of the really nice features in the Greenfoot API is the ability to find other objects in the world. As soon as you want objects to interact with each other, you need to be able to "see" these other objects. Greenfoot gives you many different ways to find other objects to suit many different kinds of scenarios. We have divided the methods into two different categories: one that is strictly based on the location of objects, and one that is using the image representation of the objects. The methods discussed here are all available when sub-classing the Actor class.

Cell based

In some scenarios, like Wombats, objects are always entirely contained within a cell, and you are only interested in the location of the object in the grid. For these scenarios we have methods that works strictly on the location of the objects. We call these methods for cell based.

When wombats are looking for leaves to eat, they look at where they are right now, to see if there is a leaf. In the foundLeaf() method in the Wombat the code to do this is:

    Actor leaf = getOneObjectAtOffset(0, 0, Leaf.class);

This method returns one object at a relative location to where the wombat is currently. The first two parameters specify the offset from the current location, which in this case is (0,0), since wombats can only eat leaves where they are right now. The third parameter specifies which types of objects we are looking for. The method will then only return objects of the given class or sub-class. If several objects exist at the location, it is undefined which one will be returned.

If you have a really big wombat that can eat several leaves at once you can use another method which will return a list of leaves:

    List leaves = getObjectsAtOffset(0, 0, Leaf.class);

The wombats, as they are implemented in the wombats scenario, are pretty dumb and does not make use of any of the senses that real wombat has. If we want the wombat to look around for leaves before moving we have several methods to choose from.

If the wombat should only be able to look at the immediate neighbours to the north, south, east and west we can use the following methods

    List leaves = getNeighbours(1, false, Leaf.class);

That call will find all objects within a walking distance of 1 cell, where you are not allow to go diagonally. If you wanted the diagonals included you should replace false with true. If the wombat should be able to look farther you can increase the distance from 1 to something larger.

Below we have made a few pictures to illustrate what cells will be considered when looking for neighbours with different parameters for the method call.

Without the diagonal:
getNeighbours(1, false, Leaf.class);

With the diagonal:
getNeighbours(1, true, Leaf.class);

A method related in functionality to the getNeighbours methods:

    List leaves = getObjectsInRange(2, Leaf.class);

This method call will return all objects (of the class Leaf and subclasses) that have a location that is within 2 cells. If the distance between two actors is exactly 2, it is considered to be in range. See the picture below for an illustration of this

 

getObjectsInRange(2, Leaf.class);

 

Representation based

Sometimes it is not precise enough to use the cell location to determine collisions. Greenfoot has a few methods that allows to check whether the graphical representations of actors overlap.

 

    Actor leaf = getOneIntersectingObject(0, 0, Leaf.class);

Be aware that these method calls requires more computation than the cell based methods and might slow down your program if it contains many actors.

 

both?

10    Keyboard input

(to be written)

11    Mouse input

(to be written)

12    Input by object dragging

 

13    Initialising a scenario

(to be written)

14    The GreenFoot class

 

15    The GreenfootImage class