Pygame screen gets cleared on flip, but only once - python-3.x

So I am trying to recreate Minesweeper in pygame. Since I am only drawing a single image over my current screen when a player clicks, I do not have frames. The problem is, when the player first clicks and I draw one image a flip the display. The first time I do that, it clears the background. After that, it doesn't clear the previous images. If I try and call flip first thing, nothing happens. I tried to flip after every image (Just trying things) and nothing changed.
Code:
import pygame, random, os, sys, math
pygame.init()
font = pygame.font.SysFont('Sans Serif 7', 15)
screenX = 1240
screenY = 720
sprites = pygame.image.load(os.path.dirname(os.path.abspath(__file__)) + "\\Minesweeper\\ImageSheet.png")
spriteList = []
#tiles = 16px, icons = 26px
class Minefield():
def __init__(self, width, height, mines, surface):
self.width = width
self.height = height
self.mine = sprites.subsurface(80, 49, 16, 16)
self.rows = []
self.surface = surface
for i in range(0, width):
self.rows.append([])
for i2 in range(0, height):
self.rows[i].append(-2)
for i in range(0, mines):
print(len(self.rows))
print(len(self.rows[1]))
x = random.randint(0, width-1)
y = random.randint(0, height-1)
print(x)
print(y)
self.rows[x][y]
self.render()
def clicked(self, rawPos):
pos = (math.floor(rawPos[0]/16), math.floor(rawPos[1]/16))
val = self.rows[pos[0]][pos[1]]
if val == -2:
mines = 1
if not pos[0] == 0 and not pos[1] == 0 and self.rows[pos[0]-1][pos[1]-1] == -1:
mines += 1
if not pos[0] == 0 and self.rows[pos[0]-1][pos[1]] == -1:
mines += 1
if not pos[0] == 0 and not pos[1] == self.height and self.rows[pos[0]-1][pos[1]+1] == -1:
mines += 1
if not pos[1] == 0 and self.rows[pos[0]][pos[1]-1] == -1:
mines += 1
if not pos[1] == self.height and self.rows[pos[0]][pos[1]+1] == -1:
mines += 1
if not pos[1] == 0 and not pos[0] == self.width and self.rows[pos[0]+1][pos[1]-1] == -1:
mines += 1
if not pos[0] == self.width and self.rows[pos[0]+1][pos[1]] == -1:
mines += 1
if not pos[1] == self.height and not pos[0] == self.width and self.rows[pos[0]+1][pos[1]+1] == -1:
mines += 1
print(mines)
self.surface.blit(spriteList[mines], (pos[0]*16, pos[1]*16))
pygame.display.flip()
elif val == -1:
playing = False
return
def render(self):
for i in range(0, self.width):
for i2 in range(0, self.height):
self.surface.blit(spriteList[0], (i*16, i2*16))
pygame.display.flip()
class Main():
def __init__(self):
self.screen = pygame.display.set_mode((screenX, screenY), pygame.RESIZABLE)
spriteList.append(sprites.subsurface(16, 49, 16, 16))
for i in range(0, 8):
print(i*16)
spriteList.append(sprites.subsurface(i*16, 65, 16, 16))
self.field = Minefield(50, 30, 30, self.screen)
def gameloop(self):
while True:
for event in pygame.event.get():
if event.type == pygame.VIDEORESIZE:
screenX = event.w
screenY = event.h
self.screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
if event.type == pygame.QUIT:
return
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
self.field.clicked(pos)
def stop(self):
pygame.display.quit()
pygame.quit()
sys.exit("Window Closed")
main = Main()
main.gameloop()
main.stop()
What I want:
On player click the only change is a 1 appears over the cell, but instead the background gets painted over by black.

pygame.display.set_mode() creates a new window surface. You've to render the background after recreating the window surface in the resize event. When the window was initialized, then immediately 1 resize event is pending.
def gameloop(self):
while True:
for event in pygame.event.get():
if event.type == pygame.VIDEORESIZE:
screenX = event.w
screenY = event.h
self.screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
self.field.render() # <-------------
Note, when the window was resized and the window surface is recreated then the entire scene has to be redrawn.
Probably your game will work only, if the window is not resizable. When you resize the window, then a new event occurs. The surface is recreated and the background is redrawn, but the "clicked" fields are lost. So they would have to be redrawn, too.

Related

How does one make collisions for sprites in pygame?

I'm trying to make collisions for my basic 2D game using pygame. I've run into an issue where the game won't start and i'm getting an error.
playercolidecheck = MyPlayer.collide(v)
File "C:\Users\marti\Desktop\Python\PyMine\PyMineFile.py", line 53,
in collide
return pygame.sprite.spritecollideany(self, enemies)
File "C:\Users\marti\AppData\Local\Programs\Python\Python37\lib\site-
packages\pygame\sprite.py", line 1586, in spritecollideany
spritecollide = sprite.rect.colliderect
AttributeError: 'Player' object has no attribute 'rect'
I have tried to to make my own custom collision but to no avail. Here is the code for the project:
import pygame
pygame.mixer.init()
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("PyMine")
clock = pygame.time.Clock()
#Images
dirtimage = pygame.image.load("dirt.png")
grassimage = pygame.image.load("grass.png")
woodimage = pygame.image.load("wood.png")
playerimage = pygame.image.load("player.png")
pygame.display.set_icon(grassimage)
drawlist = []
class Block:
def __init__(self,ID,x,y,add,w,h):
self.ID = ID
self.x = x
self.y = y
self.w = w
self.h = h
if add == True:
drawlist.append(self)
def main(self):
if self.ID == 0:
win.blit(dirtimage, (self.x,self.y))
if self.ID == 1:
win.blit(grassimage, (self.x,self.y))
if self.ID == 2:
win.blit(woodimage, (self.x,self.y))
class Player:
def __init__(self,add,x,y,):
self.x = x
self.y = y
if add == True:
drawlist.append(self)
def main(self):
win.blit(playerimage, (self.x,self.y))
def DONTUSE(self,TargetX,TargetY):
if TargetX > self.x or TargetX < self.x or TargetY > self.y or TargetY < self.y: return False
return True
def collide(self, enemies):
return pygame.sprite.spritecollideany(self, enemies)
MyBlock1 = Block(0,160,160,True,32,32)
MyBlock2 = Block(1,32,0,True,32,32)
MyBlock3 = Block(2,64,0,True,32,32)
MyPlayer = Player(False,96,0)
run = True
while run:
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
MyPlayer.x -= 32
if event.key == pygame.K_RIGHT:
MyPlayer.x += 32
win.fill((255,255,255))
MyPlayer.main()
for v in drawlist:
v.main()
playercolidecheck = MyPlayer.collide(v)
if playercolidecheck == True:
MyPlayer.y -= 0.1
pygame.draw.rect(win,(255,25,25),[MyPlayer.x,MyPlayer.y,16,16])
MyPlayer.y += 0.1
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
It gives me an error and it doesn't display anything on the screen. Altough it opens a window and names it and gives it its icon.
Your Player class does not have a pygame rect object, which is required for the spritecollideany function. So passing self into this line:
return pygame.sprite.spritecollideany(self, enemies)
You are passing your own custom class Player to the function, you need to add a rect object to your class so pygame knows where it is.
So instead of using self.x self.y for player you should have a self.rect initialized at the start and use self.rect.x, self.rect.y for those variables. Making sure to update this rect as your player moves. This puts things in a format that pygame understands.

PyGame Screen Returning Black when run

