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.
*****
Table of Contents
Book Info
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:
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.
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:
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):
...
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
...
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:
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.