I am coding an infinite 2D block world chunk system for fun. I would like to improve performance, but I'm not that worried.
Game.py
import pygame
import world
class Game:
def __init__(self, title, window_size, fps):
self.title = title
self.window_size = window_size
self.FPS = fps
pygame.init()
self.screen = pygame.display.set_mode(self.window_size)
self.window_rect = pygame.Rect((0, 0), self.window_size)
self.clock = pygame.time.Clock()
pygame.display.set_caption(self.title)
self.running = True
self.world = world.World(8)
self.cam_x = 0
self.cam_y = 0
self.draw_area = (
int(self.window_size[0] // world.CHUNK_TOTAL_SIZE) + 2,
int(self.window_size[1] // world.CHUNK_TOTAL_SIZE) + 2,
)
print(self.draw_area)
def run(self):
while self.running:
self.do_frame()
self.quit_pygame()
def do_frame(self):
self.handle_events()
self.game_logic()
self.draw()
pygame.display.update(self.window_rect)
self.clock.tick(self.FPS)
# print(self.clock.get_fps())
def quit_pygame(self):
pygame.quit()
def handle_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.KEYDOWN:
print(self.clock.get_fps())
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.cam_x += 128
if keys[pygame.K_RIGHT]:
self.cam_x -= 128
if keys[pygame.K_UP]:
self.cam_y += 128
if keys[pygame.K_DOWN]:
self.cam_y -= 128
self.world.load_pos = (
int(-self.cam_x // world.CHUNK_TOTAL_SIZE),
int(-self.cam_y // world.CHUNK_TOTAL_SIZE),
)
def draw(self):
draw_offset_x = self.cam_x + self.window_rect.centerx
draw_offset_y = self.cam_y + self.window_rect.centery
start_chunk_x = int((-draw_offset_x) // world.CHUNK_TOTAL_SIZE)
start_chunk_y = int((-draw_offset_y) // world.CHUNK_TOTAL_SIZE)
chunk_poses = [
(x, y)
for x in range(start_chunk_x, start_chunk_x + self.draw_area[0])
for y in range(start_chunk_y, start_chunk_y + self.draw_area[1])
]
chunk_textures = self.world.get_chunk_textures(chunk_poses)
self.screen.fill((255, 255, 255))
for i, chunk_texture in enumerate(chunk_textures):
chunk_pos = chunk_poses[i]
self.screen.blit(
chunk_texture,
(
(chunk_pos[0] * world.CHUNK_TOTAL_SIZE) + draw_offset_x,
(chunk_pos[1] * world.CHUNK_TOTAL_SIZE) + draw_offset_y,
),
)
def game_logic(self):
self.world.load_chunks()
new_game = Game("2D Block World", (1000, 600), 60)
new_game.run()
World.py
import random
import pygame
BLOCK_TEXTURE_NAMES = [
"textures/void.png",
"textures/air.png",
"textures/grass_block.png",
"textures/dirt.png",
"textures/stone.png",
"textures/sandstone.png",
"textures/sand.png",
"textures/bedrock.png",
"textures/oak_log.png",
"textures/oak_leaves.png",
"textures/cobblestone.png",
"textures/oak_leaves_over_log.png",
]
BACKGROUND_COLOR = (0, 200, 255)
BLOCK_SIZE = 8
CHUNK_SIZE = 32
CHUNK_TOTAL_SIZE = BLOCK_SIZE * CHUNK_SIZE
CHUNK_RANGE = range(CHUNK_SIZE)
EMPTY_CHUNK_DATA = [0] * (CHUNK_SIZE * CHUNK_SIZE)
def load_texture(texture, size):
return pygame.transform.scale(pygame.image.load(texture).convert_alpha(), size)
def _2d_to_1d(pos):
return (pos[0] * CHUNK_SIZE) + pos[1]
def block_to_chunk(block_pos):
chunk_x, block_x = divmod(block_pos[0], CHUNK_SIZE)
chunk_y, block_y = divmod(block_pos[1], CHUNK_SIZE)
return (chunk_x, chunk_y), (block_x, block_y)
class Textures:
def __init__(self):
self.block_textures = [
load_texture(texture_name, (BLOCK_SIZE, BLOCK_SIZE))
for texture_name in BLOCK_TEXTURE_NAMES
]
self.empty_chunk_surface = pygame.Surface(
(CHUNK_TOTAL_SIZE, CHUNK_TOTAL_SIZE)
).convert_alpha()
self.background_block_surface = pygame.Surface(
(BLOCK_SIZE, BLOCK_SIZE)
).convert_alpha()
self.empty_chunk_surface.fill(BACKGROUND_COLOR)
self.background_block_surface.fill(BACKGROUND_COLOR)
class World:
def __init__(self, load_distance):
self.textures = Textures()
self.chunks = {}
self.chunk_textures = {}
self.load_pos = (0, 0)
self.load_distance = load_distance
def load_chunks(self):
chunk_range_x = range(
self.load_pos[0] - self.load_distance,
self.load_pos[0] + self.load_distance + 1,
)
chunk_range_y = range(
self.load_pos[1] - self.load_distance,
self.load_pos[1] + self.load_distance + 1,
)
chunks_in_range = [(x, y) for x in chunk_range_x for y in chunk_range_y]
chunks_to_load = []
chunks_to_unload = []
for chunk_pos in chunks_in_range:
if chunk_pos not in self.chunks.keys():
chunks_to_load.append(chunk_pos)
for chunk_pos in self.chunks.keys():
if chunk_pos not in chunks_in_range:
chunks_to_unload.append(chunk_pos)
# for chunk_pos in chunks_to_load:
# self.load_chunk(chunk_pos)
distances_from_load_pos = [
abs(self.load_pos[0] - chunk_pos[0]) + abs(self.load_pos[1] - chunk_pos[1])
for chunk_pos in chunks_to_load
]
if distances_from_load_pos:
best_distance = min(distances_from_load_pos)
self.load_chunk(
chunks_to_load[distances_from_load_pos.index(best_distance)]
)
for chunk_pos in chunks_to_unload:
self.chunks.pop(chunk_pos)
self.chunk_textures.pop(chunk_pos)
def load_chunk(self, chunk_pos):
self.chunks[chunk_pos] = self.generate_data(chunk_pos)
self.chunk_textures[chunk_pos] = self.generate_chunk_texture(chunk_pos)
def get_blocks(self, block_poses):
blocks = []
for block_pos in block_poses:
chunk_pos, local_block_pos = block_to_chunk(block_pos)
if chunk_pos in self.chunks.keys():
blocks.append(self.chunks[chunk_pos][_2d_to_1d(local_block_pos)])
else:
blocks.append(0)
return blocks
def get_chunk_textures(self, chunk_poses):
chunk_textures = []
for chunk_pos in chunk_poses:
if chunk_pos in self.chunk_textures.keys():
chunk_textures.append(self.chunk_textures[chunk_pos])
else:
chunk_textures.append(self.textures.empty_chunk_surface)
return chunk_textures
def generate_data(self, pos):
data = EMPTY_CHUNK_DATA[:]
# chunk_block_x = self.pos[0] * CHUNK_SIZE
chunk_block_y = pos[1] * CHUNK_SIZE
for x in CHUNK_RANGE:
for y in CHUNK_RANGE:
# block_x = x+chunk_block_x
block_y = y + chunk_block_y
data_pos = _2d_to_1d((x, y))
if block_y < 2:
data[data_pos] = 1
elif block_y == 2:
data[data_pos] = 2
else:
data[data_pos] = 3
return data
def generate_chunk_texture(self, pos):
chunk_texture = self.textures.empty_chunk_surface.copy()
chunk_data = self.chunks[pos]
for x in CHUNK_RANGE:
for y in CHUNK_RANGE:
data = chunk_data[_2d_to_1d((x, y))]
blit_pos = (x * BLOCK_SIZE, y * BLOCK_SIZE)
chunk_texture.blit(self.textures.block_textures[data], blit_pos)
return chunk_texture
I am using python 3.8












self.fps(downcased). \$\endgroup\$