Close
0%
0%

CSV TileMap Game

CircuitPython example game that reads the initial map state from a CSV file stored on the device.

Similar projects worth following
This example game shows how to load the starting map state from a CSV file. The strings in the CSV map using a dictionary to objects that define sprite index to use for that tile as well as some information about the behavior of the tile.

The blinka sprite can be moved around the map using the D-pad. Each tile has a can_walk field that gets checked to enforce collisions with the player.

The examples are fairly basic but the system could be made to do more complex things by adding new behavior functions for tiles and/or making the map bigger and more complex.

I've only just gotten this working and need to clean it up a bit still but I thought it was neat and wanted to share. 

To run the example you need code.py, castle_sprite_sheet.bmp, and map.csv from the files section or the repo. Once you get it working edit the map.csv file with a spreadsheet app or any text editor to change the map around. If you want to take it a step further you can customize the tile stats and behaviors in the TILES dict to invent your own games.

The main code.py has been updated to the latest version and now shows an example of using a map larger than the screen, as well as allowing the player to push other entities.

Code py:

import board
import displayio
import adafruit_imageload
from displayio import Palette
from adafruit_pybadger import PyBadger
import time

# Direction constants for comparison
UP = 0
DOWN = 1
RIGHT = 2
LEFT = 3

# how long to wait between rendering frames
FPS_DELAY = 1/30

# how many tiles can fit on thes screen. Tiles are 16x16
SCREEN_HEIGHT_TILES = 8
SCREEN_WIDTH_TILES = 10

# hold the map state as it came out of the csv. Only holds non-entities.
ORIGINAL_MAP = {}

# hold the current map state if/when it changes. Only holds non-entities.
CURRENT_MAP = {}

# 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 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 = []

# 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 = {}

# list of entities that need to be on the screen currently based on the camera view
NEED_TO_DRAW_ENTITIES = []

# hold the location of the player in tile coordinates
PLAYER_LOC = (0,0)

