Here you will find the source code for the pirate animation, as described in the upcoming book Graphic Guide to Python
Copyright 2018 Antony Lees
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
def setup():
size(600, 1000, P3D)
# game board
global standard_tile
standard_tile = "standard"
global hole_tile
hole_tile = "hole"
global hole_hit_tile
hole_hit_tile = "hit"
global target_tile
target_tile = "target"
global number_of_columns
number_of_columns = 5
global tiles
tiles = []
global tile_size
tile_size = width/number_of_columns/2
global number_of_rows
number_of_rows = (height/tile_size) * 2
global x_offset
x_offset = (width-tile_size * number_of_columns)/2
grace_period = 1
# initialise tiles
for i in range(number_of_rows):
# set y coordinates (-y so scrolls downwards)
y = -i * tile_size
for j in range(number_of_columns):
# set x coordinate
x = j * tile_size + x_offset
if i > grace_period:
tile_type = random_tile_type()
tile = Tile(x, y, tile_type)
tiles.append(tile)
#cat
global cat_size
cat_size = tile_size
global cat
cat = Cat(cat_size)
# initial position
cat.x = width/2
cat.y = height/2
# display
global score
score = 0
global lives
lives = 3
# speed
global lowest_speed
lowest_speed = 5
global highest_speed
highest_speed = 20
global speed
speed = lowest_speed
def draw():
background(0)
lights()
global score
fill(255)
textSize(tile_size/2.5)
text("Score: {}".format(score), 10, tile_size/2)
global lives
text("Lives: {}".format(lives), width-(tile_size*1.5), tile_size/2)
# set speed but keep within limits
speed = constrain(score/2, lowest_speed, highest_speed)
pushMatrix()
rotateX(radians(50))
cat.draw_cat()
for tile in tiles:
if lives >= 1:
tile.y = tile.y + speed
tile.draw_tile()
# check each tile for hits
if tile.target_hit(cat.x, cat.y):
score += 1
if tile.hole_hit(cat.x, cat.y):
lives -=1
popMatrix()
recycle_tiles()
# recycle tiles off the screen
def recycle_tiles():
# get y coord of last tile
y = 0
global tiles
for tile in tiles:
if tile.y < y:
y = tile.y
# moves tiles to the end (x coord can stay the same)
new_y = y - tile_size
for tile in tiles:
# if off the screen
if tile.y > height:
tile.y = new_y
tile.type = random_tile_type()
# generate a random type of tile
def random_tile_type():
tile_type = standard_tile
# randomised tile chances
if random(0, 5) > 4:
tile_type = hole_tile
if random(0, 50) > 45:
tile_type = target_tile
return tile_type
class Cat:
def __init__(self, cat_size):
self.x = 0
self.y = 0
self.cat_colour = color(255, 200, 0)
self.cat_size = cat_size
self.tail_length = cat_size/4
self.tail_thickness = cat_size/7
self.tail = []
# initialise tail parts using 2 dimensional list
for i in range (self.tail_length):
self.tail.append([0, 0])
def draw_cat(self):
# draw body in separate matrix so can rotate
pushMatrix()
noStroke()
translate(self.x, self.y)
rotateX(frameCount/20.0) # roll
sphereDetail(7)
fill(self.cat_colour)
sphere(cat_size * 0.4)
popMatrix()
pushMatrix()
noStroke()
# draw head
translate(self.x, self.y - (cat_size/2))
sphereDetail(8)
sphere(self.cat_size * 0.2)
# draw ears
# left
pushMatrix()
#fill(0, 0, 255)
translate(-cat_size*0.1, -cat_size*0.05, cat_size*0.15)
rotateY(0.5)
rotateX(4)
sphereDetail(1)
sphere(cat_size*0.1)
popMatrix()
# right
pushMatrix()
translate(cat_size*0.1, -cat_size*0.05, cat_size*0.15)
rotateY(3)
rotateX(2)
sphereDetail(1)
sphere(cat_size*0.1)
popMatrix()
#triangle(-size*0.1, -size*0.05, -size*0.05, -size*0.2, -size*0.25, -size*0.3)
#triangle(size*0.1, -size*0.05, size*0.05, -size*0.20, size*0.25, -size*0.3)
# draw tail
sphereDetail(8)
# tail end
self.tail[self.tail_length-1][0] = (sin(frameCount * 0.2) * 10)
self.tail[self.tail_length-1][1] = cat_size/2
# rest of tail
for i in range(self.tail_length-1):
self.tail[i][0] = self.tail[i+1][0]
self.tail[i][1] = self.tail[i+1][1] + (self.tail_thickness/2)
pushMatrix()
#ellipse(tail[i].x, tail[i].y, tailThickness, tailThickness)
translate(self.tail[i][0], self.tail[i][1])
sphere(self.tail_thickness)
popMatrix()
popMatrix()
# class for tiles
class Tile:
global standard_tile_colour
standard_tile_colour = color(255) # white
global hole_tile_colour
hole_tile_colour = color(0) # black
global hole_hit_tile_colour
hole_hit_tile_colour = color(255, 0, 0) #red
global target_tile_colour
target_tile_colour = color(255, 255, 0) # yellow
def __init__(self, x, y, type):
self.x = x
self.y = y
self.type = type
def draw_tile(self):
stroke(0)
if self.type == standard_tile:
tile_colour = fill(standard_tile_colour)
elif self.type == hole_tile:
tile_colour = fill(hole_tile_colour)
elif self.type == hole_hit_tile:
tile_colour = fill(hole_hit_tile_colour)
elif self.type == target_tile:
tile_colour = fill(target_tile_colour)
square(self.x, self.y, tile_size)
def target_hit(self, cat_x, cat_y):
target_hit = self.hit(cat_x, cat_y) and self.type == target_tile
if target_hit:
self.type = standard_tile
return target_hit
def hole_hit(self, cat_x, cat_y):
hole_hit = self.hit(cat_x, cat_y) and self.type == hole_tile
if hole_hit:
self.type = hole_hit_tile
return hole_hit
# check if we hit a tile
def hit(self, cat_x, cat_y):
hit = cat_y >= self.y and cat_y <= self.y + tile_size and cat_x >= self.x and cat_x <= self.x + tile_size
return hit
def mouseMoved():
cat.x = mouseX
cat.y = mouseY
def setup():
# set the size of the window
size(600, 400)
global ships
ships = []
for i in range(10):
ships.append(PirateShip())
# waves
global wave_max_height
wave_max_height = 20
global wave_min_height
wave_min_height = 5
global wave_height
wave_height = wave_max_height
global up
up = 1
global down
down = -1
global wave_direction
wave_direction = up
def draw():
# light blue background
background(102, 205, 170)
# waves
global wave_max_height
global wave_min_height
global wave_height
global wave_direction
noFill()
# determine of the vwaes should be going up or down
if wave_height <= wave_min_height:
wave_direction = up
if wave_height >= wave_max_height:
wave_direction = down
# change the wave height for next time
wave_height += wave_direction
# wave y coordinate
wave_y = wave_max_height//2
# while the y coordinate is less than the height of the window
while wave_y <= height:
# wave x coordinate
wave_x = 0
# while the x coordinate is less than the width of the window
while wave_x <= width:
# draw a semi=circle arc
arc(wave_x, wave_y, wave_max_height, wave_height, 0, PI)
# increase the x coordinate by 20
wave_x += wave_max_height
# increase the y coordinate by 20
wave_y += wave_max_height
global ships
for ship in ships:
ship.display()
ship.move()
class PirateShip:
# pirate ship variables
x_position = 0
y_position = 0
ship_length = 0
sails = 0
sail_colour = 0
porthole_count = 0
sail_spacing = 0
speed = 0
def __init__(self):
# initialise the ship
self.initialise()
def initialise(self):
# initialise the ship at a random-ish location
self.x_position = random(600, 700)
self.y_position = random(50, 350)
self.ship_length = random(40, 100)
# speed
self.speed = self.ship_length//20
# sails
self.sails = int(min((self.ship_length//30), 3))
self.sail_colour = color(random(255), random(255), random(255))
self.sail_spacing = floor(self.ship_length / 3)
# portholes
self.porthole_count = int(self.ship_length//12)
def display(self):
# calculate ship coordinates
front_of_ship_x_position = self.x_position - (self.ship_length / 2)
back_of_ship_x_position = self.x_position + self.ship_length + (self.ship_length / 3)
# ship hull
# brown lines and fill
stroke(139, 71, 38)
fill(139, 71, 38)
# middle rectangle
rect(self.x_position, self.y_position, self.ship_length, 20)
# front and back
triangle(front_of_ship_x_position, self.y_position, self.x_position, self.y_position, self.x_position, self.y_position + 20)
triangle(self.x_position + self.ship_length, self.y_position, back_of_ship_x_position, self.y_position, self.x_position + self.ship_length, self.y_position + 20)
# draw the sails
initial_sail_x_position = self.x_position + 15
# black lines
stroke(0)
for i in range(self.sails):
# sail
fill(self.sail_colour)
quad(initial_sail_x_position + (self.sail_spacing * i), self.y_position - 40, initial_sail_x_position + (self.sail_spacing * i) + 10, self.y_position - 40, initial_sail_x_position + (self.sail_spacing * i) + 20, self.y_position - 10, initial_sail_x_position + (self.sail_spacing * i), self.y_position - 10)
# mast
fill(0)
line(initial_sail_x_position + (self.sail_spacing * i), self.y_position - 10, initial_sail_x_position + (self.sail_spacing * i), self.y_position)
line(initial_sail_x_position + (self.sail_spacing * i), self.y_position - 40, initial_sail_x_position + (self.sail_spacing * i), self.y_position - 50)
# flag
fill(0)
rect(initial_sail_x_position + (self.sail_spacing * i), self.y_position - 50, 10, 5)
# jolly roger
stroke(255)
line(initial_sail_x_position + (self.sail_spacing * i) + 2, self.y_position - 49, initial_sail_x_position + (self.sail_spacing * i) + 8, self.y_position - 46)
line(initial_sail_x_position + (self.sail_spacing * i) + 8, self.y_position - 49, initial_sail_x_position + (self.sail_spacing * i) + 2, self.y_position - 46)
stroke(0)
# draw the portholes
for i in range(self.porthole_count):
# portholes
fill(0)
circle(self.x_position+(15*i),self.y_position+10,5)
# ship has left the screen
if back_of_ship_x_position <= 0:
# start again
self.initialise()
def move(self):
self.x_position -= self.speed
