Debugging simple Ping-Pong game? - python

I've been programming a simple ping-pong game using Python's Pygame as run through Livewires. I've posted the question and a similar sample game code that runs perfectly as not everyone here will be versed in livewires and/or pygame.
Here's the code that has bugs on it. What happens is the window opens, and the "Ball" object works well and falls of the screen (as it should, it's incomplete), and the "Slab" object is gets stuck to wherever the mouse initially is. Here it is:
from livewires import games, color
games.init (screen_width = 640, screen_height = 480, fps = 50)
class Ball (games.Sprite):
def iMove (self):
self.dx = -self.dx
self.dy = -self.dy
class Slab (games.Sprite):
def mouse_moves (self):
self.x = games.mouse.x
self.y = games.mouse.y
self.iCollide()
def iCollide (self):
for Ball in overlapping_sprites:
Ball.iMove()
def main():
#Backgrounds
pingpongbackground = games.load_image ("pingpongbackground.jpg", transparent = False)
games.screen.background = pingpongbackground
#Ball: Initializing object and setting speed.
ballimage = games.load_image ("pingpongball.jpg")
theball = Ball (image = ballimage,
x = 320,
y = 240,
dx = 2,
dy = 2)
games.screen.add(theball)
#Paddle: Initializing ping pong object and setting initial poisition to the initial mouse position
slabimage = games.load_image ("pingpongpaddle.jpg")
theslab = Slab (image = slabimage,
x = games.mouse.x,
y = games.mouse.y)
games.screen.add(theslab)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main ()
And here's a piece of similar, functioning code:
# Slippery Pizza Program
# Demonstrates testing for sprite collisions
from livewires import games
import random
games.init(screen_width = 640, screen_height = 480, fps = 50)
class Pan(games.Sprite):
"""" A pan controlled by the mouse. """
def update(self):
""" Move to mouse position. """
self.x = games.mouse.x
self.y = games.mouse.y
self.check_collide()
def check_collide(self):
""" Check for collision with pizza. """
for pizza in self.overlapping_sprites:
pizza.handle_collide()
class Pizza(games.Sprite):
"""" A slippery pizza. """
def handle_collide(self):
""" Move to a random screen location. """
self.x = random.randrange(games.screen.width)
self.y = random.randrange(games.screen.height)
def main():
wall_image = games.load_image("wall.jpg", transparent = False)
games.screen.background = wall_image
pizza_image = games.load_image("pizza.bmp")
pizza_x = random.randrange(games.screen.width)
pizza_y = random.randrange(games.screen.height)
the_pizza = Pizza(image = pizza_image, x = pizza_x, y = pizza_y)
games.screen.add(the_pizza)
pan_image = games.load_image("pan.bmp")
the_pan = Pan(image = pan_image,
x = games.mouse.x,
y = games.mouse.y)
games.screen.add(the_pan)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
# kick it off!
main()
Any insight at all would be greatly appreciated!

I don't know your framework, but to keep the slab from "getting stuck" you need to update its location when the mouse is moved.
Here you initialize it:
theslab = Slab (image = slabimage,
x = games.mouse.x,
y = games.mouse.y)
Then here you add it to the game:
games.screen.add(theslab)
Presumably, the game would then call this function whenever the mouse moves:
def mouse_moves (self):
self.x = games.mouse.x
self.y = games.mouse.y
self.iCollide()
But that is either not happening, or the screen is not getting updated.
So you should find out, by doing this:
def mouse_moves (self):
print "mouse_moves: ", str(games.mouse.x), str(games.mouse.y)
self.x = games.mouse.x
self.y = games.mouse.y
self.iCollide()
If you see the output of the print statement happening when the mouse moves, you're probably not updating the screen, you'll need to check the framework docs. But I don't think that's the case. I think you are not updating the game when the mouse moves. I would imagine the framework has some sort of onMouseMove type event that you need to hook into, to allow you to update the game state (i.e. call mouse_moves()) when mouse movement occurs. Then, the next time the screen is updated, you should check for changes (invalidate the objects, mark them as dirty) and if they are dirty, update their part of the screen then mark them clean again.

