I'm trying to separate my input loop from my game logic in my simple snake game that I've made with pygame, but, I'm really struggling to figure out why nothing is happening when I run the program.
I've tried importing pygame in the subprocess, I checked for errors on the subprocess, and got nowhere. I looked on google, but I wasn't able to find any usable examples, or similar issues. Has anybody ever figured any of this stuff out?
Okay, here's the code:
import pygame
import time
import multiprocessing as mp
import random as rnd
pygame.init()
def event_to_dict(event: pygame.event) -> dict:
return {
'type': event.type,
'key': event.key if event.type == pygame.KEYDOWN else None,
}
class SnakeBoard:
def __init__(self, rows: int, columns: int):
self.rows = rows
self.columns = columns
self.vertices = []
self.odd_column = False
self.buff = []
for _ in range(self.rows):
self.buff.append([' ' for _ in range(self.columns)])
def initialize(self):
for r in range(self.rows):
for c in range(self.columns):
self.buff[r][c] = ' '
self.odd_column = (self.columns >> 1) % 2 == 1
self.buff[self.rows >> 1][self.columns >> 1] = '\u25cb'
self.vertices = [(self.rows >> 1, self.columns >> 1)]
def place_food(self):
while True:
r = rnd.randint(0, self.rows - 1)
c = rnd.randint(0, self.columns - 1)
codd = c % 2 == 1
if (codd and self.odd_column or not codd and not self.odd_column) and self.buff[r][c] != '\u25cb':
self.buff[r][c] = '\u25c9'
break
def tick(self, direction: int) -> bool:
nr, nc = self.vertices[-1]
if direction == 0:
nr -= 1
elif direction == 1:
nc += 1
elif direction == 2:
nr += 1
elif direction == 3:
nc -= 1
else:
print("Invalid direction for snake")
exit(1)
if nr >= self.rows or nc >= self.columns or nr < 0 or nc < 0 or self.buff[nr][nc] == '\u25cb':
return False
self.vertices.append((nr, nc))
self.vertices.pop(0)
return True
class SnakeGame(SnakeBoard):
def __init__(self, rows: int, columns: int):
super().__init__(rows, columns)
self.score = 0
self.direction = 0
self.initialize()
self.place_food()
def tick(self, direction: int = -1) -> bool:
v = super().tick(self.direction if direction < 0 else direction)
if self.buff[self.vertices[-1][0]][self.vertices[-1][1]] == '\u25c9':
self.score += 1
self.vertices.append(self.vertices[-1])
self.place_food()
for r in range(self.rows):
for c in range(self.columns):
if (r, c) in self.vertices:
self.buff[r][c] = '\u25cb'
elif self.buff[r][c] != '\u25c9' and self.buff[r][c] != ' ':
self.buff[r][c] = ' '
return v
class GameLoop(mp.Process):
def __init__(self, q: object, size: list):
super().__init__()
self.q = q
self.size = size
self.g = SnakeGame(size[1] // 10, size[0] // 10)
self.g.initialize()
self.g.place_food()
self.screen = None
self.game_surf = None
self.font = None
def run(self) -> None:
try:
import pygame
pygame.init()
self.screen = pygame.display.set_mode(self.size)
self.game_surf = pygame.Surface(self.size)
self.font = pygame.font.SysFont('roboto', 16)
is_running = True
while is_running:
if self.q.poll(0):
d = self.q.recv()
if d is not None:
if d['type'] == pygame.KEYDOWN:
if d['key'] == pygame.K_a:
self.g.direction = 3
elif d['key'] == pygame.K_s:
self.g.direction = 2
elif d['key'] == pygame.K_d:
self.g.direction = 1
elif d['key'] == pygame.K_w:
self.g.direction = 0
elif d['key'] == pygame.K_ESCAPE:
is_running = False
else:
is_running = False
self.game_surf.fill((255, 255, 255))
for ri, r in enumerate(self.g.buff):
for ci, c in enumerate(r):
if c == '\u25cb':
# print("Drawing a snake at {}, {}".format(ri * 10, ci * 10))
pygame.draw.circle(self.game_surf,
(0, 0, 255),
((ci * 10) + 5, (ri * 10) + 5),
5)
elif c == '\u25c9':
# wprint("Placing food at {}, {}".format(ci, ri))
pygame.draw.circle(self.game_surf,
(0, 127, 255),
((ci * 10) + 5, (ri * 10) + 5),
5)
timg = self.font.render("Score: {}, Level: {}".format(self.g.score, self.g.score // 10 + 1),
True,
(0, 0, 0))
self.screen.blit(self.game_surf, (0, 0))
self.screen.blit(timg, (0, 0))
pygame.display.flip()
if self.g.tick():
time.sleep(1 / ((int(self.g.score / 10 + 1)) * 10))
else:
timg = self.font.render("Game Over! Would you like to try again?", True, (0, 0, 0))
self.screen.blit(timg, ((self.size[0] >> 1) - 150, self.size[1] >> 1))
timg = self.font.render("Yes", True, (0, 0, 0))
btn_pos = ((self.size[0] >> 1) - 25, (self.size[1] >> 1) + 20)
self.screen.blit(timg, btn_pos)
pygame.display.flip()
while True:
event = pygame.event.wait()
if event.type == pygame.QUIT:
is_running = False
break
elif event.type == pygame.MOUSEBUTTONUP:
mx, my = pygame.mouse.get_pos()
if btn_pos[0] - 5 <= mx <= btn_pos[0] + 30 and btn_pos[1] - 5 <= my <= btn_pos[1] + 20:
self.g.initialize()
self.g.place_food()
self.g.score = 0
break
self.q.close()
except Exception as e:
print(e)
if __name__ == '__main__':
size = [800, 600]
parent, child = mp.Pipe()
p = GameLoop(child, size)
p.start()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
ed = event_to_dict(event)
parent.send(ed)
parent.close()
p.join()
pygame.quit()
Sorry, it's kinda strange, this was migrated from the console to pygame, so some of the logic is still using the unicode symbols.
pygame.event.get(). Your window runs on another process, so you need to capture the events on that process. I don't understand why you're using themultiprocessingat all.