Python Crash Course 2nd Edition - Creating an Alien [duplicate] - python

I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)

You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()

Related

Images layering over old image in Python Pygame [duplicate]

I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)
You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()

Sprites not displaying properly [duplicate]

I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)
You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()

Sprites won't show up

Cannot figure out why my code won't show sprites when I run it. The code is from a how-to book I found at my library, I have reread the code multiple times and haven't found anything different than what the book says to do. This is my first time coding on python and I haven't seen anything to help me solve my problem yet. This is all the code I've written for the game so far.
import pygame
from pygame import *
from random import randint
pygame.init()
WINDOW_WIDTH = 1100
WINDOW_HEIGHT = 600
WINDOW_RES = (WINDOW_WIDTH, WINDOW_HEIGHT)
WIDTH = 100
HEIGHT = 100
WHITE = (255, 255, 255)
SPAWN_RATE = 360
GAME_WINDOW = display.set_mode(WINDOW_RES)
display.set_caption('Attack of the vampire Pizzas!')
pizza_img = image.load('vampire.png')
pizza_surf = Surface.convert_alpha(pizza_img)
VAMPIRE_PIZZA = transform.scale(pizza_surf, (WIDTH, HEIGHT))
background_img = image.load('restaurant.jpg')
background_surf = Surface.convert_alpha(background_img)
BACKGROUND = transform.scale(background_surf, WINDOW_RES)
class VampireSprite(sprite.Sprite):
def __init__(self):
super().__init__()
self.speed = 2
self.lane = randint(0, 4)
all_vampires.add(self)
self.image = VAMPIRE_PIZZA.copy()
y = 50 + self.lane * 100
self.rect = self.image.get_rect(center = (1100, y))
def update(self, game_window):
game_window.blit(self.image, (self.rect.x, self.rect.y))
all_vampires = sprite.Group()
tile_color = WHITE
for row in range(6):
for column in range(11):
draw.rect(BACKGROUND, tile_color, (WIDTH * column, HEIGHT * row, WIDTH, HEIGHT), 1)
GAME_WINDOW.blit(BACKGROUND, (0,0))
----------------------------------------------
#Start Main Game Loop
game_running = True
#Game Loop
while game_running:
for event in pygame.event.get():
#Exit loop on quit
if event.type == QUIT:
game_running = False
if randint(1, SPAWN_RATE) == 1:
VampireSprite()
for vampire in all_vampires:
vampire.update(GAME_WINDOW)
display.update()
pygame.quit()
The Code seems to have all the correct components, except that it's a bit mixed up.
Generally when you make a sprite, it has the __init__() function - obviously for initialisation, and additionally an update() function. The update() function usually does not draw the object to the display/surface, but adjusts the position (i.e.: the sprite.rect) or changes the "look" (image) used for the sprite, for drawing later.
Sprites are usually grouped into an aptly-named Sprite Group. Once sprites are in a group, a single call to group.update() will call the update() function on every sprite contained. It's really convenient, and works well.
So tweaking your Vampire Pizza Sprite:
class VampireSprite(sprite.Sprite):
def __init__(self):
super().__init__()
self.speed = 2
self.lane = randint(0, 4)
self.image = VAMPIRE_PIZZA.copy()
y = 50 + self.lane * 100
self.rect = self.image.get_rect(center = (1100, y))
def update(self):
# TODO - do Vampire Pizzas Move?
# if so, update the self.rect
pass
And that's all that's needed. I removed the painting code, this will be handled by a sprite group.
So later on:
# Make a group of vampire sprites
all_vampires = sprite.Group()
all_vampires.add( VampireSprite() ) # Start with a single vampire
# Game Loop
game_running = True
while game_running:
# handle events
for event in pygame.event.get():
#Exit loop on quit
if event.type == QUIT:
game_running = False
# spawn some vampires, maybe
if randint(1, SPAWN_RATE) == 1:
all_vampires.add( VampireSprite() ) # add to the group
# Update/move all vampire sprites
all_vampires.update() # updates every sprite in group
# repaint the screen
GAME_WINDOW.blit(BACKGROUND, (0,0))
# draw some columns(?)
tile_color = WHITE
for row in range(6):
for column in range(11):
draw.rect(GAME_WINDOW, tile_color, (WIDTH * column, HEIGHT * row, WIDTH, HEIGHT), 1)
all_vampires.draw() # paints every sprite in group
display.update()
pygame.quit()
There's two calls from the all_vampires sprite group - all_vampires.update() and all_vampires.draw()? With just these two calls, all sprites in the group are moved (adjusted/whatever), and painted to the screen.