# return from CURRENT_MAP the tile name of the tile of the given coords
def get_tile(coords):
    return 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[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[CURRENT_MAP[tile_coords[0], tile_coords[1]]]['can_walk']

# behavior function that allows the player to push the entity
def allow_push(to_coords, from_coords, entity_obj):
    push_x_offset = 0
    push_y_offset = 0
    print("inside allow push")
    print("%s -> %s" % (from_coords, to_coords))
    if to_coords[0] < from_coords[0]:
        # moving left
        push_x_offset = -1
        push_y_offset = 0
        
    elif to_coords[0] > from_coords[0]:
        # moving right
        push_x_offset = 1
        push_y_offset = 0
        
    elif to_coords[1] < from_coords[1]:
        # moving up
        push_x_offset = 0
        push_y_offset = -1
        
    elif to_coords[1] > from_coords[1]:
        # moving down
        push_x_offset = 0
        push_y_offset = 1
    
    # coords where we will be pushing the entity to
    push_to_tile_coords = (to_coords[0]+ push_x_offset, to_coords[1]+ push_y_offset)
    
    # check if the entity is allowed to move to there
    if is_tile_moveable(push_to_tile_coords):
        #print("dict before %s" % ENTITY_SPRITES_DICT)
        
        # check if there are etity(s) at the tile we are trying to push to.
        if push_to_tile_coords in ENTITY_SPRITES_DICT:
            # append the thing we are pushing to the the list at the new coordinates in the dictionary
            ENTITY_SPRITES_DICT[push_to_tile_coords].append(entity_obj)
        else:
            # create a list with the thing we are pushing and store it in the dictionary
            ENTITY_SPRITES_DICT[push_to_tile_coords] = [entity_obj]
        
        # remove the thing we are pushing from it's old location
        ENTITY_SPRITES_DICT[to_coords].remove(entity_obj)
        
        # if there are no entities left in the old location
        if len(ENTITY_SPRITES_DICT[to_coords]) == 0:
            # delete the empty lyst
            del ENTITY_SPRITES_DICT[to_coords]
        #print("dict...
Read more »

code.py

main code py file

plain - 18.99 kB - 11/29/2019 at 21:07

Download

map.csv

map csv for main code py file

ms-excel - 876.00 bytes - 11/29/2019 at 21:07

Download

castle_sprite_sheet.bmp

spritesheet

Bitmap Image File - 3.19 kB - 11/27/2019 at 03:43

Preview

code_basic_world_state.py

(OLD VERSION) alternative code py that allows the player to push the robot around the map.

plain - 12.00 kB - 11/28/2019 at 00:11

Download

map_basic_world_state.csv

(OLD VERSION) map for the basic world state example.

ms-excel - 666.00 bytes - 11/28/2019 at 00:11

Download

  • Blinka's Breakout

    foamyguy12/01/2019 at 05:35 0 comments

    The first "full" game using this tilemap system has been released on github.

    Blinkas Breakout GitHub

    In the coming days it will get a project of it's own with some images and videos. 

  • Some Much Needed Tidying

    foamyguy11/29/2019 at 21:16 0 comments

    Now that the basics of this system are working how I had originally imagined them I've gone back and cleaned things up a bit and tried to add detailed comments about everything happening. 

    With that taken care of I have updated the code py file in the repo and files section to the latest version. Hopefully this can now serve as a good starting point for someone looking to build a tile based game.

    I will probably leave the other variants of the code py file in the repo and files section in case anyone wants to see the different stages of development. But for now I would recommend starting from the current main code py file. It has all of the features and is the most well commented.

    I think now I'll take a break from working on the engine to build one or two specific games using this system.

  • Hooray! Bigger Maps!

    foamyguy11/29/2019 at 18:33 0 comments

    I've uploaded another new alternate code py file and a new map csv file to go along with it they are named "entity_system_and_camera" in the file section and the repo. 

    This new example contains a re-worked world state and entity system. Namely they've been separated into two more distinct systems. This helped solve a lot of headaches around what happens when entities were located on the same tile. That's diving a bit into the nitty gritty though.  

    The really cool thing about this update is that it allows for the map csv to be bigger than the screen. There is a very rudimentary camera system that will auto-follow the player as they move around the larger map. In the example code the map is wider than the screen by a few tiles and the camera will follow left and right as the player moves across a certain threshold.. Take a look at the set_camera_view() function if you are interested to see how we pull out the relevant portion of the larger map to draw onto the screen. 

    Perhaps at some point I'll work on a way to load a different "zone" from a new map csv file. So the player can go in/out of buildings and between various "zones" within a larger game world without needing to have it all loaded at once.

    But before I get ahead of myself I do intend to spend a bit of time cleaning this version up a bit more and documenting it much better. Once that is complete this will probably become the main code py file in the repo 

  • Basic Word State Example - Pushing Things Around

    foamyguy11/27/2019 at 23:58 0 comments

    In the code.py file we draw the world based on the input coming from the CSV file which is pretty neat. We have blinka in the game that we an move around with the d-pad, also pretty neat. But beyond being allowed to walk on a tile or not we don't have any other interesting interactions or ways to change anything within the world.

    Lets try to tweak it to allow blinka to change the world. In our case specifically by making it so that the robot head can be pushed around, until it gets stuck in a corner that is is.

    I've added a new file code_basic_world_state.py that implements a rudimentary world state feature. Broadly speaking it allows for updating things other than the player inside the world by allowing tiles to declare a before_move function that will get called immediately before the blinka is allowed to move onto the tile. 

    Now we can define a function and put some behavior inside of it. When the player moves blinka to the tile our function gets called giving us the opportunity to update something about the world (or do anything else we want really). 

    In this example our function is allow_push(). It will let blinka push the robot by first checking to see if the next space in the correct direction is walkable, and then updating the robot sprite by moving it's position in one tile in that direction.

    The other change with this version is that the location of blinka is determined by the 'player' in the csv file rather than hard-coded as x/y values on the screen.

    As with the code.py file still this could use a bit of cleanup and some more clear commenting. I will do my best to get it done sooner rather than later but may still succumb one more time to the temptation of working on the next feature first: allowing for a maps bigger than the screen.  

View all 4 project logs

Enjoy this project?

Share

Discussions

fid wrote 11/28/2019 at 17:49 point

This looks really fun.  Thanks for sharing it.  I think it would work with the PyGamer with no changes to the code.

  Are you sure? yes | no

Dan Maloney wrote 11/27/2019 at 16:25 point

Next up: porting "Chip's Challenge" to the badge...

  Are you sure? yes | no

foamyguy wrote 11/27/2019 at 17:02 point

Omg yes! I played the Microsoft version of that game when I was a kid. I couldn't remember the name of it, so thank you for mentioning it. Remembering playing that game definitely was inspiration for this project. I do intend to build this system up enough that it could be used to create and play puzzles like the ones in Chips Challenge.

  Are you sure? yes | no

Dan Maloney wrote 11/27/2019 at 17:28 point

I'd hate to tally up the time I spent on that game. I hear there's a port of it on Steam, which I am avoiding at all costs lest I get sucked down the nostalgia hole forever,

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates