406 lines
14 KiB
Python
406 lines
14 KiB
Python
# SPDX-FileCopyrightText: 2020 Liz Clark for Adafruit Industries
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import time
|
|
from random import randint
|
|
from micropython import const
|
|
import board
|
|
import terminalio
|
|
import displayio
|
|
import adafruit_imageload
|
|
import digitalio
|
|
import simpleio
|
|
from keypad import ShiftRegisterKeys, Event
|
|
from adafruit_display_text import label
|
|
|
|
# setup for PyBadge buttons
|
|
BUTTON_LEFT = const(7)
|
|
BUTTON_UP = const(6)
|
|
BUTTON_DOWN = const(5)
|
|
BUTTON_RIGHT = const(4)
|
|
BUTTON_SEL = const(3)
|
|
BUTTON_START = const(2)
|
|
BUTTON_A = const(1)
|
|
BUTTON_B = const(0)
|
|
|
|
pad = ShiftRegisterKeys(clock=board.BUTTON_CLOCK,
|
|
data=board.BUTTON_OUT,
|
|
latch=board.BUTTON_LATCH,
|
|
key_count=8,
|
|
value_when_pressed=True,
|
|
max_events=1)
|
|
|
|
latest_event = Event(key_number=8)
|
|
last_read = 0
|
|
|
|
# enables speaker
|
|
speakerEnable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
|
|
speakerEnable.switch_to_output(value=True)
|
|
|
|
# Sprite cell values
|
|
EMPTY = 0
|
|
BLINKA_1 = 1
|
|
BLINKA_2 = 2
|
|
SPARKY = 3
|
|
HEART = 4
|
|
JUMP_1 = 5
|
|
JUMP_2 = 6
|
|
|
|
# creates display
|
|
display = board.DISPLAY
|
|
# scale=2 allows the sprites to be bigger
|
|
group = displayio.Group(scale=2)
|
|
|
|
# Blinka sprite setup
|
|
blinka, blinka_pal = adafruit_imageload.load("/spritesNew.bmp",
|
|
bitmap=displayio.Bitmap,
|
|
palette=displayio.Palette)
|
|
|
|
# creates a transparent background for Blinka
|
|
blinka_pal.make_transparent(7)
|
|
blinka_grid = displayio.TileGrid(blinka, pixel_shader=blinka_pal,
|
|
width=2, height=1,
|
|
tile_height=16, tile_width=16,
|
|
default_tile=EMPTY)
|
|
blinka_grid.x = 0
|
|
blinka_grid.y = 32
|
|
|
|
blinka_group = displayio.Group()
|
|
blinka_group.append(blinka_grid)
|
|
|
|
# first Sparky sprite
|
|
sparky0, sparky0_pal = adafruit_imageload.load("/spritesNew.bmp",
|
|
bitmap=displayio.Bitmap,
|
|
palette=displayio.Palette)
|
|
sparky0_pal.make_transparent(7)
|
|
sparky0_grid = displayio.TileGrid(sparky0, pixel_shader=sparky0_pal,
|
|
width=1, height=1,
|
|
tile_height=16, tile_width=16,
|
|
default_tile=SPARKY)
|
|
# all Sparky sprites begin off screen
|
|
sparky0_grid.x = 100
|
|
sparky0_grid.y = 32
|
|
|
|
sparky0_group = displayio.Group()
|
|
sparky0_group.append(sparky0_grid)
|
|
|
|
# 2nd Sparky sprite
|
|
sparky1, sparky1_pal = adafruit_imageload.load("/spritesNew.bmp",
|
|
bitmap=displayio.Bitmap,
|
|
palette=displayio.Palette)
|
|
sparky1_pal.make_transparent(7)
|
|
sparky1_grid = displayio.TileGrid(sparky1, pixel_shader=sparky1_pal,
|
|
width=1, height=1,
|
|
tile_height=16, tile_width=16,
|
|
default_tile=SPARKY)
|
|
sparky1_grid.x = 100
|
|
sparky1_grid.y = 32
|
|
|
|
sparky1_group = displayio.Group()
|
|
sparky1_group.append(sparky1_grid)
|
|
|
|
# 3rd Sparky sprite
|
|
sparky2, sparky2_pal = adafruit_imageload.load("/spritesNew.bmp",
|
|
bitmap=displayio.Bitmap,
|
|
palette=displayio.Palette)
|
|
sparky2_pal.make_transparent(7)
|
|
sparky2_grid = displayio.TileGrid(sparky2, pixel_shader=sparky2_pal,
|
|
width=1, height=1,
|
|
tile_height=16, tile_width=16,
|
|
default_tile=SPARKY)
|
|
sparky2_grid.x = 100
|
|
sparky2_grid.y = 32
|
|
|
|
sparky2_group = displayio.Group()
|
|
sparky2_group.append(sparky2_grid)
|
|
|
|
# heart sprite group
|
|
life_bit, life_pal = adafruit_imageload.load("/spritesNew.bmp",
|
|
bitmap=displayio.Bitmap,
|
|
palette=displayio.Palette)
|
|
life_grid = displayio.TileGrid(life_bit, pixel_shader=life_pal,
|
|
width=3, height=1,
|
|
tile_height=16, tile_width=16,
|
|
default_tile=HEART)
|
|
|
|
life_group = displayio.Group()
|
|
life_group.append(life_grid)
|
|
|
|
# adding all graphics groups to the main display group
|
|
group.append(blinka_group)
|
|
group.append(sparky0_group)
|
|
group.append(sparky1_group)
|
|
group.append(sparky2_group)
|
|
group.append(life_group)
|
|
|
|
# text area for the running score
|
|
score_text = " "
|
|
font = terminalio.FONT
|
|
score_color = 0x0000FF
|
|
|
|
# text for "game over" graphic
|
|
game_over_text = label.Label(font, text = " ", color = 0xFF00FF)
|
|
# score text
|
|
score_area = label.Label(font, text=score_text, color=score_color)
|
|
# text for "new game" graphic
|
|
new_game_text = label.Label(font, text = " ", color = 0xFF00FF)
|
|
|
|
# coordinants for text areas
|
|
score_area.x = 57
|
|
score_area.y = 6
|
|
game_over_text.x = 13
|
|
game_over_text.y = 30
|
|
new_game_text.x = 8
|
|
new_game_text.y = 30
|
|
# creating a text display group
|
|
text_group = displayio.Group()
|
|
text_group.append(score_area)
|
|
text_group.append(game_over_text)
|
|
text_group.append(new_game_text)
|
|
# adding text group to main display group
|
|
group.append(text_group)
|
|
|
|
# displaying main display group
|
|
display.root_group = group
|
|
|
|
# state for hit detection
|
|
crash = False
|
|
# states to see if a Sparky is on screen
|
|
sparky0 = False
|
|
sparky1 = False
|
|
sparky2 = False
|
|
|
|
# array of Sparky states
|
|
sparky_states = [sparky0, sparky1, sparky2]
|
|
# array of x location for Sparky's
|
|
sparky_x = [sparky0_grid.x, sparky1_grid.x, sparky2_grid.x]
|
|
|
|
# function to display the heart sprites for lives
|
|
def life():
|
|
for _ in range(0, 3):
|
|
life_grid[_, 0] = EMPTY
|
|
for hearts in range(life_count):
|
|
life_grid[hearts, 0] = HEART
|
|
|
|
# lives at beginning of the game
|
|
life_count = 3
|
|
|
|
# variables for scoring
|
|
jump_score = 0
|
|
total_score = 0
|
|
bonus = 0
|
|
# state for Blinka being in default 'slither' mode
|
|
snake = True
|
|
# state to check if Blinka has jumped over Sparky
|
|
cleared = False
|
|
# state for the end of a game
|
|
end = False
|
|
# state for a new game beginning
|
|
new_game = True
|
|
# state for detecting game over
|
|
game_over = False
|
|
# variable to change between Blinka's two slither sprites
|
|
b = 1
|
|
# variable to hold time.monotonic() count for Blinka slither animation
|
|
slither = 0
|
|
# variables to hold time.monotonic() count to delay Sparky spawning
|
|
blue = 0
|
|
smoke = 0
|
|
monster = 0
|
|
# jump button press state
|
|
jump_pressed = False
|
|
|
|
while True:
|
|
|
|
# checks if button has been pressed
|
|
if (last_read + 0.01) < time.monotonic():
|
|
pad.events.get_into(latest_event)
|
|
last_read = time.monotonic()
|
|
# new game
|
|
if new_game and not game_over:
|
|
# graphics for new game splash screen
|
|
blinka_grid.y = 16
|
|
blinka_grid[0] = JUMP_1
|
|
blinka_grid[1] = JUMP_2
|
|
sparky0_grid.x = 5
|
|
sparky1_grid.x = 40
|
|
sparky2_grid.x = 65
|
|
score_area.text = str(300)
|
|
new_game_text.text = "BLINKA JUMP"
|
|
life()
|
|
# if start is pressed...
|
|
if latest_event:
|
|
if latest_event.key_number == BUTTON_START:
|
|
# prepares display for gameplay
|
|
print("start game")
|
|
new_game_text.text = " "
|
|
life_count = 3
|
|
start = time.monotonic()
|
|
new_game = False
|
|
end = False
|
|
sparky0_grid.x = 100
|
|
sparky1_grid.x = 100
|
|
sparky2_grid.x = 100
|
|
# if game has started...
|
|
if not game_over and not new_game:
|
|
# gets time.monotonic() to have a running score
|
|
mono = time.monotonic()
|
|
score = mono - start
|
|
# adds 10 points every time a Sparky is cleared
|
|
total_score = score + jump_score
|
|
# displays score as text
|
|
score_area.text = str(int(total_score))
|
|
|
|
# puts Sparky states and x location into callable arrays
|
|
for s in range(3):
|
|
sparky_state = sparky_states[s]
|
|
sparky_location = sparky_x[s]
|
|
|
|
# Sparkys are generated using a staggered delay
|
|
# and matching an int to a random int
|
|
# 1st Sparky
|
|
if (blue + 0.03) < time.monotonic():
|
|
if randint(1, 15) == 3:
|
|
sparky_states[0] = True
|
|
blue = time.monotonic()
|
|
# 2nd Sparky
|
|
if (smoke + 0.07) < time.monotonic():
|
|
if randint(1, 15) == 7:
|
|
sparky_states[1] = True
|
|
smoke = time.monotonic()
|
|
# 3rd Sparky
|
|
if (monster + 0.12) < time.monotonic():
|
|
if randint(1, 15) == 12:
|
|
sparky_states[2] = True
|
|
monster = time.monotonic()
|
|
# if a Sparky is generated, it scrolls across the screen 1 pixel at a time
|
|
# 1st Sparky
|
|
if sparky_states[0] is True:
|
|
sparky0_grid.x -= 1
|
|
sparky_x[0] = sparky0_grid.x
|
|
display.refresh(target_frames_per_second=120)
|
|
# when a Sparky is 16 pixels off the display,
|
|
# it goes back to its starting position
|
|
if sparky0_grid.x is -16:
|
|
sparky_states[0] = False
|
|
sparky0_grid.x = 100
|
|
sparky_x[0] = sparky0_grid.x
|
|
# 2nd Sparky
|
|
if sparky_states[1] is True:
|
|
sparky1_grid.x -= 1
|
|
sparky_x[1] = sparky1_grid.x
|
|
display.refresh(target_frames_per_second=120)
|
|
if sparky1_grid.x is -16:
|
|
sparky_states[1] = False
|
|
sparky1_grid.x = 100
|
|
sparky_x[1] = sparky1_grid.x
|
|
# 3rd Sparky
|
|
if sparky_states[2] is True:
|
|
sparky2_grid.x -= 1
|
|
sparky_x[2] = sparky2_grid.x
|
|
display.refresh(target_frames_per_second=120)
|
|
if sparky2_grid.x is -16:
|
|
sparky_states[2] = False
|
|
sparky2_grid.x = 100
|
|
sparky_x[2] = sparky2_grid.x
|
|
|
|
# if no lives are left then the game ends
|
|
if life_count is 0:
|
|
game_over = True
|
|
|
|
# if the A button is pressed then Blinka is no longer in the default
|
|
# slither animation aka she jumps
|
|
if latest_event.key_number == BUTTON_A:
|
|
if latest_event.pressed:
|
|
jump_pressed = True
|
|
snake = False
|
|
else:
|
|
jump_pressed = False
|
|
snake = True
|
|
|
|
# heart sprites are displayed to show life count
|
|
life()
|
|
|
|
# if Blinka is slithering...
|
|
if snake:
|
|
# Blinka default location
|
|
blinka_grid.y = 32
|
|
# empty 2nd tile so that the jump sprite can be shown using
|
|
# the same tilegrid
|
|
blinka_grid[1] = EMPTY
|
|
# every .15 seconds Blinka's slither sprite changes
|
|
# so that her slithering is animated
|
|
# b holds tilegrid position to display correct sprite
|
|
if (slither + 0.15) < time.monotonic():
|
|
blinka_grid[0] = b
|
|
b += 1
|
|
slither = time.monotonic()
|
|
if b > 2:
|
|
b = 1
|
|
# if a Sparky collides with Blinka while she is slithering...
|
|
for s in range(3):
|
|
if sparky_x[s] == 8 and blinka_grid.y == 32:
|
|
# tone is played
|
|
simpleio.tone(board.SPEAKER, 493.88, 0.05)
|
|
simpleio.tone(board.SPEAKER, 349.23, 0.05)
|
|
# lose a life
|
|
life_count = life_count - 1
|
|
# if the A button is pressed then...
|
|
else:
|
|
# Blinka JUMPS
|
|
# y location changes one row up and both jump sprites are shown
|
|
blinka_grid.y = 16
|
|
blinka_grid[0] = JUMP_1
|
|
blinka_grid[1] = JUMP_2
|
|
# if Blinka jumps over a Sparky...
|
|
for j in range(3):
|
|
if sparky_x[j] == 8 and not cleared:
|
|
# 10 points to the player
|
|
bonus += 1
|
|
jump_score = bonus * 10
|
|
cleared = True
|
|
# special victory tone is played
|
|
simpleio.tone(board.SPEAKER, 523.25, 0.005)
|
|
simpleio.tone(board.SPEAKER, 783.99, 0.005)
|
|
|
|
if not jump_pressed:
|
|
# resets back to Blinka animation
|
|
snake = True
|
|
# resets that Blinka has not jumped over a Sparky
|
|
cleared = False
|
|
|
|
# if there are no more lives, the game is over
|
|
if game_over and not new_game:
|
|
# game over text is displayed
|
|
game_over_text.text = "GAME OVER"
|
|
score_area.text = " "
|
|
# end game tone is played
|
|
# and then the screen holds with the last
|
|
# sprites on screen and game over text
|
|
if not end:
|
|
simpleio.tone(board.SPEAKER, 220, 0.05)
|
|
simpleio.tone(board.SPEAKER, 207.65, 0.05)
|
|
simpleio.tone(board.SPEAKER, 196, 0.5)
|
|
end = True
|
|
|
|
# if the start button is pressed...
|
|
if latest_event and game_over:
|
|
if latest_event.key_number == BUTTON_START:
|
|
# display, states and score are reset for gameplay
|
|
game_over_text.text = " "
|
|
life_count = 3
|
|
start = time.monotonic()
|
|
game_over = False
|
|
end = False
|
|
total_score = 0
|
|
jump_score = 0
|
|
bonus = 0
|
|
score = 0
|
|
blue = 0
|
|
smoke = 0
|
|
monster = 0
|
|
sparky0_grid.x = 100
|
|
sparky1_grid.x = 100
|
|
sparky2_grid.x = 100
|
|
# game begins again with all Sparky's off screen
|