While trying to run a game in which is being developed for a project, the code would not work and causes the screen to be black. The Mycharacter is a character, enemy is for a enemy character, obstacle is for obstacles within the game. The design is supposed to be a character chasing the enemy while the enemy is chasing the character to gain points with obstructions in the way.
We are importing other classes into this function and main class.
All help will be appricated.
Heres how the code looks:
import pygame
import time
import random
import Mycharacter
import enemy
import obstacle
import json
GREEN = (0, 255, 0)
class Controller:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((800, 800))
self.background = pygame.Surface(self.screen.get_size()).convert()
#self.button_image = pygame.image.load("start_button.png").convert
self.button = pygame.Rect(350, 50, 100, 100)
#self.button_image.get_rect()
self.font = pygame.font.Font("Shark_Soft_Bites.TTF", 60)
self.text = pygame.font.Font.render(self.font,"Start", True, (255, 0, 0))
#self.score_text = pygame.font.Font.render("Highscore: "+ str())
self.file = open("highscores.json", "r")
self.currentState = "Start"
self.obstacles = []
for i in range(5):
x = random.randrange(50, 600)
y = random.randrange(60, 700)
self.obstacles.append(obstacle.Obstacle((x, y), 'rock.png' ))
self.enemies = []
for i in range(3) :
if i == 0:
x = 725
y = 100
elif i == 1:
x = 75
y = 700
elif i == 2:
x = 725
y = 700
self.enemies.append(enemy.Enemy((x, y), "enemy.png"))
self.Mycharacter = Mycharacter.Mycharacter((75, 100), "head6.png")
self.mysprites = pygame.sprite.Group((self.Mycharacter,) + tuple(self.enemies) + tuple(self.obstacles))
self.mysprites2 = pygame.sprite.Group(tuple(self.enemies) + tuple(self.obstacles))
self.score = 0
self.end_time = 0
self.start_time = 0
self.time = (self.end_time-self.start_time) * 1000
def mainLoop(self):
"""
This is the main loop for the game that calls the other functions in this class
in order to create a start screen, run the game, and present the high score and player
score at the end of the game.
Param list: None
Return list: None
"""
self.done = False
while not self.done:
if self.currentState == "start":
self.startGame(self)
elif self.currentState == "running":
self.start_time = pygame.time.get_ticks()
self.runGame(self)
elif self.currentState == "end":
self.endGame(self)
pygame.quit
def startGame(self):
"""
This is the function for the start of the game. It fills the screen
with a different color background and creates the start button that the user
can click on to begin running the game.
Param list: None
Returns: None
"""
start = True
while start:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
start = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
if self.button.collidepoint(mouse_pos):
self.currentState = "running"
start = False
self.screen.fill((255, 0, 255))
pygame.draw.rect(self.screen, (0, 0, 0), self.button)
self.screen.blit(self.text, (350,50))
pygame.display.flip()
pygame.quit
def runGame(self):
run = True
clock = pygame.time.Clock
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
run = False
if event.type == pygame.KEYDOWN:
#Under here is the game logic and i don't know if i should have the random enemy movement under here
if(event.key == pygame.K_UP):
self.Mycharacter.move_up()
elif(event.key == pygame.K_DOWN):
self.Mycharacter.move_down()
elif(event.key == pygame.K_LEFT):
self.Mycharacter.move_left()
elif(event.key == pygame.K_RIGHT):
self.Mycharacter.move_right()
self.score += 1
if self.score > 400:
if self.score > 800:
if self.score > 1200:
self.enemy.speed(18)
else:
self.enemy.speed(16)
else:
self.enemy.speed(14)
else:
self.enemy.speed(12)
self.mysprites.update()
#self.mysprites2.group_collide
#possibly sprite.groupcollide to save myself these two loops but I like the loops
#also idk if the dokill means it will .kill() or something else
for i in range(len(self.obstacles)):
if(pygame.sprite.collide_rect(self.Mycharacter, self.obstacle[i])):
self.Mycharacter.kill()
self.currentState = "end"
self.end_time = pygame.time.get_ticks()
run = False
for i in range(len(self.enemies)):
if(pygame.sprite.collide_rect(self.Mycharacter, self.enemies[i])):
self.Mycharacter.kill()
self.currentState = "end"
self.end_time = pygame.time.get_ticks()
run = False
self.screen.fill(GREEN)
#self.obstacles.draw(self.screen)
#self.enemies.draw(self.screen)
self.screen.blit(self.background, (0, 0))
self.mysprites.draw()
#self.screen.blit(self.Mycharacter,(self.Mycharacter.rect.x, self.Mycharacter.rect.y))
#drawing code
#update screen with what has been drawn
pygame.display.flip()
clock.tick(60)
pygame.quit
def endGame(self):
#self.screen = pygame.display.set_mode(800, 800)
#self.background = pygame.Surface(self.screen.get_size()).convert()
end = False
while not end:
for event in pygame.event.get():
if event == pygame.QUIT:
end = True
self.done = True
else:
line = json.load(self.file.readline())
num = line["one"]
self.file.close()
if self.time > int(num):
self.file = open("highscores.json", "w")
newstr = "highscore:"+str(num)
jsonstr = json.dump(newstr)
self.file.write(newstr)
self.file.close
self.file = open("highscores.json", "r")
newline = json.load(self.file.readline())
score = newline["highscore"]
self.file.close()
self.screen.fill((0, 0, 255))
#create text and rect to blit onto screen to display high score
self.screen.blit("Highscore: " + str(score), (350, 50))
self.screen.blit("Your Score: " + str(num), (350, 150))
pygame.display.flip()
pygame.quit
def main():
the_game = Controller()
the_game.mainLoop()
main()
The first problem that I can see is that you pass self as an argument to the different scene methods self.startGame(self) in the mainLoop. Just remove the self.
In the runGame method, you have indented a lot of your code incorrectly. The game logic and drawing code shouldn't be in the event loop but in the outer while loop. It should still draw everything, though, but only if events are in the queue (for example if the mouse gets moved).
Also, the self.background surface is just black because you never fill it with another color.
Side notes: You forgot some parentheses behind clock = pygame.time.Clock an pygame.quit.
In the endGame method, you load the json file every time an event occurs (f.e. mouse movements). You should better do that once per mouse click or something similar.
Here's a minimal example that works correctly:
import pygame
GREEN = pygame.Color('green')
class Controller:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((800, 800))
self.background = pygame.Surface(self.screen.get_size()).convert()
self.background.fill(GREEN) # Fill the background if it shouldn't be black.
self.clock = pygame.time.Clock()
self.currentState = "start"
self.button = pygame.Rect(350, 50, 100, 100)
def mainLoop(self):
self.done = False
while not self.done:
if self.currentState == "start":
self.startGame()
elif self.currentState == "running":
self.start_time = pygame.time.get_ticks()
self.runGame()
def startGame(self):
start = True
while start:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
start = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
if self.button.collidepoint(mouse_pos):
self.currentState = "running"
return # Back to the mainloop
self.screen.fill((255, 0, 255))
pygame.draw.rect(self.screen, (0, 0, 110), self.button)
pygame.display.flip()
self.clock.tick(60)
def runGame(self):
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
run = False
self.screen.blit(self.background, (0, 0))
pygame.draw.rect(self.screen, (200, 0, 0), [20, 30, 50, 90])
pygame.display.flip()
self.clock.tick(60)
def main():
the_game = Controller()
the_game.mainLoop()
main()
pygame.quit()

