This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 2 years ago.
I tried adding gravity to my player in pygame, but I can move the player with key controls but gravity is not working, Here I used sprite class for making this. I got the rect of the image then add the x momentum and y momentum. X momentum worked while moving the player but y momentum didn't work while adding the gravity. Please help!
# Platformer
import pygame
# Basic Setup
pygame.init()
clock = pygame.time.Clock()
# Game Colors
white = (255, 255, 255)
light_blue = (105, 142, 255)
# Game Settings
game_title = "Platformer!"
screen_width = 1280
screen_height = 750
fps = 120
player_speed = 4
player_gravity = 0.2
# Main Window
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption(game_title)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("IMGs/player.png")
self.image.set_colorkey(white)
self.rect = self.image.get_rect(center=(screen_width / 2, screen_height / 2))
self.x_momentum = 0
self.y_momentum = 0
self.moving_right = False
self.moving_left = False
def move(self):
# Set the movement to zero at first
self.x_momentum = 0
self.y_momentum = 0
# Move the player on key press
if self.moving_right:
self.x_momentum += player_speed
elif self.moving_left:
self.x_momentum -= player_speed
# Add Gravity
self.y_momentum += player_gravity
# Move the player
self.rect.x += self.x_momentum
self.rect.y += self.y_momentum
def update(self):
self.move()
class Main:
def __init__(self):
# Sprites
self.all_sprites = pygame.sprite.Group()
# Player Sprite
self.player = Player()
self.all_sprites.add(self.player)
def draw(self, surface):
self.all_sprites.draw(surface)
def update(self):
self.all_sprites.update()
main_game = Main()
def main_game_loop():
while True:
# Handling Inputs
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
main_game.player.moving_right = True
elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
main_game.player.moving_left = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
main_game.player.moving_right = False
elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
main_game.player.moving_left = False
# Draw / Render
screen.fill(light_blue)
main_game.draw(screen)
main_game.update()
pygame.display.update()
# Manage Speed
clock.tick(fps)
main_game_loop()
The gravity doesn't work because self.y_momentum is set 0 at the begin of Player.move:
class Player(pygame.sprite.Sprite):
# [...]
def move(self):
# Set the movement to zero at first
self.x_momentum = 0
self.y_momentum = 0
# [...]
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the coordinates gets lost when the position stored in the Rect object is decremented:
# Move the player
self.rect.x += self.x_momentum
self.rect.y += self.y_momentum
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .topleft) of the rectangle:
class Player(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.x = screen_width // 2
self.y = screen_height // 2
self.rect = self.image.get_rect(center = (self.x, self.y))
# [...]
def move(self):
# [...]
# Move the player
self.x += self.x_momentum
self.y += self.y_momentum
self.rect.topleft = round(self.x), round(self.y)
Related
This question already has answers here:
How can i shoot a bullet with space bar?
(1 answer)
How do I stop more than 1 bullet firing at once?
(1 answer)
Closed 1 year ago.
I have created bullets for my car sprite to shoot but when I press space bar the bullet sprite comes out but disappears.When I press the space bar the bullet comes out but disappears right there instead of traveling all the way up to the top of the pygame screen like I want it to. How can I fix this?? I have tried a lot of different things now but I'm stuck.
autopilot.py code:
import pygame
import debris
import car
pygame.init()
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("AutoPilot")
screen.fill((255,255,255))
#fps
FPS = 120
clock = pygame.time.Clock()
#background img
bg = pygame.image.load('background/street.png').convert_alpha()
#define variables
######################CAR/DEBRIS##########################
car = car.Car(1,5)
debris = debris.Debris(1,5)
##########################################################
#groups
car_group = pygame.sprite.Group()
car_group.add(car)
debris_group = pygame.sprite.Group()
debris_group.add(debris)
#game runs here
run = True
while run:
#draw street
screen.blit(bg,[0,0])
#update groups
car_group.update()
#car_group.draw(screen)
#draw debris
debris.draw()
#draw car
car.draw()
car.move()
#update bullets
car.bullet_update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#check if key is down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.key == pygame.K_a:
car.movingLeft = True
if event.key == pygame.K_d:
car.movingRight = True
#shoot bullets
if event.key == pygame.K_SPACE:
car.shoot()
#check if key is up
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
car.movingLeft = False
if event.key == pygame.K_d:
car.movingRight = False
#update the display
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
car.py code:
import pygame
#screen height & width
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH,HEIGHT))
#car class
class Car(pygame.sprite.Sprite):
def __init__(self, scale, speed):
pygame.sprite.Sprite.__init__(self)
#load bullets
self.vel = 10
self.bullet = pygame.image.load('car/bullet.png').convert_alpha()
self.rect1 = self.bullet.get_rect()
self.y = float(self.rect1.y)
self.speed = speed
#self.x = x
#self.y = y
self.moving = True
self.frame = 0
self.flip = False
self.direction = 0
#load car
self.images = []
img = pygame.image.load('car/car.png').convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.update_time = pygame.time.get_ticks()
self.movingLeft = False
self.movingRight = False
self.rect.x = 465
self.rect.y = 325
#draw car to screen
def draw(self):
screen.blit(self.image,(self.rect.centerx, self.rect.centery))
#move car
def move(self):
# reset the movement variables
dx = 0
dy = 0
# moving variables
if self.movingLeft and self.rect.x > 33:
dx -= self.speed
self.flip = True
self.direction = -1
if self.movingRight and self.rect.x < 900:
dx += self.speed
self.flip = False
self.direction = 1
# update rectangle position
self.rect.x += dx
self.rect.y += dy
#shoot bullets
def shoot(self):
bullets = [self.bullet]
for _ in bullets:
screen.blit(self.bullet,(self.rect.x + 32, self.rect.y))
#update bullet travel
def bullet_update(self):
self.y += self.vel
self.rect1 = self.y
Add a bullet list to the Car class:
class Car(pygame.sprite.Sprite):
def __init__(self, scale, speed):
# [...]
self.bullet_list = []
Add a the position of the bullet to the list when SPACE is pressed. The starting position of the bullet is the position of the car:
class Car(pygame.sprite.Sprite):v
# [...]
def shoot(self):
self.bullet_list.append([self.rect.x, self.rect.y])
Move the bullets:
class Car(pygame.sprite.Sprite):
# [...]
def bullet_update(self):
for bullet_pos in self.bullet_list[:]:
bullet_pos[0] += self.vel
if bullet_pos[0] > 1000:
self.bullet_list.remove(bullet_pos)
Draw the bullets with the car:
class Car(pygame.sprite.Sprite):
# [...]
def draw(self):
for bullet_pos in self.bullet_list:
screen.blit(self.bullet, bullet_pos)
screen.blit(self.image, self.rect.center)
This question already has answers here:
How can i shoot a bullet with space bar?
(1 answer)
How do I stop more than 1 bullet firing at once?
(1 answer)
Closed 1 year ago.
I have created bullets for my car sprite to shoot but when I press space bar the bullet sprite comes out but disappears.When I press the space bar the bullet comes out but disappears right there instead of traveling all the way up to the top of the pygame screen like I want it to. How can I fix this?? I have tried a lot of different things now but I'm stuck.
autopilot.py code:
import pygame
import debris
import car
pygame.init()
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("AutoPilot")
screen.fill((255,255,255))
#fps
FPS = 120
clock = pygame.time.Clock()
#background img
bg = pygame.image.load('background/street.png').convert_alpha()
#define variables
######################CAR/DEBRIS##########################
car = car.Car(1,5)
debris = debris.Debris(1,5)
##########################################################
#groups
car_group = pygame.sprite.Group()
car_group.add(car)
debris_group = pygame.sprite.Group()
debris_group.add(debris)
#game runs here
run = True
while run:
#draw street
screen.blit(bg,[0,0])
#update groups
car_group.update()
#car_group.draw(screen)
#draw debris
debris.draw()
#draw car
car.draw()
car.move()
#update bullets
car.bullet_update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#check if key is down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.key == pygame.K_a:
car.movingLeft = True
if event.key == pygame.K_d:
car.movingRight = True
#shoot bullets
if event.key == pygame.K_SPACE:
car.shoot()
#check if key is up
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
car.movingLeft = False
if event.key == pygame.K_d:
car.movingRight = False
#update the display
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
car.py code:
import pygame
#screen height & width
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH,HEIGHT))
#car class
class Car(pygame.sprite.Sprite):
def __init__(self, scale, speed):
pygame.sprite.Sprite.__init__(self)
#load bullets
self.vel = 10
self.bullet = pygame.image.load('car/bullet.png').convert_alpha()
self.rect1 = self.bullet.get_rect()
self.y = float(self.rect1.y)
self.speed = speed
#self.x = x
#self.y = y
self.moving = True
self.frame = 0
self.flip = False
self.direction = 0
#load car
self.images = []
img = pygame.image.load('car/car.png').convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.update_time = pygame.time.get_ticks()
self.movingLeft = False
self.movingRight = False
self.rect.x = 465
self.rect.y = 325
#draw car to screen
def draw(self):
screen.blit(self.image,(self.rect.centerx, self.rect.centery))
#move car
def move(self):
# reset the movement variables
dx = 0
dy = 0
# moving variables
if self.movingLeft and self.rect.x > 33:
dx -= self.speed
self.flip = True
self.direction = -1
if self.movingRight and self.rect.x < 900:
dx += self.speed
self.flip = False
self.direction = 1
# update rectangle position
self.rect.x += dx
self.rect.y += dy
#shoot bullets
def shoot(self):
bullets = [self.bullet]
for _ in bullets:
screen.blit(self.bullet,(self.rect.x + 32, self.rect.y))
#update bullet travel
def bullet_update(self):
self.y += self.vel
self.rect1 = self.y
Add a bullet list to the Car class:
class Car(pygame.sprite.Sprite):
def __init__(self, scale, speed):
# [...]
self.bullet_list = []
Add a the position of the bullet to the list when SPACE is pressed. The starting position of the bullet is the position of the car:
class Car(pygame.sprite.Sprite):v
# [...]
def shoot(self):
self.bullet_list.append([self.rect.x, self.rect.y])
Move the bullets:
class Car(pygame.sprite.Sprite):
# [...]
def bullet_update(self):
for bullet_pos in self.bullet_list[:]:
bullet_pos[0] += self.vel
if bullet_pos[0] > 1000:
self.bullet_list.remove(bullet_pos)
Draw the bullets with the car:
class Car(pygame.sprite.Sprite):
# [...]
def draw(self):
for bullet_pos in self.bullet_list:
screen.blit(self.bullet, bullet_pos)
screen.blit(self.image, self.rect.center)
This question already has answers here:
Why is nothing drawn in PyGame at all?
(2 answers)
Closed 1 year ago.
Below is the code... Bullet class is defined first, class is called in _fire_bullet, _update_screen, and _check_KEYDOWN. When spacebar is pressed the event handler should take in the event key, call _fire_bullet function where a bullet object is created and added to sprite list. Then fired and moves across screen. As of now its going to move the wrong direction but I will correct that issue easily when I can actually see the bullet.
#make a pygame window with a blue background
import pygame
import sys
from pygame.sprite import Sprite
class Bullet(Sprite):
""" A class to manage bullets fired from the ship """
def __init__(self, game):
""" create a bullet object at the ships current position """
super().__init__()
self.screen = game.screen
self.bullet_speed = 1.0
self.bullet_width = 300
self.bullet_height = 150
self.bullet_color = (0, 200, 200)
#create a bullet at rect (0,0) and the set the correct position
self.rect = pygame.Rect(0, 0, self.bullet_width, self.bullet_height)
self.rect.midright = game.rect.midright
#store the bullets position as a decimal value
self.y = float(self.rect.y)
self.x = float(self.rect.x)
def update(self):
""" move the bullet up the screen """
#update the decimal position of the bullet.
self.y -= self.bullet_speed
self.rect.x = self.x
#uipdate the rect position
self.rect.y = self.y
def draw_bullet(self):
""" draw the bullet to the screen """
pygame.draw.rect(self.screen, self.bullet_color, self.rect)
class Game:
""" a class the creates a window with a blue screen """
def __init__(self):
pygame.init()
#--------------------------------------------------------------------------------------------
#screen size, color, caption
self.screen = pygame.display.set_mode((1200,800)) #create attribute to hold display settings
self.bg_color = (250,250,250) #create attribute to hold RGB color (blue)
pygame.display.set_caption("Blue Screen")
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#tank drawing
self.screen_rect = self.screen.get_rect() #get the screen rect dim
self.image = pygame.image.load('images/tank.bmp') #load the image from directory
self.rect = self.image.get_rect() #get the image rect dim
self.rect.center = self.screen_rect.center #store the screens center x/y coord
#tank movement
self.tank_moving_left = False
self.tank_moving_right = False
self.tank_moving_up = False
self.tank_moving_down = False
self.tank_speed = 1 #tank pixels
self.direction_right = self.image #holds right image
self.direction_left = pygame.transform.flip(self.image, True, False) #holds left image
#--------------------------------------------------------------------------------------------
self.bullets = pygame.sprite.Group()
def move(self):
""" move tnak tank_speed based on direction of movement (key pressed)
also detect collision """
if self.tank_moving_right and self.rect.right < self.screen_rect.right:
self.rect.x += self.tank_speed
if self.tank_moving_left and self.rect.left > self.screen_rect.left:
self.rect.x -= self.tank_speed
if self.tank_moving_down and self.rect.bottom < self.screen_rect.bottom:
self.rect.y += self.tank_speed
if self.tank_moving_up and self.rect.top > self.screen_rect.top:
self.rect.y -= self.tank_speed
def blitme(self):
""" draw the image of the tank """
self.screen.blit(self.image, self.rect)
def _update_screen(self):
""" update screen """
self.screen.fill(self.bg_color)
self.blitme()
pygame.display.flip()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
print(bullet.rect.midright)
def _check_KEYDOWN(self, event):
""" when key is press either quit, or move direction of arrow pressed and flip image """
if event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_RIGHT:
self.tank_moving_right = True
self.image = self.direction_right
elif event.key == pygame.K_LEFT:
self.tank_moving_left = True
self.image = self.direction_left
elif event.key == pygame.K_UP:
self.tank_moving_up = True
elif event.key == pygame.K_DOWN:
self.tank_moving_down = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
print(1)
def _check_KEYUP(self, event):
""" when key is let go stop moving """
if event.key == pygame.K_RIGHT:
self.tank_moving_right = False
elif event.key == pygame.K_LEFT:
self.tank_moving_left = False
elif event.key == pygame.K_UP:
self.tank_moving_up = False
elif event.key == pygame.K_DOWN:
self.tank_moving_down = False
def _fire_bullet(self):
""" create a bullet and add it to the bullets group """
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def run_game(self):
""" loops the game/ updates screen/ checks for key clicks"""
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_KEYDOWN(event)
elif event.type == pygame.KEYUP:
self._check_KEYUP(event)
self.move()
self.bullets.update()
self._update_screen()
if __name__ == '__main__':
a = Game()
a.run_game()
Your _update_screen() function is drawing your bullets after updating the display. The bullets then get overwritten next time _update_screen() is called when you fill the screen with the background color.
If you reorder your screen update function as follows you should be able to see your bullets:
def _update_screen(self):
""" update screen """
self.screen.fill(self.bg_color)
self.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
print(bullet.rect.midright)
pygame.display.flip()
Also, you can create an image for your bullet by changing your initialization function:
…
#create a bullet at rect (0,0) and the set the correct position
self.image = pygame.Surface((self.bullet_width, self.bullet_height))
self.image.fill(self.bullet_color)
self.rect = self.image.get_rect()
Then you won't need a draw_bullet() function, you can replace the drawing of individual bullets in _update_screen() with self.bullets.draw(self.screen)
I am making a game in python using pygmae. And I am having trouble with Init, it says that It has 2 arguments. but I only gave, one I found other solutions online but those did not quite work, I also got this error multiple times in the past and normally when I would fix it, it would be in very unusual ways such as places where the code is all the way down on the other side of the screen. I get that is how python works, but i'm pretty sure even in the self method I gave, I only gave it one positional argument> Here is the error code if anyone is wondering (I was doing this to make the enemy appear on the screen, so all of that is for the enemy, this problem originated from the fact that I was trying to make the enemy appear on the screen)
enemy = Enemy(enemy_image)
TypeError: init() takes 1 positional argument but 2 were given
Here is my code:
import pygame, sys
# classes
# Player class
class Player(pygame.sprite.Sprite):
def __init__(self, image):
super().__init__()
self.image = image
self.rect = self.image.get_rect(center = (screen_width//2, screen_height//2))
def update(self):
self.rect.center = pygame.mouse.get_pos()
def create_bullet(self):
return Bullet(*pygame.mouse.get_pos())
# bullet class
class Bullet (pygame.sprite.Sprite):
def __init__(self,pos_x,pos_y):
super().__init__()
self.image = pygame.Surface((50,10))
self.image.fill((255,255,0))
self.rect = self.image.get_rect(center = (pos_x,pos_y))
def update(self):
self.rect.x += 5
#Making the enemy work
enemy_speed_factor = 1.5
class Enemy:
def __init__(self):
"""Initialize the enemy and set its starting position"""
self.screen = screen
#Load the enemy image and get its rect
self.image = pygame.image.load("Enemy4.png")
self.rect = self.image.get_rect()
self.scree_rect = screen.get_rect()
#start each new enemy at the bottom of the screen
self.rect.centerx = self.scree_rect.centerx
self.rect.bottom = self.scree_rect.bottom
#store a decimal value for the ships x and y center
self.centerx = float(self.rect.centerx)
self.centery = float(self.rect.centery)
#Movement flag
self.moving_right = False
self.moving_left = False
self.moving_down = False
self.moving_up = False
def update(self):
"""Update the enemys position based on the movement flag"""
#Upade the enemy's center value, not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.centerx += self.ai_settings.ship_speed_factor
if self.mobing_left and self.rect.left >0:
self.centerx -= self.ai_settings.ship_speed_factor
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.centery =+ self.ai_settings.ship_speed_factor
if self.moving_down and self.rect.top > self.screen_rect.top:
self.centery -= self.ai_settings.ship_speed_factor
#Update rect object from self.center
if self.moving_up or self.moving_down:
self.rect.centery = self.centery
if self.moving_left or self.moving_right:
self.rect.centerx = self.centerx
def blitme(selfself, self):
"""draw the enemy at its current location"""
self.screen.blit(self.image, self.rect)
#making the movements for the enemy
def check_keydown_events(events, ship):
"""responds to keypresses"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_DOWN:
ship.moving_down = True
elif event.key == pygame.K_UP:
ship.moving_up = True
def check_keyup_evets(event, ship):
"""responds to key releases"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
elif event.key == pygame.K_DOWN:
ship.moving_down = False
elif event.key == pygame.K_UP:
ship.moving_up = False
def check_keyup_events(event, ship):
pass
def check_events(ship):
"""Respond to keypresses and mouse events"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, enemy)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship):
"""Update images on the screen and flip to the new screen"""
#Redeaw the screen during each pass through the loop
screen.fill(ai_settings.bg_color)
enemy.blitme()
#Make the most recently drawn screeen visible
pygame.display.flip()
def blitme(self):
self.screen.blit(self)
# general setup
pygame.init()
clock = pygame.time.Clock()
# game screen
screen_width = 1920
screen_height = 1080
screen = pygame .display.set_mode((screen_width, screen_height))
background = pygame.image.load("BackGround.png")
# player
player_image = pygame.image.load("Charachter2.png")
player = Player(player_image)
player_group = pygame.sprite.Group()
player_group.add(player)
# enemy
enemy_image = pygame.image.load("Enemy4.png")
enemy = Enemy(enemy_image)
enemy_group = pygame.sprite.Group()
enemy_group.add(enemy)
# Bullet
bullet_group = pygame.sprite.Group()
# caption
pygame.display.set_caption("Wild-West Shooter")
# makes game quit
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame:quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
bullet_group.add(player.create_bullet())
player_group.update()
bullet_group.update()
# draw
screen.blit(background,(0,0)) # background
player_group.draw(screen) # player
bullet_group.draw(screen) # bullets
enemy.draw(screen)
#thief.draw(screen) # enemy
pygame.display.flip()
clock.tick(120)
# makes game run
if __name__ == "__main__" :
theApp = App()
theApp.on_execute()
The problem is that your __init__ method for the Enemy class takes only 1 argument, which is the self argument. Python passes this automatically when an object is created for a class.
For example, if you have a class Foo, and its __init__ method is defined as:
def __init__ (self):
# Do stuff
and you create an object as such:
bar = Foo()
Python automatically converts the object call and adds a self argument. Think of it as self argument always being there.
Now, if you want to pass two arguments, you need to edit the __init__ method as such:
def __init__(self, image):
# Do stuff
This way, the __init__ method accepts 2 arguments, the self and the image. Then you can proceed to create an object as follows:
enemy = Enemy(enemy_image)
And Python will add the self argument to it automatically so that you final object is enemy = Enemy(self, enemy_image)
My problem is very simple. The bullets I fire sticks to the screen if I shoot fast. If I shoot slowly, they don't stick. Anyone have an idea how this phenomenon occurs?
screenshot of the bullets sticking to the screen
Below I have entered the code. I follow this default game flowchart:
I am curious about the origin of the problem. Is it the code or hardware?
import sys
import pygame
from pygame.sprite import Sprite
from pygame.sprite import Group
# pygame initializing
pygame.init()
#create the screen surface
screen = pygame.display.set_mode((800, 700))
class Color():
def __init__(self):
self.black = (0, 0, 0)
self.white = (255, 255, 255)
self.red = (255, 0, 0)
self.green = (0, 255, 0)
self.green_lambda = (10, 255, 150)
self.blue = (0, 0, 255)
# set up the colors
color = Color() # make an instance of this class - this makes some colors available
class Spaceship(Sprite):
"""
This class represents the Spaceship.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self):
""" Constructor"""
# Call the parent class (Sprite) constructor
super().__init__()
width = 22
height = 32
self.screen = screen
self.image = pygame.Surface((width, height))
self.image.fill(color.black)
self.image.set_colorkey(color.black)
pygame.draw.polygon(self.image, color.green_lambda, [[10,0],[15,22],[20,30],[10,27],[0,30],[5,22]],2)
self.rect = self.image.get_rect()
self.screen_rect = self.screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# As the rect method only take integers we store a
# This value is only used at the beginning, i.e. before the game loop starts
self.center_x = self.rect.centerx
self.center_y = self.rect.centery
class Bullet(Sprite):
"""
This class represents the bullets.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self):
# Call the parent class (Sprite) constructor
super().__init__()
self.image = pygame.Surface((8,10))
self.image.fill(color.red)
self.image.set_colorkey((color.red))
pygame.draw.ellipse(self.image, color.green, [1, 0, 5, 8], 2)
self.rect = self.image.get_rect()
self.rect.centerx = defender.rect.centerx
self.rect.bottom = defender.rect.top
# def function to move the bullets
def update_pos(self):
self.rect.y -= bullet_speed
# create spaceship instance
defender = Spaceship()
# create group to store sprites in
all_sprites_list = Group()
all_sprites_list.add(defender)
ship_speed = 0.5
bullet_speed = 3
def run_game():
m_right = False
m_left = False
m_up = False
m_down = False
new_bullet = False
while True:
"""This is the user interaction section"""
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
sys.exit()
elif event.key == pygame.K_RIGHT:
m_right = True
elif event.key == pygame.K_LEFT:
m_left = True
elif event.key == pygame.K_UP:
m_up = True
elif event.key == pygame.K_DOWN:
m_down = True
elif event.key == pygame.K_SPACE:
new_bullet = Bullet()
#print(dir(new_bullet))
all_sprites_list.add(new_bullet)
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
m_right = False
elif event.key == pygame.K_LEFT:
m_left = False
elif event.key == pygame.K_UP:
m_up = False
elif event.key == pygame.K_DOWN:
m_down = False
"""Below is the game logic, which gets input from the user interaction
section and more"""
# Movement of spaceship depending on the flag boolean value and on screen width and height
if m_right and defender.rect.right < defender.screen_rect.right:
defender.center_x += ship_speed
if m_left and defender.rect.left > defender.screen_rect.left:
defender.center_x -= ship_speed
if m_up and defender.rect.top > defender.screen_rect.top:
defender.center_y -= ship_speed
if m_down and defender.rect.bottom < defender.screen_rect.bottom:
defender.center_y += ship_speed
# The cumulative value (which is a float number) for the spaceships movement
# is given to the spaceship rect variable (which can only be integer) now.
# This enables fine adjusting of the speed
defender.rect.centerx = defender.center_x
defender.rect.centery = defender.center_y
all_sprites_list.update()
screen.fill(color.black)
if new_bullet:
new_bullet.update_pos()
# Below the bullets which leaves the screen display are deleted
if new_bullet.rect.bottom < defender.screen_rect.top:
all_sprites_list.remove(new_bullet)
all_sprites_list.draw(screen)
print(all_sprites_list)
pygame.display.flip()
run_game()
instead of just updating the position of new_bullet
# if new_bullet:
# new_bullet.update_pos()
# # Below the bullets which leaves the screen display are deleted
# if new_bullet.rect.bottom < defender.screen_rect.top:
# all_sprites_list.remove(new_bullet)
update the position of all bullets
for bullet in all_sprites_list:
if isinstance(bullet,Bullet):
bullet.update_pos()
if bullet.rect.bottom < defender.screen_rect.top:
all_sprites_list.remove(bullet)
del bullet
Joran Beasley's answer is correct. I'd just like to point out that you can also put the behavior of the sprites into their update methods which get called automatically when you call all_sprites_list.update(). You can actually move most of the code in the while loop to the update methods.
I've got an example with these changes and some more tips in the comments (a quick code review):
import pygame
from pygame.sprite import Sprite
from pygame.sprite import Group
# I'd just define some global constants for the colors.
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
GREEN_LAMBDA = (10, 255, 150)
class Spaceship(Sprite):
"""This class represents the Spaceship."""
def __init__(self, screen):
"""Constructor"""
super().__init__()
self.screen = screen
# pygame.SRCALPHA makes the surface transparent.
self.image = pygame.Surface((22, 32), pygame.SRCALPHA)
pygame.draw.polygon(
self.image, GREEN_LAMBDA,
[[10,0],[15,22],[20,30],[10,27],[0,30],[5,22]], 2
)
self.screen_rect = self.screen.get_rect()
# You can pass the position as the midbottom argument to `get_rect`.
self.rect = self.image.get_rect(midbottom=self.screen_rect.midbottom)
self.center_x = self.rect.centerx
self.center_y = self.rect.centery
# I've removed the `m_right`, etc. variables and just set the speed
# of the sprite in the event loop.
self.max_speed = 3.5
self.speed_x = 0
self.speed_y = 0
def update(self):
# Move the sprite.
self.center_x += self.speed_x
self.center_y += self.speed_y
self.rect.centerx = self.center_x
self.rect.centery = self.center_y
# Keep the sprite on the screen.
if not self.screen_rect.contains(self.rect):
self.rect.clamp_ip(self.screen_rect)
self.center_x, self.center_y = self.rect.center
class Bullet(Sprite):
"""This class represents the bullets."""
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((8, 10), pygame.SRCALPHA)
pygame.draw.ellipse(self.image, GREEN, [1, 0, 5, 8], 2)
self.rect = self.image.get_rect(midbottom=pos)
self.speed = 3 # The speed is now an attribute.
def update(self):
self.rect.y -= self.speed
if self.rect.top < 0:
self.kill() # Remove the sprite from all groups.
def run_game():
pygame.init()
screen = pygame.display.set_mode((800, 700))
clock = pygame.time.Clock() # Use a clock to limit the frame rate.
defender = Spaceship(screen)
all_sprites = Group() # Changed the name because groups are not lists.
all_sprites.add(defender)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return
elif event.key == pygame.K_RIGHT:
defender.speed_x = defender.max_speed
elif event.key == pygame.K_LEFT:
defender.speed_x = -defender.max_speed
elif event.key == pygame.K_UP:
defender.speed_y = -defender.max_speed
elif event.key == pygame.K_DOWN:
defender.speed_y = defender.max_speed
elif event.key == pygame.K_SPACE:
new_bullet = Bullet(defender.rect.midtop) # Pass the pos.
all_sprites.add(new_bullet)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT and defender.speed_x > 0:
defender.speed_x = 0
elif event.key == pygame.K_LEFT and defender.speed_x < 0:
defender.speed_x = 0
elif event.key == pygame.K_UP and defender.speed_y < 0:
defender.speed_y = 0
elif event.key == pygame.K_DOWN and defender.speed_y > 0:
defender.speed_y = 0
all_sprites.update() # Calls the update methods of all sprites.
screen.fill(BLACK)
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60) # Limit the frame rate to 60 FPS.
run_game()