Enemies won't display

I'm trying to create a top down shooter, but my enemies won't spawn in. I'm trying to have the enemy move in a diagonal pattern but also be able to bounce off of the wall kind of like a top down bouncing ball. I'm also trying to have a new enemy spawn every 10 seconds, but when I run the program nothing shows up, there is no error either. Can someone please help.
import pygame
pygame.init()
import random
import time
inPlay = True
width=900
height=700
screen = pygame.display.set_mode((width, height))
#enemy
enemyFrequency=10
enemyPause=enemyFrequency
killEnemy=True
#------------------------------#
# classes #
#------------------------------#
######################################################
class Enemy(pygame.sprite.Group):
def __init__(self,ballX,ballY,ballSpeed,picture2=None):
pygame.sprite.Group.__init__(self)
self.ballX=ballX
self.ballY=ballY
self.ballSpeed=ballSpeed
self.image2=pygame.image.load(picture2)
def move(self):
for enemys in self:
self.ballX+=self.ballSpeed
self.ballY+=self.ballSpeed
def decay(self):
for enemys in self:
if enemys.y > height:
self.remove(enemys)
#------------------------------#
# functions #
#------------------------------#
def redraw_game_window():
screen.fill((30, 30, 30))
enemys.draw(screen)
pygame.display.update()
#------------------------------#
# main program #
#------------------------------#
RB=width-player.rect.width
CEILING = 3*player.rect.height # for ship movement
FLOOR = height - player.rect.height #
#enemy
enemies=pygame.sprite.Group()
enemySpeed=3
eX=random.randint(0,RB)
eY=random.randint(-height,0)
enemys=Enemy (eX,eY,enemySpeed,'asteroid.png')
t0 = pygame.time.clock()
dt = 0
enemyCount = 0
clock = pygame.time.Clock()
while inPlay:
redraw_game_window()
for event in pygame.event.get():
if event.type == pygame.QUIT:
inPlay=False
#enemies spawning
if dt < nSeconds:
t1 = time.process_time()
dt = t1 - t0
enemies.add(enemys)
else:
enemyInstance = Enemy()
enemyCount += 1
t0 = time.clock()
dt = 0
enemys.move()
enemys.decay()
clock.tick(30)
pygame.quit()
I hacked your code to the point where it seemed to be doing what the code describes, and what matches your question. There were a lot of things missing in this code, maybe you cut them out before posting to SO to make the code shorter.
The crux of the problem is the handling of the enemy sprites. I don't know if it was by design, but the original code was defining what seemed like a sprite, but based on a sprite group.
So I modified the code based on the idea that enemies should be a global sprite group, and went from there "fixing" things. There was no Player class defined, I added one. I could not follow the timing code, it was using pygame.time.clock() (which is an object) as a time value. It seemed like this code was keeping a delta, and counting time to respawn a new enemy, I had to re-write this bit to get it to work - my apologies.
The other comment I want to make - sprites can define an update() function. This function should handle the position changes and appearance of the sprite. PyGame sprite groups will handle the calling of this function automatically (if it's defined) in the sprite group update() meta-call. This gives the program a clean and simple way to handle sprite animation.
import pygame
pygame.init()
import random
import time
inPlay = True
width=900
height=700
screen = pygame.display.set_mode((width, height))
#enemy
enemies = pygame.sprite.Group() # Holds all enemy sprites
enemyFrequency = 1000 # milliseconds between enemy spawn
killEnemy = True
enemyCount = 0
#------------------------------#
# classes #
#------------------------------#
######################################################
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('player.png').convert_alpha()
self.rect = self.image.get_rect()
self.rect.center = (100,100) # TODO - position properly
def update(self):
# TODO
pass
class Enemy(pygame.sprite.Sprite):
def __init__(self, ballX, ballY, ballSpeed, picture2=None):
pygame.sprite.Sprite.__init__(self)
self.ballSpeed = ballSpeed
self.image = pygame.image.load(picture2).convert_alpha()
self.rect = self.image.get_rect()
self.rect.center = ( ballX, ballY )
def update(self):
global enemies # group of all enemy sprites (to which this sprite belongs)
global height # window height
self.rect.x += self.ballSpeed
self.rect.y += self.ballSpeed
# decay the enemy
if ( self.rect.y > height ):
enemies.remove(self) # went off screen, delete it
#------------------------------#
# functions #
#------------------------------#
def redraw_game_window():
screen.fill((30, 30, 30))
enemies.draw(screen)
pygame.display.update()
#------------------------------#
# main program #
#------------------------------#
player = Player()
RB=width-player.rect.width
CEILING = 3*player.rect.height # for ship movement
FLOOR = height - player.rect.height #
# start with 3 enemies
for i in range( 3 ):
enemySpeed = 3
eX=random.randint(0,RB)
eY=random.randint(-height,0)
enemies.add( Enemy(eX,eY,enemySpeed,'asteroid.png') )
clock = pygame.time.Clock()
last_enemy_spawn_time = 0
while inPlay:
time_now = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
inPlay=False
# is it time to spawn a new enemy?
if ( time_now - last_enemy_spawn_time > enemyFrequency ):
last_enemy_spawn_time = time_now # reset timer
#enemies spawning
eX=random.randint(0,RB)
eY=random.randint(-height,0)
enemies.add( Enemy(eX,eY,enemySpeed,'asteroid.png') )
enemyCount += 1
enemies.update() # call the update() of every sprite
redraw_game_window()
#enemies.decay() -- MOVED INTO Enemy.update()
clock.tick(30)
pygame.quit()

About releasing memory when using pygame and Python

For example, a projectile flies off screen, does the program still compute its location, speed, etc.?
If so, how to release it?
import pygame
from pygame.locals import *
from sys import exit
pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()
sprite = pygame.image.load(sprite_image_filename)
x = 0.
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.blit(background, (0,0))
screen.blit(sprite, (x, 100))
x+= 10.
pygame.display.update()
Yes, the location, speed, etc. still have to be computed, otherwise no object that is off-screen could ever enter the screen area again. Pygame is smart enough not to attempt to render these objects.
It's usually advisable to use pygame sprites and sprite groups which allow you to remove sprites simply by calling self.kill(). You could also use lists or sets to store your objects, but then you have to write a bit more code yourself.
So I'd first define a pygame.Rect (the game_area) with the size of your screen or a bit larger (in the example below I use a smaller one). Rects have a contains method that you can use to check if your sprite's rect is inside the game_area rect. If the sprite is outside, just call self.kill() and pygame will remove the sprite from all associated sprite groups.
import random
import pygame as pg
from pygame.math import Vector2
class Projectile(pg.sprite.Sprite):
def __init__(self, pos, game_area):
super().__init__()
self.image = pg.Surface((5, 5))
self.image.fill(pg.Color('aquamarine2'))
self.rect = self.image.get_rect(center=pos)
self.vel = Vector2(2, 0).rotate(random.randrange(360))
self.pos = Vector2(pos)
self.game_area = game_area
def update(self):
self.pos += self.vel
self.rect.center = self.pos
if not self.game_area.contains(self.rect):
self.kill()
def main():
screen = pg.display.set_mode((640, 480))
game_area = pg.Rect(60, 60, 520, 360)
game_area_color = pg.Color('aquamarine2')
clock = pg.time.Clock()
all_sprites = pg.sprite.Group(Projectile(game_area.center, game_area))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
all_sprites.add(Projectile(game_area.center, game_area))
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.draw.rect(screen, game_area_color, game_area, 2)
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Yes it does, but since you have only a single projectile (incremented using x), you can easily choose what to do using a few if statements. The process becomes harder when there are multiple projectiles (which you need to store in a container), you should apply this.
Here is an example
for projectile in projectile_list:
# Check if the position is inside the screen
if 0 < projectile.x < WIDTH and 0 < projectile.y < HEIGHT:
# Do position operations
This way, you only process what is required. You can apply similar method to remove unused projectiles from the list or whatever container you are using.

Categories