Render isometric map on a pygame screen (Python, 2D)

Hello there

I’m trying to render an isometric map in pygame, it looks like this

  • it’s a 16x16 map
  • tilesize is set at 111/64 pixels
  • the images for the tiles are 111/128 pixels.
  • the linchpin object is meant to put the four voxels at the center of the screen via its coordinates

I’m using the pytmx module to import the map. However, I have been unable to correctly translate the supplied x/y coordinates to something that pygame can show on the screen. In addition, the coordinates for placed objects seem to have its own rules, separate from the tiles.

In pygame, the coordinates start at the topleft (x=0, y=0) and directions pointing to right/down for x/y result in positive values, opposite directions result in negative values.

This is the function I used to translate the x/y values of the imported tiles (which is not working as intended, of course):

def to_isometric2(x: int, y: int) -> Vector2:
    iso_x = (x - y) * TILE_CWIDTH # 111 / 2 = 55
    iso_y = (x + y) * TILE_CHEIGHT # 128 / 2 = 64
    return  Vector2(iso_x, iso_y) + Vector2(SCREEN_ISO_CENTER) # iso-center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)

How do I translate the coordinates from Tiled to pygame?

Since your to_isometric2 functions looks fine at first sight, provided those constants are the correct values, can you show a screenshot of the result in pygame? It might give a hint about where this is going wrong.

Object coordinates map to the screen rather similar to tiles, btw, except that they use more fine-grained values based on the map’s tile height:

QPointF IsometricRenderer::pixelToScreenCoords(qreal x, qreal y) const
{
    const int tileWidth = map()->tileWidth();
    const int tileHeight = map()->tileHeight();
    const int originX = map()->height() * tileWidth / 2;
    const qreal tileY = y / tileHeight;
    const qreal tileX = x / tileHeight;

    return QPointF((tileX - tileY) * tileWidth / 2 + originX,
                   (tileX + tileY) * tileHeight / 2);
}

I got something to show now, had to change the voxel’s height to the map’s tile height (64px instead of 128):


Now the problem is to translate the linchpin’s coordinates to the center of the screen. When I’m using it to create an offset, I’m getting this:

The code responsible for (trying) to render with the linchpin at the screen’s center:

def draw(self, surface, position) -> None:
    # position == (linchpin.x, linchpin.y)
    self.offset.x = -position.x + SCREEN_CENTER.x
    self.offset.y = -position.y + SCREEN_CENTER.y
    for sprite in self:
        surface.blit(sprite.image, sprite.rect.topleft + self.offset)

I’m sorry I don’t think I can help you further with this. I would only suggest that before trying to center the view on the linchpin object, to just render the object on the map. That way I think it’s easier to check whether it matches the position in Tiled.

Once it has the right position relative to the tiles, you can figure out how to translate the view to center it on the screen.

Good idea, I put the linchpin on the screen:


I’ll try and figure out the rest. Thank you for the assistance.

Is there a way to get the grid positions from the object? Something like this
img1
instead of this (which is what the pytmx module imports from Tiled)

To get the grid position of an object, you need to divide its position (both x and y) by the tile height of the map.