Skip to content
Home » Kivy Part 47 – Slugrace – Main Game Logic Classes

Kivy Part 47 – Slugrace – Main Game Logic Classes

Spread the love

In the preceding parts of the series we were working on the screens of our app. We’re now pretty much done with the graphical layer of our game, but what about the logic? In this and a couple following parts of the series we’ll implement the infrastructure for the game logic and then we’ll program the logic.

But before we start, here’s some info for you.

*****

Book Info

I just published my Kivy book, GUI Programming with Python and Kivy. It’s pretty long (over 800 pages) and comprehensive. And, which also counts, easy to read. The book contains lots of illustrations.

This book covers all the basics that you need to know to start programming GUI applications with Python and Kivy. Throughout the book we are building a GUI application from scratch, a fully functional game using all kinds of tools that Kivy has to offer. It’s our Slugrace project, but covered in a much more in-depth manner.

Each part of the book starts with a theoretical introduction of a topic or idea that we then implement in the project. I assume you have no prior knowledge of the Kivy library, but you should have at least some basic knowledge of the Python programming language, including the object-oriented programming paradigm as this is what we will be using a lot in this book.

The book covers all the basic elements of Kivy that you have to know, like widgets, layouts, Kivy ids and properties, graphics, screens, animation, sound. Finally we’ll deploy the app to Windows. It is pretty comprehensive and after you finish it, I’m sure you’ll be able to create your own awesome GUI apps of any kind, not just games.

I hope you will have at least as much fun reading the book as I had writing it.

As far as this Kivy series is concerned, the following parts will contain the most important elements covered in the book. However, some elements will be presented in a simplified way here on my blog or omitted completely.

____________

If you are interested, you can purchase the book in four versions. Here are the links:

1) ebook – pdf version on my website – in full color

Here you can see the description of the book, sample graphics from the book and the full table of contents.

2) ebook – Kindle version on Amazon – in full color

3) paperback version on Amazon – in black and white

4) paperback version on Amazon – in full color

*****

And Now Let’s Move On…

To start with, we’ll add two classes to our project: Slug and Player. The Slug class will contain all the code, both Python and kv, that is related to the slugs. The Player class will contain all the player code. There will be four instances of the Slug class in the game, one for each of the slugs. There will also be four instances of the Player class because there may be up to four players in the game. By players I mean the users of the app. There will be no graphical representation of the player.

The Slug class

Anyway, let’s start with the Slug class. When you play the game now, you will see the four slugs on the track:

slugs

As there will be four slugs and they will all have the same functionalities, it’s convenient to extract the code that handles them into a separate class.

But first, let’s have a look at how the slugs are handled now. They are now embedded in the Race screen, as images. Have a look at the following Python code:

# File name: race.py

...

class SlugImage(RelativeLayout):
    body_image = StringProperty('')
    eye_image = StringProperty('')
    y_position = NumericProperty(0)

class RaceScreen(Screen):
    ...

So, we have the SlugImage class defined with three properties. Here’s how the slug images are implemented in the kv file:

# File name: race.kv
#:import race race

...

<SlugImage>:
    pos_hint: {'x': .09, 'center_y': root.y_position}
    size_hint: None, None
    size: 143, 30

    # the body image
    Image:
        source: 'assets/slugs/' + root.body_image + '.png' if root.body_image else None

    # the left eye image    
    Image:
        canvas.before:
            PushMatrix
            Rotate:
                angle: 30
                axis: 0, 0, 1
                origin: self.x, self.center_y
        canvas.after:
            PopMatrix                    

        source: 'assets/slugs/' + root.eye_image + '.png' if root.eye_image else None
        pos_hint: {'x': .95, 'y': .45}
        size_hint: 0.25, 0.25

    # the right eye image
    Image:
        canvas.before:
            PushMatrix
            Rotate:
                angle: -30
                axis: 0, 0, 1
                origin: self.x, self.center_y
        canvas.after:
            PopMatrix

        source: 'assets/slugs/' + root.eye_image + '.png' if root.eye_image else None
        pos_hint: {'x': .95, 'y': .3}
        size_hint: 0.25, 0.25

