1
\$\begingroup\$

I know there are a few questions on here similar to mine, but none of them seem to fit my needs.

I'm using PyGame to implement a tile based game similar to Final Fantasy or Zelda from the early Nintendo and Super Nintendo generation consoles.

enter image description here

The issue I'm running into is that I can't figure out how make a general way to render and position the tiles within view of the player camera. Once the camera starts moving, finding how much of an image is present in the camera view is very difficult, and then positioning it correctly is more complicated.

Any suggestions on how I can figure out a general way to create the grid correctly?

\$\endgroup\$
  • \$\begingroup\$ It really depends on the specific type of camera scrolling you want. Even among early NES games there's a variety of implementations. Furthermore, the two games you specifically reference use different scrolling schemes. I'd recommend reading the Theory and Practice of Cameras in Side-Scrollers, picking something that seems like a good fit for your design & trying that. If you get stuck you can come back with a focused question & get a focused answer. \$\endgroup\$ – Pikalek Mar 4 '16 at 2:02
  • \$\begingroup\$ I just want the map view to move when I get close to the edge and recenter again. \$\endgroup\$ – justthom8 Mar 4 '16 at 2:54
  • 1
    \$\begingroup\$ How are you storing your map data? How are you currently rendering your map? Do you have a camera view that you can extract visible tiles from? \$\endgroup\$ – jgallant Mar 4 '16 at 12:47
  • \$\begingroup\$ I'm storing my map data in a list, so that it's basically a matrix of tiles. I've been contemplating being able to only show part of a tile in my view, but I think it would be simpler just to make the camera be restricted to moving the length of the tiles. \$\endgroup\$ – justthom8 Mar 4 '16 at 23:23
1
\$\begingroup\$

To make the camera move when the player gets near the edge of the screen, you're going to need to set up a collision for the player to hit.

Camera Move Example

In the image, as the player moves closer to the displays edge, the player hits an invisible box that triggers the movement. Now, you may notice that instead of the display moving, it has the Map, in red, moving against the display.

That is because the map tiles should be blitted on a different surface than the display with the map moving the opposite direction of the player. This is due to the process of cancellation of momentum which I go into more detail in another question I answered here.

To implement this effect, the player is going to need be able to move around on the display itself within that box and only trigger the map to start moving when the player hits the box. An example of this would look something like this:

class Player:
    def __init__(self):
        self.image = pygame.Surface((32,32)) # Player Image
        self.image.fill((255,0,0))
        self.rect = pygame.Rect((284,284),(32,32))
        #Player Rect centered on a 600x600 display

        self.map_pos = (0,0)
        # The maps surfaces starting coordinates on the display

        self.moveBox = (100,100,500,500) # The move box.
        # (Lower x boundary,Lower y boundary,Upper x boundary, Upper y boundary)

    def move(self):
        mx,my = self.map_pos # Translate map_pos data

        # Get key press and move player 
        key = pygame.key.get_pressed() 
        if key[pygame.K_w]:
            self.rect.y -= 8
        if key[pygame.K_a]:
            self.rect.x -= 8
        if key[pygame.K_s]:
            self.rect.y += 8
        if key[pygame.K_d]:
            self.rect.x += 8

        # Check if player is hitting the x boundaries
        if player.rect.x <= self.moveBox[0]: # if player is hitting boundary
            self.rect.x += 8 # Return player to original pos
            mx += 8 # Move map against the player
            # note the original movement for this block was -x
        elif player.rect.x >= self.moveBox[2]-32:
            self.rect.x -= 8
            mx -= 8

        # Check if player is hitting the y boundaries
        if player.rect.y <= self.moveBox[1]:
            self.rect.y += 8
            my += 8
        elif player.rect.y >= self.moveBox[3]-32:
            self.rect.y -= 8
             my -= 8
        self.map_pos = (mx,my)
    def render(self,display):
        display.blit(self.image,(self.rect.x,self.rect.y))

The above is a sample player class that takes a player move event, checks if the player hit the move box, and moves the map if the box is being hit. If the box is not being hit, the player will move on the display normally. The below is small working script that illustrates the process. Feel free to play around with it.

import pygame
pygame.init()
#
display = pygame.display.set_mode((600,600))
clock = pygame.time.Clock()
#
scene = [
    "XXXXXXXXXXXXXXXXXXXXXX",
    "X--------------------X",
    "X--------------------X",
    "X--------------------X",
    "XXXXXXXX-------------X",
    "X--------------------X",
    "X------X-------------X",
    "X--XX--XXXXXXXXXXXXXXX",
    "X--XX--X-------------X",
    "X--------------------X",
    "X------X-------------X",
    "XXXXXXXXXXXXXXXXXXXXXX"]
#
map = pygame.Surface((len(scene[0])*64,len(scene)*64)) 
x,y = 0,0
for row in scene:
    for tile in row:
        if tile in "-":
            pygame.draw.rect(map,(0,155,0),((x,y),(64,64)))
        elif tile in "X":
            pygame.draw.rect(map,(125,125,125),((x,y),(64,64)))
        else:
            pygame.draw.rect(map,(255,128,122),((x,y),(64,64)))
        x += 64
    y += 64
    x = 0
#
class Player:
    def __init__(self):
        self.image = pygame.Surface((32,32))
        self.image.fill((255,0,0))
        self.rect = pygame.Rect((284,284),(32,32))
        self.map_pos = (0,0)
        self.moveBox = (100,100,500,500)
    def move(self):
        mx,my = self.map_pos
        key = pygame.key.get_pressed()
        if key[pygame.K_w]:
            self.rect.y -= 8
        if key[pygame.K_a]:
            self.rect.x -= 8
        if key[pygame.K_s]:
            self.rect.y += 8
        if key[pygame.K_d]:
            self.rect.x += 8
        if player.rect.x <= self.moveBox[0]:
            self.rect.x += 8
            mx += 8
        elif player.rect.x >= self.moveBox[2]-32:
            self.rect.x -= 8
            mx -= 8
        if player.rect.y <= self.moveBox[1]:
            self.rect.y += 8
            my += 8
        elif player.rect.y >= self.moveBox[3]-32:
            self.rect.y -= 8
            my -= 8
        self.map_pos = (mx,my)
    def render(self,display):
        display.blit(self.image,(self.rect.x,self.rect.y))
#
player = Player()
#
RUNNING = True
while RUNNING:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            RUNNING = False
    #
    player.move()
    #
    display.fill((0,155,0))
    display.blit(map,player.map_pos)
    player.render(display)
    #
    pygame.display.flip()
#
pygame.quit()
\$\endgroup\$
  • \$\begingroup\$ Thank you! This is exactly what I was looking for! It had been alluding me for a few days! \$\endgroup\$ – justthom8 Mar 18 '16 at 19:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.