OOP pygame movement issues - python

I am just getting started with pygame. My Player instances position does not update on the surface even though it's x value is changed accordingly.
'''__main__.py'''
import script
if __name__ == '__main__':
script.setup()
script.update()
-
'''script.py'''
import pygame
from player import Player
from enemy import Enemy
from ball import Ball
def setup():
global window, player, enemy, ball
pygame.init()
pygame.display.set_caption('Pong')
window = pygame.display.set_mode((800, 600))
player = Player(40, window.get_height() / 2 - 100 / 2, 20, 100)
enemy = Enemy(window.get_width() - 40 - 20, window.get_height() / 2 - 100 / 2, 20, 100)
def draw():
player.update()
enemy.update()
pygame.display.update()
player.draw(window)
enemy.draw(window)
def update():
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
player.up = True
if keys[pygame.K_DOWN]:
player.down = True
if not keys[pygame.K_UP]:
player.up = False
if not keys[pygame.K_DOWN]:
player.down = False
draw()
pygame.quit()
-
'''player.py'''
import pygame
class Player:
'''Player'''
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = (255, 255, 255)
self.rect = (self.x, self.y, self.width, self.height)
self.vel = 10
self.up = False
self.down = False
def draw(self, surface):
pygame.draw.rect(surface, self.color, self.rect)
def update(self):
if self.up:
self.x -= self.vel
if self.down:
self.x += self.vel
The instance's x value changes when I press up or down, but for some reason it does not get drawn on screen. I have tried moving the draw method and the player.update method but can't seem to get it to work properly.

Look at the Player.draw method. It draws using self.rect.
The problem with your update method is that it never updates self.rect. So self.rect stays the same as after constructor.
Remove self.rect as attribute and replace it with either simple method (Option A) or property (Option B):
class Player:
# Options A:
def get_rect(self):
return (self.x, self.y, self.width, self.height)
# Option B:
#property
def rect(self):
return (self.x, self.y, self.width, self.height)
Or update self.rect inside update method. But I do not recommend that. Having it separately was a reason why you got your no-changing-position-bug in the first place.

Related

Bullet not moving upwards because of OOP

