In order to allow the map to be larger than the screen, or shaped differently than a plain rectangle we use a CAMERA_VIEW
dictionary. Just like the full map dictionary, the keys are (x,y) coordinate tuples and the values are tile type strings. But unlike the full map dictionary the CAMERA_VIEW
is always the exact same size and shape 10x8 tiles in this game, which matches up with the screen size on the PyGamer and PyBadge devices.
There are two functions related to camera management: set_camera_view()
and draw_camera_view()
.
set_camera_view()
will build the CAMERA_VIEW
dictionary based on the current game state. It accepts parameters for x, y starting point within the map as well as width and height. The example game will always use 10, 8
for the size so it fits perfectly on the 160x128
pixel screen. If you wanted to adapt the game code to work with a different sized screen you could change these values.
draw_camera_view()
will draw the current contents of the CAMERA_VIEW
dictionary onto the screen. It will also check for the existence of and draw any entities that are within the coordinates of the CAMERA_VIEW
.
In this game, the camera will always stay centered on the player because we reference the player location in the x, and y value parameters when set_camera_view()
is called.
Here are the set_camera_view()
and draw_camera_view()
function definitions:
# 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)
In this game, the camera will always stay mostly centered on the player, because we reference the player location in the x, and y value parameters when set_camera_view()
is called. The max and min functions are used to minimize the amount of "outside the map" area that we show.
Here is the code inside the main loop that calls set_camera_view()
and draw_camera_view()
:
# inside main game loop: 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()