Just put this update method in the slab class:
def update(self):
self.x=games.mouse.x
self.y=games.mouse.y
It will automatically run once every fiftieth of a second (your update speed). Currently, you just start the slab at the position of the mouse, and leave it there.

Related

Why all balloons are hidden and not only the one I click?

I'm creating a simple game with Python using turtle package.
My aim is to have some balloons on the screen running from right to left and then when one of them is clicked to make it disappear.
What I have going wrong is that when I click one balloon, all of them are disappeared!
Here is my code
1- Main
from turtle import Screen
from balloon import Balloon
import time
screen = Screen()
screen.title('Balloons Nightmare')
screen.setup(width=600, height=600)
screen.bgpic(picname='sky-clouds.gif')
screen.tracer(0)
balloons_manager = Balloon()
current_x = 0
current_y = 0
screen.listen()
screen.onclick(fun=balloons_manager.explode_balloon, btn=1)
game_is_on = True
while game_is_on:
time.sleep(0.1)
screen.update()
balloons_manager.create_balloon()
balloons_manager.move_balloon()
screen.exitonclick()
2- balloon module
import random
from turtle import Turtle
COLORS = ["red", "yellow", "green", "blue", "black"]
MOVEMENT_SPEED = 2
class Balloon:
def __init__(self):
self.all_balloons = []
self.balloon_speed = MOVEMENT_SPEED
self.x = 0
self.y = 0
self.hidden = None
def create_balloon(self):
random_choice = random.randint(1, 9)
if random_choice == 1:
new_balloon = Turtle("circle")
new_balloon.penup()
new_balloon.turtlesize(stretch_wid=2, stretch_len=0.75)
new_balloon.color(random.choice(COLORS))
random_y_cor = random.randint(-50, 280)
new_balloon.goto(320, random_y_cor)
self.hidden = new_balloon.isvisible()
self.all_balloons.append(new_balloon)
def move_balloon(self):
for balloon in self.all_balloons:
balloon.backward(self.balloon_speed)
def explode_balloon(self, x, y):
for balloon in range(len(self.all_balloons)):
print(self.all_balloons[balloon].position())
self.all_balloons[balloon].hideturtle()
# for balloon in self.all_balloons:
# balloon.hideturtle()
I tried so many changes but nothing helped me, example of what I tried so far
getting the current x,y coordinates of the balloon so that on click to hide only the one with these coordinates but didn't work for me or I did something wrong with it
Any hints will be appreciated.
Thank you
I fixed the issue by adding an inner function in create_balloon function
def create_balloon(self):
random_choice = random.randint(1, 9)
if random_choice == 1:
new_balloon = Turtle("circle")
new_balloon.penup()
new_balloon.turtlesize(stretch_wid=2, stretch_len=0.75)
new_balloon.color(random.choice(COLORS))
random_y_cor = random.randint(-50, 280)
new_balloon.goto(320, random_y_cor)
def hide_the_balloon(x, y):
return new_balloon.hideturtle()
new_balloon.onclick(hide_the_balloon)
self.all_balloons.append(new_balloon)
Here's your code triggered by the click handler:
def explode_balloon(self, x, y):
for balloon in range(len(self.all_balloons)):
print(self.all_balloons[balloon].position())
self.all_balloons[balloon].hideturtle()
This loops over all balloons and hides them unconditionally.
You probably want to use an if in there to only conditionally trigger the hiding behavior. Compare the x and y coordinates of the click against the current balloon in the loop. Only hide the balloon if the distance is less than a certain amount (say, the radius of the balloon).
Another approach is to use turtle.onclick to add a handler function that will be trigged when the turtle is clicked.
Related:
How to see if a mouse click is on a turtle in python
Python find closest turtle via mouse click
Why is my Python Turtle program slowing down drastically the longer it runs? (since you're never removing turtles and constantly adding new ones, this is a good thread to take a look at)

Problems with a game

I am a young Python programmer. I decided to create a 2D game using pygame Purpose of the game: Drive as much distance as possible on the car, without crashing into objects that will "spawn" during the game. The car will drive across the field.
I have problems with decoration sprites (during the game, trees will "fall" down on the edges of the window) Fig1.pic1
So, trees should spawn right after the previous ones reach the middle of the window, but when new trees spawn, this is what happens to me: Fig2. And the game starts to freeze pic2
Here is my code:
from superwires import games, color
from random import randrange
games.init(screen_width = 530, screen_height = 600, fps = 60)
#Car sprite
class Car(games.Sprite):
image = games.load_image("C:/python/car.bmp")
def __init__(self):
super(Car, self).__init__(image = Car.image,
x = games.mouse.x,
bottom = games.screen.height - 10)
self.score = games.Text(value = 0, size = 25, color = color.yellow,
top = 5, right = games.screen.width/2)
games.screen.add(self.score)
def update(self):
self.x = games.mouse.x
if self.left < 65:
self.left = 65
if self.right > games.screen.width - 65:
self.right = games.screen.width - 65
#Tree sprite
class Bush1(games.Sprite):
image = games.load_image("C:/python/bush.bmp")
speed = 1
def __init__(self, x = 20, y = 100):
super(Bush1, self).__init__(image = Bush1.image,
x = x, y = y,
dy = Bush1.speed)
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush1()
newbush.__init__(x = 20, y = -100)
games.screen.add(newbush)
class Bush2(games.Sprite):
image = games.load_image("C:/python/bush.bmp")
speed = 1
def __init__(self, x = 515, y = 100):
super(Bush2, self).__init__(image = Bush2.image,
x = x, y = y,
dy = Bush2.speed)
#Spawning new trees
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush2()
newbush.__init__(x = 515, y = -100)
games.screen.add(newbush)
#Start
def main():
road = games.load_image("road.jpg", transparent = False)
games.screen.background = road
bush1 = Bush1()
bush2 = Bush2()
car = Car()
games.screen.add(bush1)
games.screen.add(bush2)
games.screen.add(car)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main()
I'll be glad to know where I made a mistake.
Used: Python 3.9, superwires, games
Here's your problem. Once your first bush reaches the halfway point, you create two new bushes ON EVERY FRAME. That's what you're seeing in your pic2 -- you have hundreds of slightly overlapping bushes. You need to create a new bush only when the old one is EXACTLY at the halfway point, not AT OR BELOW the halfway point:
if self.bottom == games.screen.height//2:
You might also consider deleting bushes once they fall off the bottom
This may not address the specific problem you're running across, but it might help to unpack and zero in on what is happening.
Here you are creating a new Bush1 and Bush2 object:
bush1 = Bush1()
bush2 = Bush2()
Note that the code for these two classes are virtually identical. The differences are only in the init defaults. It is equivalent to doing this:
bush1 = Bush1()
bush2 = Bush1(515, 100)
It seems you have a misunderstanding, based on this, about how classes in Python work. This is reinforced by the two almost-equivelent-but-for-constants update blocks:
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush1()
newbush.__init__(x = 20, y = -100)
games.screen.add(newbush)
On the third line of these blocks you're creating a new object. Then you're re-calling that objects __init__ method. When you create a new object, a couple of hidden 'private' methods are run, the last of which is the __init__ method. Typically if you need instantiation of an object to change, you would alter that method. You should never need to re-call that method.
Finally, you are not, so far as I can tell, actually moving the bushes. I suspect when you say 'starts to freeze' what is happening is you have many instantiations of the bushes overlapping one another, and the game is taking longer and longer to process each loop. Putting a print statement in each init method would probably help identify this problem, but it's possible your frameworks might have a better way to do that. (I am not familiar with them.)
Refactoring a bit to clean up the usage of classes and scoping variables properly should yield something along the lines of this (though almost certainly there is more we could do):
from superwires import games, color
from random import randrange
games.init(screen_width = 530, screen_height = 600, fps = 60)
#Car sprite
class Car(games.Sprite):
def __init__(self, image: str):
self.__image = games.load_image(image)
# Is the car really instantiated at the point the mouse currently is at?
super().__init__(image = self.__image,
x = games.mouse.x,
bottom = games.screen.height - 10)
# Is 'score' really a field of 'car'?
self.score = games.Text(value = 0,
size = 25,
color = color.yellow,
top = 5,
right = games.screen.width/2)
games.screen.add(self.score)
def update(self):
self.x = games.mouse.x
self.left = 65 if self.left < 65 else self.left
# This should probably be defined in a broader scope
max_right = games.screen.width - 65
self.right = self.right if self.right <= max_right else max_right
class Bush(games.Sprite):
speed = 1
def __init__(self,
image: str
x: int,
y: int):
self.__image = games.load_image(image)
self.__start_x = x
self.__start_y = y
super().__init__(image = self.__image,
x = x,
y = y,
dy = Bush.speed)
def update(self):
if self.bottom > games.screen.height/2:
# uses the same initial values as this object
newbush = Bush(self.__image,
self.__start_x,
-100) # start vertically above the screen
games.screen.add(newbush)
#Start
def main():
road = games.load_image("road.jpg", transparent = False)
games.screen.background = road
bush1 = Bush("C:/python/bush.bmp", 20, 100)
bush2 = Bush("C:/python/bush.bmp", 515, 100)
car = Car("C:/python/car.bmp")
games.screen.add(bush1)
games.screen.add(bush2)
games.screen.add(car)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main()

Pygame pixel perfect collision not working as expected

I am creating tetris using pygame. i want to use collision detection so that when the shape in play comes into contact with any other previously played shapes, i can stop the shape, as per the logic of tetris. i came across pixel perfect collision using masks. i have followed some tutorials online, however the pixel detection returns true every time a new shape comes into play, not when any shapes collide. sorry in advance for the long code, its the bare minimum for the code to actually and still containing the game element of it. i think there is something wrong with my approach which is causing this error. I basically have a function that everytime the shape in play comes into contact with the 'floor' that shape is held in that position and a new shape is created. i think ive overcomplicated it, in turn creating this error. thanks in advance
import pygame
import sys
import shapelogic
pygame.init()
screensize = width, height = 800, 595
screen = pygame.display.set_mode(screensize)
background_image =pygame.image.load("/Users/marceason/PycharmProjects/Tetris/Wooden_background.jpg").convert_alpha()
myshape = 0
stop_movement = 0
blit_count = 0
stored_shapes = pygame.sprite.Group()
stored_shapes_with_coords = []
extra_blit_required = False
index = 0
count = 0
listofshapes = []
class shapemanager():
def __init__(self):
self.listofshapes = []
def create_another_instance(self):
global count
count += 1
string = "Shape_{0},".format(count)
another_shape = Shape(string)
self.listofshapes.append(another_shape)
global index
object = self.listofshapes[index]
index += 1
return object
def load_shape(self):
shape = self.create_another_instance()
shape.load_shapes()
class Shape(pygame.sprite.Sprite):
def __init__(self, name):
pygame.sprite.Sprite.__init__(self)
self.name = name
self.x = 50
self.y = 100
self.move_event = pygame.USEREVENT + 1
self.reached_bottom_event = pygame.USEREVENT + 2
self.one_sec_timer = 1000
self.half_sec_timer = 500
self.reachbottomflag = False
self.movement_possible = True
self.image = pygame.image.load(
"/Users/marceason/PycharmProjects/Tetris/Tetris_Shapes/Green_Shape_1_Position_1.png")
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
def move_shape(self):
if self.movement_possible:
key_input = pygame.key.get_pressed()
if key_input[pygame.K_LEFT]:
self.x -= 16
if key_input[pygame.K_RIGHT]:
self.x += 16
if not self.reachbottomflag:
if key_input[pygame.K_DOWN]:
self.y += 16
def reachbottom(self):
if self.y >= 560:
self.reachbottomflag = True
def no_movement_possible(self):
self.movement_possible = False
def assign_shape():
global myshape
global stop_movement
myshape = sl.create_another_instance()
pygame.time.set_timer(myshape.move_event, myshape.one_sec_timer)
stop_movement = pygame.time.set_timer(myshape.reached_bottom_event, myshape.half_sec_timer)
def blit_used_shapes():
global screen
global blit_count
blit_count = len(stored_shapes_with_coords)
local_count = 0
while local_count < blit_count:
screen.blit(stored_shapes_with_coords[local_count][0], (stored_shapes_with_coords[local_count][1], stored_shapes_with_coords[local_count][2]))
local_count += 1
sl = shapemanager()
##### HERE IS THE PIXEL DETECTION #####
result = pygame.sprite.spritecollide(myshape, stored_shapes, False, pygame.sprite.collide_mask)
## Main loop ##
assign_shape()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
screen.blit(background_image, (0, 0))
screen.blit(myshape.image, (myshape.x, myshape.y))
myshape.move_shape()
key_input = pygame.key.get_pressed()
if key_input[pygame.K_SPACE]:
myshape.rotate_shape()
myshape.reachbottom()
if myshape.reachbottomflag:
if event.type == myshape.reached_bottom_event:
myshape.no_movement_possible()
stored_shape_tuple = [myshape.image, myshape.x, myshape.y]
stored_shapes_with_coords.append(stored_shape_tuple)
stored_shapes.add(myshape)
extra_blit_required = True
assign_shape()
####### PIXEL DETECTION IS HERE IN FOR LOOP ####
if result:
print("this should only execute when two shapes touch!!")
if extra_blit_required:
blit_used_shapes()
pygame.display.update()
The issue is that you are not updating the sprites rect attribute. The sprites rects all have position (0, 0) (since you do not set it in the call to self.image.get_rect()) and as a result the masks will all overlap and collide.
If you read the docs for pygame.sprite.collide_mask you will note that it says that your sprites need to have mask and rect attributes. You have a rect in your sprite and you set it in the __init__(), but you do not keep it updated when you move the sprite. You just change the x and y attributes without adjusting the rect position. The reason that the collide_mask wants a rect is that it uses that to determine the offset parameter for the pygame.mask.Mask.overlap() call that it uses. The important thing to realize is that masks themselves do not have a position, they need the rects to determine the relative positions of the masks.
This is similar to images/surfaces not having a position and needing a rect to track that for them.
On a separate issue, the way you are blit'ing the sprites to the screen makes no sense. You are not using the abilities of the sprite groups to draw and worse you are keeping the image, x and y of the sprite in a separate list and not containing it in the sprite itself. You should go look at some examples of pygame sprite based code. There are lots of examples out there.

Understand object orientated python - initialization of objects in pygame

I'm learning Object Orientated Python and understand the main principals of classes and creating objects from classes however I need something explained Re: the pygame code below. I'm struggling to get my head around what's happening when sprite lists are being created and the two lines of code under the code which creates the ball object (allsprites.add etc). In other words what are sprites and why are lists of them created? Why isn't the ball object just created from the class on its own? Why does it need to be added to a sprite list?? What's going on? Any explanation would be greatly appreciated.
"""
Sample Breakout Game
Sample Python/Pygame Programs
Simpson College Computer Science
http://programarcadegames.com/
http://simpson.edu/computer-science/
"""
# --- Import libraries used for this program
import math
import pygame
# Define some colors
black = (0, 0, 0)
white = (255, 255, 255)
blue = (0, 0, 255)
# Size of break-out blocks
block_width = 23
block_height = 15
class Block(pygame.sprite.Sprite):
"""This class represents each block that will get knocked out by the ball
It derives from the "Sprite" class in Pygame """
def __init__(self, color, x, y):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create the image of the block of appropriate size
# The width and height are sent as a list for the first parameter.
self.image = pygame.Surface([block_width, block_height])
# Fill the image with the appropriate color
self.image.fill(color)
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# Move the top left of the rectangle to x,y.
# This is where our block will appear..
self.rect.x = x
self.rect.y = y
class Ball(pygame.sprite.Sprite):
""" This class represents the ball
It derives from the "Sprite" class in Pygame """
# Speed in pixels per cycle
speed = 10.0
# Floating point representation of where the ball is
x = 0.0
y = 180.0
# Direction of ball (in degrees)
direction = 200
width = 10
height = 10
# Constructor. Pass in the color of the block, and its x and y position
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create the image of the ball
self.image = pygame.Surface([self.width, self.height])
# Color the ball
self.image.fill(white)
# Get a rectangle object that shows where our image is
self.rect = self.image.get_rect()
# Get attributes for the height/width of the screen
self.screenheight = pygame.display.get_surface().get_height()
self.screenwidth = pygame.display.get_surface().get_width()
def bounce(self, diff):
""" This function will bounce the ball
off a horizontal surface (not a vertical one) """
self.direction = (180 - self.direction) % 360
self.direction -= diff
def update(self):
""" Update the position of the ball. """
# Sine and Cosine work in degrees, so we have to convert them
direction_radians = math.radians(self.direction)
# Change the position (x and y) according to the speed and direction
self.x += self.speed * math.sin(direction_radians)
self.y -= self.speed * math.cos(direction_radians)
# Move the image to where our x and y are
self.rect.x = self.x
self.rect.y = self.y
# Do we bounce off the top of the screen?
if self.y <= 0:
self.bounce(0)
self.y = 1
# Do we bounce off the left of the screen?
if self.x <= 0:
self.direction = (360 - self.direction) % 360
self.x = 1
# Do we bounce of the right side of the screen?
if self.x > self.screenwidth - self.width:
self.direction = (360 - self.direction) % 360
self.x = self.screenwidth - self.width - 1
# Did we fall off the bottom edge of the screen?
if self.y > 600:
return True
else:
return False
class Player(pygame.sprite.Sprite):
""" This class represents the bar at the bottom that the player controls. """
def __init__(self):
""" Constructor for Player. """
# Call the parent's constructor
pygame.sprite.Sprite.__init__(self)
self.width = 75
self.height = 15
self.image = pygame.Surface([self.width, self.height])
self.image.fill((white))
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.screenheight = pygame.display.get_surface().get_height()
self.screenwidth = pygame.display.get_surface().get_width()
self.rect.x = 0
self.rect.y = self.screenheight-self.height
def update(self):
""" Update the player position. """
# Get where the mouse is
pos = pygame.mouse.get_pos()
# Set the left side of the player bar to the mouse position
self.rect.x = pos[0]
# Make sure we don't push the player paddle
# off the right side of the screen
if self.rect.x > self.screenwidth - self.width:
self.rect.x = self.screenwidth - self.width
# Call this function so the Pygame library can initialize itself
pygame.init()
# Create an 800x600 sized screen
screen = pygame.display.set_mode([800, 600])
# Set the title of the window
pygame.display.set_caption('Breakout')
# Enable this to make the mouse disappear when over our window
pygame.mouse.set_visible(0)
# This is a font we use to draw text on the screen (size 36)
font = pygame.font.Font(None, 36)
# Create a surface we can draw on
background = pygame.Surface(screen.get_size())
# Create sprite lists
blocks = pygame.sprite.Group()
balls = pygame.sprite.Group()
allsprites = pygame.sprite.Group()
# Create the player paddle object
player = Player()
allsprites.add(player)
# Create the ball
ball = Ball()
allsprites.add(ball)
balls.add(ball)
# The top of the block (y position)
top = 80
# Number of blocks to create
blockcount = 32
# --- Create blocks
# Five rows of blocks
for row in range(5):
# 32 columns of blocks
for column in range(0, blockcount):
# Create a block (color,x,y)
block = Block(blue, column * (block_width + 2) + 1, top)
blocks.add(block)
allsprites.add(block)
# Move the top of the next row down
top += block_height + 2
# Clock to limit speed
clock = pygame.time.Clock()
# Is the game over?
game_over = False
# Exit the program?
exit_program = False
# Main program loop
while exit_program != True:
# Limit to 30 fps
clock.tick(30)
# Clear the screen
screen.fill(black)
# Process the events in the game
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_program = True
# Update the ball and player position as long
# as the game is not over.
if not game_over:
# Update the player and ball positions
player.update()
game_over = ball.update()
# If we are done, print game over
if game_over:
text = font.render("Game Over", True, white)
textpos = text.get_rect(centerx=background.get_width()/2)
textpos.top = 300
screen.blit(text, textpos)
# See if the ball hits the player paddle
if pygame.sprite.spritecollide(player, balls, False):
# The 'diff' lets you try to bounce the ball left or right
# depending where on the paddle you hit it
diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2)
# Set the ball's y position in case
# we hit the ball on the edge of the paddle
ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1
ball.bounce(diff)
# Check for collisions between the ball and the blocks
deadblocks = pygame.sprite.spritecollide(ball, blocks, True)
# If we actually hit a block, bounce the ball
if len(deadblocks) > 0:
ball.bounce(0)
# Game ends if all the blocks are gone
if len(blocks) == 0:
game_over = True
# Draw Everything
allsprites.draw(screen)
# Flip the screen and show what we've drawn
pygame.display.flip()
pygame.quit()
You don't need to add the balls and blocks to sprite lists - it's just a matter of convenience. You could manually check each ball for a collision, but it's easier to just tell pygame to check them all for you
# See if the ball hits the player paddle
if pygame.sprite.spritecollide(player, balls, False):
# The 'diff' lets you try to bounce the ball left or right
# depending where on the paddle you hit it
diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2)
# Set the ball's y position in case
# we hit the ball on the edge of the paddle
ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1
ball.bounce(diff)
You could draw each thing to the screen separately on each frame, but it's easier just to tell pygame to do it for you:
# Draw Everything
allsprites.draw(screen)
Things can be in more than one list as required, for example a ball is added to the balls list so that you can easily check for collisions, but also added to the allsprites list so that you can easily draw everything on the screen
# Create the ball
ball = Ball()
allsprites.add(ball)
balls.add(ball)
Edit:
An important distinction is that allsprites is actually a sprite.Group. It has a list of sprites inside it, but it also has other methods like draw.
To address your question of "what is a Sprite", it's simply a thing that gets drawn on screen. pygame methods like sprite.Group.draw expect a list of things with certain attributes - such as update. The easiest way to make sure that you provide all of those attributes with the right names is to subclass Sprite, however this is also a (strongly recommended) convenience thing - for instance, this is from the pygame source code:
While it is possible to design sprite and group classes that don't
derive from the Sprite and AbstractGroup classes below, it is strongly
recommended that you extend those when you add a Sprite or Group
class.
So what specifically does subclassing Sprite get you? Let's take a look at the source. Here's how to find the source code for a python module:
>>> import pygame.sprite
>>> pygame.sprite.__file__
'c:\\Python27\\lib\\site-packages\\pygame\\sprite.py'
>>>
Every python module has a __file__ attribute that tells you where the source is located (well not quite every). If you open it up in your editor, and scroll down, you see the class definition for Sprite:
class Sprite(object):
"""simple base class for visible game objects
pygame.sprite.Sprite(*groups): return Sprite
The base class for visible game objects. Derived classes will want to
override the Sprite.update() and assign a Sprite.image and
Sprite.rect attributes. The initializer can accept any number of
Group instances to be added to.
When subclassing the Sprite, be sure to call the base initializer before
adding the Sprite to Groups.
"""
def __init__(self, *groups):
self.__g = {} # The groups the sprite is in
if groups: self.add(groups)
def add(self, *groups):
"""add the sprite to groups
Sprite.add(*groups): return None
Any number of Group instances can be passed as arguments. The
Sprite will be added to the Groups it is not already a member of.
"""
has = self.__g.__contains__
for group in groups:
if hasattr(group, '_spritegroup'):
if not has(group):
group.add_internal(self)
self.add_internal(group)
else: self.add(*group)
def remove(self, *groups):
"""remove the sprite from groups
Sprite.remove(*groups): return None
Any number of Group instances can be passed as arguments. The Sprite will
be removed from the Groups it is currently a member of.
"""
has = self.__g.__contains__
for group in groups:
if hasattr(group, '_spritegroup'):
if has(group):
group.remove_internal(self)
self.remove_internal(group)
else: self.remove(*group)
def add_internal(self, group):
self.__g[group] = 0
def remove_internal(self, group):
del self.__g[group]
def update(self, *args):
"""method to control sprite behavior
Sprite.update(*args):
The default implementation of this method does nothing; it's just a
convenient "hook" that you can override. This method is called by
Group.update() with whatever arguments you give it.
There is no need to use this method if not using the convenience
method by the same name in the Group class.
"""
pass
def kill(self):
"""remove the Sprite from all Groups
Sprite.kill(): return None
The Sprite is removed from all the Groups that contain it. This won't
change anything about the state of the Sprite. It is possible to continue
to use the Sprite after this method has been called, including adding it
to Groups.
"""
for c in self.__g.keys():
c.remove_internal(self)
self.__g.clear()
def groups(self):
"""list of Groups that contain this Sprite
Sprite.groups(): return group_list
Return a list of all the Groups that contain this Sprite.
"""
return self.__g.keys()
def alive(self):
"""does the sprite belong to any groups
Sprite.alive(): return bool
Returns True when the Sprite belongs to one or more Groups.
"""
return (len(self.__g) != 0)
def __repr__(self):
return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g))
So in summary, you don't have to subclass Sprite - you could just provide all of these methods on your own - but it's easier if you do ;)