How do I create a button that changes sprites when clicked in pygame?

I am currently having trouble with buttons in pygame. At the moment I am testing myself in pygame and I am trying to create a flappy bird type of game. What I am trying to achieve is that when I click on the play button on the main menu it will change the button sprite and run the main game.
I have managed to define the button function and get it to switch to the main game when I click. The only problem is that it doesn't show a different sprite when clicked and I can click anywhere in the application to switch to the main game instead of having to click on the button.
Any help would be much appreciated.
Thanks in Advance
import time
import random
import pygame
from pygame.locals import *
pygame.init()
#Predefined Colors
white = (255,255,255)
black = (0,0,0)
red = (200,0,0)
light_red = (255,0,0)
yellow = (200,200,0)
light_yellow = (255,255,0)
green = (34,177,76)
light_green = (0,255,0)
blue = (0,0,255)
light_blue = (0, 0, 200)
player_list = (
# Red Bird
('assets/sprites/redbird-upflap.png', 'assets/sprites/redbird-midflap.png',
'assets/sprites/redbird-downflap.png'),
# Blue Bird
('assets/sprites/bluebird-upflap.png', 'assets/sprites/bluebird-midflap.png',
'assets/sprites/bluebird-downflap.png'),
# Yellow Bird
('assets/sprites/yellowbird-upflap.png', 'assets/sprites/yellowbird-midflap.png',
'assets/sprites/yellowbird-downflap.png')
)
background_list = (
('assets/sprites/background-day.png', 'assets/sprites/background-night.png')
)
pipe_list = (
('assets/sprites/pipe-green.png', 'assets/sprites/pipe-red.png')
)
FPS = 30
images, sounds = {}, {}
def main():
global base_x, base_y, clock, gameDisplay, display_height, display_width
display_width = 288
display_height = 512
base_x = 0
base_y = display_height * 0.79
clock = pygame.time.Clock()
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Flappy Bird")
#Loading icon sprite
images['Icon'] = pygame.image.load('assets/sprites/yellowbird-midflap.png')
pygame.display.set_icon(images['Icon'])
#Loading all the Numbers sprites
images['Numbers'] = (
pygame.image.load('assets/sprites/0.png').convert_alpha(),
pygame.image.load('assets/sprites/1.png').convert_alpha(),
pygame.image.load('assets/sprites/2.png').convert_alpha(),
pygame.image.load('assets/sprites/3.png').convert_alpha(),
pygame.image.load('assets/sprites/4.png').convert_alpha(),
pygame.image.load('assets/sprites/5.png').convert_alpha(),
pygame.image.load('assets/sprites/6.png').convert_alpha(),
pygame.image.load('assets/sprites/7.png').convert_alpha(),
pygame.image.load('assets/sprites/8.png').convert_alpha(),
pygame.image.load('assets/sprites/9.png').convert_alpha()
)
#Game Over Sprite
images['Game Over'] = pygame.image.load('assets/sprites/gameover.png').convert_alpha()
#Starting Game sprite
images['Starting Game'] = pygame.image.load('assets/sprites/startgame-screen.png').convert_alpha()
#Flappy Bird Logo sprite
images['Flappy Bird Logo'] = pygame.image.load('assets/sprites/flappybird-logo.png').convert_alpha()
#Base Ground sprite
images['Base Ground'] = pygame.image.load('assets/sprites/base.png').convert_alpha()
#Play Button Up sprite
images['Play Button Up'] = pygame.image.load('assets/sprites/playbutton-up.png').convert_alpha()
#Play Button Down sprite
images['Play Button Down'] = pygame.image.load('assets/sprites/playbutton-down.png').convert_alpha()
#Quit Button Up sprite
#images['Quit Button Up'] = pygame.image.load('assets/sprites/quitbutton-up.png').convert_alpha()
#Quit Button Down sprite
#images['Quit Button Down'] = pygame.image.load('assets/sprites/quitbutton-down.png').convert_alpha()
#Sounds
# sounds['Die'] = pygame.mixer.Sound('assets/audio/die.wav')
# sounds['Hit'] = pygame.mixer.Sound('assets/audio/hit.wav')
# sounds['Point'] = pygame.mixer.Sound('assets/audio/point.wav')
# sounds['swoosh'] = pygame.mixer.Sound('assets/audio/swoosh.wav')
# sounds['wing'] = pygame.mixer.Sound('assets/audio/wing.wav')
while True:
#Select random Background sprites
random_background = random.randint(0, len(background_list) - 1)
images['Background'] = pygame.image.load(background_list[random_background]).convert()
#Select random Player sprites
random_player = random.randint(0, len(player_list) - 1)
images['Player'] = (
pygame.image.load(player_list[random_player][0]).convert_alpha(),
pygame.image.load(player_list[random_player][1]).convert_alpha(),
pygame.image.load(player_list[random_player][2]).convert_alpha()
)
#Select random Pipe sprite
random_pipe = random.randint(0, len(pipe_list) - 1)
images['Pipe'] = pygame.image.load(pipe_list[random_pipe])
main_menu()
pygame.display.update()
clock.tick(FPS)
def button(action = None):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if action == 'Play':
button = images['Play Button Up'].get_rect()
for event in pygame.event.get():
if click[0] == 1:
if button.collidepoint(cur):
print ('Mouse Over')
images['Play Button Down']
main_game()
else:
gameDisplay.blit(images['Play Button Up'], (0, -10))
def main_menu():
global player_index, player_x, player_y
player_index = 0
player_x = int(display_width * 0.2)
player_y = int((display_height - images['Player'] [0].get_height()) / 2)
menu = True
while menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
gameDisplay.blit(images['Flappy Bird Logo'], (50, -30))
gameDisplay.blit(images['Player'][player_index], (125, 140))
gameDisplay.blit(images['Play Button Up'], (10, 10))
button(action = 'Play')
pygame.display.update()
clock.tick(FPS)
def main_game():
gameExit = False
gameOver = False
player_x = 0
player_y = 0
while not gameExit:
if gameOver == True:
gameDisplay.blit(images['Game Over'], 50, 50)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Starting Game'], (0, 0))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
gameDisplay.blit(images['Player'][player_index], (player_x, player_y))
pygame.display.update()
clock.tick(FPS)
main()
Here's a minimal example to demonstrate how you can switch between different button images.
When the user presses a mouse button (a pygame.MOUSEBUTTONDOWN event is added to the queue), check if the event.pos collides with the button rect and if it collides, set the image to the "down" version.
When the user releases the button (pygame.MOUSEBUTTONUP event), just set the image back to the original version.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
GRAY = pg.Color('gray15')
BLUE = pg.Color('dodgerblue1')
LIGHTBLUE = pg.Color('lightskyblue1')
BUTTON_UP_IMG = pg.Surface((50, 30))
BUTTON_UP_IMG.fill(BLUE)
BUTTON_DOWN_IMG = pg.Surface((50, 30))
BUTTON_DOWN_IMG.fill(LIGHTBLUE)
def main():
clock = pg.time.Clock()
font = pg.font.Font(None, 30)
# Currently selected button image.
button_image = BUTTON_UP_IMG
button_rect = button_image.get_rect(topleft=(200, 200))
x = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if button_rect.collidepoint(event.pos):
button_image = BUTTON_DOWN_IMG
elif event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
button_image = BUTTON_UP_IMG
if button_rect.collidepoint(event.pos):
print('Button pressed.')
x += 1
screen.fill(GRAY)
screen.blit(button_image, button_rect)
txt = font.render(str(x), True, BLUE)
screen.blit(txt, (260, 206))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
I would actually recommend to use classes, pygame sprites and sprite groups instead of just rects and images. Then you can easily create as many instances of the button class as you want.
import pygame as pg
pg.init()
GRAY= pg.Color('gray12')
BLUE = pg.Color('dodgerblue1')
FONT = pg.font.Font(None, 30)
BUTTON_UP_IMG = pg.Surface((50, 30))
BUTTON_UP_IMG.fill(BLUE)
BUTTON_DOWN_IMG = pg.Surface((50, 30))
BUTTON_DOWN_IMG.fill(pg.Color('lightskyblue1'))
# The Button is a pygame sprite, that means we can add the
# instances to a sprite group and then update and render them
# by calling `sprite_group.update()` and `sprite_group.draw(screen)`.
class Button(pg.sprite.Sprite):
def __init__(self, pos, callback):
pg.sprite.Sprite.__init__(self)
self.image = BUTTON_UP_IMG
self.rect = self.image.get_rect(topleft=pos)
self.callback = callback
def handle_event(self, event):
"""Handle events that get passed from the event loop."""
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if self.rect.collidepoint(event.pos):
self.image = BUTTON_DOWN_IMG
elif event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
self.image = BUTTON_UP_IMG
if self.rect.collidepoint(event.pos):
print('Button pressed.')
# Call the function that we passed during the
# instantiation. (In this case just `increase_x`.)
self.callback()
class Game:
def __init__(self):
self.screen = pg.display.set_mode((800, 600))
self.clock = pg.time.Clock()
self.x = 0
self.buttons = pg.sprite.Group(
Button((200, 200), callback=self.increase_x),
Button((500, 200), callback=self.decrease_x))
self.done = False
# A callback function that we pass to the button instance.
# It gets called if a collision in the handle_event method
# is detected.
def increase_x(self):
"""Increase self.x if button is pressed."""
self.x += 1
def decrease_x(self):
"""Decrease self.x if button is pressed."""
self.x -= 1
def run(self):
while not self.done:
self.handle_events()
self.run_logic()
self.draw()
self.clock.tick(30)
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
for button in self.buttons:
button.handle_event(event)
def run_logic(self):
self.buttons.update()
def draw(self):
self.screen.fill(GRAY)
self.buttons.draw(self.screen)
txt = FONT.render(str(self.x), True, BLUE)
self.screen.blit(txt, (360, 206))
pg.display.flip()
if __name__ == "__main__":
Game().run()
pg.quit()
Thanks for the Answer to my previous question your answer solved my previous issue, but now I have ran into another issue.
When I press my button on the main menu it changes sprites correctly and loads the main game correctly, but as soon as I move my mouse it switches back to the main menu. My best guess is that it is because it is looping the whole button sequence, but I am not entirely sure.
import pygame
pygame.init()
display_width = 288
display_height = 512
def main_menu():
done = False
play_button_image = images['Play Button Up']
play_button_rect = play_button_image.get_rect(topleft=(30,15))
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if play_button_rect.collidepoint(event.pos):
play_button_image = images['Play Button Down']
elif event.type == MOUSEBUTTONUP:
if event.button == 1:
play_button_image = images['Play Button Up']
if play_button_rect.collidepoint(event.pos):
main_game()
gameDisplay.blit(play_button_image, play_button_rect)
pygame.display.update()
clock.tick(FPS)
def main_game():
gameExit = False
gameOver = False
player_index = 0
player_x = int(display_width * 0.2)
player_y = int((display_height - images['Player'][0].get_height()) / 2)
player_index_gen = cycle([0, 1, 2, 1])
loop_iter = 0
starting_game_x = int((display_width - images['Starting Game'].get_width()) / 2)
starting_game_y = int(display_height * 0.12)
base_x = 0
base_shift = images['Base Ground'].get_width() - images['Background'].get_width()
player_move_vals = {'val': 0, 'dir': 1}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN or pygame.K_SPACE or pygame.K_UP:
sounds['Wing']
return {
'player_y': player_y + player_move_vals['val'],
'base_x': base_x,
'player_index_gen': player_index_gen
}
if (loop_iter + 1) % 5 == 0:
player_index = next(player_index_gen)
loop_iter = (loop_iter + 1) % 30
base_x = -((-base_x + 4) % base_shift)
player_move(player_move_vals)
# draw sprites
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Player'][player_index],
(player_x, player_y + player_move_vals['val']))
gameDisplay.blit(images['Starting Game'], (starting_game_x, starting_game_y))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
pygame.display.update()
clock.tick(FPS)
if __name__ == '__main__':
main()

