0
\$\begingroup\$

I have a tilemap where a character moves from tile to tile. I'm trying to make it possible for the character to move outside the screen and reappear on the other side, just like the tunnel in Pacman.

The movement works like following:

  1. Look for which arrow key is being pressed.
  2. Check if the tile where the user wants to go to is walkable. If not, go back to 1.
  3. If walkable, set it as target and start moving towards the tile.
  4. When arrived to the target, go back to 1.

The problem I have is that there're no tiles outside the screen. I've tried to set the target to the tile on the other side of the map if the target doesn't exist, but then the character would move across the whole screen.

I've tried a couple other solutions that either don't work or require lots of additional code in many different files.

Does anyone know a good solution or recommend another approach?

I'm programming in Python with Pygame but know other languages in general. Here's MVCE code for my class:

import pygame
import math

TILE_SIZE = (16, 16)


class Player(pygame.sprite.Sprite):

    def __init__(self, pos, size):
        super().__init__()
        self.rect = pygame.Rect(pos, size)  # Rectangle representing the character's pos.
        self.image = pygame.Surface(size)
        self.position = pos  # In order to have the position in float precision, (x, y).
        self.speed = 80  # Pixels per second
        self.tile_index = (pos[1] // TILE_SIZE[1], pos[0] // TILE_SIZE[0])  # (Row, column).
        self.moving = False
        self.target = self.rect

    def handle_events(self, level):
        key = pygame.key.get_pressed()
        if key[pygame.K_w] or key[pygame.K_UP]:
            self.set_target(level, y_offset=-1)
        elif key[pygame.K_s] or key[pygame.K_DOWN]:
            self.set_target(level, y_offset=1)
        elif key[pygame.K_a] or key[pygame.K_LEFT]:
            self.set_target(level, x_offset=-1)
        elif key[pygame.K_d] or key[pygame.K_RIGHT]:
            self.set_target(level, x_offset=1)

    def set_target(self, level, x_offset=0, y_offset=0):
        tile = level.get_tile(self.tile_index, x_offset, y_offset)
        if tile.is_walkable:
            self.target = tile.rect
            self.moving = True

    def move(self, dt):
        distance_x = self.target.x - self.rect.x
        distance_y = self.target.y - self.rect.y
        # Make sure the player move at 'speed * dt' or distance, depending on the smallest.
        x = math.copysign(min(abs(distance_x), self.speed * dt), distance_x)
        y = math.copysign(min(abs(distance_y), self.speed * dt), distance_y)
        self.position = (self.position[0] + x, self.position[1] + y)
        self.rect.topleft = self.position

    def stop_if_arrived(self):
        if self.rect.contains(self.target):
            self.moving = False
            self.tile_index = self.target.y // TILE_SIZE[1], self.target.x // TILE_SIZE[0]

    def update(self, level, dt, *ignore):
        if not self.moving:
            self.handle_events(level)
        else:
            self.move(dt)
            self.stop_if_arrived()
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

This is very simple. Just add another row of tiles "off screen" for the player to walk into and have that trigger the teleport. Or limit the visible tiles to one less row than you are now using.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @This is what I did ultimately (add rows off screen) and it works splendid. I had tried this approach but scraped it because I didn't want to 'hardcode' tiles in, which I later realized was stupid since all tiles are in a way 'hardcoded' in place. I still have this pre-optimization and 'make everything dynamic' mindset. This comment is for future readers to understand that this sort of thinking hinders one's progression. Thanks for your answer! \$\endgroup\$ Commented Sep 14, 2016 at 7:09
3
\$\begingroup\$

Your set_target() method currently chooses the next tile to move your player. You will need to know the bounds of your tilemap to move the player to the other side when reaching a wall. A simple way to do this teleportation is to check if the current x or y value is the size of your map and if the next move will make the value less than or greater than it, change the value to the opposing bounds.

For example: You have a 50x50 tilemap (origin is (0, 0) and in top left) and your player is at position (10, 49). The player moves down, causing the next move to be (10, 50) which is out of bounds. Instead, change the y-coordinate to be 0, teleporting the player to the opposite side.

\$\endgroup\$
4
  • \$\begingroup\$ This was one of the things I tried and didn't work as I expected unfortunately. The character has to exit the tilemap (the screen) and be completely outside it before I can teleport the character. That's were the problem is, but thanks for your efforts! \$\endgroup\$ Commented Sep 9, 2016 at 21:30
  • \$\begingroup\$ Are you able to adjust the coordinates that you teleport with? eg. if your sprite is 10px square you teleport the character when they are 10px beyond the edge of your map? \$\endgroup\$ Commented Sep 12, 2016 at 21:25
  • \$\begingroup\$ Your answer is great but unfortunately didn't work in my case. +1 for useful implementations in other games though! \$\endgroup\$ Commented Sep 14, 2016 at 7:11
  • \$\begingroup\$ @jdkorv11 The problem was the movement was from tile to tile. If an object was inside the map and set a target tile outside the map, it would give an KeyError (my tilemap is a dict). Also, if a target tile was set then the object had to move to it before choosing another target, otherwise I would have to reset many attributes and potentially break the algorithm (or at least the flow of the code). So the object couldn't start to move outside the screen to begin with. \$\endgroup\$ Commented Sep 14, 2016 at 7:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .