Why is my program not decreasing a life if the answer is wrong? - python

For my program, I am making a game which involves multiple choice questions. Along with this, I have used three separate heart images to show the lives to the user.
In the code, I set conditions so that if the lives for example is 1, only one heart image is blitted on the screen. The number of lives will only decrease if the answer the user has chosen is incorrect.
Below shows the relevant part of my code:
class GameState:
def __init__(self, difficulty):
self.difficulty = difficulty
self.questions = [
("Q1: 4 _ 6 = 10?"),
("Q2: ___ = 1"),
("Q3: 1 * 3?")
]
self.answers = [4, 2, 2]
self.current_question = None
self.question_index = 0
def pop_question(self):
q = self.questions[0]
self.questions.remove(q)
self.current_question = q
self.question_index += 1
return q
def answer(self, answer):
lives = 3
if self.answers == self.current_question[1]:
lives = lives - 1
else:
lives = lives
if lives == 1:
screen.blit(Heart, (500, 10))
if lives == 2:
screen.blit(Heart, (500, 10))
screen.blit(Heart, (X // 2, 10))
if lives == 3:
screen.blit(Heart, (500, 10))
screen.blit(Heart1, (X // 2, 10))
screen.blit(Heart2, (775, 10))
class GameScene:
def __init__(self):
if SimpleScene.FONT == None:
SimpleScene.FONT = pygame.freetype.SysFont(None, 32)
self.rects = []
for n in range(4):
rect = pygame.Rect(420, (n * 70) + 300, 500, 50)
self.rects.append(rect)
self.choices = [['x', '-', '*', '+'], ["number", "fruit", "weather", "letter"], ["4", "3", "-2", "13"]]
def start(self, gamestate):
self.background = pygame.Surface((X, Y))
self.background.fill(pygame.Color("white"))
self.gamestate = gamestate
question = gamestate.pop_question()
SimpleScene.FONT.render_to(self.background, (20, 150), question, (blue))
def draw(self, screen):
screen.blit(self.background, (0, 0))
n = 0
for rect in self.rects:
if rect.collidepoint(pygame.mouse.get_pos()):
pygame.draw.rect(screen, pygame.Color('darkgrey'), rect)
pygame.draw.rect(screen, pygame.Color('darkgrey'),
rect, 5)
screen.blit(Heart, (500, 10))
screen.blit(Heart1, (X // 2, 10))
screen.blit(Heart2, (775, 10))
for i in range(len(self.choices)):
if self.gamestate.question_index == i + 1:
SimpleScene.FONT.render_to(screen, (rect.x + 30, rect. y + 20), str(self.choices[i][n]),
(green))
SimpleScene.FONT.render_to(screen, (rect.x + 29, rect.y + 19), str(self.choices [i][n]),
(green))
n += 1
def update(self, events, dt):
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN:
n = 1
for rect in self.rects:
if rect.collidepoint(event.pos):
self.gamestate.answer(n)
if self.gamestate.questions:
return ('GAME', self.gamestate)
else:
quit()
n += 1
When I run the program, none of the hearts disappear. Is it because of a mistake I made while setting the condition of the lives to minus by one if the answer is wrong?

lives needs to be an attribute of GameState:
class GameState:
def __init__(self, difficulty):
# [...]
self.lives = 3
# [...]
def pop_question(self):
q = self.questions[0]
self.current_question = q
return q
def answer(self, answer):
if answer != self.answers[self.question_index]:
self.lives -= 1
else:
self.question_index += 1
self.questions.pop(0)
However you must draw the hearts in GameScene.draw, dependent on the value of self.gamestate.lives:
class GameScene:
# [...]
def draw(self, screen):
screen.blit(self.background, (0, 0))
if self.gamestate.lives >= 1:
screen.blit(Heart, (500, 10))
if self.gamestate.lives >= 2:
screen.blit(Heart1, (X // 2, 10))
if self.gamestate.lives >= 3:
screen.blit(Heart2, (775, 10))
n = 0
for rect in self.rects:
if rect.collidepoint(pygame.mouse.get_pos()):
pygame.draw.rect(screen, pygame.Color('darkgrey'), rect)
pygame.draw.rect(screen, pygame.Color('darkgrey'),
rect, 5)
for i in range(len(self.choices)):
if self.gamestate.question_index == i:
SimpleScene.FONT.render_to(screen, (rect.x + 30, rect. y + 20), str(self.choices[i][n]),
(green))
SimpleScene.FONT.render_to(screen, (rect.x + 29, rect.y + 19), str(self.choices [i][n]),
(green))
n += 1
End the game when
def main():
# [...]
while True:
# [...]
game = scene.update(events, dt)
if game:
next_scene, state = game
if next_scene:
scene = scenes[next_scene]
scene.start(state)
if state and state.lives == 0:
print("GAME OVER")
break

Related

Mapping Grid Position To Matrix Position

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.

my pygame window crashes whenever i added the sound/music in my game [duplicate]

This question already has answers here:
How can I play an mp3 with pygame?
(7 answers)
how to play wav file in python?
(5 answers)
Closed 5 months ago.
I'm not so sure why but my game screen window/pygame window sometimes freezes or is not responding because of the added SFX or music in my game. If I remove the SFX or music the game runs smoothly. Hoping that anyone can help me or at least tell me why it keeps freezing. Thanks very much.
The specific code for music and sounds in class Game can be found in load_data method, run method, and update method.
This is where you can find my music, target SFX when hit, bonus SFX when hit.
Below is from my main.py module
import pygame as py
import sys
from os import kill, path
from Setting import *
from Sprite import *
import random
#BUGs (BULLET DISAPPEAR WHEN PAUSED [PROBABLY FROM get_tick()])
#add highscore later in end screen
class Game():
def __init__(self):
py.mixer.pre_init(44100, -16, 1, 512)
py.init()
self.screen = py.display.set_mode((Width, Height),py.RESIZABLE)
py.display.set_caption("Hit Me!")
self.clock = py.time.Clock()
self.load_data()
self.score_value = 0
self.time_value = 90
self.paused = False
self.channel1 = py.mixer.Channel(1)
self.channel2 = py.mixer.Channel(2)
self.channel3 = py.mixer.Channel(3)
def draw_data(self, text, font_name, size, color, x, y,type=0, align="center", value=""):
font = py.font.Font(font_name, size)
if type == 0:
text_surface = font.render(text + str(value), True, color)
if self.paused == True:
text_surface.set_alpha(125)
else:
text_surface = font.render(text, True, color)
text_rect = text_surface.get_rect()
if align == "nw":
text_rect.topleft = (x, y)
if align == "ne":
text_rect.topright = (x, y)
if align == "sw":
text_rect.bottomleft = (x, y)
if align == "se":
text_rect.bottomright = (x, y)
if align == "n":
text_rect.midtop = (x, y)
if align == "s":
text_rect.midbottom = (x, y)
if align == "e":
text_rect.midright = (x, y)
if align == "w":
text_rect.midleft = (x, y)
if align == "center":
text_rect.center = (x, y)
self.screen.blit(text_surface, text_rect)
def load_data(self):
game_folder = path.dirname(__file__)
self.map_data = []
with open(path.join(game_folder, 'map.txt'), 'rt') as f:
for line in f:
self.map_data.append(line)
image_folder = path.join(game_folder, 'images')
sfx_folder = path.join(game_folder, 'sfx_music')
self.dim_screen = py.Surface(self.screen.get_size()).convert_alpha()
self.dim_screen.fill((0, 0, 0, 150))
self.player_img = py.image.load(
path.join(image_folder, character)).convert_alpha()
self.bullet_img = py.image.load(
path.join(image_folder, bullets)).convert_alpha()
self.target_img = py.image.load(
path.join(image_folder, targets)).convert_alpha()
self.target_img = py.transform.scale(
self.target_img, (TileSize + 20, TileSize + 20))
self.bonus_img = py.image.load(
path.join(image_folder, time)).convert_alpha()
self.bonus_img = py.transform.scale(
self.bonus_img, (TileSize + 30, TileSize + 30))
#sound loading
self.music = py.mixer.Sound(path.join(sfx_folder,background_music))
self.music.set_volume(0.25)
self.gun_sfx = py.mixer.Sound(path.join(sfx_folder,gun_sound))
self.bonus_sfx = py.mixer.Sound(path.join(sfx_folder,bonus_sound))
self.target_sfx = py.mixer.Sound(path.join(sfx_folder,target_sound))
self.gun_sfx.set_volume(0.4)
self.target_sfx.set_volume(0.5)
def new(self):
self.sprites = py.sprite.Group()
self.walls = py.sprite.Group()
self.grass = py.sprite.Group()
self.targets = py.sprite.Group()
self.bullets = py.sprite.Group()
self.bonuss = py.sprite.Group()
for row, tiles in enumerate(self.map_data):
for col, tile in enumerate(tiles):
if tile == 'P':
self.player = Player(self, col, row)
py.time.set_timer(py.USEREVENT, 1000)
def newTarget(self):
self.targets = py.sprite.Group()
for row, tiles in enumerate(self.map_data):
for col, tile in enumerate(tiles):
if tile == '.':
target_chances = random.randint(0, 100)
if target_chances <= 10: # 10% chance
tile = 'T'
if tile == 'M':
bonus_chances = random.randint(0,100)
if bonus_chances <= 5: # 5% chance bonus time (add 2 or 3 seconds to timer) [specified area]
tile = 'B'
if tile == 'T':
Target(self, col, row)
if tile == 'B':
Bonus(self,col,row)
def run(self):
self.playing = True
self.music_played = False
if not self.music_played:
self.channel3.play(self.music, loops = -1)
self.music_played = True
while self.playing:
self.dt = self.clock.tick(FPS) / 1000
self.events()
if not self.paused:
self.update()
self.channel3.unpause()
if self.paused:
self.channel3.pause()
self.draw()
def quit(self):
py.quit()
sys.exit()
def update(self):
self.sprites.update()
hits = py.sprite.groupcollide(
self.targets, self.bullets, True, True, py.sprite.collide_mask)
bonus_hits = py.sprite.groupcollide(
self.bonuss, self.bullets, True, True, py.sprite.collide_mask)
#for bullets & targets
for hit in hits:
hit.kill()
self.score_value += 1
self.target_sfx_played = False
if not self.target_sfx_played:
self.channel1.play(self.target_sfx)
self.target_sfx_played = True
#for bullets & bonus
for hit in bonus_hits:
hit.kill()
self.time_value += 3
self.bonus_sfx_played = False
if not self.bonus_sfx_played:
self.channel2.play(self.bonus_sfx)
self.bonus_sfx_played = True
#if there is no target in screen, it will create a new set
if len(self.targets) == 0:
self.newTarget()
def drawGrid(self):
for x in range(0, Width, TileSize):
py.draw.line(self.screen, Black, (x, 0), (x, Height))
for y in range(0, Height, TileSize):
py.draw.line(self.screen, Black, (0, y), (Width, y))
def draw(self):
py.display.set_caption("{:.2f}".format(self.clock.get_fps()))
self.screen.fill(BGcolor)
self.drawGrid()
self.sprites.draw(self.screen)
py.draw.rect(self.screen, Green, self.player.rect, 1)
if self.paused:
self.screen.blit(self.dim_screen, (0, 0))
self.draw_data("Paused",'freesansbold.ttf', 105, White, Width / 2, Height / 2,1)
self.draw_data("Score: ",'freesansbold.ttf', 40, White, 100, 35,value = self.score_value)
self.draw_data("Time: ",'freesansbold.ttf', 40, White, 450, 35 ,value = self.time_value)
self.draw_data("HighScore: ",'freesansbold.ttf', 40, White, Width - 225, 35) # add data in end game
py.display.flip()
def events(self):
for event in py.event.get():
if event.type == py.QUIT:
self.quit()
if event.type == py.KEYDOWN:
if event.key == py.K_ESCAPE:
self.paused = not self.paused
if event.type == py.MOUSEBUTTONDOWN:
if event.button == 1:
if not self.paused:
self.player.shoot()
if event.type == py.USEREVENT:
if not self.paused:
self.time_value -=1
def show_start_screen(self):
pass
def show_go_screen(self):
pass
# main loop
g = Game()
g.show_start_screen()
while True:
g.new()
g.run()
g.show_go_screen()
In the Sprite.py module, this is where you can find the SFX for gun shooting whenever I press/hold the left mouse button.
Below is from my Sprite.py module
import pygame as py
from Setting import *
from pygame.locals import *
vec = py.math.Vector2
class Player(py.sprite.Sprite):
def __init__(self, game,x,y):
self.groups = game.sprites
py.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.orig_image = game.player_img
self.rect = self.orig_image.get_rect()
self.pos = vec(x,y) * TileSize
self.rect.center = self.pos
self.last_shot = 0
self.gun_sfx = game.gun_sfx
self.channel4 = py.mixer.Channel(4)
def shoot(self):
if py.mouse.get_pressed()[0]:
now = py.time.get_ticks()
if now - self.last_shot > bullet_rate:
self.gun_sfx_played = False
if not self.gun_sfx_played:
self.channel4.play(self.gun_sfx)
self.gun_sfx_played = True
self.last_shot = now
dir = py.mouse.get_pos() - self.pos
radius, angle = dir.as_polar()
direction = vec(1, 0).rotate(angle - 3)
pos = self.pos + Barrel_offset.rotate(angle)
Bullet(self.game, pos, direction, -angle)
def rotate(self):
dir = py.mouse.get_pos() - self.pos
radius, angle = dir.as_polar()
self.image = py.transform.rotate(self.orig_image, -angle)
self.rect = self.image.get_rect(center=self.rect.center)
def update(self):
self.shoot()
self.rotate()
Below is from my Setting.py
# sounds
gun_sound = 'gun_sfx.mp3'
background_music = 'bg_music.mp3'
target_sound = 'target_sfx_broken.mp3'
bonus_sound = 'hourglass_sfx.mp3'

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)

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.

Pygame objects only move on mouseclick

I have some simple circles (boids) that move in a way that simulates birds; they should avoid getting too close to each other while maintaining the same general heading et cetera.
I'm using pygame but the circles don't move unless I press one of the buttons in the GUI, which seems kind of strange but I can't figure out where I messed up.
The most relevant part of the code is probably the gui function and the draw function inside the Boid class.
import pygame
import numpy as np
import sys
import math
class BoidWorld:
# Boid movement parameters
w_separation = 10
w_alignment = 1
w_cohesion = 1
w_avoidance = 0
w_flee = 50
dim = 0 # dim*dim = Size of world
neighbour_radius = 100
max_velocity = 100
# Objects in world
boids = []
predators = []
obstacles = []
def __init__(self, dim):
self.dim = dim
def update_boid_velocity(self, boid):
# Flee from predators, if any
predator = self.get_predator(boid)
flee_x, flee_y = self.calc_flee_force(boid, predator)
# Avoid obstacles, if any
obstacle = self.get_obstacle(boid)
avoid_x, avoid_y = self.calc_avoidance_force(boid, obstacle)
# Get neighbours within radius r
neighbours = self.get_neighbours(boid)
sep_x, sep_y = self.calc_separation_force(boid, neighbours)
align_x, align_y = self.calc_alignment_force(neighbours)
coh_x, coh_y = self.calc_cohesion_force(neighbours)
boid.velocity_x += self.w_separation * sep_x + self.w_alignment * align_x + self.w_cohesion * coh_x + \
self.w_avoidance * avoid_x + self.w_flee * flee_x
boid.velocity_y += self.w_separation * sep_y + self.w_alignment * align_y + self.w_cohesion * coh_y + \
self.w_avoidance * avoid_y + self.w_flee * flee_y
# Limit velocity by creating unit vectors and multiplying by max velocity
v = math.sqrt(boid.velocity_x**2 + boid.velocity_y**2)
if v > self.max_velocity:
boid.velocity_x = boid.velocity_x*self.max_velocity/v
boid.velocity_y = boid.velocity_y*self.max_velocity/v
boid.position_x += boid.velocity_x
boid.position_y += boid.velocity_y
print(boid.velocity_x, boid.velocity_y)
# Wrap around
if boid.position_x > self.dim or boid.position_x < 0:
boid.position_x %= self.dim
if boid.position_y > self.dim or boid.position_y < 0:
boid.position_y %= self.dim
def update_predator_velocity(self, predator):
pass
def calc_separation_force(self, boid, neighbours):
sep_x = 0.
sep_y = 0.
for b in neighbours:
sep_x = sep_x - (b.position_x - boid.position_x)
sep_y = sep_y - (b.position_y - boid.position_y)
return sep_x, sep_y
def calc_alignment_force(self, neighbours):
if not neighbours: return 0, 0
avg_heading_x = 0.
avg_heading_y = 0.
for b in neighbours:
avg_heading_x += b.velocity_x
avg_heading_y += b.velocity_y
return avg_heading_x/len(neighbours), avg_heading_y/len(neighbours)
def calc_cohesion_force(self, neighbours):
if not neighbours: return 0, 0
avg_pos_x = 0.
avg_pos_y = 0.
for b in neighbours:
avg_pos_x += b.position_x
avg_pos_y += b.position_y
return avg_pos_x/len(neighbours), avg_pos_y/len(neighbours)
# Flee straight away from predators
def calc_flee_force(self, boid, predator):
if not predator: return 0
return boid.position - predator.position
# Avoid obstacles
def calc_avoidance_force(self, boid, obstacle):
if not obstacle: return 0
return 0
# Predators chasing boids
def calc_chasing_force(self, predator, boids):
return 0
def get_predator(self, boid):
for predator in self.predators:
if self.is_neighbour(predator, boid):
return predator
return None
def get_obstacle(self, boid):
for obstacle in self.obstacles:
if self.is_neighbour(obstacle, boid):
return obstacle
return None
def is_neighbour(self, boid1, boid2):
if np.power(boid2.position_x - boid1.position_x, 2) + \
np.power(boid2.position_y - boid1.position_y, 2) \
< np.power(self.neighbour_radius, 2):
return True
return False
def get_neighbours(self, boid):
neighbours = []
for b in self.boids:
if b != boid and self.is_neighbour(b, boid):
neighbours.append(b)
return neighbours
def add_boid(self):
self.boids.append(Boid(
self.rand_position(), self.rand_position(),
self.rand_velocity(), self.rand_velocity()
))
def add_obstacle(self):
self.obstacles.append(Obstacle(
self.rand_position(), self.rand_position()))
def add_predator(self):
self.predators.append(Predator(
self.rand_position(), self.rand_position(),
self.rand_velocity(), self.rand_velocity()
))
def remove_boids(self):
self.boids = []
def remove_obstacles(self):
self.obstacles = []
def remove_predators(self):
self.predators = []
def rand_position(self):
return float(np.random.randint(0, self.dim))
def rand_velocity(self):
return float(np.random.randint(0, self.max_velocity))
class Boid(object):
color_circle = (100, 0, 0)
color_line = (100, 0, 100)
radius = 10
position_x = 0.
position_y = 0.
velocity_x = 0.
velocity_y = 0.
def __init__(self, position_x, position_y, velocity_x, velocity_y):
self.position_x = position_x
self.position_y = position_y
self.velocity_x = velocity_x
self.velocity_y = velocity_y
def draw(self, screen):
pygame.draw.circle(screen, self.color_circle, (int(round(self.position_x)), int(round(self.position_y))),
self.radius, 0)
# Velocity vector
pygame.draw.lines(screen, self.color_line, False, [
(int(round(self.position_x)), int(round(self.position_y))),
(int(round(self.position_x+self.velocity_x)), int(round(self.position_y+self.velocity_y)))
], 2)
class Predator(Boid):
color_circle = (100, 55, 0)
color_line = (100, 0, 100)
radius = 20
class Obstacle:
color = (0, 33, 50)
position_x = 0.
position_y = 0.
radius = 15
def __init__(self, position_x, position_y):
self.position_x = position_x
self.position_y = position_y
def draw(self, screen):
pygame.draw.circle(screen, self.color, (int(round(self.position_x)), int(round(self.position_y))),
self.radius, 0)
def main():
pygame.init()
boid_world = BoidWorld(800)
boid_world.add_boid()
gui(boid_world)
def gui(boid_world):
weight_inc = 0.1
btn_boid_add = Button('Add boid')
btn_boid_rem = Button('Remove boids')
btn_obst_add = Button('Add obstacle')
btn_obst_rem = Button('Remove obstacles')
btn_pred_add = Button('Add predator')
btn_pred_rem = Button('Remove predators')
btn_sep_p = Button('+')
btn_sep_m = Button('-')
btn_ali_p = Button('+')
btn_ali_m = Button('-')
btn_coh_p = Button('+')
btn_coh_m = Button('-')
pygame.font.init()
font = pygame.font.Font(None, 20)
font_color = (255, 255, 255)
screen = pygame.display.set_mode((1200, 800))
screen_half = screen.subsurface((400, 0, 800, 800))
pygame.display.set_caption('Boids')
clock = pygame.time.Clock()
run = True
while run:
screen.fill((0, 0, 0))
mouse = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if btn_boid_add.obj.collidepoint(mouse):
boid_world.add_boid()
elif btn_boid_rem.obj.collidepoint(mouse):
boid_world.remove_boids()
elif btn_obst_add.obj.collidepoint(mouse):
boid_world.add_obstacle()
elif btn_obst_rem.obj.collidepoint(mouse):
boid_world.remove_obstacles()
elif btn_pred_add.obj.collidepoint(mouse):
boid_world.add_predator()
elif btn_pred_rem.obj.collidepoint(mouse):
boid_world.remove_predators()
elif btn_sep_m.obj.collidepoint(mouse):
boid_world.w_separation -= weight_inc
elif btn_sep_p.obj.collidepoint(mouse):
boid_world.w_separation += weight_inc
elif btn_ali_p.obj.collidepoint(mouse):
boid_world.w_alignment -= weight_inc
elif btn_ali_m.obj.collidepoint(mouse):
boid_world.w_alignment += weight_inc
elif btn_coh_m.obj.collidepoint(mouse):
boid_world.w_cohesion -= weight_inc
elif btn_coh_p.obj.collidepoint(mouse):
boid_world.w_cohesion += weight_inc
btn_boid_add.draw(screen, mouse, (10, 10, 100, 20), (15, 15))
btn_boid_rem.draw(screen, mouse, (120, 10, 130, 20), (125, 15))
btn_obst_add.draw(screen, mouse, (10, 40, 100, 20), (15, 45))
btn_obst_rem.draw(screen, mouse, (120, 40, 130, 20), (125, 45))
btn_pred_add.draw(screen, mouse, (10, 70, 100, 20), (15, 75))
btn_pred_rem.draw(screen, mouse, (120, 70, 130, 20), (125, 75))
btn_sep_m.draw(screen, mouse, (120, 100, 20, 20), (125, 105))
btn_sep_p.draw(screen, mouse, (150, 100, 20, 20), (155, 105))
btn_ali_m.draw(screen, mouse, (120, 130, 20, 20), (125, 135))
btn_ali_p.draw(screen, mouse, (150, 130, 20, 20), (155, 135))
btn_coh_m.draw(screen, mouse, (120, 160, 20, 20), (125, 165))
btn_coh_p.draw(screen, mouse, (150, 160, 20, 20), (155, 165))
screen.blit(font.render('Separation', 1, font_color), (15, 105))
screen.blit(font.render('Alignment', 1, font_color), (15, 135))
screen.blit(font.render('Cohesion', 1, font_color), (15, 165))
for boid in boid_world.boids:
boid_world.update_boid_velocity(boid)
boid.draw(screen_half)
for obstacle in boid_world.obstacles:
obstacle.draw(screen_half)
for predator in boid_world.predators:
boid_world.update_predator_velocity(predator)
predator.draw(screen_half)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
class Button:
def __init__(self, text):
self.text = text
self.is_hover = False
self.default_color = (100, 100, 100)
self.hover_color = (255, 255, 255)
self.font_color = (100, 0, 0)
self.obj = None
def label(self):
font = pygame.font.Font(None, 20)
return font.render(self.text, 1, self.font_color)
def color(self):
if self.is_hover:
return self.hover_color
else:
return self.default_color
def draw(self, screen, mouse, rectcoord, labelcoord):
# create rect obj, draw, and change color based on input
self.obj = pygame.draw.rect(screen, self.color(), rectcoord)
screen.blit(self.label(), labelcoord)
# change color if mouse over button
self.check_hover(mouse)
def check_hover(self, mouse):
# adjust is_hover value based on mouse over button - to change hover color
if self.obj.collidepoint(mouse):
self.is_hover = True
else:
self.is_hover = False
if __name__ == "__main__":
main()
It happens because you do all calculation inside
elif event.type == pygame.MOUSEBUTTONDOWN:
MOUSEBUTTONDOWN means that button changes state from UP to DOWN (and it takes very short time). It doesn't means button is holding pressed all the time
If you need to check weather button is holding pressed then use pygame.mouse.get_pressed() but use it outside/after for event loop.
It is similar to key events:

Categories