<RaceScreen>:
    ...
                                    
        ### THE TRACK ###
        ...
                

                # slug images
                # Speedster
                SlugImage:
                    body_image: 'speedsterBody'
                    eye_image: 'speedsterEye'
                    y_position: .875                
                
                # Trusty
                SlugImage:
                    body_image: 'trustyBody'
                    eye_image: 'trustyEye'
                    y_position: .625

                # Iffy
                SlugImage:
                    body_image: 'iffyBody'
                    eye_image: 'iffyEye'
                    y_position: .375

                # Slowpoke
                SlugImage:
                    body_image: 'slowpokeBody'
                    eye_image: 'slowpokeEye'
                    y_position: .125
                    
            # winner
            ...

This works pretty well and now we have two options, either will be fine. The first option is that we could have a separate SlugImage class for the image of the slug and a separate Slug class for the logic of the slug object and then programmatically bind them together. The second option that we could have just one Slug class and put all the logic and representation in it. In this project we’ll choose the second option, so we’ll create a Slug class and move all the slug-related code into it.

Your Panda3D Magazine

Make Awesome Games and Other 3D Apps

with Panda3D and Blender using Python.

Cool stuff, easy to follow articles.

Get the magazine here (PDF).

In the race.kv file there are five class rules above the root widget: RaceScreenManager, SlugStats, PlayerStats, SlugInfo and SlugImage. Although most of them need access to slug-related data, actually only the SlugImage class is a real representation of a slug itself. The others are just widgets with some information about the slugs. So, we will move the SlugImage code into a separate file and leave the other class rules in the Race screen. Later we’ll feed the slug data to them as well.

So, add a new file and name it slug.py. Here’s a quick reminder on how to add a new file in VSC, because we haven’t done it for a long time:

new file

Now, let’s add all the necessary imports to it and let’s move the SlugImage class from the race.py file to the slug.py class. Here’s what the code in the slug.py file should look now:

# File name: slug.py

# We'll need this stuff, so let's import it.
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import StringProperty, NumericProperty

# This exact code was in the race.py file before. Now it's here.
# Don't forget to remove it in the race.py file.
class SlugImage(RelativeLayout):
    body_image = StringProperty('')
    eye_image = StringProperty('')
    y_position = NumericProperty(0)

Now the class takes care of the image only, but later it will also be used for the logic. So, a more appropriate name for the class will be just Slug. Modify the code like so:

# File name: slug.py

...

class Slug(RelativeLayout):
    ...

Python Jumpstart Course

Learn the basics of Python, including OOP.

with lots of exercises, easy to follow

The course is available on Udemy.

Next, add the slug.kv file and move the whole SlugImage rule from race.kv to slug.kv. Remember to change the name SlugImage to Slug. Also import the slug.py file in the kv file with the alias slug. So, the file should now look like so:

# File name: slug.kv
#:import slug slug

<Slug>:
    pos_hint: {'x': .09, 'center_y': root.y_position}
    size_hint: None, None
    size: 143, 30

    # the body image
    Image:
        source: 'atlas://assets/slugs/slugs/' + root.body_image if root.body_image else None

    # the left eye image    
    Image:
        canvas.before:
            PushMatrix
            Rotate:
                angle: 30
                axis: 0, 0, 1
                origin: self.x, self.center_y
        canvas.after:
            PopMatrix                    

        source: 'atlas://assets/slugs/slugs/' + root.eye_image if root.eye_image else None
        pos_hint: {'x': .95, 'y': .45}
        size_hint: 0.25, 0.25

    # the right eye image
    Image:
        canvas.before:
            PushMatrix
            Rotate:
                angle: -30
                axis: 0, 0, 1
                origin: self.x, self.center_y
        canvas.after:
            PopMatrix

        source: 'atlas://assets/slugs/slugs/' + root.eye_image if root.eye_image else None
        pos_hint: {'x': .95, 'y': .3}
        size_hint: 0.25, 0.25

