Mapping Grid Position To Matrix Position - python

I needed help, I was trying to map the position of a ghost in its current position such as (225, 175) to its position on the matrix (7,9), not sure what the math is to calculate its relative position, the reason why im asking is because it makes it easier to detect if theres a wall in matrix position as opposed to current position. I want to be able to do this so I can decide its next move at an intersection.
import pygame
import time
import random
import pickle
import math
pygame.init()
pygame.mixer.init()
pygame.display.set_caption("Pac-Man")
# Sets the size of the screen via (WIDTH, HEIGHT)
SCREEN_WIDTH = 478
SCREEN_HEIGHT = 608
# Speed of Characters
SPEED = 1
# Frames per second, how fast the game runs
FPS = 50
# Colors (RED,GREEN,BLUE)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)
# Sets the WIDTH and HEIGHT of the window
WINDOW = (SCREEN_WIDTH, SCREEN_HEIGHT)
# Displays the screen
SCREEN = pygame.display.set_mode(WINDOW)
CLOCK = pygame.time.Clock()
PacManStartSurface = pygame.transform.scale(pygame.image.load
("PacManStart.png"), (23, 23))
PacManStartSurface.convert()
PacManStartRect = PacManStartSurface.get_rect(topleft =
(((SCREEN_WIDTH - 25) // 2),
(SCREEN_HEIGHT + 144) // 2))
PacManSurface = pygame.transform.scale(pygame.image.load
("PacManRight.png"), (23, 23))
PacManSurface.convert()
PacManRect = PacManStartSurface.get_rect(topleft =
(((SCREEN_WIDTH - 125) // 2),
(SCREEN_HEIGHT + 144) // 2))
CurrentSurface = PacManStartSurface
CurrentRect = PacManStartRect
BackgroundSurface = pygame.image.load("Background.png").convert()
PinkGhostSurface = pygame.transform.scale(pygame.image.load("PinkGhost.png")
.convert(), (23, 23))
PinkGhostRect = PinkGhostSurface.get_rect()
YellowGhostSurface = pygame.transform.scale(pygame.image.load
("YellowGhost.png")
.convert(), (23, 23))
YellowGhostRect = YellowGhostSurface.get_rect()
RedGhostSurface = pygame.transform.scale(pygame.image.load("RedGhost.png")
.convert(), (23, 23))
RedGhostRect = RedGhostSurface.get_rect()
BlueGhostSurface = pygame.transform.scale(pygame.image.load("BlueGhost.png")
.convert(), (23, 23))
BlueGhostRect = BlueGhostSurface.get_rect()
pygame.mixer.music.load('power_pellet.wav')
Font = pygame.font.Font("emulogic.ttf", 15)
class PacMan():
def __init__(self):
self.LIVES = 3
class Maze():
def __init__(self):
self.DOTS = []
self.WALLS = []
self.ENERGIZER = []
self.GHOSTS = []
self.WALLS_XY = []
self.BLOCK_WIDTH = 25
self.BLOCK_HEIGHT = 25
self.MAZE_OFFSET_X = 0
self.MAZE_OFFSET_Y = 50
# 0 - Dots
# 1 - Walls
# 2 - Empty Spaces
# 3 - Energizers
# 4 - Ghosts
self.MATRIX = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], \
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1], \
[1,3,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,3,1], \
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], \
[1,0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,0,1], \
[1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1], \
[1,1,1,1,0,1,1,1,2,1,2,1,1,1,0,1,1,1,1], \
[2,2,2,1,0,1,2,2,2,4,2,2,2,1,0,1,2,2,2], \
[1,1,1,1,0,1,2,1,1,1,1,1,2,1,0,1,1,1,1], \
[0,0,0,0,0,2,2,1,4,4,4,1,2,2,0,0,0,0,0], \
[1,1,1,1,0,1,2,1,1,1,1,1,2,1,0,1,1,1,1], \
[2,2,2,1,0,1,2,2,2,2,2,2,2,1,0,1,2,2,2], \
[1,1,1,1,0,1,2,1,1,1,1,1,2,1,0,1,1,1,1], \
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1], \
[1,3,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,3,1], \
[1,0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,1], \
[1,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1], \
[1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1], \
[1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1], \
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], \
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
# BackgroundImage(X, Y, WIDTH, HEIGHT)
self.MAZE_X = self.BLOCK_WIDTH * (len(self.MATRIX[0])
+ self.MAZE_OFFSET_X)
self.MAZE_Y = self.BLOCK_HEIGHT * (len(self.MATRIX)
+ self.MAZE_OFFSET_Y)
self.MAZE_WIDTH = self.BLOCK_WIDTH * len(self.MATRIX[0])
self.MAZE_HEIGHT = self.BLOCK_HEIGHT * len(self.MATRIX)
def DrawMaze(self, MazeSurface):
for ROW in range(len(self.MATRIX)):
for COLUMN in range(len(self.MATRIX[0])):
# Saves the position of each dot
if self.MATRIX[ROW][COLUMN] == 0:
self.DOTS.append([(self.BLOCK_WIDTH * COLUMN),
(self.BLOCK_HEIGHT * ROW), 4, 4])
# Saves the position of each wall
if self.MATRIX[ROW][COLUMN] == 1:
self.WALLS.append(pygame.draw.rect(MazeSurface, WHITE,
[((self.BLOCK_WIDTH) * COLUMN),
((self.BLOCK_HEIGHT) * ROW),
self.BLOCK_WIDTH, self.BLOCK_HEIGHT]))
# Saves the position of each Energizer
if self.MATRIX[ROW][COLUMN] == 3:
self.ENERGIZER.append([(self.BLOCK_WIDTH * COLUMN),
(self.BLOCK_HEIGHT * ROW), 14, 14])
if self.MATRIX[ROW][COLUMN] == 4:
self.GHOSTS.append([(self.BLOCK_WIDTH * COLUMN),
(self.BLOCK_HEIGHT * ROW), 23, 23])
for WALL in self.WALLS:
X, Y, WIDTH, HEIGHT = WALL
self.WALLS_XY.append((X, Y))
class Main(Maze):
def __init__(self):
# Inherits Maze class
Maze.__init__(self)
self.TimeBetweenBites = 0.1
self.LastBiteTime = time.time()
self.MouthOpen = False
self.PacManDirection = ""
self.GhostDirection = ""
self.SCORE = 0
self.HIGH_SCORE = 0
self.GridSizeX = SCREEN_HEIGHT // 19
self.GridSizeY = SCREEN_HEIGHT // 32
def PacManMovement(self):
key = pygame.key.get_pressed()
if key[pygame.K_LEFT] and not key[pygame.K_UP] \
and not key[pygame.K_DOWN]:
self.PacManDirection = "LEFT"
elif key[pygame.K_RIGHT] and not key[pygame.K_UP] \
and not key[pygame.K_DOWN]:
self.PacManDirection = "RIGHT"
elif key[pygame.K_UP] and not key[pygame.K_LEFT] \
and not key[pygame.K_RIGHT]:
self.PacManDirection = "UP"
elif key[pygame.K_DOWN] and not key[pygame.K_LEFT] \
and not key[pygame.K_RIGHT]:
self.PacManDirection = "DOWN"
def ContinuePacManMovement(self):
if self.PacManDirection == "LEFT":
CurrentRect.x -= SPEED
self.PacManWallDetection(-1, 0, CurrentRect)
if self.PacManDirection == "RIGHT":
CurrentRect.x += SPEED
self.PacManWallDetection(1, 0, CurrentRect)
if self.PacManDirection == "UP":
CurrentRect.y -= SPEED
self.PacManWallDetection(0, -1, CurrentRect)
if self.PacManDirection == "DOWN":
CurrentRect.y += SPEED
self.PacManWallDetection(0, 1, CurrentRect)
def ContinueGhostMovement(self):
if self.GhostDirection == "LEFT":
PinkGhostRect.x -= SPEED
self.GhostWallDetection(-1, 0, PinkGhostRect)
if self.GhostDirection == "RIGHT":
PinkGhostRect.x += SPEED
self.GhostWallDetection(1, 0, PinkGhostRect)
if self.GhostDirection == "UP":
PinkGhostRect.y -= SPEED
self.GhostWallDetection(0, -1, PinkGhostRect)
if self.GhostDirection == "DOWN":
PinkGhostRect.y += SPEED
self.GhostWallDetection(-1, 0, PinkGhostRect)
def PacManTeleport(self):
if CurrentRect.right < 0:
CurrentRect.right = SCREEN_WIDTH + 20
if CurrentRect.left > SCREEN_WIDTH:
CurrentRect.right = 0
def GhostTeleport(self, Intersection):
if PinkGhostRect.right < 0:
PinkGhostRect.right = SCREEN_WIDTH + 20
Intersection.append("LEFT")
if PinkGhostRect.left > SCREEN_WIDTH:
PinkGhostRect.right = 0
Intersection.append("RIGHT")
def PacManWallDetection(self, x, y, CurrentRect):
CurrentRect.right += x
for WALL in self.WALLS:
COLLIDE = CurrentRect.colliderect(WALL)
if COLLIDE:
if x < 0:
CurrentRect.left = WALL.right
CurrentSurface = pygame.transform.rotate(PacManSurface, 180)
MazeSurface.blit(CurrentSurface, CurrentRect)
if x > 0:
CurrentRect.right = WALL.left
break
CurrentRect.top += y
for WALL in self.WALLS:
COLLIDE = CurrentRect.colliderect(WALL)
if COLLIDE:
if y < 0:
CurrentRect.top = WALL.bottom
if y > 0:
CurrentRect.bottom = WALL.top
break
def GhostWallDetection(self, x, y, PinkGhostRect):
PinkGhostRect.right += x
for WALL in self.WALLS:
COLLIDE = PinkGhostRect.colliderect(WALL)
if COLLIDE:
if x < 0:
PinkGhostRect.left = WALL.right
if random.randrange(0, 100) <= 40:
self.GhostDirection = "RIGHT"
if x > 0:
PinkGhostRect.right = WALL.left
if random.randrange(0, 100) <= 40:
self.GhostDirection = "LEFT"
break
PinkGhostRect.top += y
for WALL in self.WALLS:
COLLIDE = PinkGhostRect.colliderect(WALL)
if COLLIDE:
if y < 0:
PinkGhostRect.top = WALL.bottom
if random.randrange(0, 100) <= 40:
self.GhostDirection = "DOWN"
if y > 0:
PinkGhostRect.bottom = WALL.top
if random.randrange(0, 100) <= 40:
self.GhostDirection = "UP"
break
def GetAvailableMoves(self):
Intersection = []
self.GhostTeleport(Intersection)
print(PinkGhostRect.topleft)
print(self.WALLS_XY)
if ((PinkGhostRect.x - 1, PinkGhostRect.y)) not in self.WALLS_XY:
Intersection.append("LEFT")
if ((PinkGhostRect.x + 1, PinkGhostRect.y)) not in self.WALLS_XY:
Intersection.append("RIGHT")
if ((PinkGhostRect.x, PinkGhostRect.y - 1)) not in self.WALLS_XY:
Intersection.append("UP")
if ((PinkGhostRect.x, PinkGhostRect.y + 1)) not in self.WALLS_XY:
Intersection.append("DOWN")
print(Intersection)
return Intersection
def EatDots(self):
for ROW in range(len(self.MATRIX)):
for COLUMN in range(len(self.MATRIX[0])):
for DOT in self.DOTS:
CHOMP = CurrentRect.colliderect(DOT)
if CHOMP:
Main.PlaySound(self, 0)
self.DOTS.remove(DOT)
self.MATRIX[ROW][COLUMN] = 3
self.SCORE += 10
if self.SCORE > self.HIGH_SCORE:
self.HIGH_SCORE = self.SCORE
return str(self.SCORE), str(self.HIGH_SCORE)
def EatEnergizer(self):
for ROW in range(len(self.MATRIX)):
for COLUMN in range(len(self.MATRIX[0])):
for POWERUP in self.ENERGIZER:
CHOMP = CurrentRect.colliderect(POWERUP)
if CHOMP:
self.ENERGIZER.remove(POWERUP)
self.MATRIX[ROW][COLUMN] = 3
self.SCORE += 50
Main.PlaySound(self, 1)
if self.SCORE > self.HIGH_SCORE:
self.HIGH_SCORE = self.SCORE
return str(self.SCORE), str(self.HIGH_SCORE)
def EatGhosts(self):
pass
def DrawDots(self):
for POSITION in self.DOTS:
X = POSITION[0] + 13
Y = POSITION[1] + 13
WIDTH = POSITION[2]
HEIGHT = POSITION[3]
pygame.draw.circle(MazeSurface, YELLOW, (X, Y),
WIDTH // 2, HEIGHT // 2)
def DrawEnergizer(self):
for POSITION in self.ENERGIZER:
X = POSITION[0] + 13
Y = POSITION[1] + 13
WIDTH = POSITION[2]
HEIGHT = POSITION[3]
pygame.draw.circle(MazeSurface, YELLOW, (X, Y),
WIDTH // 2, HEIGHT // 2)
def DrawGhosts(self):
MazeSurface.blit(PinkGhostSurface, PinkGhostRect)
MazeSurface.blit(YellowGhostSurface, YellowGhostRect)
MazeSurface.blit(RedGhostSurface, RedGhostRect)
MazeSurface.blit(BlueGhostSurface, BlueGhostRect)
def GhostStartPosition(self):
X, Y, WIDTH, HEIGHT = self.GHOSTS[0]
PinkGhostRect.x = X
PinkGhostRect.y = Y
X, Y, WIDTH, HEIGHT = self.GHOSTS[1]
YellowGhostRect.x = X
YellowGhostRect.y = Y
X, Y, WIDTH, HEIGHT = self.GHOSTS[2]
RedGhostRect.x = X
RedGhostRect.y = Y
X, Y, WIDTH, HEIGHT = self.GHOSTS[3]
BlueGhostRect.x = X
BlueGhostRect.y = Y
def PlaySound(self, Track):
if Track == 0:
Eat = pygame.mixer.Sound("pacman_chomp.wav")
Eat.play()
pygame.mixer.fadeout(400)
if Track == 1:
EatPellet = pygame.mixer.Sound("pacman_eatghost.wav")
EatPellet.play()
pygame.mixer.music.play(7)
pygame.mixer.fadeout(400)
def ShowScore(self):
global Font
OneUpText = Font.render("1UP", True, WHITE)
OneUpTextRect = OneUpText.get_rect(center = (70, 10))
# Displays current score
OneUpScoreText = Font.render(str(self.SCORE), True, WHITE)
OneUpScoreRect = OneUpScoreText.get_rect(center =
((SCREEN_WIDTH - 290)
// 2, 26))
HighScoreText = Font.render("High Score", True, WHITE)
HighScoreTextRect = HighScoreText.get_rect(center =
(SCREEN_WIDTH // 2, 10))
# Displays High Score
HighScoreNumber = Font.render(str(self.HIGH_SCORE), True, WHITE)
HighScoreNumberRect = HighScoreNumber.get_rect(center =
((SCREEN_WIDTH + 90)
// 2, 26))
SCREEN.blit(OneUpText, OneUpTextRect)
SCREEN.blit(OneUpScoreText, OneUpScoreRect)
SCREEN.blit(HighScoreText, HighScoreTextRect)
SCREEN.blit(HighScoreNumber, HighScoreNumberRect)
def PacManBite(self):
global CurrentSurface
CurrentTime = time.time()
if CurrentTime - self.LastBiteTime >= self.TimeBetweenBites:
self.LastBiteTime = CurrentTime
if self.MouthOpen:
CurrentSurface = PacManStartSurface
else:
CurrentSurface = PacManSurface
self.MouthOpen = not self.MouthOpen
if self.PacManDirection == "LEFT":
CurrentSurface = pygame.transform.rotate(CurrentSurface, 180)
if self.PacManDirection == "RIGHT":
CurrentSurface = CurrentSurface
if self.PacManDirection == "UP":
CurrentSurface = pygame.transform.rotate(CurrentSurface, 90)
if self.PacManDirection == "DOWN":
CurrentSurface = pygame.transform.rotate(CurrentSurface, 270)
def PacManLives(self):
pass
def Update(self):
Player.PacManTeleport()
Player.ContinuePacManMovement()
Player.ContinueGhostMovement()
Player.GetAvailableMoves()
MazeSurface.blit(BackgroundSurface, BackgroundRect)
Player.DrawDots()
Player.DrawEnergizer()
Player.DrawGhosts()
Player.EatDots()
Player.EatEnergizer()
MazeSurface.blit(CurrentSurface, CurrentRect)
Player.PacManBite()
SCREEN.blit(MazeSurface, MazeRect)
Player.ShowScore()
Player = Main()
BackgroundSurface = pygame.transform.scale(BackgroundSurface,
(Player.MAZE_WIDTH,
Player.MAZE_HEIGHT))
BackgroundRect = BackgroundSurface.get_rect()
MazeSurface = pygame.Surface((Player.MAZE_WIDTH, Player.MAZE_HEIGHT))
MazeRect = MazeSurface.get_rect(topleft = (Player.MAZE_OFFSET_X,
Player.MAZE_OFFSET_Y))
Player.DrawMaze(MazeSurface)
Player.GhostStartPosition()
'''
Before the game starts ...
pregame = True
while pregame:
if key button pressed:
pregame = False
run = True
'''
run = True
while run:
SCREEN.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
Player.PacManMovement()
Player.Update()
pygame.display.update()
CLOCK.tick(FPS)
pygame.quit()

You just need to divide the position in the grid by the size of a tile with the // (floor-division) operator:
pos_in_maze = (225, 175)
column = pos_in_maze[0] // Player.BLOCK_WIDTH
row = pos_in_maze[1] // Player.BLOCK_HEIGHT
Since the size of a tile is 25 the results are 9 for the column and 7 for the row.
screen_pos = (225, 175)
column = (screen_pos[0] - Player.MAZE_OFFSET_X) // Player.BLOCK_WIDTH
row = (screen_pos[1] - Player.MAZE_OFFSET_Y) // Player.BLOCK_HEIGHT
Since MAZE_OFFSET_X is 0 and MAZE_OFFSET_Y is 50 the results are 9 for the column and 5 for the row.

Related

how can i center the numbers in the two grids?

this is the program of the game, it contains everything but i need help to center the numbers inside the grids, i did some attempts i put them in comments, please help me
imports of my program
import numpy as np
import random
import pygame
from pygame.locals import *
constructor of the class
class Py2048:
def __init__(self):
self.N = 4
self.grid1 = np.zeros((self.N, self.N), dtype=int) #initialiasation de la grid avec des 0
self.grid2 = np.zeros((self.N, self.N), dtype=int) #initialiasation
self.cellSize = 70
self.gap = 3
self.windowBgColor = (187, 173, 160)
self.blockSize = self.cellSize + self.gap * 2
self.W = 700
self.H = self.W
pygame.init()
pygame.display.set_caption("2048")
pygame.font.init()
self.myfont = pygame.font.SysFont('Comic Sans MS', 30)
self.screen = pygame.display.set_mode((self.W, self.H))
adding a new number to the grids
def new_number(self, k=1):
free_poss1 = list(zip(*np.where(self.grid1 == 0))) #position de la grid
free_poss2 = list(zip(*np.where(self.grid2 == 0)))
for pos in random.sample(free_poss1, k=k): #random 2 ou 4
if random.random() < .1:
self.grid1[pos] = 4
else:
self.grid1[pos] = 2
for pos in random.sample(free_poss2, k=k): #random 2 ou 4
if random.random() < .1:
self.grid2[pos] = 4
else:
self.grid2[pos] = 2
#staticmethod
def _get_nums(this):
this_n = this[this != 0]
this_n_sum = []
skip = False
for j in range(len(this_n)):
if skip:
skip = False
continue
if j != len(this_n) - 1 and this_n[j] == this_n[j + 1]:
new_n = this_n[j] * 2
skip = True
else:
new_n = this_n[j]
this_n_sum.append(new_n)
return np.array(this_n_sum)
def make_move(self, move): #move
for i in range(self.N):
if move in 'lr':
this1 = self.grid1[i, :]
this2 = self.grid2[i, :]
else:
this1 = self.grid1[:, i]
this2 = self.grid2[:, i]
flipped = False
if move in 'rd':
flipped = True
this1 = this1[::-1]
this2 = this2[::-1]
this_n1 = self._get_nums(this1)
this_n2 = self._get_nums(this2)
new_this1 = np.zeros_like(this1)
new_this1[:len(this_n1)] = this_n1
new_this2 = np.zeros_like(this2)
new_this2[:len(this_n2)] = this_n2
if flipped:
new_this1 = new_this1[::-1]
new_this2 = new_this2[::-1]
if move in 'lr':
self.grid1[i, :] = new_this1
self.grid2[i, :] = new_this2
else:
self.grid1[:, i] = new_this1
self.grid2[:, i] = new_this2
this is where i have the problem, when i draw the two grids i dont know how to center the numbers in them
def draw_game(self):
self.screen.fill(self.windowBgColor)
for i in range(self.N):
rectY = self.blockSize * i + self.gap
for j in range(self.N):
n1 = self.grid1[i][j]
n2 = self.grid2[i][j]
rectX = 200 + self.blockSize * j + self.gap
pygame.draw.rect(
self.screen,
(255, 255, 255),
pygame.Rect(rectX, 40 + rectY, self.cellSize, self.cellSize),
border_radius = 6
)
pygame.draw.rect(
self.screen,
(255, 255, 255),
pygame.Rect(rectX, 360 + rectY, self.cellSize, self.cellSize),
border_radius = 6
)
if n1 == 0 and n2 == 0:
continue
text_surface1 = self.myfont.render(f'{n1}', True, (0, 0, 0))
text_rect1 = text_surface1.get_rect(center=(rectX ,
rectY ))
self.screen.blit(text_surface1, text_rect1)
# text_surface2 = self.myfont.render(f'{n2}', True, (0, 0, 0))
# text_rect2 = text_surface2.get_rect(center=(rectX ,
# 360 + rectY ))
# self.screen.blit(text_surface2, text_rect2)
#staticmethod
def wait_for_key():
while True:
for event in pygame.event.get():
if event.type == QUIT:
return 'q'
if event.type == KEYDOWN:
if event.key == K_UP:
return 'u'
elif event.key == K_RIGHT:
return 'r'
elif event.key == K_LEFT:
return 'l'
elif event.key == K_DOWN:
return 'd'
elif event.key == K_q or event.key == K_ESCAPE:
return 'q'
def play(self):
self.new_number(k=2)
while True:
self.draw_game()
pygame.display.flip()
cmd = self.wait_for_key()
if cmd == 'q':
break
old_grid1 = self.grid1.copy()
old_grid2 = self.grid2.copy()
self.make_move(cmd)
print(game.grid1)
print(game.grid2)
if all((self.grid1 == old_grid1).flatten()) and all((self.grid2 == old_grid2).flatten()):
continue
self.new_number()
if __name__ == '__main__':
game = Py2048()
game.play()
this is the main code for the modified 2048 game that i wanna create
See How to Center Text in Pygame. When calculating the text rectangle, the center of the text rectangle must be set by the center of the grid. Store the tile rectangle in a variable and use it to set the center of the text rectangle:
for i in range(self.N):
rectY = self.blockSize * i + self.gap
for j in range(self.N):
n1 = self.grid1[i][j]
n2 = self.grid2[i][j]
rectX = 200 + self.blockSize * j + self.gap
tile_rect1 = pygame.Rect(rectX, 40 + rectY, self.cellSize, self.cellSize)
pygame.draw.rect(self.screen, (255, 255, 255), tile_rect1, border_radius = 6)
tile_rect2 = pygame.Rect(rectX, 360 + rectY, self.cellSize, self.cellSize),
pygame.draw.rect(self.screen, (255, 255, 255), tile_rect2, border_radius = 6)
if n1 == 0 and n2 == 0:
continue
text_surface1 = self.myfont.render(f'{n1}', True, (0, 0, 0))
text_rect1 = text_surface1.get_rect(center = tile_rect1.center)
self.screen.blit(text_surface1, text_rect1)
text_surface2 = self.myfont.render(f'{n2}', True, (0, 0, 0))
text_rect2 = text_surface2.get_rect(center = tile_rect2.center)
self.screen.blit(text_surface2, text_rect2)

I need help troubelshotting why my buttons arent being detected properly

My game is a simple slot game that's based around a numpy grid system and is played with left click to spin and r to reset the board. Im trying to implement a point system to make it more exciting but i cant get the code to detect when im clicking the buttons correctly.
The part of the code that seems to be malfunctioning is the posCheckRight and posCheckLeft functions (close to the bottom part of the code, row 160 - 184). They work correctly sometimes but more than often not, to replicate my issues try clicking nearby the buttons and check the inputs
import pygame as pg
import sys
import numpy as np
import random as rand
pg.init()
width = 800
height = 800
lineWidth = 15
winLineWidth = 15
boardRows = 4
boardCols = 4
squareSize = 200
space = 55
windowName = "Slots"
windowNameInt = 0
cost = 50
costtxt = "Cost: {}".format(cost)
fontClr = (255,99,71)
player = 1
game_over = False
posCheckLvlR = 0
posCheckLvlL = 0
bgColor = (200, 200, 0)
lineColor = (0, 0, 180)
triangleColor = (255, 0, 0)
winLineColor = (220, 220, 220)
circleColor = (239, 231, 200)
crossColor = (66, 66, 66)
screen = pg.display.set_mode((width, height))
pg.display.set_caption(windowName)
screen.fill(bgColor)
board = np.zeros((boardRows, boardCols))
def drawLines() :
#Line 1 vert
pg.draw.line(screen, lineColor, (0, squareSize), (width, squareSize), lineWidth)
#Line 2 vert
pg.draw.line(screen, lineColor, (0, 2 * squareSize), (width, 2 * squareSize), lineWidth)
#Line 3 vert
pg.draw.line(screen, lineColor, (0, 3 * squareSize), (width, 3 * squareSize), lineWidth)
#Line 1 hori
pg.draw.line(screen, lineColor, (squareSize, 0), (squareSize, height), lineWidth)
#Line 2 hori
pg.draw.line(screen, lineColor, (2 * squareSize, 0), (2 * squareSize, height), lineWidth)
#Line 3 hori
pg.draw.line(screen, lineColor, (3 * squareSize, 0), (3 * squareSize, height - 60), lineWidth)
def drawPointSyst() :
#(rightest point)(top point)(bottom point)
pg.draw.polygon(screen, (triangleColor), ((790, 760), (760, 730), (760, 790)))
#(leftest point)(top point)(bottom point)
pg.draw.polygon(screen, (triangleColor), ((720, 760), (750, 730), (750, 790)))
#temp square
pg.draw.rect(screen, (triangleColor), (720, 730, 30, 60))
pg.draw.rect(screen, (triangleColor), (760, 730, 30, 60))
myFont = pg.font.SysFont(None, 50)
textSurface = myFont.render(costtxt, True, (fontClr))
#(x,y)
screen.blit(textSurface, (560, 750))
snake1 = pg.image.load("snake.png")
snake2 = pg.image.load("blackSnake.png")
def drawShapes():
for row in range(boardRows):
for col in range(boardCols):
if board[row][col] == 1:
screen.blit(snake2, (int( col * squareSize + squareSize//2 - 32), int( row * squareSize + squareSize//2 - 32)))
elif board[row][col] == 2:
screen.blit(snake1, (int( col * squareSize + squareSize//2 - 32), int( row * squareSize + squareSize//2 - 32)))
def markSquare(row, col):
shape = rand.randint(1,2)
board[row][col] = shape
def freeSquare(row, col):
return board[row][col] == 0
def boardCheck():
for row in range(boardRows):
for col in range(boardCols):
if board[row][col] == 0:
return False
return True
def checkWin(player):
global windowNameInt
#All vertical
for col in range(boardCols):
if board[0][col] == player and board[1][col] == player and board[2][col] == player and board[3][col] == player:
vertWinLine(col)
windowNameInt += 1
#All horizontal
for row in range(boardRows):
if board[row][0] == player and board[row][1] == player and board[row][2] == player and board[row][3] == player:
horiWinLine(row)
windowNameInt += 1
#From bottom right to top left
if board[3][0] == player and board[2][1] == player and board[1][2] == player and board[0][3] == player:
drawAscDiagonal()
windowNameInt += 1
#From top left to bottom right
if board[0][0] == player and board[1][1] == player and board[2][2] == player and board[3][3] == player:
drawDescDiagonal()
windowNameInt += 1
def vertWinLine(col):
posX = col * squareSize + squareSize//2
color = winLineColor
pg.draw.line(screen, color, (posX, 15), (posX, height - 15), lineWidth)
# print("verti win")
def horiWinLine(row):
posY = row * squareSize + squareSize//2
color = winLineColor
pg.draw.line(screen, color, (15, posY), (width - 15, posY), winLineWidth)
# print("hori win")
def drawAscDiagonal():
color = winLineColor
pg.draw.line(screen, color, (15, height - 15), (width - 15, 15), winLineWidth)
# print("asc win")
def drawDescDiagonal():
color = winLineColor
pg.draw.line(screen, color, (15, 15), (width - 15, height - 15), winLineWidth)
# print("diag win")
def restart():
screen.fill(bgColor)
drawLines()
drawPointSyst()
windowName = (str(windowNameInt))
pg.display.set_caption(windowName)
for row in range(boardRows):
for col in range(boardCols):
board[row][col] = 0
drawLines()
drawPointSyst()
def posCheckLeft(pos) :
global posCheckLvlL
for x in pos :
if posCheckLvlL % 2 == 0 :
if x > 720 and x < 750 :
posCheckLvlL += 1
pass
elif posCheckLvlL % 2 == 1 :
if x > 730 and x < 790 :
posCheckLvlL += 1
return True
return False
def posCheckRight(pos) :
global posCheckLvlR
for x in pos :
if posCheckLvlR % 2 == 0 :
if x > 760 and x < 790 :
posCheckLvlR += 1
pass
elif posCheckLvlR % 2 == 1 :
if x > 730 and x < 790 :
posCheckLvlR += 1
return True
return False
def game() :
for event in pg.event.get():
if event.type == pg.QUIT:
sys.exit()
if event.type == pg.MOUSEBUTTONDOWN:
pos = pg.mouse.get_pos()
print(pos)
if posCheckLeft(pos) :
print("left")
if posCheckRight(pos) :
print("right")
while not boardCheck() :
randMouseX = rand.randint(0, width - 1)
randMouseY = rand.randint(0, height - 1)
clickedRow = int(randMouseY // squareSize)
clickedCol = int(randMouseX // squareSize)
# print("Click ", pos, "Grid coordinates: ", clickedRow, clickedCol)
if freeSquare(clickedRow, clickedCol) :
markSquare(clickedRow, clickedCol)
drawShapes()
checkWin(1)
checkWin(2)
if event.type == pg.KEYDOWN:
if event.key == pg.K_r:
restart()
pg.display.update()
while True: game()
If you need to check the (x,y) position of the clicks, then you just need to check x and y. I believe this does what you want:
def posCheckLeft(pos) :
x,y = pos
return 720 < x < 750 and 730 < y < 790
def posCheckRight(pos) :
x,y = pos
return 760 < x < 790 and 730 < y < 790

Sliding image puzzle game - image not centered when game window is resized

I am working on a sliding puzzle game, but when I adjust the game window of the game the image with tiles is stock on the left side and it cannot go with center. I borrowed this code from GitHub. I am new with python and starting to explore new things. I want to learn more in python and thank you in advance. These are the codes:
import pygame as pg
import os.path
import random
import sys
class PuzzlerGame():
def init(self):
global BASICFONT
window_width = 1380
# window_width =700
window_height = 770
self.tile_width = 150
# self.tile_width = 75
self.tile_height = 150
# self.tile_height = 75
self.coloumn = 4
self.rows = 4
self.img_list = [0, "image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg",
"image5.jpg", "image6.jpg", "image7.jpg", "image8.jpg",
"image9.jpg", "image10.jpg", ]
self.empty_tile = (3, 3)
global emptyc, emptyr
emptyc, emptyr = 3, 3
self.color = (255, 130, 130)
# white = (215,215,215)
self.yellow = (255, 255, 0)
self.red = (200, 15, 15)
self.black = (0, 0, 0)
self.tiles = {}
pg.init()
self.gameWindow = pg.display.set_mode((window_width, window_height))
# pg.display.set_caption("Puzzler")
pg.display.set_caption("Fun City slide puzzle")
# self.gameWindow.fill(white)
self.gameWindow.fill(self.red)
pg.display.update()
if (os.path.isfile('level.txt')):
lfile = open('level.txt', 'r')
# print(storefile)
self.level = int(lfile.read())
# self.level=str(lfile.read())
# print(self.highscore)
lfile.close()
else:
self.level = 1
# self.intro()
self.start(1)
def message(self, v1, u1, text):
rect_w = 70
rect_h = 70
font = pg.font.SysFont('comicsansms', 25)
TextSurf = font.render(text, True, self.black)
TextRect = TextSurf.get_rect()
TextRect.center = ((v1 * rect_w + ((rect_w - 3) / 2)),
(u1 * rect_h + (rect_h / 2)))
self.gameWindow.blit(TextSurf, TextRect)
pg.display.update()
def buttons(self, text):
# rect_w = 70
rect_w = 180
# rect_h = 70
rect_h = 180
color = self.color
# additional button
mouse_pos = pg.mouse.get_pos()
click = pg.mouse.get_pressed()
if (self.v * rect_w + rect_w - 3 > mouse_pos[0] > self.v * rect_w
and self.u * rect_h + rect_h - 3 > mouse_pos[1] > self.u * rect_h):
if int(text) <= self.level:
# if str(text)<=self.level:
color = (255, 30, 30)
if click[0] == 1:
self.start(int(text))
else:
pass
pg.draw.rect(self.gameWindow, color, [self.v * rect_w, self.u * rect_h,
rect_w - 100, rect_h - 3])
self.message(self.v, self.u, text)
# self.message(text)
pg.display.update()
def intro(self):
# additional for button
# NEW_SURF, NEW_RECT = self.makeText("New game", self.yellow, window_width - 120, window_height - 90)
while True:
self.v = 4
self.u = 5
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
for rec in range(1, 2): # Level Number showing
# self.labels(300, 430, "Tap to Start", (0, 0, 255))
# self.buttons(str(rec))
# self.labels()
# self.message(self.v,self.u,str(rec))
self.v += 1
if self.v == 8:
self.v = 4
self.u += 1
#############################################################################
def labels(self, v1, u1, text, color, size=20):
font = pg.font.SysFont('comicsansms', size)
TextSurf = font.render(text, True, color)
TextRect = TextSurf.get_rect()
# print(TextRect)
TextRect.center = (v1, u1)
self.gameWindow.blit(TextSurf, TextRect)
pg.display.update()
def check(self):
global game_over
j, k = 0, 0
tag_list = []
for i in range(1, 17):
# print("checking ",i,tiles[(j,k)])
tag = "tag" + str(i)
# print(tag,j,k)
if self.tiles[(j, k)][1] == tag:
tag_list.append(tag)
j += 1
if j > 3:
k += 1
j = 0
else:
break
if i == 16:
print("GAME FINISHED")
game_over = True
def shift(self, c, r):
global emptyc, emptyr
rect_color = (255, 255, 255) # the square for suffling
# rect_color = (0,0,0)
self.gameWindow.blit(
self.tiles[(c, r)][0],
(emptyc * self.tile_width, emptyr * self.tile_height))
'''pg.draw.rect(gameWindow,black,[c*tile_width,r*tile_height,
tile_width-1,tile_height-1])'''
self.gameWindow.blit(
self.tiles[self.empty_tile][0],
(c * self.tile_width, r * self.tile_height))
# state[(emptyc, emptyr)] = state[(c, r)]
# state[(c, r)] = empty_tile
temp = self.tiles[(c, r)]
# print(temp,c,r)
self.tiles[(c, r)] = self.tiles[(emptyc, emptyr)]
self.tiles[(emptyc, emptyr)] = temp
emptyc, emptyr = c, r
# tiles[(emptyc, emptyr)].fill(black)
pg.draw.rect(self.gameWindow, rect_color, [c * self.tile_width, r * self.tile_height,
self.tile_width - 1, self.tile_height - 1])
self.empty_tile = (emptyc, emptyr)
# empty_tile.fill(0,0,0)
pg.display.flip()
def shuffle(self):
global emptyc, emptyr
# keep track of last shuffling direction to avoid "undo" shuffle moves
last_r = 0
for i in range(100):
# slow down shuffling for visual effect
pg.time.delay(50)
while True:
# pick a random direction and make a shuffling move
# if that is possible in that direction
r = random.randint(1, 4)
if (last_r + r == 5):
# don't undo the last shuffling move
continue
if r == 1 and (emptyc > 0):
self.shift(emptyc - 1, emptyr) # shift left
elif r == 4 and (emptyc < self.coloumn - 1):
self.shift(emptyc + 1, emptyr) # shift right
elif r == 2 and (emptyr > 0):
self.shift(emptyc, emptyr - 1) # shift up
elif r == 3 and (emptyr < self.rows - 1):
self.shift(emptyc, emptyr + 1) # shift down
else:
# the random shuffle move didn't fit in that direction
continue
last_r = r
break # a shuffling move was made
def start(self, l):
f = 1
imageX = 350
imageY = 50
global level, game_over
game_over = False
level = l
img = self.img_list[level]
self.image = pg.image.load("./Res/" + img)
button = pg.image.load("./efx/" + "button.jpg")
self.button = pg.image.load("./efx/" + "button.jpg")
self.gameWindow.fill((190, 190, 190)) # color of the window
for r in range(self.coloumn):
for c in range(self.rows):
tag = "tag" + str(f)
tile = self.image.subsurface(c * self.tile_width, r * self.tile_height,
self.tile_width - 1, self.tile_height - 1)
f += 1
self.tiles[(c, r)] = (tile, tag)
if (c, r) == self.empty_tile:
pg.draw.rect(self.gameWindow, (255, 255, 255),
# pg.draw.rect(self.gameWindow,(260,260,260),
[c * self.tile_width, r * self.tile_height,
self.tile_width - 1, self.tile_height - 1])#width and height of the white tile
break
self.gameWindow.blit(tile, (c * self.tile_width, r * self.tile_height)) # uploading the image through the window
#self.gameWindow.blit(tile,(imageX,imageY))
pg.display.update()
# print(tile)
# print(tiles)
# text = "Level "+str(level)
text = "Have fun!"
self.labels(350, 625, text, (0, 0, 255))
# self.labels(300,625,"Click to start Game",(0,0,255))
self.labels(700, 300, "Tap to start Game", (0, 0, 255))
self.gameWindow.blit(button, (640, 180))
pg.display.update()
self.gameloop()
def gameloop(self):
started = False
show_sol = False
global level
# self.gameWindow.fill((190,190,190),(150,610,300,40))
while True:
if game_over:
self.labels(300, 300, "Good job well played", (255, 100, 30), 50)
# self.labels(300,625,"Click to next Level",(0,0,255))
for event in pg.event.get():
# print(event)
if event.type == pg.QUIT:
pg.quit()
sys.exit()
if event.type == pg.MOUSEBUTTONDOWN:
# print(event.type)
# print(event.dict)
# shuffle()
if not started:
self.shuffle()
self.gameWindow.fill((190, 190, 190), (150, 610, 300, 40))
# self.labels(300,625,"Right click to see Solution",(0,0,255))
started = True
if game_over:
level += 1
# self.labels(300,300,"Good job well played",(255,100,30),50)
# self.labels(300,625,"Click to next Level",(0,0,255))
if self.level < level:
self.level += 1
file = open("level.txt", "w")
file.write(str(self.level))
file.close()
self.start(level)
if event.dict['button'] == 1:
mouse_pos = pg.mouse.get_pos()
c = mouse_pos[0] // self.tile_width
r = mouse_pos[1] // self.tile_height
# print("dot posn",emptyc,emptyr)
# print("mouse posn",c,r)
if c == emptyc and r == emptyr:
continue
elif c == emptyc and (r == emptyr - 1 or r == emptyr + 1):
self.shift(c, r)
self.check()
elif r == emptyr and (c == emptyc - 1 or c == emptyc + 1):
self.shift(c, r)
self.check()
# print(c,r)
elif event.dict['button'] == 3:
saved_image = self.gameWindow.copy()
# print(saved_image)
# gameWindow.fill(255,255,255)
self.gameWindow.blit(self.image, (0, 0))
pg.display.flip()
show_sol = True
elif show_sol and (event.type == pg.MOUSEBUTTONUP):
# stop showing the solution
self.gameWindow.blit(saved_image, (0, 0))
pg.display.flip()
show_sol = False
if name == "main":
PuzzlerGame()
The dest argument of pygame.Surface.blit() can also be a rectangle. To center an image on the screen get the bounding rectangle of the image with pygame.Surface.get_rect. Set the center of the rectangle by the center of the screen. Use the rectangle to blit the image:
game_window_rect = self.gameWindow.get_rect()
dest_rect = saved_image.get_rect(center = game_window_rect.center)
self.gameWindow.blit(saved_image, dest_rect)

Efficiency in Infections - Pygame

I am currently making an infection survival game for my A-level coursework, and I am struggling on how I can make this efficient.
When the cells get infected I need to check more and more cells, my computer science teacher recommended I save infections as a boolean value as I can do a lot more with that later, however it makes it more inefficient due to me having to eventually check the amount of cells squared which causes a lot of framerate issues.
My original idea was to store the uninfected and infected in separate lists but my comp sci teacher said I was over complicating it, however this didn't have any framerate issues.
A lot of my code has taken inspiration from this question Random movement pygame, especially when it comes to the cell movement.
TLDR: I want to make my code more efficient but I can't think of how
My code:
import sys, random, pygame
import matplotlib.pyplot as plt
from pygame.locals import *
import time
pygame.init()
#Window details
windowWidth = 400
windowHeight = 400
pixSize = 2
FPS = 60
screen = pygame.display.set_mode((windowWidth, windowHeight))
pygame.display.set_caption("Infection Game")
class Cell:
def __init__(self):
self.xPos = random.randrange(1,windowWidth)
self.yPos = random.randrange(1,windowHeight)
self.speed = 2
self.isInfected = False
self.infectionRange = 5
self.move = [None, None]
self.direction = None
def cellDraw(self):
if self.isInfected == False:
pygame.draw.rect(screen, (255,255,255), (self.xPos,self.yPos,pixSize,pixSize),0)
else:
pygame.draw.rect(screen, (0,255,0), (self.xPos,self.yPos,pixSize,pixSize),0)
def cellMovement(self):
directions = {"S":((-1,2),(1,self.speed)),"SW":((-self.speed,-1),(1,self.speed)),"W":((-self.speed,-1),(-1,2)),"NW":((-self.speed,-1),(-self.speed,-1)),"N":((-1,2),(-self.speed,-1)),"NE":((1,self.speed),(-self.speed,-1)),"E":((1,self.speed),(-1,2)),"SE":((1,self.speed),(1,self.speed))} #((min x, max x)(min y, max y))
directionsName = ("S","SW","W","NW","N","NE","E","SE") #possible directions
if random.randrange(0,5) == 2: #move about once every 5 frames
if self.direction == None: #if no direction is set, set a random one
self.direction = random.choice(directionsName)
else:
a = directionsName.index(self.direction) #get the index of direction in directions list
b = random.randrange(a-1,a+2) #set the direction to be the same, or one next to the current direction
if b > len(directionsName)-1: #if direction index is outside the list, move back to the start
b = 0
self.direction = directionsName[b]
self.move[0] = random.randrange(directions[self.direction][0][0],directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0],directions[self.direction][1][1]) + 0.35
if self.xPos < 5 or self.xPos > windowWidth - 5 or self.yPos < 5 or self.yPos > windowHeight - 5: #if cell is near the border of the screen, change direction
if self.xPos < 5:
self.direction = "E"
elif self.xPos > windowWidth - 5:
self.direction = "W"
elif self.yPos < 5:
self.direction = "S"
elif self.yPos > windowHeight - 5:
self.direction = "N"
self.move[0] = random.randrange(directions[self.direction][0][0],directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0],directions[self.direction][1][1]) + 0.35
if self.move[0] != None: #add the relative coordinates to the cells coordinates
self.xPos += self.move[0]
self.yPos += self.move[1]
def Infect(self):
for i in cellList:
if (self.xPos > i.xPos - self.infectionRange and self.xPos < i.xPos + self.infectionRange) and (self.yPos > i.yPos - self.infectionRange and self.yPos < i.yPos + self.infectionRange):
i.isInfected = True
xgraph = []
ygraph = []
cellList = []
startTime = time.time()
for i in range(1000):
cell = Cell()
cellList.append(cell)
cellList[0].isInfected = True
def gameLoop():
while True:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.fill((0,0,0))
for i in cellList:
i.cellDraw()
i.cellMovement()
for i in cellList:
if i.isInfected == True:
i.Infect()
infectList.append(i)
xgraph.append(time.time()-startTime)
ygraph.append(len(infectList))
plt.plot(xgraph,ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
if len(infectList) == 1000:
plt.show()
pygame.display.update() #update display
pygame.time.Clock().tick(FPS) #limit FPS
gameLoop()
First off, I've changed some of your code:
if self.isInfected == False:
if self.direction == None:
To
if not self.isInfected:
if self.direction is None:
Just so it reads a little a nicer.
Secondly, I've vectorized the Infect function:
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
It takes the same time for this number of cells, but should scale better.
It turns out creating the array takes almost all the time. So you can create it once, pull it out of the loop and shave off a bunch of time:
def Infect(self, uninfected, uninfected_array):
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
# To prevent errors with empty arrays
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
i.Infect(uninfected, uninfected_array)
# To prevent errors when everyone is infected
if infected == 0:
infected = len(cellList) - len(uninfected)
Lastly, you don't really seem to be using the infectList, so I replaced it with a counter:
infected = 0
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
infected += 1
As a side note, I'd change the UI controls a bit so it's easier to graph, instead of quitting using sys.exit it's nicer just to break out of the while loop. You also only plot the results once:
running = True
while running:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
running = False
...
pygame.quit()
plt.plot(xgraph, ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
plt.show()
Implementing all this results in:
import random
import pygame
import matplotlib.pyplot as plt
from pygame.locals import *
import time
import numpy as np
pygame.init()
# Window details
windowWidth = 400
windowHeight = 400
pixSize = 2
FPS = 60
screen = pygame.display.set_mode((windowWidth, windowHeight))
pygame.display.set_caption("Infection Game")
class Cell:
def __init__(self):
self.xPos = random.randrange(1, windowWidth)
self.yPos = random.randrange(1, windowHeight)
self.speed = 2
self.isInfected = False
self.infectionRange = 5
self.move = [None, None]
self.direction = None
def cellDraw(self):
if not self.isInfected:
pygame.draw.rect(screen, (255, 255, 255), (self.xPos, self.yPos, pixSize, pixSize), 0)
else:
pygame.draw.rect(screen, (0, 255, 0), (self.xPos, self.yPos, pixSize, pixSize), 0)
def cellMovement(self):
directions = {"S": ((-1, 2), (1, self.speed)), "SW": ((-self.speed, -1), (1, self.speed)),
"W": ((-self.speed, -1), (-1, 2)), "NW": ((-self.speed, -1), (-self.speed, -1)),
"N": ((-1, 2), (-self.speed, -1)), "NE": ((1, self.speed), (-self.speed, -1)),
"E": ((1, self.speed), (-1, 2)),
"SE": ((1, self.speed), (1, self.speed))} # ((min x, max x)(min y, max y))
directionsName = ("S", "SW", "W", "NW", "N", "NE", "E", "SE") # possible directions
if random.randrange(0, 5) == 2: # move about once every 5 frames
if self.direction is None: # if no direction is set, set a random one
self.direction = random.choice(directionsName)
else:
a = directionsName.index(self.direction) # get the index of direction in directions list
b = random.randrange(a - 1,
a + 2) # set the direction to be the same, or one next to the current direction
if b > len(directionsName) - 1: # if direction index is outside the list, move back to the start
b = 0
self.direction = directionsName[b]
self.move[0] = random.randrange(directions[self.direction][0][0], directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0], directions[self.direction][1][1]) + 0.35
if self.xPos < 5 or self.xPos > windowWidth - 5 or self.yPos < 5 or self.yPos > windowHeight - 5: # if cell is near the border of the screen, change direction
if self.xPos < 5:
self.direction = "E"
elif self.xPos > windowWidth - 5:
self.direction = "W"
elif self.yPos < 5:
self.direction = "S"
elif self.yPos > windowHeight - 5:
self.direction = "N"
self.move[0] = random.randrange(directions[self.direction][0][0], directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0], directions[self.direction][1][1]) + 0.35
if self.move[0] is not None: # add the relative coordinates to the cells coordinates
self.xPos += self.move[0]
self.yPos += self.move[1]
def Infect(self, uninfected, uninfected_array):
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
xgraph = []
ygraph = []
cellList = []
startTime = time.time()
for i in range(1000):
cell = Cell()
cellList.append(cell)
cellList[0].isInfected = True
def gameLoop():
running = True
while running:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
running = False
screen.fill((0, 0, 0))
for i in cellList:
i.cellDraw()
i.cellMovement()
infected = 0
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
i.Infect(uninfected, uninfected_array)
infected += 1
if infected == 0:
infected = len(cellList) - len(uninfected)
xgraph.append(time.time() - startTime)
ygraph.append(infected)
pygame.display.update() # update display
pygame.time.Clock().tick(FPS) # limit FPS
pygame.quit()
# figured this is what you wanted to do ;)
plt.plot(xgraph, ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
plt.show()
gameLoop()
And it runs smooth

Putting the scrolling camera in a mini-window in pygame

I'm trying to create a game where the action is shown in a little box within the main screen object, freeing up the surrounding space for text and menus and what-not. Since the map is larger than the allotted window, I coded a basic "camera" that follows the player around. It mostly works, but I'm having trouble "trimming off" the area outside of this window.
Here's the relevant bits of code (EDITED to provide Working Example):
import pygame, os, sys
from pygame.locals import *
pygame.init()
RIGHT = 'RIGHT'
LEFT = 'LEFT'
UP = 'UP'
DOWN = 'DOWN'
class Camera():
def __init__(self, screen, x_ratio = 1, y_ratio = 1, x_offset = 0, y_offset = 0):
self.screen = screen.copy()
self.rec = self.screen.get_rect()
self.rec.width *= x_ratio
self.rec.height *= y_ratio
self.x_offset = x_offset
self.y_offset = y_offset
def get_pos(self):
return (self.x_offset - self.rec.x, self.y_offset - self.rec.y)
def get_window(self):
w = pygame.Rect(self.rec)
w.topleft = (0 - self.rec.x, 0 - self.rec.y)
return w
def move(self, x, y):
"""Move camera into new position"""
self.rec.x = x
self.rec.y = y
def track(self, obj):
while obj.rec.left < self.rec.left:
self.rec.x -= 1
while obj.rec.right > self.rec.right:
self.rec.x += 1
while obj.rec.top < self.rec.top:
self.rec.y -= 1
while obj.rec.bottom > self.rec.bottom:
self.rec.y += 1
class Map:
def __init__(self, width, height):
self.width = width
self.height = height
self.rec = pygame.Rect(0,0,self.width,self.height)
def draw(self, screen):
pygame.draw.rect(screen, (200,200,200), self.rec)
class Obj:
def __init__(self, char, x = 0, y = 0, width = 0, height = 0):
self.width = width
self.height = height
self.rec = pygame.Rect(x, y, width, height)
self.cur_map = None
self.timers = {}
#Dummying in chars for sprites
self.char = char
self.x_dir = 1
self.y_dir = 1
self.speed = 1
self.moving = False
def move(self):
if self.x_dir != 0 or self.y_dir != 0:
new_x = self.rec.x + (self.x_dir*self.speed)
new_y = self.rec.y + (self.y_dir*self.speed)
new_rec = pygame.Rect(new_x, new_y, self.width, self.height)
#Keep movement within bounds of map
while new_rec.left < self.cur_map.rec.left:
new_rec.x += 1
while new_rec.right > self.cur_map.rec.right:
new_rec.x -= 1
while new_rec.top < self.cur_map.rec.top:
new_rec.y += 1
while new_rec.bottom > self.cur_map.rec.bottom:
new_rec.y -= 1
self.rec = new_rec
def set_dir(self, d):
self.x_dir = 0
self.y_dir = 0
if d == LEFT:
self.x_dir = -1
elif d == RIGHT:
self.x_dir = 1
elif d == UP:
self.y_dir = -1
elif d == DOWN:
self.y_dir = 1
def set_moving(self, val = True):
self.moving = val
class Game:
def __init__(self):
self.screen_size = (800, 600)
self.screen = pygame.display.set_mode(self.screen_size)
self.map_screen = self.screen.copy()
self.title = 'RPG'
pygame.display.set_caption(self.title)
self.camera = Camera(self.screen, 0.75, 0.75)#, 10, 75)
self.fps = 80
self.clock = pygame.time.Clock()
self.debug = False
self.bg_color = (255,255,255)
self.text_size = 18
self.text_font = 'Arial'
self.text_style = pygame.font.SysFont(self.text_font, self.text_size)
self.key_binds = {LEFT : [K_LEFT, K_a], RIGHT : [K_RIGHT, K_d], UP : [K_UP, K_w], DOWN : [K_DOWN, K_s],
'interact' : [K_RETURN, K_z], 'inventory' : [K_i, K_SPACE], 'quit' : [K_ESCAPE]}
self.player = Obj('p', 0, 0, 10, self.text_size)
def draw(self, obj):
char = obj.char
self.draw_text(char, obj.rec.x, obj.rec.y, screen = self.map_screen)
def draw_text(self, text, x, y, color = (0,0,0), screen = None):
textobj = self.text_style.render(text, 1, color)
textrect = textobj.get_rect()
textrect.x = x
textrect.y = y
if screen == None:
"""Use default screen"""
self.screen.blit(textobj, textrect)
else:
screen.blit(textobj, textrect)
def play(self):
done = False
cur_map = Map(800, 800)
self.map_screen = pygame.Surface((cur_map.width, cur_map.height))
self.map_screen.fill(self.bg_color)
bg = pygame.Surface((cur_map.width, cur_map.height))
cur_map.draw(bg)
self.player.cur_map = cur_map
while not done:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key in self.key_binds[LEFT]:
self.player.set_dir(LEFT)
self.player.set_moving()
elif event.key in self.key_binds[RIGHT]:
self.player.set_dir(RIGHT)
self.player.set_moving()
elif event.key in self.key_binds[UP]:
self.player.set_dir(UP)
self.player.set_moving()
elif event.key in self.key_binds[DOWN]:
self.player.set_dir(DOWN)
self.player.set_moving()
elif event.type == KEYUP:
self.player.set_moving(False)
if self.player.moving:
self.player.move()
self.camera.track(self.player)
self.clock.tick()
self.screen.fill(self.bg_color)
self.map_screen.blit(bg, (0,0))
self.draw(self.player)
pygame.draw.rect(self.map_screen, (0,0,0), self.camera.rec, 1)
#self.screen.blit(self.map_screen, (0,0), [0 - self.camera.rec.x, 0 - self.camera.rec.y, self.camera.rec.width, self.camera.rec.height])
self.screen.blit(self.map_screen, self.camera.get_pos(), self.camera.get_window())
pygame.display.flip()
game = Game()
game.play()
Moving the player past past the bounds of the camera's window causes the window to roll up completely and disappear. I tried adjusting the blitting coordinates, as advised earlier, but it seems to only change the direction in which the window rolls up.
From your updated code, the blitting coordinates for self.screen.blit(...) are still changing: self.camera.get_window() changes value because rec.x and rec.y are values referring to the player position within the map. Hence you should define a constant minimap coordinate, this should be the same as the camera offset.
self.screen.blit(self.map_screen, (self.camera.x_offset,self.camera.y_offset), (*self.camera.get_pos(), self.camera.rec.width, self.camera.rec.height))
Change the Camera().get_pos() to:
def get_pos(self):
return (self.rec.x, self.rec.y)
I believe I only changed the self.screen.blit(...) and stopped using or rewrote your Camera functions as you're confusing yourself with all the rec variables.
To illustrate it working amend the Map().draw(screen) to:
def draw(self, screen):
pygame.draw.rect(screen, (200,200,200), self.rec)
pygame.draw.circle(screen, (255, 255, 255), (50, 50), 20, 2)
One tip as well don't draw the entire map at each loop, just the part that will be visible.

Categories