How to speed up python's 'turtle' function and stop it freezing at the end

I have written a turtle program in python, but there are two problems.
It goes way too slow for larger numbers, I was wonder how I can speed up turtle.
It freezes after it finishes and when clicked on, says 'not responding'
This is my code so far:
import turtle
#Takes user input to decide how many squares are needed
f=int(input("How many squares do you want?"))
c=int(input("What colour would you like? red = 1, blue = 2 and green =3"))
n=int(input("What background colour would you like? red = 1, blue = 2 and green =3"))
i=1
x=65
#Draws the desired number of squares.
while i < f:
i=i+1
x=x*1.05
print ("minimise this window ASAP")
if c==1:
turtle.pencolor("red")
elif c==2:
turtle.pencolor("blue")
elif c==3:
turtle.pencolor("green")
else:
turtle.pencolor("black")
if n==1:
turtle.fillcolor("red")
elif n==2:
turtle.fillcolor("blue")
elif n==3:
turtle.fillcolor("green")
else:
turtle.fillcolor("white")
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.up()
turtle.rt(9)
turtle.down()
By the way: I am on version 3.2!
Set turtle.speed("fastest").
Use the turtle.mainloop() functionality to do work without screen refreshes.
Disable screen refreshing with turtle.tracer(0, 0) then at the end do turtle.update()
Python turtle goes very slowly because screen refreshes are performed after every modification is made to a turtle.
You can disable screen refreshing until all the work is done, then paint the screen, it will eliminate the millisecond delays as the screen furiously tries to update the screen from every turtle change.
For example:
import turtle
import random
import time
screen = turtle.Screen()
turtlepower = []
turtle.tracer(0, 0)
for i in range(1000):
t = turtle.Turtle()
t.goto(random.random()*500, random.random()*1000)
turtlepower.append(t)
for i in range(1000):
turtle.stamp()
turtle.update()
time.sleep(3)
This code makes a thousand turtles at random locations, and displays the picture in about 200 milliseconds.
Had you not disabled screen refreshing with turtle.tracer(0, 0) command, it would have taken several minutes as it tries to refresh the screen 3000 times.
https://docs.python.org/2/library/turtle.html#turtle.delay
For reference, turtle being slow is an existing problem.
Even with speed set to max, turtle can take quite a long time on things like fractals.
Nick ODell reimplemented turtle for speed here: Hide Turtle Window?
import math
class UndrawnTurtle():
def __init__(self):
self.x, self.y, self.angle = 0.0, 0.0, 0.0
self.pointsVisited = []
self._visit()
def position(self):
return self.x, self.y
def xcor(self):
return self.x
def ycor(self):
return self.y
def forward(self, distance):
angle_radians = math.radians(self.angle)
self.x += math.cos(angle_radians) * distance
self.y += math.sin(angle_radians) * distance
self._visit()
def backward(self, distance):
self.forward(-distance)
def right(self, angle):
self.angle -= angle
def left(self, angle):
self.angle += angle
def setpos(self, x, y = None):
"""Can be passed either a tuple or two numbers."""
if y == None:
self.x = x[0]
self.y = x[1]
else:
self.x = x
self.y = y
self._visit()
def _visit(self):
"""Add point to the list of points gone to by the turtle."""
self.pointsVisited.append(self.position())
# Now for some aliases. Everything that's implemented in this class
# should be aliased the same way as the actual api.
fd = forward
bk = backward
back = backward
rt = right
lt = left
setposition = setpos
goto = setpos
pos = position
ut = UndrawnTurtle()

Categories

Resources