Having problems making Pong in Python with Pygame. Paddle extends when I move it

So like many beginner python programmers, I have decided to code a pong game. This is my second attempt. The first was hard coded with no classes and few functions, so I have started from scratch. I currently have a paddle class and a main loop. I have coded the functionality for the paddles to move up and down, but I have a problem. When I press the keys to move the paddles, the just extend up and down, they don't actually move. Here is my code thus far:
#PONG GAME IN PYTHON WITH PYGAME
import pygame
import time
pygame.init()
white = (255, 244, 237)
black = (0, 0, 0)
largeFont = pygame.font.Font("pongFont.TTF", 75)
mediumFont = pygame.font.Font("pongFont.TTF", 50)
smallFont = pygame.font.Font("pongFont.TTF", 25)
displayWidth = 800
displayHeight = 600
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
pygame.display.set_caption("Pong")
FPS = 60
menuFPS = 10
clock = pygame.time.Clock()
#Paddle Class
class Paddle:
def __init__(self, player):
self.length = 100
self.width = 8
self.yVelocity = 0
self.y = (displayHeight - self.length) / 2
#Puts player 1 paddle on left and player 2 on right
if player == 1:
self.x = 3 * self.width
elif player == 2:
self.x = displayWidth - 4 * self.width
#Did paddle hit top or bottom?
def checkWall(self):
if self.y <= 0:
return "top"
elif self.y >= displayHeight - self.length:
return "bottom"
def stop(self):
self.yVelocity = 0
def moveUp(self):
if self.checkWall() == "top":
self.stop()
else:
self.yVelocity = -self.width
def moveDown(self):
if self.checkWall() == "bottom":
self.stop()
else:
self.yVelocity = self.width
#Draw the paddle
def draw(self):
self.y += self.yVelocity
gameDisplay.fill(white, rect = [self.x, self.y, self.width, self.length])
paddle1 = Paddle(1)
paddle2 = Paddle(2)
gameFinish = False
#Main Loop
while not gameFinish:
#Event Loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
#Get all pressed keys
keyPressed = pygame.key.get_pressed()
#Move paddle1 if s or w is pressed
if keyPressed[pygame.K_w]:
paddle1.moveUp()
elif keyPressed[pygame.K_s]:
paddle1.moveDown()
else:
paddle1.stop()
#Move paddle2 if UP or DOWN is pressed
if keyPressed[pygame.K_UP]:
paddle2.moveUp()
elif keyPressed[pygame.K_DOWN]:
paddle2.moveDown()
else:
paddle2.stop()
paddle1.draw()
paddle2.draw()
pygame.display.update()
clock.tick(FPS)
Thanks in advance to anyone who can help!
Clear the screen before, you are drawing a new paddle when the old paddle is still there.
Use something like gameDisplay.fill(white) to clear your screen.
This is because you already have the paddle sprite on when you move it. Effectively what you want to do is destroy the old paddle and then draw the old one, otherwise the old one will still exists when you create the new one, making a merging effect.

