Skip to content

Instantly share code, notes, and snippets.

@nitori
Last active May 6, 2025 21:37
Show Gist options
  • Save nitori/a2f2efcc3dd30ba5c4c4385a0d12cb74 to your computer and use it in GitHub Desktop.
Save nitori/a2f2efcc3dd30ba5c4c4385a0d12cb74 to your computer and use it in GitHub Desktop.
render order
import pygame
from pygame import Vector2
from dataclasses import dataclass
@dataclass
class Tile:
name: str
image: pygame.Surface
position: Vector2
@dataclass
class SingleTile(Tile):
# origin is local
origin: Vector2
@property
def world_origin(self):
return self.position + self.origin
@dataclass
class MultiTile(Tile):
# origins are local
origins: tuple[Vector2, Vector2]
@property
def world_origins(self):
return (
self.position + self.origins[0],
self.position + self.origins[1],
)
def __post_init__(self):
"""
Make sure first vector has the smaller x-value.
So the line goes from left to right.
"""
a, b = self.origins
self.origins = (a, b) if a.x < b.x else (b, a)
class CompareFunc:
"""
Python does not have a "compare func" for sorting like other languages (not anymore).
But you can effectively get the same behaviour by using a class
as key-func, that implements __lt__ for comparison.
"""
def __init__(self, tile: SingleTile | MultiTile):
self.tile = tile
@staticmethod
def compare(single: SingleTile, multi: MultiTile):
a, b = multi.world_origins
v = b - a
p = single.world_origin - a
return v.cross(p)
def __lt__(self, other: 'CompareFunc'):
if isinstance(self.tile, SingleTile) and isinstance(other.tile, SingleTile):
# simple y-sort
return self.tile.world_origin.y < other.tile.world_origin.y
if isinstance(self.tile, SingleTile) and isinstance(other.tile, MultiTile):
# do the cross-product sort
return self.compare(self.tile, other.tile) < 0
if isinstance(self.tile, MultiTile) and isinstance(other.tile, SingleTile):
# do the cross-product sort (just different order)
return self.compare(other.tile, self.tile) >= 0
# multi-on-multi case, not sure yet, just sort by y.
return self.tile.world_origins[0].y < other.tile.world_origins[0].y
class Player:
def __init__(self, pos, speed=150):
self.image = pygame.image.load('player.png')
self.speed = speed
self.rect = pygame.FRect(self.image.get_rect(center=pos))
def update(self, delta: float):
keys = pygame.key.get_pressed()
direction = Vector2(
keys[pygame.K_d] - keys[pygame.K_a],
keys[pygame.K_s] - keys[pygame.K_w],
)
if direction.length_squared() > 0:
direction.normalize_ip()
movement = direction * self.speed * delta
self.rect.x += movement.x
self.rect.y += movement.y
def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.Clock()
player = Player((50, 300))
player_tile = SingleTile(
name='PLAYER',
image=player.image,
position=Vector2(player.rect.topleft),
origin=Vector2(14, 94),
)
entities = [
player_tile,
SingleTile(
name='other_player',
image=pygame.image.load('player.png').convert_alpha(),
position=Vector2(450, 270),
origin=Vector2(14, 94),
),
SingleTile(
name='other_player2',
image=pygame.image.load('player.png').convert_alpha(),
position=Vector2(400, 470),
origin=Vector2(14, 94),
),
MultiTile(
name='house',
image=pygame.image.load('house.png').convert_alpha(),
position=Vector2(250, 250),
origins=(Vector2(1, 225), Vector2(255, 224)),
),
MultiTile(
name='bed',
image=pygame.image.load('bed.png').convert_alpha(),
position=Vector2(150, 150),
origins=(Vector2(3, 47), Vector2(104, 60)),
),
]
print()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
if event.type == pygame.MOUSEBUTTONDOWN:
print(event.pos)
delta = clock.tick(60) / 1000
player.update(delta)
player_tile.position = Vector2(player.rect.topleft)
# SORT!!!
entities.sort(key=CompareFunc)
print('\r', *[e.name for e in entities], end='')
screen.fill('black')
# render sorted list
for entity in entities:
screen.blit(entity.image, entity.position)
# Debug lines
if isinstance(entity, SingleTile):
pygame.draw.circle(screen, 'red', entity.world_origin, 3, 3)
elif isinstance(entity, MultiTile):
pygame.draw.line(screen, 'red', entity.world_origins[0], entity.world_origins[1], 3)
pygame.display.flip()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment