adding Circuit Python tilemap game

This commit is contained in:
FoamyGuy 2020-05-17 09:45:32 -05:00
parent f22cb03215
commit 101e919517
17 changed files with 1068 additions and 0 deletions

View file

@ -0,0 +1,90 @@
import board
import displayio
import adafruit_imageload
#from tilegame_assets.controls_helper import controls
import ugame
display = board.DISPLAY
player_loc = {"x": 4, "y": 3}
# Load the sprite sheet (bitmap)
sprite_sheet, palette = adafruit_imageload.load("tilegame_assets/castle_sprite_sheet.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
# Create the sprite TileGrid
sprite = displayio.TileGrid(sprite_sheet, pixel_shader=palette,
width=1,
height=1,
tile_width=16,
tile_height=16,
default_tile=0)
# Create the castle TileGrid
castle = displayio.TileGrid(sprite_sheet, pixel_shader=palette,
width=10,
height=8,
tile_width=16,
tile_height=16)
# Create a Group to hold the sprite and add it
sprite_group = displayio.Group()
sprite_group.append(sprite)
# Create a Group to hold the castle and add it
castle_group = displayio.Group(scale=1)
castle_group.append(castle)
# Create a Group to hold the sprite and castle
group = displayio.Group()
# Add the sprite and castle to the group
group.append(castle_group)
group.append(sprite_group)
# Castle tile assignments
# corners
castle[0, 0] = 3 # upper left
castle[9, 0] = 5 # upper right
castle[0, 7] = 9 # lower left
castle[9, 7] = 11 # lower right
# top / bottom walls
for x in range(1, 9):
castle[x, 0] = 4 # top
castle[x, 7] = 10 # bottom
# left/ right walls
for y in range(1, 7):
castle[0, y] = 6 # left
castle[9, y] = 8 # right
# floor
for x in range(1, 9):
for y in range(1, 7):
castle[x, y] = 7 # floor
# put the sprite somewhere in the castle
sprite.x = 16 * player_loc["x"]
sprite.y = 16 * player_loc["y"]
# Add the Group to the Display
display.show(group)
prev_btn_vals = ugame.buttons.get_pressed()
while True:
cur_btn_vals = ugame.buttons.get_pressed()
if not prev_btn_vals & ugame.K_UP and cur_btn_vals & ugame.K_UP:
player_loc["y"] = max(1, player_loc["y"]-1)
if not prev_btn_vals & ugame.K_DOWN and cur_btn_vals & ugame.K_DOWN:
player_loc["y"] = min(6, player_loc["y"]+1)
if not prev_btn_vals & ugame.K_RIGHT and cur_btn_vals & ugame.K_RIGHT:
player_loc["x"] = min(8, player_loc["x"]+1)
if not prev_btn_vals & ugame.K_LEFT and cur_btn_vals & ugame.K_LEFT:
player_loc["x"] = max(1, player_loc["x"]-1)
# update the the player sprite position
sprite.x = 16 * player_loc["x"]
sprite.y = 16 * player_loc["y"]
# update the previous values
prev_btn_vals = cur_btn_vals

View file

@ -0,0 +1,91 @@
import board
import displayio
import adafruit_imageload
from tilegame_assets.controls_helper import controls
display = board.DISPLAY
player_loc = {"x":4, "y":3}
# Load the sprite sheet (bitmap)
sprite_sheet, palette = adafruit_imageload.load("tilegame_assets/castle_sprite_sheet_edited.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
# make the color at 0 index transparent.
palette.make_transparent(0)
# Create the sprite TileGrid
sprite = displayio.TileGrid(sprite_sheet, pixel_shader=palette,
width=1,
height=1,
tile_width=16,
tile_height=16,
default_tile=0)
# Create the castle TileGrid
castle = displayio.TileGrid(sprite_sheet, pixel_shader=palette,
width=10,
height=8,
tile_width=16,
tile_height=16)
# Create a Group to hold the sprite and add it
sprite_group = displayio.Group()
sprite_group.append(sprite)
# Create a Group to hold the castle and add it
castle_group = displayio.Group(scale=1)
castle_group.append(castle)
# Create a Group to hold the sprite and castle
group = displayio.Group()
# Add the sprite and castle to the group
group.append(castle_group)
group.append(sprite_group)
# Castle tile assignments
# corners
castle[0, 0] = 3 # upper left
castle[9, 0] = 5 # upper right
castle[0, 7] = 9 # lower left
castle[9, 7] = 11 # lower right
# top / bottom walls
for x in range(1, 9):
castle[x, 0] = 4 # top
castle[x, 7] = 10 # bottom
# left/ right walls
for y in range(1, 7):
castle[0, y] = 6 # left
castle[9, y] = 8 # right
# floor
for x in range(1, 9):
for y in range(1, 7):
castle[x, y] = 7 # floor
# put the sprite somewhere in the castle
sprite.x = 16 * player_loc["x"]
sprite.y = 16 * player_loc["y"]
# Add the Group to the Display
display.show(group)
prev_btn_vals = controls.button
while True:
cur_btn_vals = controls.button
if not prev_btn_vals.up and cur_btn_vals.up:
player_loc["y"] = max(1, player_loc["y"]-1)
if not prev_btn_vals.down and cur_btn_vals.down:
player_loc["y"] = min(6, player_loc["y"]+1)
if not prev_btn_vals.right and cur_btn_vals.right:
player_loc["x"] = min(8, player_loc["x"]+1)
if not prev_btn_vals.left and cur_btn_vals.left:
player_loc["x"] = max(1, player_loc["x"]-1)
# update the the player sprite position
sprite.x = 16 * player_loc["x"]
sprite.y = 16 * player_loc["y"]
# update the previous values
prev_btn_vals = cur_btn_vals

View file

@ -0,0 +1,68 @@
import board
import displayio
import adafruit_imageload
display = board.DISPLAY
# Load the sprite sheet (bitmap)
sprite_sheet, palette = adafruit_imageload.load("tilegame_assets/castle_sprite_sheet.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
# Create the sprite TileGrid
sprite = displayio.TileGrid(sprite_sheet, pixel_shader=palette,
width=1,
height=1,
tile_width=16,
tile_height=16,
default_tile=0)
# Create the castle TileGrid
castle = displayio.TileGrid(sprite_sheet, pixel_shader=palette,
width=10,
height=8,
tile_width=16,
tile_height=16)
# Create a Group to hold the sprite and add it
sprite_group = displayio.Group()
sprite_group.append(sprite)
# Create a Group to hold the castle and add it
castle_group = displayio.Group(scale=1)
castle_group.append(castle)
# Create a Group to hold the sprite and castle
group = displayio.Group()
# Add the sprite and castle to the group
group.append(castle_group)
group.append(sprite_group)
# Castle tile assignments
# corners
castle[0, 0] = 3 # upper left
castle[9, 0] = 5 # upper right
castle[0, 7] = 9 # lower left
castle[9, 7] = 11 # lower right
# top / bottom walls
for x in range(1, 9):
castle[x, 0] = 4 # top
castle[x, 7] = 10 # bottom
# left/ right walls
for y in range(1, 7):
castle[0, y] = 6 # left
castle[9, y] = 8 # right
# floor
for x in range(1, 9):
for y in range(1, 7):
castle[x, y] = 7 # floor
# put the sprite somewhere in the castle
sprite.x = 16 * 4
sprite.y = 16 * 3
# Add the Group to the Display
display.show(group)
while True:
pass

View file

@ -0,0 +1,603 @@
import time
import random
import gc
import board
import displayio
import adafruit_imageload
from displayio import Palette
import terminalio
from adafruit_display_text import label
from tilegame_assets.controls_helper import controls
from tilegame_assets.tiles import TILES
from tilegame_assets.tiles import take_item
from tilegame_assets.states import *
from tilegame_assets.fun_facts import FACTS
from tilegame_assets.text_helper import wrap_nicely
# Direction constants for comparison
UP = 0
DOWN = 1
RIGHT = 2
LEFT = 3
# how long to wait between rendering frames
FPS_DELAY = 1 / 60
# how many tiles can fit on thes screen. Tiles are 16x16 pixels
SCREEN_HEIGHT_TILES = 8
SCREEN_WIDTH_TILES = 10
# list of maps in order they should be played
MAPS = ["map0.csv", "map1.csv"]
GAME_STATE = {
# hold the map state as it came out of the csv. Only holds non-entities.
"ORIGINAL_MAP": {},
# hold the current map state as it changes. Only holds non-entities.
"CURRENT_MAP": {},
# Dictionary with touple keys that map to lists of entity objects.
# Each one has the index of the sprite in the ENTITY_SPRITES list
# and the tile type string
"ENTITY_SPRITES_DICT": {},
# hold the location of the player in tile coordinates
"PLAYER_LOC": (0, 0),
# list of items the player has in inventory
"INVENTORY": [],
# how many hearts there are in this map level
"TOTAL_HEARTS": 0,
# sprite object to draw for the player
"PLAYER_SPRITE": None,
# size of the map
"MAP_WIDTH": 0,
"MAP_HEIGHT": 0,
# which map level within MAPS we are currently playing
"MAP_INDEX": 0,
# current state of the state machine
"STATE": STATE_PLAYING,
}
# dictionary with tuple keys that map to tile type values
# e.x. {(0,0): "left_wall", (1,1): "floor"}
CAMERA_VIEW = {}
# how far offset the camera is from the GAME_STATE['CURRENT_MAP']
# used to determine where things are at in the camera view vs. the MAP
CAMERA_OFFSET_X = 0
CAMERA_OFFSET_Y = 0
# list of sprite objects, one for each entity
ENTITY_SPRITES = []
# list of entities that need to be on the screen currently based on the camera view
NEED_TO_DRAW_ENTITIES = []
# return from GAME_STATE['CURRENT_MAP'] the tile name of the tile of the given coords
def get_tile(coords):
return GAME_STATE["CURRENT_MAP"][coords[0], coords[1]]
# return from TILES dict the tile object with stats and behavior for the tile at the given coords.
def get_tile_obj(coords):
return TILES[GAME_STATE["CURRENT_MAP"][coords[0], coords[1]]]
# check the can_walk property of the tile at the given coordinates
def is_tile_moveable(tile_coords):
return TILES[GAME_STATE["CURRENT_MAP"][tile_coords[0], tile_coords[1]]]["can_walk"]
print("after funcs {}".format(gc.mem_free()))
# display object variable
display = board.DISPLAY
# Load the sprite sheet (bitmap)
sprite_sheet, palette = adafruit_imageload.load(
"tilegame_assets/sprite_sheet.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette,
)
# make green be transparent so entities can be drawn on top of map tiles
palette.make_transparent(0)
# Create the castle TileGrid
castle = displayio.TileGrid(
sprite_sheet,
pixel_shader=palette,
width=10,
height=8,
tile_width=16,
tile_height=16,
)
# Create a Group to hold the sprites and add it
sprite_group = displayio.Group(max_size=32)
# Create a Group to hold the castle and add it
castle_group = displayio.Group()
castle_group.append(castle)
# Create a Group to hold the sprite and castle
group = displayio.Group()
# Add the sprite and castle to the group
group.append(castle_group)
group.append(sprite_group)
def load_map(file_name):
global ENTITY_SPRITES, CAMERA_VIEW
# empty the sprite_group
for cur_s in ENTITY_SPRITES:
sprite_group.remove(cur_s)
# remove player sprite
try:
sprite_group.remove(GAME_STATE["PLAYER_SPRITE"])
except:
pass
# reset map and other game state objects
GAME_STATE["ORIGINAL_MAP"] = {}
GAME_STATE["CURRENT_MAP"] = {}
ENTITY_SPRITES = []
GAME_STATE["ENTITY_SPRITES_DICT"] = {}
CAMERA_VIEW = {}
GAME_STATE["INVENTORY"] = []
GAME_STATE["TOTAL_HEARTS"] = 0
# Open and read raw string from the map csv file
f = open("tilegame_assets/{}".format(file_name), "r")
map_csv_str = f.read()
f.close()
# split the raw string into lines
map_csv_lines = map_csv_str.replace("\r", "").split("\n")
# set the WIDTH and HEIGHT variables.
# this assumes the map is rectangular.
GAME_STATE["MAP_HEIGHT"] = len(map_csv_lines)
GAME_STATE["MAP_WIDTH"] = len(map_csv_lines[0].split(","))
# loop over each line storing index in y variable
for y, line in enumerate(map_csv_lines):
# ignore empty line
if line != "":
# loop over each tile type separated by commas, storing index in x variable
for x, tile_name in enumerate(line.split(",")):
print("%s '%s'" % (len(tile_name), str(tile_name)))
# if the tile exists in our main dictionary
if tile_name in TILES.keys():
# if the tile is an entity
if (
"entity" in TILES[tile_name].keys()
and TILES[tile_name]["entity"]
):
# set the map tiles to floor
GAME_STATE["ORIGINAL_MAP"][x, y] = "floor"
GAME_STATE["CURRENT_MAP"][x, y] = "floor"
if tile_name == "heart":
GAME_STATE["TOTAL_HEARTS"] += 1
# if it's the player
if tile_name == "player":
# Create the sprite TileGrid
GAME_STATE["PLAYER_SPRITE"] = displayio.TileGrid(
sprite_sheet,
pixel_shader=palette,
width=1,
height=1,
tile_width=16,
tile_height=16,
default_tile=TILES[tile_name]["sprite_index"],
)
# set the position of sprite on screen
GAME_STATE["PLAYER_SPRITE"].x = x * 16
GAME_STATE["PLAYER_SPRITE"].y = y * 16
# set position in x,y tile coords for reference later
GAME_STATE["PLAYER_LOC"] = (x, y)
# add sprite to the group
sprite_group.append(GAME_STATE["PLAYER_SPRITE"])
else: # not the player
# Create the sprite TileGrid
entity_srite = displayio.TileGrid(
sprite_sheet,
pixel_shader=palette,
width=1,
height=1,
tile_width=16,
tile_height=16,
default_tile=TILES[tile_name]["sprite_index"],
)
# set the position of sprite on screen
# default to off the edge
entity_srite.x = -16
entity_srite.y = -16
# add the sprite object to ENTITY_SPRITES list
ENTITY_SPRITES.append(entity_srite)
# print("setting GAME_STATE['ENTITY_SPRITES_DICT'][%s,%s]" % (x,y))
# create an entity obj
entity_obj = {
"entity_sprite_index": len(ENTITY_SPRITES) - 1,
"map_tile_name": tile_name,
}
# if there are no entities at this location yet
if (x, y) not in GAME_STATE["ENTITY_SPRITES_DICT"]:
# create a list and add it to the dictionary at the x,y location
GAME_STATE["ENTITY_SPRITES_DICT"][x, y] = [entity_obj]
else:
# append the entity to the existing list in the dictionary
GAME_STATE["ENTITY_SPRITES_DICT"][x, y].append(
entity_obj
)
else: # tile is not entity
# set the tile_name into MAP dictionaries
GAME_STATE["ORIGINAL_MAP"][x, y] = tile_name
GAME_STATE["CURRENT_MAP"][x, y] = tile_name
else: # tile type wasn't found in dict
print("tile: %s not found in TILES dict" % tile_name)
# add all entity sprites to the group
print("appending {} sprites".format(len(ENTITY_SPRITES)))
for entity in ENTITY_SPRITES:
sprite_group.append(entity)
print("loading map")
load_map(MAPS[GAME_STATE["MAP_INDEX"]])
# Add the Group to the Display
display.show(group)
# variables to store previous value of button state
prev_up = False
prev_down = False
prev_left = False
prev_right = False
# helper function returns true if player is allowed to move given direction
# based on can_walk property of the tiles next to the player
def can_player_move(direction):
if direction == UP:
tile_above_coords = (
GAME_STATE["PLAYER_LOC"][0],
GAME_STATE["PLAYER_LOC"][1] - 1,
)
return TILES[
GAME_STATE["CURRENT_MAP"][tile_above_coords[0], tile_above_coords[1]]
]["can_walk"]
if direction == DOWN:
tile_below_coords = (
GAME_STATE["PLAYER_LOC"][0],
GAME_STATE["PLAYER_LOC"][1] + 1,
)
return TILES[
GAME_STATE["CURRENT_MAP"][tile_below_coords[0], tile_below_coords[1]]
]["can_walk"]
if direction == LEFT:
tile_left_of_coords = (
GAME_STATE["PLAYER_LOC"][0] - 1,
GAME_STATE["PLAYER_LOC"][1],
)
return TILES[
GAME_STATE["CURRENT_MAP"][tile_left_of_coords[0], tile_left_of_coords[1]]
]["can_walk"]
if direction == RIGHT:
tile_right_of_coords = (
GAME_STATE["PLAYER_LOC"][0] + 1,
GAME_STATE["PLAYER_LOC"][1],
)
return TILES[
GAME_STATE["CURRENT_MAP"][tile_right_of_coords[0], tile_right_of_coords[1]]
]["can_walk"]
# set the appropriate tiles into the CAMERA_VIEW dictionary
# based on given starting coords and size
def set_camera_view(startX, startY, width, height):
global CAMERA_OFFSET_X
global CAMERA_OFFSET_Y
# set the offset variables for use in other parts of the code
CAMERA_OFFSET_X = startX
CAMERA_OFFSET_Y = startY
# loop over the rows and indexes in the desired size section
for y_index, y in enumerate(range(startY, startY + height)):
# loop over columns and indexes in the desired size section
for x_index, x in enumerate(range(startX, startX + width)):
# print("setting camera_view[%s,%s]" % (x_index,y_index))
try:
# set the tile at the current coordinate of the MAP into the CAMERA_VIEW
CAMERA_VIEW[x_index, y_index] = GAME_STATE["CURRENT_MAP"][x, y]
except KeyError:
# if coordinate is out of bounds set it to floor by default
CAMERA_VIEW[x_index, y_index] = "floor"
# draw the current CAMERA_VIEW dictionary and the GAME_STATE['ENTITY_SPRITES_DICT']
def draw_camera_view():
# list that will hold all entities that have been drawn based on their MAP location
# any entities not in this list should get moved off the screen
drew_entities = []
# print(CAMERA_VIEW)
# loop over y tile coordinates
for y in range(0, SCREEN_HEIGHT_TILES):
# loop over x tile coordinates
for x in range(0, SCREEN_WIDTH_TILES):
# tile name at this location
tile_name = CAMERA_VIEW[x, y]
# if tile exists in the main dictionary
if tile_name in TILES.keys():
# if there are entity(s) at this location
if (x + CAMERA_OFFSET_X, y + CAMERA_OFFSET_Y) in GAME_STATE[
"ENTITY_SPRITES_DICT"
]:
# default background for entities is floor
castle[x, y] = TILES["floor"]["sprite_index"]
# if it's not the player
if tile_name != "player":
# loop over all entities at this location
for entity_obj_at_tile in GAME_STATE["ENTITY_SPRITES_DICT"][
x + CAMERA_OFFSET_X, y + CAMERA_OFFSET_Y
]:
# set appropriate x,y screen coordinates
# based on tile coordinates
ENTITY_SPRITES[
int(entity_obj_at_tile["entity_sprite_index"])
].x = (x * 16)
ENTITY_SPRITES[
int(entity_obj_at_tile["entity_sprite_index"])
].y = (y * 16)
# add the index of the entity sprite to the draw_entities
# list so we know not to hide it later.
drew_entities.append(
entity_obj_at_tile["entity_sprite_index"]
)
else: # no entities at this location
# set the sprite index of this tile into the CASTLE dictionary
castle[x, y] = TILES[tile_name]["sprite_index"]
else: # tile type not found in main dictionary
# default to floor tile
castle[x, y] = TILES["floor"]["sprite_index"]
# if the player is at this x,y tile coordinate accounting for camera offset
if GAME_STATE["PLAYER_LOC"] == ((x + CAMERA_OFFSET_X, y + CAMERA_OFFSET_Y)):
# set player sprite screen coordinates
GAME_STATE["PLAYER_SPRITE"].x = x * 16
GAME_STATE["PLAYER_SPRITE"].y = y * 16
# loop over all entity sprites
for index in range(0, len(ENTITY_SPRITES)):
# if the sprite wasn't drawn then it's outside the camera view
if index not in drew_entities:
# hide the sprite by moving it off screen
ENTITY_SPRITES[index].x = int(-16)
ENTITY_SPRITES[index].y = int(-16)
# variable to store timestamp of last drawn frame
last_update_time = 0
# variables to store movement offset values
x_offset = 0
y_offset = 0
def show_splash(new_text, color, vertical_offset=18):
text_area.text = ""
text_area.text = new_text
text_area.y = round(text_area.text.count("\n") * vertical_offset / 2)
text_area.color = color
group.append(splash)
# game message background bmp file
with open(
"tilegame_assets/game_message_background.bmp", "rb"
) as game_message_background:
# Make the splash context
splash = displayio.Group(max_size=4)
odb = displayio.OnDiskBitmap(game_message_background)
bg_grid = displayio.TileGrid(odb, pixel_shader=displayio.ColorConverter())
splash.append(bg_grid)
# Text for the message
text_group = displayio.Group(max_size=8, scale=1, x=14, y=18)
text_area = label.Label(terminalio.FONT, text=" " * 180, color=0xD39AE5)
text_group.append(text_area)
splash.append(text_group)
# main loop
while True:
# set the current button values into variables
cur_btn_vals = controls.button
cur_up = cur_btn_vals.up
cur_down = cur_btn_vals.down
cur_right = cur_btn_vals.right
cur_left = cur_btn_vals.left
cur_a = cur_btn_vals.a
if GAME_STATE["STATE"] == STATE_WAITING:
print(cur_a)
if cur_a:
GAME_STATE["STATE"] = STATE_PLAYING
group.remove(splash)
if GAME_STATE["STATE"] == STATE_PLAYING:
# check for up button press / release
if not cur_up and prev_up:
if can_player_move(UP):
x_offset = 0
y_offset = -1
# check for down button press / release
if not cur_down and prev_down:
if can_player_move(DOWN):
x_offset = 0
y_offset = 1
# check for right button press / release
if not cur_right and prev_right:
if can_player_move(RIGHT):
x_offset = 1
y_offset = 0
# check for left button press / release
if not cur_left and prev_left:
if can_player_move(LEFT):
x_offset = -1
y_offset = 0
# if any offset is not zero then we need to process player movement
if x_offset != 0 or y_offset != 0:
# variable to store if player is allowed to move
can_move = False
# coordinates the player is moving to
moving_to_coords = (
GAME_STATE["PLAYER_LOC"][0] + x_offset,
GAME_STATE["PLAYER_LOC"][1] + y_offset,
)
# tile name of the spot player is moving to
moving_to_tile_name = GAME_STATE["CURRENT_MAP"][
moving_to_coords[0], moving_to_coords[1]
]
# if there are entity(s) at spot the player is moving to
if moving_to_coords in GAME_STATE["ENTITY_SPRITES_DICT"]:
print("found entity(s) where we are moving to")
# loop over all entities at the location player is moving to
for entity_obj in GAME_STATE["ENTITY_SPRITES_DICT"][
moving_to_coords
]:
print("checking entity %s" % entity_obj["map_tile_name"])
# if the entity has a before_move behavior function
if "before_move" in TILES[entity_obj["map_tile_name"]].keys():
print(
"calling before_move %s, %s, %s"
% (
moving_to_coords,
GAME_STATE["PLAYER_LOC"],
entity_obj,
)
)
# call the before_move behavior function act upon it's result
if TILES[entity_obj["map_tile_name"]]["before_move"](
moving_to_coords,
GAME_STATE["PLAYER_LOC"],
entity_obj,
GAME_STATE,
):
# all the movement if it returned true
can_move = True
else:
# break and don't allow movement if it returned false
break
else: # entity does not have a before_move function
# allow movement
can_move = True
if can_move:
# set the player loc variable to the new coords
GAME_STATE["PLAYER_LOC"] = moving_to_coords
else: # no entities at the location player is moving to
# set player loc variable to new coords
GAME_STATE["PLAYER_LOC"] = moving_to_coords
# reset movement offset variables
y_offset = 0
x_offset = 0
# set previous button values for next iteration
prev_up = cur_up
prev_down = cur_down
prev_right = cur_right
prev_left = cur_left
# current time
now = time.monotonic()
# if it has been long enough based on FPS delay
if now > last_update_time + FPS_DELAY:
# Set camera to 10x8 centered on the player
# Clamped to (0, MAP_WIDTH) and (0, MAP_HEIGHT)
set_camera_view(
max(
min(
GAME_STATE["PLAYER_LOC"][0] - 4,
GAME_STATE["MAP_WIDTH"] - SCREEN_WIDTH_TILES,
),
0,
),
max(
min(
GAME_STATE["PLAYER_LOC"][1] - 3,
GAME_STATE["MAP_HEIGHT"] - SCREEN_HEIGHT_TILES,
),
0,
),
10,
8,
)
# draw the camera
draw_camera_view()
# if player beat this map
if GAME_STATE["STATE"] == STATE_MAPWIN:
GAME_STATE["MAP_INDEX"] += 1
# if player has beaten all maps
if GAME_STATE["MAP_INDEX"] >= len(MAPS):
GAME_STATE["MAP_INDEX"] = 0
GAME_STATE["STATE"] = STATE_WAITING
show_splash(
"You Win \n =D \nCongratulations. \nStart Over?", 0x29C1CF
)
load_map(MAPS[GAME_STATE["MAP_INDEX"]])
else:
# prompt to start next
GAME_STATE["STATE"] = STATE_WAITING
show_splash(
"You beat this level\n =D \nCongratulations. \nStart Next?",
0x29C1CF,
)
load_map(MAPS[GAME_STATE["MAP_INDEX"]])
# game over from sparky
elif GAME_STATE["STATE"] == STATE_LOST_SPARKY:
GAME_STATE["MAP_INDEX"] = 0
GAME_STATE["STATE"] = STATE_WAITING
game_over_text = "Be careful not to \ntouch Sparky unless \nyou've collected \nenough Mho's.\nStarting Over"
show_splash(game_over_text, 0x25AFBB)
load_map(MAPS[GAME_STATE["MAP_INDEX"]])
# talking to minerva
elif GAME_STATE["STATE"] == STATE_MINERVA:
GAME_STATE["STATE"] = STATE_WAITING
random_fact = random.choice(FACTS)
minerva_txt = wrap_nicely("Minerva: {}".format(random_fact), 23)
show_splash(minerva_txt, 0xD39AE5, 10)
# store the last update time
last_update_time = now

View file

@ -0,0 +1,5 @@
# Creating Your First Tilemap Game With CircuitPython
Respository for an Example Tilemap Game built with Circuit Python for PyGamer, PyBdage, or Pew Pew M4 Devices.
Tutorial for this proroject is at [https://learn.adafruit.com/creating-your-first-tilemap-game-with-circuit-python/](https://learn.adafruit.com/creating-your-first-tilemap-game-with-circuit-python/)

View file

@ -0,0 +1,2 @@
adafruit_display_text
adafruit_imageload

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -0,0 +1,13 @@
# Fun facts to be read to the player by Minerva
FACTS = [
"The digitalio module lets you read and write HIGH and LOW values with IO pins.",
"JSON stands for Javascript Object Notation. It's a language for storing and transfering data.",
"PMW stands for pulse width modulation. The pulsio module lets you use PWM pins.",
"REPL stands for Read Evaluate Print Loop. You access it by sending ctrl-C via serial.",
"Released in 1979 Namco's arcade game Galaxian was the first tile-based video game.",
"Python dictionaries can have keys and values of any type! Even functions.",
"Circuitpython was initially released July 19, 2017.",
"The displayio module was introducted in Circuipython 4.0. It simplified drawing images and text on screens.",
"The adafruit_dotstar and neopixel libraries make it easy control dazzling addressable RGB LEDs.",
"You can sense human touch on an IO pin with the touchio module."
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,8 @@
top_left_wall,top_wall,top_wall,top_right_wall,,,top_left_wall,top_wall,top_wall,top_wall,top_right_wall,,,
left_wall,player,floor,floor,top_wall,top_wall,floor,floor,floor,right_wall,right_wall,top_wall,top_wall,top_right_wall
left_wall,floor,floor,mho,floor,floor,floor,floor,top_wall,right_wall,minerva,floor,sparky,right_wall
left_wall,minerva,floor,floor,heart,floor,floor,left_wall,floor,sparky,floor,floor,floor,right_wall
left_wall,top_wall,top_wall,sparky,top_wall,top_wall,top_wall,left_wall,floor,right_wall,floor,floor,floor,right_wall
left_wall,floor,floor,mho,floor,floor,floor,floor,floor,right_wall,heart,floor,robot,right_wall
left_wall,floor,floor,floor,floor,floor,heart,floor,floor,right_wall,right_wall,bottom_wall,bottom_wall,bottom_right_wall
bottom_left_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_right_wall,,,
1 top_left_wall top_wall top_wall top_right_wall top_left_wall top_wall top_wall top_wall top_right_wall
2 left_wall player floor floor top_wall top_wall floor floor floor right_wall right_wall top_wall top_wall top_right_wall
3 left_wall floor floor mho floor floor floor floor top_wall right_wall minerva floor sparky right_wall
4 left_wall minerva floor floor heart floor floor left_wall floor sparky floor floor floor right_wall
5 left_wall top_wall top_wall sparky top_wall top_wall top_wall left_wall floor right_wall floor floor floor right_wall
6 left_wall floor floor mho floor floor floor floor floor right_wall heart floor robot right_wall
7 left_wall floor floor floor floor floor heart floor floor right_wall right_wall bottom_wall bottom_wall bottom_right_wall
8 bottom_left_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_right_wall

View file

@ -0,0 +1,17 @@
top_left_wall,top_wall,top_wall,top_right_wall,floor,floor,top_left_wall,top_wall,top_wall,top_wall,top_right_wall,,,
left_wall,mho,floor,floor,top_wall,top_wall,minerva,floor,right_wall,floor,heart,top_wall,top_wall,top_right_wall
left_wall,floor,player,floor,floor,left_wall,floor,floor,sparky,floor,floor,floor,floor,right_wall
left_wall,floor,floor,floor,floor,sparky,floor,floor,right_wall,mho,floor,floor,floor,right_wall
left_wall,floor,floor,floor,floor,left_wall,floor,floor,right_wall,top_wall,top_wall,sparky,top_wall,right_wall
left_wall,floor,mho,floor,floor,left_wall,floor,floor,right_wall,floor,floor,floor,floor,right_wall
left_wall,floor,floor,floor,floor,left_wall,heart,floor,right_wall,floor,floor,floor,mho,right_wall
bottom_left_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,heart,floor,floor,right_wall
,top_left_wall,top_wall,top_wall,top_wall,top_wall,top_right_wall,floor,floor,floor,left_wall,floor,floor,right_wall
,left_wall,robot,floor,floor,floor,sparky,floor,floor,floor,left_wall,heart,floor,right_wall
,left_wall,floor,floor,floor,floor,right_wall,floor,floor,floor,sparky,floor,floor,right_wall
,left_wall,floor,floor,minerva,floor,right_wall,floor,heart,floor,left_wall,floor,floor,right_wall
,left_wall,floor,floor,floor,floor,right_wall,top_wall,top_wall,top_wall,left_wall,floor,floor,right_wall
,left_wall,floor,floor,floor,floor,right_wall,floor,floor,floor,left_wall,floor,mho,right_wall
,left_wall,floor,floor,floor,floor,sparky,floor,mho,floor,sparky,floor,minerva,right_wall
,left_wall,floor,floor,floor,floor,right_wall,floor,floor,floor,left_wall,floor,floor,right_wall
,bottom_left_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_right_wall
1 top_left_wall top_wall top_wall top_right_wall floor floor top_left_wall top_wall top_wall top_wall top_right_wall
2 left_wall mho floor floor top_wall top_wall minerva floor right_wall floor heart top_wall top_wall top_right_wall
3 left_wall floor player floor floor left_wall floor floor sparky floor floor floor floor right_wall
4 left_wall floor floor floor floor sparky floor floor right_wall mho floor floor floor right_wall
5 left_wall floor floor floor floor left_wall floor floor right_wall top_wall top_wall sparky top_wall right_wall
6 left_wall floor mho floor floor left_wall floor floor right_wall floor floor floor floor right_wall
7 left_wall floor floor floor floor left_wall heart floor right_wall floor floor floor mho right_wall
8 bottom_left_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall heart floor floor right_wall
9 top_left_wall top_wall top_wall top_wall top_wall top_right_wall floor floor floor left_wall floor floor right_wall
10 left_wall robot floor floor floor sparky floor floor floor left_wall heart floor right_wall
11 left_wall floor floor floor floor right_wall floor floor floor sparky floor floor right_wall
12 left_wall floor floor minerva floor right_wall floor heart floor left_wall floor floor right_wall
13 left_wall floor floor floor floor right_wall top_wall top_wall top_wall left_wall floor floor right_wall
14 left_wall floor floor floor floor right_wall floor floor floor left_wall floor mho right_wall
15 left_wall floor floor floor floor sparky floor mho floor sparky floor minerva right_wall
16 left_wall floor floor floor floor right_wall floor floor floor left_wall floor floor right_wall
17 bottom_left_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_wall bottom_right_wall

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -0,0 +1,16 @@
# State machine constants
# playing the game: draw the map, listen for D-pad buttons to move player
STATE_PLAYING = 0
# player beat this map level
STATE_MAPWIN = 1
# waiting for player to press A button to continue
STATE_WAITING = 2
# player lost by touching sparky
STATE_LOST_SPARKY = 3
# minerva shows a fun fact
STATE_MINERVA = 4

View file

@ -0,0 +1,23 @@
def wrap_nicely(string, max_chars):
""" From: https://www.richa1.com/RichardAlbritton/circuitpython-word-wrap-for-label-text/
A helper that will return the string with word-break wrapping.
:param str string: The text to be wrapped.
:param int max_chars: The maximum number of characters on a line before wrapping.
"""
string = string.replace('\n', '').replace('\r', '') # strip confusing newlines
words = string.split(' ')
the_lines = []
the_line = ""
for w in words:
if len(the_line+' '+w) <= max_chars:
the_line += ' '+w
else:
the_lines.append(the_line)
the_line = w
if the_line:
the_lines.append(the_line)
the_lines[0] = the_lines[0][1:]
the_newline = ""
for w in the_lines:
the_newline += '\n'+w
return the_newline

View file

@ -0,0 +1,132 @@
from tilegame_assets.states import *
# Minerva before_move. Set game state to STATE_MINERVA
def minerva_walk(to_coords, from_coords, entity_obj, GAME_STATE):
GAME_STATE['STATE'] = STATE_MINERVA
return False
# Sparky before_move. If user does not have a Mho in inventory they lose.
# If user does have Mho subtract one from inventory and consume Sparky.
def sparky_walk(to_coords, from_coords, entity_obj, GAME_STATE):
if GAME_STATE['INVENTORY'].count("mho") > 0:
GAME_STATE['INVENTORY'].remove("mho")
GAME_STATE['ENTITY_SPRITES_DICT'][to_coords].remove(entity_obj)
if len(GAME_STATE['ENTITY_SPRITES_DICT'][to_coords]) == 0:
del GAME_STATE['ENTITY_SPRITES_DICT'][to_coords]
if (-1, -1) in GAME_STATE['ENTITY_SPRITES_DICT']:
GAME_STATE['ENTITY_SPRITES_DICT'][-1, -1].append(entity_obj)
else:
GAME_STATE['ENTITY_SPRITES_DICT'][-1, -1] = [entity_obj]
return True
else:
GAME_STATE['STATE'] = STATE_LOST_SPARKY
return True
# Robot before_move. If user has all Hearts they win the map.
def robot_walk(to_coords, from_coords, entity_obj, GAME_STATE):
if GAME_STATE['INVENTORY'].count("heart") == GAME_STATE['TOTAL_HEARTS']:
GAME_STATE["STATE"] = STATE_MAPWIN
return True
return False
# Remove the item from this location and add it to player inventory.
def take_item(to_coords, from_coords, entity_obj, GAME_STATE):
print(entity_obj)
GAME_STATE['INVENTORY'].append(entity_obj["map_tile_name"])
GAME_STATE['ENTITY_SPRITES_DICT'][to_coords].remove(entity_obj)
if len(GAME_STATE['ENTITY_SPRITES_DICT'][to_coords]) == 0:
del GAME_STATE['ENTITY_SPRITES_DICT'][to_coords]
if (-1, -1) in GAME_STATE['ENTITY_SPRITES_DICT']:
GAME_STATE['ENTITY_SPRITES_DICT'][-1, -1].append(entity_obj)
else:
GAME_STATE['ENTITY_SPRITES_DICT'][-1, -1] = [entity_obj]
return True
# main dictionary that maps tile type strings to objects.
# each one stores the sprite_sheet index and any necessary
# behavioral stats like can_walk or before_move
TILES = {
# empty strings default to floor and no walk.
"": {
"sprite_index": 10,
"can_walk": False
},
"floor": {
"sprite_index": 10,
"can_walk": True
},
"top_wall": {
"sprite_index": 7,
"can_walk": False
},
"top_right_wall": {
"sprite_index": 8,
"can_walk": False
},
"top_left_wall": {
"sprite_index": 6,
"can_walk": False
},
"bottom_right_wall": {
"sprite_index": 14,
"can_walk": False
},
"bottom_left_wall": {
"sprite_index": 12,
"can_walk": False
},
"right_wall": {
"sprite_index": 11,
"can_walk": False
},
"left_wall": {
"sprite_index": 9,
"can_walk": False
},
"bottom_wall": {
"sprite_index": 13,
"can_walk": False
},
"robot": {
"sprite_index": 1,
"can_walk": True,
"entity": True,
"before_move": robot_walk
},
"heart": {
"sprite_index": 5,
"can_walk": True,
"entity": True,
"before_move": take_item
},
"mho": {
"sprite_index": 2,
"can_walk": True,
"entity": True,
"before_move": take_item
},
"sparky": {
"sprite_index": 4,
"can_walk": True,
"entity": True,
"before_move": sparky_walk
},
"minerva": {
"sprite_index": 3,
"can_walk": True,
"entity": True,
"before_move": minerva_walk
},
"player": {
"sprite_index": 0,
"entity": True,
}
}