I have a simple shooter game using pygame. I'm having some problems making the bullet's y coordinate slowly increasing up. I know this is something to do with the way I've programmed the Player class even though a bullet Rect is in it. I think I have to change the update function inside it. This is my code:
import pygame, random, sys, time
pygame.init()
#Constants
WIDTH = 800
HEIGHT = 500
BLACK = (0, 0, 0)
WHITE = (255, 255, 255) # Background Colour
RED = (255, 0, 0)
GREEN = (0, 255, 0)
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pygame Shooter Game")
clock = pygame.time.Clock()
fps = 60
run = True
class Player():
def __init__(self, width, colour, x, y):
self.width = width
self.colour = colour
self.x = x
self.y = y
self.vel = 5
self.shoot = False
self.player = pygame.Rect(self.x, self.y, self.width, self.width)
self.cartridge = pygame.Rect(0, 0, self.width/2, self.width/2)
self.bullet = pygame.Rect(0, 0, 10, 20)
self.shoot = False
def draw(self, win):
self.win = win
pygame.draw.rect(self.win, self.colour, self.player) # Draw player(rect)
pygame.draw.rect(self.win, GREEN, self.cartridge) #Draw cartridge
if self.shoot:
pygame.draw.rect(self.win, BLACK, self.bullet)
def move(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and self.x > 0: self.x -= self.vel # We don't do elif cuz we want them to be able to move diagonally
if keys[pygame.K_RIGHT] and self.x < WIDTH-self.width: self.x += self.vel
if keys[pygame.K_UP] and self.y > 0: self.y -= self.vel
if keys[pygame.K_DOWN] and self.y < HEIGHT-self.width: self.y += self.vel
if keys[pygame.K_SPACE]:
self.shoot = True
def update(self):
self.player = pygame.Rect(self.x, self.y, self.width, self.width)
self.cartridge.midbottom = self.player.midtop
self.bullet.midbottom = self.cartridge.midtop
if self.shoot:
while self.bullet.y > 0:
self.bullet.y -= 1
def main(win):
run = True
player = Player(50, RED, WIDTH/2, HEIGHT/2)
while run:
win.fill(WHITE)
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
player.move()
player.update()
player.draw(win)
pygame.display.update()
pygame.quit()
sys.exit()
main(window)
Also, how can I make the create classes for each individual Cartridge and Bullet, to make the whole code more efficient?
update is invoked continuously in the main application loop. Therefore, no additional animation loops are required for the update. Change the loop to a selection (change while to if):
while self.bullet.y > 0:
if self.bullet.y > 0:
self.bullet.y -= 1
The starting position of the bullet must be set when the bullet is shot, rather than continuously when the bullet is updated:
class Player():
# [...]
def move(self):
# [...]
if keys[pygame.K_SPACE]:
self.shoot = True
self.bullet.midbottom = self.cartridge.midtop # <--- INSERT
def update(self):
self.player = pygame.Rect(self.x, self.y, self.width, self.width)
self.cartridge.midbottom = self.player.midtop
# self.bullet.midbottom = self.cartridge.midtop <--- DELETE
if self.shoot:
if self.bullet.y > 0: # <--- if (not while)
self.bullet.y -= 1
See also:
How can i shoot a bullet with space bar?
How do I stop more than 1 bullet firing at once?

how to make rectangle "sprint"

import pygame
width = 500
height = 500
win = pygame.display.set_mode((width, height))
pygame.display.set_caption("Client")
running = False
clientNumber = 0
class Player():
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
self.rect = (x, y, width, height)
self.vel = 3
def draw(self, win):
pygame.draw.rect(win, self.color, self.rect)
def move(self):
keys = pygame.key.get_pressed()
running = bool
if keys[pygame.K_LEFT]:
self.x -= self.vel
if keys[pygame.K_RIGHT]:
self.x += self.vel
if keys[pygame.K_UP]:
self.y -= self.vel
if keys[pygame.K_DOWN]:
self.y += self.vel
if keys[pygame.K_a] and not running:
self.vel += 3
running = True
if not keys[pygame.K_a]:
running = False
self.rect = (self.x, self.y, self.width, self.height)
def redrawWindow(win, player):
win.fill((255, 255, 255))
player.draw(win)
pygame.display.update()
def main():
run = True
p = Player(50, 50, 100, 100, (0, 0, 255))
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
p.move()
redrawWindow(win, p)
main()
I succeed in making a rectangle thing that can be moved by keyboard input.
and now I want to change the velocity of the rectangle like, while, pressing 'a', The velocity change to 6 from 3.
but I have no idea how to do that.
I tried to make a "running" bool variable to speed it up only when you pressed it not while pressing it.
But all of my efforts gone wrong.
Compute the current velocity (current_vel) dependent on the key state of a. Use current_vel to move the player instead of self.vel:
class Player():
# [...]
def move(self):
keys = pygame.key.get_pressed()
current_vel = self.vel
if keys[pygame.K_a]:
current_vel += 3
if keys[pygame.K_LEFT]:
self.x -= current_vel
if keys[pygame.K_RIGHT]:
self.x += current_vel
if keys[pygame.K_UP]:
self.y -= current_vel
if keys[pygame.K_DOWN]:
self.y += current_vel
self.rect = (self.x, self.y, self.width, self.height)

Creating collision in pygame [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
i'm having a problem with my code. So, I want to create a game, in Pygame, where the bananas fall from the sky, and the monkey have to grab them. I'm having quite a hard time creating a collision between those two (spent hours trying already).
So, this is my code:
import pygame, sys, random, time, os
from pygame.locals import *
#Variáveis necessárias
banana_speed = 5
monkey_speed = 20
WIDTH = 800
HEIGHT = 800
pontos = 0
vidas = 3
#Nome do jogo
pygame.display.set_caption("Catch the fruit")
#Tamanho do ecrã do jogo
screen = pygame.display.set_mode((WIDTH, HEIGHT))
class Macaco(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load('monkey.png')
self.rect = self.image
self.x = 300
self.y = 640
def keyboard(self):
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]:
self.x += monkey_speed
elif key[pygame.K_LEFT]:
self.x -= monkey_speed
def draw (self, screen):
screen.blit(self.rect, (self.x, self.y))
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
class Banana(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load('banana.png')
self.rect = self.image
self.x = random.randrange(0,WIDTH)
self.y = -50
def draw(self, screen):
self.y = self.y + banana_speed
screen.blit(self.rect,(self.x, self.y))
#Funções necessárias para o Loop
macaco = Macaco()
banana = Banana()
Background = Background('background.png', [0,0])
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
while vidas > 0:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.type == QUIT:
running = False
screen.blit(Background.image, Background.rect)
pressed_keys = pygame.key.get_pressed()
macaco.keyboard()
macaco.draw(screen)
banana.draw(screen)
pygame.display.update()
edit:
So i'm trying another solution and did this:
import pygame, sys, random, time, os
from pygame.locals import *
#Variáveis necessárias
banana_speed = 5
monkey_speed = 20
WIDTH = 800
HEIGHT = 800
pontos = 0
vidas = 3
green = (0, 0 , 255)
#Nome do jogo
pygame.display.set_caption("Catch the fruit")
#Tamanho do ecrã do jogo
screen = pygame.display.set_mode((WIDTH, HEIGHT))
class Macaco(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.image.load('monkey.png')
self.image = pygame.Surface([WIDTH, HEIGHT])
self.x = 300
self.y = 640
self.image.fill(green)
def keyboard(self):
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]:
self.x += monkey_speed
elif key[pygame.K_LEFT]:
self.x -= monkey_speed
def draw (self, screen):
screen.blit(self.rect, (self.x, self.y))
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
class Banana(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.image.load('banana.png')
self.image = pygame.Surface([WIDTH, HEIGHT])
self.x = random.randrange(0, WIDTH)
self.y = -50
def draw(self, screen):
self.y = self.y + banana_speed
screen.blit(self.rect,(self.x, self.y))
#Funções necessárias para o Loop
macaco = Macaco()
banana = Banana()
Background = Background('background.png', [0,0])
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
while vidas > 0:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.type == QUIT:
running = False
screen.blit(Background.image, Background.rect)
pressed_keys = pygame.key.get_pressed()
macaco.keyboard()
macaco.draw(screen)
banana.draw(screen)
blocks_hit_list = pygame.sprite.spritecollide(macaco, banana, True)
for blocks in blocks_hit_list:
pontos +=1
pygame.display.update()
Could you give me some help please?
I've created two sprite groups: all_sprites which contains all sprites and is used to update and draw them with just two lines of code, and the bananas group which is used to check for collisions between the macaco and the bananas. Then we need the pygame.sprite.spritecollide function to get the collided sprites. You have to pass a sprite instance and a sprite group and it'll check for you if the sprite has collided with the sprites in the group. It returns the collided bananas as a list over which you can iterate to do something with them or for example to increment a points counter.
You have to call the __init__ method of the parent class in your sprites to be able to use them correctly with sprite groups (do that with the super function super().__init__() (in Python 2 super(Macaco, self).__init__()).
To update the positions of your sprites, set their topleft (or center) attribute to the new x, y coordinates.
To get a rect from an image call self.rect = self.image.get_rect().
import sys
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 800))
class Macaco(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill((190, 140, 20))
self.rect = self.image.get_rect()
self.x = 300
self.y = 640
self.speed = 20
def keyboard(self, keys):
if keys[pygame.K_RIGHT]:
self.x += self.speed
elif keys[pygame.K_LEFT]:
self.x -= self.speed
def update(self):
self.rect.topleft = self.x, self.y
class Banana(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill((230, 230, 40))
self.rect = self.image.get_rect()
self.x = random.randrange(0, 770)
self.y = -50
self.speed = 5
def update(self):
self.y += self.speed
self.rect.topleft = self.x, self.y
macaco = Macaco()
banana = Banana()
all_sprites = pygame.sprite.Group(macaco, banana)
bananas = pygame.sprite.Group(banana)
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
keys = pygame.key.get_pressed()
macaco.keyboard(keys)
all_sprites.update()
# Collision detection. Check if macaco collided with bananas group,
# return collided bananas as a list. dokill argument is True, so
# that collided bananas will be deleted.
collided_bananas = pygame.sprite.spritecollide(macaco, bananas, True)
for collided_banana in collided_bananas:
print('Collision.')
screen.fill((70, 40, 70))
all_sprites.draw(screen)
pygame.display.update()
clock.tick(30)
pygame.quit()
sys.exit()

What is wrong with this code? I can't seem to get the bullet to be shot from the rocket

Basically the rocket moves up and down, stops when it reaches the top or bottom and recognizes that the spacebar is being pressed. However the bullet won't shoot from the ship. I'm new to python and pygame. Anyways here is the code, thank you for any help!:
import pygame
import sys
from pygame.locals import*
bg_color = (0, 191, 255)
d_w = 1200
d_h= 800
class Rocket(object):
def __init__(self):
self.image = pygame.image.load('Rocket.bmp')
self.x = 0
self.y = d_h/2
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 6.5
if key[pygame.K_UP]:
if self.y > 0:
self.y -= dist
elif key[pygame.K_DOWN]:
if self.y < 775:
self.y += dist
elif key[pygame.K_SPACE]:
Bullet.update(Bullet)
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([2, 5])
self.image.fill(0, 0, 0)
self.rect = self.image.get_rect()
def update(self):
""" Move the bullet. """
self.image = pygame.Surface([2, 5])
self.image.fill((0, 0, 0))
self.rect = self.image.get_rect()
self.rect.x += 3
pygame.init()
screen = pygame.display.set_mode((d_w, d_h))
pygame.display.set_caption("Game Character")
Mario = pygame.image.load('Mario_Sprite.bmp')
rocket = Rocket()
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
rocket.handle_keys()
screen.fill(bg_color)
rocket.draw(screen)
pygame.display.update()
clock.tick(72)
Bullet.update(Bullet)
You're calling update statically. You need to create an instance of the class and then update that instance, as in:
bullet = Bullet()
...
bullet.update()

Building a keyboard controlled game in pygame. I'm getting an indentation error but I dont know why

This is my game so far. I was able to display the Player and make it move with the keyboard. On a separate program I displayed the Enemy class randomly within the range. When I combined the two programs I started getting a bunch of indentation errors. If I fix one, another one pops ups. Please help!
import pygame
import os
import random
black = (0,0,0)
white = (255,255,255)
red = (255, 0, 0)
green = (0, 100, 0)
# This class represents the bar at the bottom that the player controls
class Player(object):
def __init__(self):
self.image = pygame.image.load("player_one.png").convert()
self.image.set_colorkey(white)
self.width = 15
self.height = 15
self.x = 940
self.y = 240
def handle_keys(self):
key = pygame.key.get_pressed()
if key[pygame.K_DOWN]:
if self.y < 470:
self.y += self.height
elif key[pygame.K_UP]:
if self.y > 0:
self.y -= self.height
if key[pygame.K_RIGHT]:
if self.x < 940:
self.x += self.width
elif key[pygame.K_LEFT]:
if self.x > 0:
self.x -= self.width
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Enemy(object):
def __init__(self):
self.image = pygame.image.load(image).convert()
self.image.set_colorkey(white)
image_rect = image.get_rect()
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
def draw(self, screen):
surface.blit(self.image, self.rect)
def update(self):
self.rect.topleft = random.randint(60, 220+1), random.randint( 0, 475+1)
class Game():
def __init__(self):
pygame.init()
pygame.display.set_caption('Best Football Game Ever!')
self.screen = pygame.display.set_mode((1000, 500))
self.multi_enemies = []
for i in range(1, 4):
enemy = Enemy("enemy_"+str(i)+".png")
enemy.update()
self.multi_enemies.append(enemy)
def run(self):
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
for enemy in self.multi_enemies:
enemy.update()
#---updates----
# place for updates
# --- draws ---
for enemy in self.multi_enemies:
enemy.draw(self.screen)
for x in range(60,940,35):
pygame.draw.line(screen, white, [x, 0], [x, 500], 1)
player.handle_keys()
self.screen.fill(green)
pygame.display.flip()
clock.tick(20)
pygame.quit()
Game().run()
You are mixing tabs with spaces for indentation. The only way this "works" is if you have your tabstop set to 8
Since nearly everyone uses 4 spaces for indentation, a tab looks like two levels of indentation, but Python only counts it as one
Here tabs are highlighted in yellow:
Edit-Select All
then go to
Format- Untabify
That should fix your problem

Categories