Part 1

In part 1 we’re going to create our first new class to model a single sheep, and then later a whole flock.

Getting Started

Create a new file in Mu and enter the following lines:

WIDTH = 500
HEIGHT = 400
TITLE = "Animal Simulation"

Press Run and save the file as animals.py in your mu_code directory.

You should see a new, empty window appear.

Creating an animal

You remember that we use Actor objects to draw sprites on the screen? If not, take a look at Flappy Bird Tutorial for a nice simple introduction.

So we are going to create a new kind of an Actor to represent our on-screen animals.

First, import the random library as we’ll need this to position the animals. Add the import random line as the first line of your code.

Now let’s add our new class. Add this code:

class Animal(Actor):

    def __init__(self):
        super().__init__('sheep.png')
        self.x = random.randint(WIDTH*1/4, WIDTH*3/4)
        self.y = random.randint(HEIGHT*1/4, HEIGHT*3/4)

So what does all that mean? Well class Animals creates a new class called Animal. And an Animal is a new kind of Actor because we said class Animal(Actor). That means that Animal can do everything Actor can do, which is handy because it means it already knows how to draw itself and move around the screen.

The function (or more correctly, method) __init__ is called the constructor, it’s what we want to happen when we make a new Animal.

So let’s do that, let’s make a new animal. Add this single line of code under your class and don’t indent it as we want this to be outside the class code. The new line is marked in yellow below:

class Animal(Actor):

    def __init__(self):
        super().__init__('sheep.png')
        self.x = random.randint(WIDTH*1/4, WIDTH*3/4)
        self.y = random.randint(HEIGHT*1/4, HEIGHT*3/4)

a = Animal()

And to draw it, add this code underneath:

def draw():
    a.draw()

Now Run your program to see what happens.

So we have a single sheep on the screen!

Let’s add some movement

Functions inside classes are called methods. Functions outside are just called functions. This is important to remember, as you’ll see in a minute.

So let’s add a new method to Animal called move – you will need to ensure that this code is indented to match the __init__ method, as before we’ve marked the new lines in yellow:

class Animal(Actor):

    def __init__(self):
        super().__init__('sheep.png')
        self.x = random.randint(WIDTH*1/4, WIDTH*3/4)
        self.y = random.randint(HEIGHT*1/4, HEIGHT*3/4)

    def move(self):
       self.x += 1
       self.y += 0.5

When does this function get called, when does the code run?

Never! Let’s fix that. Add a new function at the end of your program, under the draw function:

def update():
  a.move()

Now if you Run this code you’ll see your sheep move, and make a line across the screen. Just a straight line, which isn’t very realistic movement.

More animals

We can easily create more animals, by adding code like this:

b = Animal()
c = Animal()

However, they won’t get drawn or move because we don’t reference b or c in our draw or update functions. Let’s fix that.

We could just add b.move() but then if we wanted to add more we’d have a lot of typing to do. So the way we keep track of many items is to use a list and a nice way to do this with classes is to get the class to keep the list for us.

Add these two new lines code between to your Animal class:

class Animal(Actor):

    all = []

    def __init__(self):
        super().__init__('sheep.png')
        self.x = random.randint(WIDTH*1/4, WIDTH*3/4)
        self.y = random.randint(HEIGHT*1/4, HEIGHT*3/4)
        Animal.all.append(self)

Now each time we create an animal, it gets added to the all list.

Now change the functions draw and update like so:

def draw():
    for a in Animal.all: a.draw()

def update():
    for a in Animal.all: a.move()

Finally, we have a simpler way to create an animal, we can just do this:

Animal()
Animal()
Animal()

That creates 3 animals. Try it and run your code to see it working.

Moving in different directions

OK, the sheep are pretty boring, let’s make them move randomly.

In the method move change the code so that it is random:

def move(self):
  self.x += random.uniform(-1,1)
  self.y += random.uniform(-1,1)

And let’s also clear the screen in draw so that we don’t leave trails behind the sheep:

def draw():
  screen.clear()
  for a in Animal.all: a.draw()

Hmmm… have you noticed that the sheep seem to wobble back and forth, and don’t really go anywhere? Why do you think this is?

Let’s fix that by giving each sheep a x and y speed when we create them. We set up this kind of thing in the constructor method (in __init__):

Add these two lines before the append statement:

    def __init__(self):
        super().__init__('sheep.png')
        self.x = random.randint(WIDTH*1/4, WIDTH*3/4)
        self.y = random.randint(HEIGHT*1/4, HEIGHT*3/4)
        self.xspeed = random.uniform(-1,1)
        self.yspeed = random.uniform(-1,1)
        Animal.all.append(self)

Can you see what you need to change in the move method next to use these two new variables? Hint: the x and y speed is now stored in self.xspeed and self.yspeed – these are called member variables, just like regular variables but each Animal has its own copy.

So your class should now look like this:

class Animal(Actor):

    all = []

    def __init__(self):
        super().__init__('sheep.png')
        self.x = random.randint(WIDTH*1/4, WIDTH*3/4)
        self.y = random.randint(HEIGHT*1/4, HEIGHT*3/4)
        self.xspeed = random.uniform(-1,1)
        self.yspeed = random.uniform(-1,1)
        Animal.all.append(self)

    def move(self):
        self.x += self.xspeed
        self.y += self.yspeed

Sheep flock together

So our sheep are not very sheep like. They are not interested in each other, they just head off in a straight line and eventually leave the screen and never come back. Let’s fix this in Part 2.