Now, for this to work, we must slightly modify our code. In the race.kv file we must replace all the instances of the SlugImage class with instances of the Slug class:

# File name: race.kv
#:import race race

# Screen Manager
<RaceScreenManager>:
    ...

<SlugStats>:     
    ...

<PlayerStats>: 
    ...

<SlugInfo>:
    ...

# <SlugImage> moved to slug.kv and renamed Slug
...

<RaceScreen>:
    ...
                                    
        ### THE TRACK ###
        ...

            # track 
            ...
                

                # the slugs
                # Speedster
                Slug:
                    body_image: 'speedsterBody'
                    eye_image: 'speedsterEye'
                    y_position: .875                
                
                # Trusty
                Slug:
                    body_image: 'trustyBody'
                    eye_image: 'trustyEye'
                    y_position: .625

                # Iffy
                Slug:
                    body_image: 'iffyBody'
                    eye_image: 'iffyEye'
                    y_position: .375

                # Slowpoke
                Slug:
                    body_image: 'slowpokeBody'
                    eye_image: 'slowpokeEye'
                    y_position: .125
                    
            # winner
            ...

Blender Jumpstart Course

Learn the basics of 3D modeling in Blender.

step-by-step, easy to follow, visually rich

The course is available on Udemy and on Skillshare.

We also have to load the new kv file to the main.py file, so let’s just add the line of code that we need:

# File name: main.py

...

# kv files
Builder.load_file('settings.kv')
Builder.load_file('race.kv')
Builder.load_file('gameover.kv')
Builder.load_file('widgets.kv')

# We need this for the slugs.
Builder.load_file('slug.kv')

class SlugraceScreenManager(ScreenManager):
    ...

Now if you run the app, you will see the slugs like before:

slug race

The Player class

The next class that we need is the Player class. This will be used to hold information about the players, so their names, initial money, how much money they currently have, how much they bet, and so on. We’ll be adding stuff to this class as our program grows. For now, let’s create a basic version of the class.

First let’s add a new file and name it player.py. There’s not going to be a corresponding kv file because the player objects will have no visual representation.

However, we want to use Kivy properties inside the class. For this to be possible at all, the Player class must inherit from the EventDispatcher class, which is defined in the kivy.event module. So, don’t forget to add this class in the imports section. We could also inherit from the Widget class or any other class that inherits from it, but the EventDispatcher class will do.

Inside the Player class we’ll define a couple properties. In particular, define the following properties (and also don’t forget to import the all the necessary property classes before):

name – a StringProperty defaulting to an empty string,

initial_money – a NumericProperty defaulting to 0 (this is for the amount of money the player starts with),

money – a NumericProperty defaulting to 0 (this is for amount of money the player has at a particular time),

money_before_race – a NumericProperty defaulting to 0 (this is for the amount of money the player has before the race starts),

money_won – a NumericProperty defaulting to 0 (this is for the amount of money won by the player in the race, negative if the player loses),

bet – a NumericProperty defaulting to 1 (this is for the amount of money the player has put on their chosen slug),

Here’s the player.py file:

# File name: player.py

# We need some property classes.
from kivy.properties import StringProperty, NumericProperty

# We need this to use Kivy properties at all.
from kivy.event import EventDispatcher

# The class must inherit from EventDispatcher.
class Player(EventDispatcher):
    name = StringProperty('')
    initial_money = NumericProperty(0)  # amount of money the player starts with
    money = NumericProperty(0) # amount of money the player has at a particular time              
    money_before_race = NumericProperty(0)  # amount of money the player has before the race starts
    money_won = NumericProperty(0)  # amount of money won by the player in the race (negative if the player loses)
    bet = NumericProperty(1) # amount of money the player has put on their chosen slug

That’s it for now. We’re going to use the Player class soon.


Spread the love

Leave a Reply