A moving rectangle becomes stuck after hitting the edge of the window

I created a rectangle in the middle of the window, and used key 'w', 's', 'a', 'd', to move it. My problem is every time the rectangle hits the edge of the window is becomes stuck and cannot move any more.
import pygame, sys, time
from pygame.locals import *
class Tile:
bcolor = pygame.Color('black')
rcolor = pygame.Color('white')
def __init__(self, surface):
self.surface = surface
self.size = 30
self.x = self.surface.get_width()//2 - self.size//2
self.y = self.surface.get_height()//2 - self.size//2
self.rect = pygame.Rect(self.x, self.y, self.size, self.size)
self.speed = 10
self.rcolor = Tile.rcolor
self.bcolor = Tile.bcolor
def draw(self):
pygame.draw.rect(self.surface, self.rcolor, self.rect)
def moveup(self):
if self.rect.top < self.speed:
self.speed = self.rect.top
self.rect.move_ip(0, -self.speed)
def movedown(self):
maxbottom = self.surface.get_height()
if maxbottom - self.rect.bottom < self.speed:
self.speed = maxbottom - self.rect.bottom
self.rect.move_ip(0,self.speed)
def moveleft(self):
if self.rect.left < self.speed:
self.speed = self.rect.left
self.rect.move_ip(-self.speed, 0)
def moveright(self):
maxright = self.surface.get_width()
if maxright - self.rect.right < self.speed:
self.speed = maxright - self.rect.right
self.rect.move_ip(self.speed, 0)
def handlekeydown(self, key):
if key == K_w:
self.moveup()
if key == K_s:
self.movedown()
if key == K_a:
self.moveleft()
if key == K_d:
self.moveright()
def update(self):
self.surface.fill(self.bcolor)
self.draw()
def main():
pygame.init()
pygame.key.set_repeat(20, 20)
surfacesize = (500,400)
title = 'Practice'
framedelay = 0.02
surface = pygame.display.set_mode(surfacesize, 0, 0)
pygame.display.set_caption(title)
tile = Tile(surface)
gameover = False
tile.draw()
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN and not gameover:
tile.handlekeydown(event.key)
tile.update()
pygame.display.update()
time.sleep(framedelay)
main()
Just look at this piece of code:
def moveup(self):
if self.rect.top < self.speed:
self.speed = self.rect.top
self.rect.move_ip(0, -self.speed)
Once you move to the top of the screen, self.rect.top becomes 0, which is smaller than self.speed, so the condition of the if statement is True and in the next line you set self.speed to 0, hence all your move_ip calls won't move the Rect (because self.speed is now 0).
If you want to prevent the Rect from leaving the screen, better just use clamp_ip:
...
def moveup(self):
self.rect.move_ip(0, -self.speed)
def movedown(self):
self.rect.move_ip(0,self.speed)
def moveleft(self):
self.rect.move_ip(-self.speed, 0)
def moveright(self):
self.rect.move_ip(self.speed, 0)
def handlekeydown(self, key):
if key == K_w:
self.moveup()
if key == K_s:
self.movedown()
if key == K_a:
self.moveleft()
if key == K_d:
self.moveright()
self.rect.clamp_ip(pygame.display.get_surface().get_rect())
...
(There are some other issues with your code, but that's another topic.)

Resources