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.
Related
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.
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()
I'm trying to blit an image of grass onto my game in a specific area, but I'm having trouble doing so because it's in a class. I want to do something like this..
grass_platform.draw(screen, (200, 200))
NOTE: I know this actually doesn't work
Here's my code...
import pygame
pygame.init()
#Screen Size
screen_size = [1024,576]
#Display Window
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption('The Adventures of Fresco the Explorer')
#Clock
clock = pygame.time.Clock()
#Colors
black = (0,0,0)
white = (255,255,255)
#Game Start
gameStart = False
#Backgrounds
forest = pygame.image.load('forest.png')
#Gravity
gravity =-10
fall = True
#Player
fresco = pygame.image.load('fresco v2.png').convert()
fresco = pygame.transform.scale(fresco,(32,136))
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, filename):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.image = fresco
self.rect = self.image.get_rect()
def update (self):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
self.rect.x -= 6
self.image = pygame.transform.flip(fresco, True, False)
elif event.key == pygame.K_d:
self.rect.x += 6
self.image = pygame.transform.flip(fresco, False, False)
elif event.key == pygame.K_w:
self.rect.y -= 20
def draw(self, screen):
screen.blit(self.image, self.rect)
player = Player(0, 0, 'fresco v2.png')
#Grass Platform
grass = pygame.image.load('grass.png')
grass = pygame.transform.scale(grass, (90, 90))
class Grass (pygame.sprite.Sprite):
def __init__(self, filename):
self.image = grass
self.rect = self.image.get_rect()
def draw(self, screen):
screen.blit(self.image,self.rect)
grass_platform = Grass('grass.png')
#Game Loop
while not gameStart:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameStart = True
#Background Blitted
screen.blit(forest,(0,0))
#Falling Event
if fall == True:
if gravity:
player.rect.y -= gravity
#Class Blitted
player.update()
player.draw(screen)
grass_platform.draw(screen)
#Updates Screen
pygame.display.update()
#FPS
clock.tick(30)
pygame.quit()
I dont know if i understood you correctly but if you mean just passing a coordinate parameter and blitting to that position that should be quite easy.
class Grass (pygame.sprite.Sprite):
def __init__(self, filename):
self.image = grass
self.rect = self.image.get_rect()
def draw(self, screen, pos):
screen.blit(self.image, pos)
and then when you draw() you just do this:
grass_platform.draw(screen, (300, 200))
or on the other hand if you want to keep the position you could add
this code to the init method:
def __init__(self, filename, x, y):
self.x = x
self.y = y
and then acces it later
def draw(self, screen):
window.blit(img, (self.x, self.y))
Some notes:
Player and Grass are subclasses from Sprite, so they have already a draw function which does exactly what you are doing. Just remove them.
the following part
#Falling Event
if fall == True:
if gravity:
player.rect.y -= gravity
is specific to the Player class and should be moved to the Player class' update function
When using the Sprite class, the rect attribute is used to store the position of the sprite, hence the x and y attribute in your Player class don't make much sense. Remove them, and use the x and y parameters to change the x and y attribute of the rect instead:
self.rect = self.image.get_rect(x=x, y=y)
Hence, if you want to draw grass_platform at a specific position, just alter it's rect, e.g.:
grass_platform.rect.topleft = (200, 200)
instead of calling draw and update on each of your sprites manually, just but them into a Group, and call draw and update on that Group.
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.
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.)