14 Creeper¶
Using what we learned in the previous chapter 13 Textures let’s create a custom texture to make a Creeper like object that will change it’s apperance. The Creeper will count down and disapperas when the count down reaches zero. Copy tutorial 00_flat_world.py to a new file with the following command, replacing TVR with your initials:
cp 00_flat_world.py 14_creeper_part_1_TVR.py
New Textures¶
First open the file “story_textures.png” and look at all the new textures we’ve added to the file. There are some for a basic green creeper that will be two blocks tall and some for the countdown, where the creeper turns red and displays the number of seconds remaining until it disappears.
Make TEXTURE_PATH
point at this file as well by going to approximately line
76 and changing the TEXTURE_PATH
statement to say
TEXTURE_PATH = 'story_textures.png'
.
TEXTURE_PATH = 'story_textures.png'
This file is a different size compared to the previous files we’ve used, so we
need to adjust how we get each texture block to make sure each texture is
displayed properly. Go line 52 which reads def tex_coord(x, y, n=4):
and
change it so that n
defaults to 8. The line should read:
def tex_coord(x, y, n=8):
Previously we had texture files that were 4 by 4 groups of textures, but
“story_textures.png” is 8 by 8. n
just represents the dimensions of the
file and tells the code how many pieces to split the overall image into to get
each individual texture.
Adding the Creeper to the World - Part 1¶
First, let’s add some variables to keep track of the creeper. Go to the end of
the Model class’ __init__
function and just before the call to
self._initialize()
add the following variables:
self.creeper_time = 0
self.creeper_status = False
self.creeper_pos = (0, 0, 0)
creeper_time
will help us track how long the creeper has existed and the
state of the countdown clock. creeper_status
wil simply state whether or
not a creeper is in the world. creeper_pos
will store the position where
the creeper is located.
Next we need to define all the textures needed for the creeper. Go to line 83
where we defined TEXTURE_PATH
and add the following texture definitions
below it:
CREEPER_HEAD = tex_coords((4, 1), (4, 1), (4, 0))
CREEPER_BODY = tex_coords((4, 1), (4, 1), (4, 1))
CR_HEAD = tex_coords((4, 4), (4, 4), (4, 2))
CR_1 = tex_coords((4, 4), (4, 4), (0, 4))
CR_2 = tex_coords((4, 4), (4, 4), (1, 4))
CR_3 = tex_coords((4, 4), (4, 4), (2, 4))
CR_4 = tex_coords((4, 4), (4, 4), (3, 4))
CR_5 = tex_coords((4, 4), (4, 4), (4, 3))
Now go down to on_mouse_press
and replace it with the following:
def on_mouse_press(self, x, y, button, modifiers):
""" Called when a mouse button is pressed. See pyglet docs for button
amd modifier mappings.
Parameters
----------
x, y : int
The coordinates of the mouse click. Always center of the screen if
the mouse is captured.
button : int
Number representing mouse button that was clicked. 1 = left button,
4 = right button.
modifiers : int
Number representing any modifying keys that were pressed when the
mouse button was clicked.
"""
if self.exclusive:
vector = self.get_sight_vector()
block, previous = self.model.hit_test(self.position, vector)
if (button == mouse.RIGHT) or \
((button == mouse.LEFT) and (modifiers & key.MOD_CTRL)):
# ON OSX, control + left click = right click.
if self.model.creeper_status:
self.model.remove_block(self.model.creeper_pos)
self.model.creeper_pos = (self.model.creeper_pos[0],
self.model.creeper_pos[1] + 1,
self.model.creeper_pos[2])
self.model.remove_block(self.model.creeper_pos)
self.model.creeper_status = False
if previous and not self.model.creeper_status:
self.model.add_block(previous, CREEPER_BODY)
previous = (previous[0], previous[1] + 1, previous[2])
self.model.add_block(previous, CREEPER_HEAD)
self.model.creeper_pos = (previous[0], previous[1] - 1, previous[2])
self.model.creeper_time = time.time()
self.model.creeper_status = True
elif button == pyglet.window.mouse.LEFT and block:
texture = self.model.world[block]
if texture != STONE:
self.model.remove_block(block)
else:
self.set_exclusive_mouse(True)
The two new sections of code are the inner most if statements. Let’s walk through them a little bit.
The first says if self.model.creeper_status
. This block handles if the user
tries to build another creeper when one is already actively counting down. If
there already is an active creeper, remove it from the world and set
creeper_status
to false.
The second part then says if previous and not self.model.creepre_Status
, so
this section of code only runs if a block is selected and there isn’t already
an active creeper. If both these conditions are met, then we want to add a
creeper, record the time the creeper was built to start the countdown, and set
creeper_status
to True
.
Now if you run the code, you will be able to build a creeper by pressing the right mouse button. Notice though that you can only ever have one creeper at a time.
Start the Countdown - Part 2¶
Now let’s make the creeper countdown to mark how much longer it will be there. Copy tutorial 14_creeper_part_1.py to a new file with the following command, replacing TVR with your initials:
cp 14_creeper_part_1.py 14_creeper_part_2_TVR.py
Start with adding a new function that will make the decisions regarding what
type of block the creeper should have. Below the on_draw
function at line
832, add the function below of creeper_countdown
.
def creeper_countdown(self):
t = time.time()
t_creeper = self.model.creeper_time
diff = t - t_creeper
pos = self.model.creeper_pos
if diff > 1:
if pos in self.model.world:
self.model.remove_block(pos)
pos = (pos[0], pos[1] + 1, pos[2])
if pos in self.model.world:
self.model.remove_block(pos)
if diff > 6 and self.model.creeper_status:
self.model.creeper_status = False
elif diff > 5:
self.model.add_block(self.model.creeper_pos, CR_1)
self.model.add_block(pos, CR_HEAD)
elif diff > 4:
self.model.add_block(self.model.creeper_pos, CR_2)
self.model.add_block(pos, CR_HEAD)
elif diff > 3:
self.model.add_block(self.model.creeper_pos, CR_3)
self.model.add_block(pos, CR_HEAD)
elif diff > 2:
self.model.add_block(self.model.creeper_pos, CR_4)
self.model.add_block(pos, CR_HEAD)
elif diff > 1:
self.model.add_block(self.model.creeper_pos, CR_5)
self.model.add_block(pos, CR_HEAD)
This funciton takes the difference between the time of when the creeper was built and the current time and uses this difference to decide what numbered block the body of the creeper should display.
Now go to the on_draw
function and add a call to creeper_countdown
so
that if creeper_status
is True
, this function will be called
on_draw
should then read as below:
def on_draw(self):
""" Called by pyglet to draw the canvas.
"""
self.clear()
self.set_3d()
glColor3d(1, 1, 1)
if self.model.creeper_status:
self.creeper_countdown()
self.model.batch.draw()
self.draw_focused_block()
self.set_2d()
self.draw_label()
self.draw_reticle()
Now you can build a creeper as if you were going to build a regular block and watch it turn red and countdown before disappearing.