How does one make collisions for sprites in pygame? - python-3.x

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.

Related

Pygame screen gets cleared on flip, but only once

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.

Need suggestions for fixing ABC error?

I'm creating a boid flocking simulator and getting some errors. Suspecting they are issued by wrong usage of ABC. But I can't fix the error, been trying for a day.
the primary issue is in the Check_input class. flyers is somehow undefined and I'm trying to append them to the FlyerList if the input is registered.
Linked the whole code for full perspective.
from precode import Vector2D, intersect_rectangle_circle, intersect_circles
from abc import ABC, abstractmethod
from config import *
import pygame
class Flyer(ABC):
#abstractmethod
def draw(self):
pass
#abstractmethod
def move(self):
pass
class Boids(Flyer):
def __init__(self):
self.radius = 7
self.pos = Vector2D(100,100)
self.speed = Vector2D(1,1)
pass
def draw(self,screen):
pygame.draw.circle(screen, black, (int(self.pos.x), int(self.pos.y)),
self.radius)
pass
def move(self):
self.pos += self.speed
if self.pos.x + self.radius >= screen_res[0]:
self.speed.x *= -1
if self.pos.x - self.radius <= 0:
self.speed.x = self.speed.x * -1
if self.pos.y - self.radius >= 0:
self.speed.y *= -1
if self.pos.y + self.radius < screen_res[1]:
self.speed.y *= -1
pass
class Hoiks(Flyer):
def __init__(self):
self.radius = 9
self.pos = Vector2D(200,200)
self.speed = Vector2D(8,8)
def draw(self,screen):
pygame.draw.circle(screen, red, (int(self.pos.x), int(self.pos.y)),
self.radius)
pass
def move(self):
self.pos += self.speed
if self.pos.x + self.radius >= screen_res[0]:
self.speed.x *= -1
if self.pos.x - self.radius <= 0:
self.speed.x *= -1
if self.pos.y - self.radius >= 0:
self.speed.y *= -1
if self.pos.y + self.radius < screen_res[1]:
self.speed.y *= -1
class FlyerList():
flyers = []
def move_all(self):
for flyer in self.flyers:
flyer.move()
def draw_all(self):
for flyer in self.flyers:
flyer.draw()
class Check_input():
def boidspawn(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
flyers.append(Boids)
def hoikspawn(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
flyers.append(Hoiks)
class Game:
def __init__(self):
pygame.init()
self.flyer_list = FlyerList()
self.check_input = Check_input()
def Gameloop(self,event):
while 1:
self.flyer_list.move_all()
self.flyer_list.draw_all()
self.check_input.boidspawn(event)
self.check_input.hoikspawn(event)
# self.check_collision()
def game_code():
# pygame.init()
game = Game()
boid = Boids()
hoik = Hoiks()
bats = FlyerList
check = Check_input()
screen = pygame.display.set_mode(screen_res)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and
event.key == pygame.K_ESCAPE:
exit()
pygame.draw.rect(screen, (255,255,255), (0, 0, screen.get_width(),
screen.get_height()))
time_passed = clock.tick(30) # limit to 30FPS
time_passed_seconds = time_passed / 1000.0 # convert to seconds
x, y = pygame.mouse.get_pos()
game.Gameloop(event)
pygame.display.update()
if __name__ == '__main__':
game_code()

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()

Understanding & making infinite bullets to shoot in Pygame

I'm learning python and also pygame, and I want to know why the bullets doesn't display, I tough that maybe the screen.update_screen() can be interfering but no, that's not the case, I need help to undertand how Bullets work in python because clearly my method is not working, I've seen many methods in other posts, and they use a limited ammount of bullets to shoot (don't know why, in my case I want infinite bullets) so what should I add to see the Bullets, I know that I need to add a remover for seing a "movement" in the display, but I don't know how, any help is appreciated.
# -*- coding: utf-8 -*-
import pygame
class Screen(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
pygame.display.set_caption("Space Game")
self.screen_width = 800
self.screen_heigh = 600
self.picture = pygame.image.load("screen.png")
self.screen = pygame.display.set_mode((self.screen_width, self.screen_heigh))
def update_screen(self):
self.screen.blit(self.picture, (0, 0))
def update_obj(self, object):
self.screen.blit(object.picture, object.rect)
def update_shoot(self, object):
for y in range(object.rect.centery, 600, 10):
self.screen.blit(object.picture, (object.rect.centerx, object.rect.centery + y))
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.picture = pygame.image.load("ship.png")
self.rect = self.picture.get_rect()
self.rect.centerx = 400
self.rect.centery = 500
class Bullet(pygame.sprite.Sprite):
def __init__(self, object):
pygame.sprite.Sprite.__init__(self)
self.picture = pygame.image.load("shoot.png")
self.rect = self.picture.get_rect()
self.rect.centerx = object.rect.centerx
self.rect.centery = (object.rect.centery + 25)
def main():
pygame.init()
done = False
clock = pygame.time.Clock()
screen = Screen()
ship = Ship()
bullet = Bullet(ship)
while not done:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
ship.rect.centerx -= 5
if keys[pygame.K_RIGHT]:
ship.rect.centerx += 5
if keys[pygame.K_UP]:
ship.rect.centery -= 5
if keys[pygame.K_DOWN]:
ship.rect.centery += 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
done = True
if event.key == pygame.K_SPACE:
print ("shoot")
#for some weird reason the bullet doesn't display
screen.update_shoot(bullet)
screen.update_screen()
screen.update_obj(ship)
pygame.display.update()
clock.tick(10)
pygame.quit()
if __name__ == "__main__":
main()
To shoot bullets you usually create instances of a Bullet class and add them to a list, pygame.sprite.Group or another container. Then you iterate over this container and call the update method of the bullet in which its position is changed. To blit the images of the sprites/objects you iterate again over the container and just blit the images onto the screen. With sprite groups you can just call sprite_group.update() and sprite_group.draw(screen) instead of iterating yourself. BTW, pygame sprites have to have a self.image attribute not a self.picture in order to work with sprite groups (take a look at Program Arcade Games for more information).
I started to modify a few things in your example to show you how to use sprite groups, but then ended up changing your whole Screen class into a Game class (which I recommend to use in the future).
import sys
import pygame
class Game:
def __init__(self):
self.done = False
self.screen_width = 800
self.screen_height = 600
self.image = pygame.Surface((800, 600))
self.image.fill((30, 40, 50))
self.screen = pygame.display.set_mode(
(self.screen_width, self.screen_height))
# all_sprites is used to update and draw all sprites together.
self.all_sprites = pygame.sprite.Group()
# You'll probably need a separate bullet_group
# later for collision detection with enemies.
self.bullet_group = pygame.sprite.Group()
self.ship = Ship()
self.all_sprites.add(self.ship)
bullet = Bullet(self.ship)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
def handle_events(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.ship.rect.centerx -= 5
if keys[pygame.K_RIGHT]:
self.ship.rect.centerx += 5
if keys[pygame.K_UP]:
self.ship.rect.centery -= 5
if keys[pygame.K_DOWN]:
self.ship.rect.centery += 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.done = True
if event.key == pygame.K_SPACE:
bullet = Bullet(self.ship)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
def update(self):
# Calls `update` methods of all contained sprites.
self.all_sprites.update()
def draw(self):
self.screen.blit(self.image, (0, 0))
self.all_sprites.draw(self.screen) # Draw the contained sprites.
pygame.display.update()
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((20, 30))
self.image.fill((50, 170, 230))
# A nicer way to set the start pos with `get_rect`.
self.rect = self.image.get_rect(center=(400, 500))
class Bullet(pygame.sprite.Sprite):
def __init__(self, ship):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((7, 7))
self.image.fill((230, 140, 30))
self.rect = self.image.get_rect()
self.rect.centerx = ship.rect.centerx
self.rect.centery = ship.rect.centery - 25
def update(self):
self.rect.y -= 5 # Move up 5 pixels per frame.
def main():
pygame.init()
pygame.display.set_caption('Space Game')
clock = pygame.time.Clock()
game = Game()
while not game.done:
game.handle_events()
game.update()
game.draw()
clock.tick(30)
if __name__ == '__main__':
main()
pygame.quit()
sys.exit()

Python, please help me that why my rect cant move

I was trying to make a basic game using pygame, but the rectangle i created can not move after i press the key, please teach me how to fix it,thank you
import pygame, sys, time
from pygame.locals import *
class Tile:
def __init__(self,surface):
self.surface = surface
self.x = 250
self.y = 200
self.position = (self.x, self.y)
self.color = pygame.Color('red')
self.speed = 5
self.size = 30
self.rect = pygame.Rect(self.position[0],self.position[1],self.size,self.size)
def draw(self):
pygame.draw.rect(self.surface,self.color,self.rect)
def moveUp(self):
if self.rect.top < self.speed:
self.speed = self.rect.top
self.y = self.y - self.speed
def moveDown(self):
maxBottom = self.surface.get_height()
if maxBottom - self.rect.bottom < self.speed:
self.speed = maxBottom - self.rect.bottom
self.y = self.y + self.speed
def moveLeft(self):
if self.rect.left < self.speed:
self.speed = self.rect.left
self.x = self.x - self.speed
def moveRight(self):
maxRight = self.surface.get_width()
if maxRight - self.rect.right < self.speed:
self.speed = maxRight - self.rect.right
self.x = self.x + self.speed
def move(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 main():
pygame.init()
pygame.key.set_repeat(20, 20)
surfaceSize = (500, 400)
windowTitle = 'Pong'
frameDelay = 0.01
surface = pygame.display.set_mode(surfaceSize, 0, 0)
pygame.display.set_caption(windowTitle)
gameOver = False
tile = Tile(surface)
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.move(event.key)
pygame.display.update()
time.sleep(frameDelay)
main()
I was trying to make a basic game using pygame, but the rectangle i created can not move after i press the key, please teach me how to fix it,thank you
You're only updating your own x and y variables, and not the PyGame ones for the rect, you can fix it quickest in the draw function, but it's really an updating operation so should go in those functions:
def draw(self):
self.rect.top = self.y # You need to update the position of the rect
self.rect.left = self.x # and not just your own x, y variables.
pygame.draw.rect(self.surface,self.color,self.rect)
Then your indentation for updating the display is off, but you need an extra line anyway:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN and not gameOver:
tile.move(event.key)
surface.fill((0, 0, 0)) # Fill the screen with black to hide old rect
tile.draw() # Re-draw the tile in new position
pygame.display.update() # Update the display
Last thing to fix, don't use time.sleep for the frame delay, use PyGame's inbuilt clock function. Here's a tutorial: https://www.youtube.com/watch?v=pNjSyBlbl_Q&nohtml5=False
Your if statements looks suspect to me:
if self.rect.left < self.speed
How is self.speed related to self.rect.left?
You should instead be shifting the entire rect by self.speed in the direction you want. Try that.
I also find adding print statements throughout the code and looking at the console can help a lot. An even better solution would be to use a debugger.

Resources