Related
this function is supposed to find if there is above a set number of points within the window, and if there is draw a rectangle around it and then subdivide by recursively calling the same function on all four quadrants of the previous rectangle. The behaviour seems almost right for the first 2 recursions and then seems to go a bit awry.. I also set a max depth so that it dosn't recurse too long. Im using pygame to draw the rectangles on the screen. Im assuming I have got muddled in my logic somewhere.
def recursion(x,x2,y,y2,max):
#draws a square
pygame.draw.rect(screen,(0,255,255),(x,y,x2,y2),1)
currentMax = 0
total_points = 0
if max >= 30:
return None
currentMax = max + 1
for i in range(len(xlist)):
if xlist[i] in range(x,x2) and ylist[i] in range(y,y2):
total_points += 1
if total_points > 3:
recursion(int(x),int(x2/2),int(y),int(y2/2),currentMax)#top_left
recursion(int(x2/2),int(x2),int(y),int(y2/2),currentMax)#top_right
recursion(int(x),int(x2/2),int(y2/2),int(y2),currentMax)#bottom_left
recursion(int(x2/2),int(x2),int(y2/2),int(y2),currentMax)#bottom_right
I also call it once to start the recursion with:
recursion(int(0),int(1000),int(0),int(1000),0)
The points are generated using:
for i in range(5):
xlist.append(random.randint(0,1000))
ylist.append(random.randint(0,1000))
When drawing a rectangle with pygame.draw.rect you have to specify the top, left point and the width and height instead of the bottom right point.
Furthermore, the computation of the center is wrong. A maximum depth of 30 (2^30) is far too much and you can use the // (floor division) operator.
Start with max >= 5 and total_points >= 1:
def recursion(x,x2,y,y2,depth):
if depth >= 5:
return
depth += 1
total_points = 0
for i in range(len(xlist)):
if x < xlist[i] <= x2 and y < ylist[i] <= y2:
total_points += 1
if total_points >= 1:
pygame.draw.rect(screen, (0,255,255), (x, y, x2-x, y2-y), 1)
centerx = (x+x2) // 2
centery = (y+y2) // 2
recursion(x, centerx, y, centery, depth)
recursion(centerx, x2, y, centery, depth)
recursion(x, centerx, centery, y2, depth)
recursion(centerx, x2, centery, y2, depth)
Minimal example:
import pygame, random
pygame.init()
screen = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
def recursion(x,x2,y,y2,depth):
if depth >= 5:
return
depth += 1
total_points = 0
for i in range(len(xlist)):
if x < xlist[i] <= x2 and y < ylist[i] <= y2:
total_points += 1
if total_points >= 1:
pygame.draw.rect(screen, (0,255,255), (x, y, x2-x, y2-y), 1)
centerx = (x+x2) // 2
centery = (y+y2) // 2
recursion(x, centerx, y, centery, depth)
recursion(centerx, x2, y, centery, depth)
recursion(x, centerx, centery, y2, depth)
recursion(centerx, x2, centery, y2, depth)
xlist = []
ylist = []
for i in range(5):
xlist.append(random.randrange(screen.get_width()))
ylist.append(random.randrange(screen.get_height()))
screen.fill(0)
recursion(0, screen.get_width(), 0, screen.get_height(), 0)
for p in zip(xlist, ylist):
pygame.draw.circle(screen, (255, 0, 0), p, 8)
pygame.display.flip()
#pygame.image.save(screen, "grid.png")
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
i have a problem with this code, i am a new person with programming and been using the book "how to think like a computer scientist 3rd edition" and he did not solve exercise 2 of chapter 17 this given: "he deliberately left a mistake in the code to animate Duke. If you click on one of the checkerboard squares to the right of Duke, he salutes anyway. Why? Find a one-line solution to the error ", I've tried many forms but I have not succeeded, I leave you all the code and the images that I have used
PS: images must have the name: ball.png and duke_spritesheet.png
import pygame
gravity = 0.025
my_clock = pygame.time.Clock()
class QueenSprite:
def __init__(self, img, target_posn):
self.image = img
self.target_posn = target_posn
(x, y) = target_posn
self.posn = (x, 0) # Start ball at top of its column
self.y_velocity = 0 # with zero initial velocity
def update(self):
self.y_velocity += gravity
(x, y) = self.posn
new_y_pos = y + self.y_velocity
(target_x, target_y) = self.target_posn # Unpack the position
dist_to_go = target_y - new_y_pos # How far to our floor?
if dist_to_go < 0: # Are we under floor?
self.y_velocity = -0.65 * self.y_velocity # Bounce
new_y_pos = target_y + dist_to_go # Move back above floor
self.posn = (x, new_y_pos) # Set our new position.
def draw(self, target_surface): # Same as before.
target_surface.blit(self.image, self.posn)
def contains_point(self, pt):
""" Return True if my sprite rectangle contains point pt """
(my_x, my_y) = self.posn
my_width = self.image.get_width()
my_height = self.image.get_height()
(x, y) = pt
return ( x >= my_x and x < my_x + my_width and
y >= my_y and y < my_y + my_height)
def handle_click(self):
self.y_velocity += -2 # Kick it up
class DukeSprite:
def __init__(self, img, target_posn):
self.image = img
self.posn = target_posn
self.anim_frame_count = 0
self.curr_patch_num = 0
def update(self):
if self.anim_frame_count > 0:
self.anim_frame_count = (self.anim_frame_count + 1 ) % 60
self.curr_patch_num = self.anim_frame_count // 6
def draw(self, target_surface):
patch_rect = (self.curr_patch_num * 50, 0,
50, self.image.get_width())
target_surface.blit(self.image, self.posn, patch_rect)
def contains_point(self, pt):
""" Return True if my sprite rectangle contains pt """
(my_x, my_y) = self.posn
my_width = self.image.get_width()
my_height = self.image.get_height()
(x, y) = pt
return ( x >= my_x and x < my_x + my_width and
y >= my_y and y < my_y + my_height)
def handle_click(self):
if self.anim_frame_count == 0:
self.anim_frame_count = 5
def draw_board(the_board):
""" Draw a chess board with queens, as determined by the the_board. """
pygame.init()
colors = [(255,0,0), (0,0,0)] # Set up colors [red, black]
n = len(the_board) # This is an NxN chess board.
surface_sz = 480 # Proposed physical surface size.
sq_sz = surface_sz // n # sq_sz is length of a square.
surface_sz = n * sq_sz # Adjust to exactly fit n squares.
# Create the surface of (width, height), and its window.
surface = pygame.display.set_mode((surface_sz, surface_sz))
ball = pygame.image.load("ball.png")
# Use an extra offset to centre the ball in its square.
# If the square is too small, offset becomes negative,
# but it will still be centered :-)
ball_offset = (sq_sz-ball.get_width()) // 2
all_sprites = [] # Keep a list of all sprites in the game
# Create a sprite object for each queen, and populate our list.
for (col, row) in enumerate(the_board):
a_queen = QueenSprite(ball,
(col*sq_sz+ball_offset, row*sq_sz+ball_offset))
all_sprites.append(a_queen)
# Load the sprite sheet
duke_sprite_sheet = pygame.image.load("duke_spritesheet.png")
# Instantiate two duke instances, put them on the chessboard
duke1 = DukeSprite(duke_sprite_sheet,(sq_sz*2, 0))
duke2 = DukeSprite(duke_sprite_sheet,(sq_sz*5, sq_sz))
# Add them to the list of sprites which our game loop manages
all_sprites.append(duke1)
all_sprites.append(duke2)
while True:
# Look for an event from keyboard, mouse, etc.
ev = pygame.event.poll()
if ev.type == pygame.QUIT:
break;
if ev.type == pygame.KEYDOWN:
key = ev.dict["key"]
if key == 27: # On Escape key ...
break # leave the game loop.
if key == ord("r"):
colors[0] = (255, 0, 0) # Change to red + black.
elif key == ord("g"):
colors[0] = (0, 255, 0) # Change to green + black.
elif key == ord("b"):
colors[0] = (0, 0, 255) # Change to blue + black.
if ev.type == pygame.MOUSEBUTTONDOWN: # Mouse gone down?
posn_of_click = ev.dict["pos"] # Get the coordinates.
for sprite in all_sprites:
if sprite.contains_point(posn_of_click):
sprite.handle_click()
break
for sprite in all_sprites:
sprite.update()
# Draw a fresh background (a blank chess board)
for row in range(n): # Draw each row of the board.
c_indx = row % 2 # Alternate starting color
for col in range(n): # Run through cols drawing squares
the_square = (col*sq_sz, row*sq_sz, sq_sz, sq_sz)
surface.fill(colors[c_indx], the_square)
# Now flip the color index for the next square
c_indx = (c_indx + 1) % 2
# Ask every sprite to draw itself.
for sprite in all_sprites:
sprite.draw(surface)
my_clock.tick(60) # Waste time so that frame rate becomes 60 fps
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
draw_board([0, 5, 3, 1, 6, 4, 2]) # 7 x 7 to test window size
PS: I think the error is here but it did not succeed
return ( x >= my_x and x + my_width and y >= my_y and y < my_y + my_height)
The issue is caused by the face, that "duke_spritesheet.png" is a sprite sheet. When you define the rectangular region which is covered by the object, then you have to use the width of a single image, rather than the width of the entire sprite sheet:
my_width = self.image.get_width()
my_width = 50
Change this in the method contains_point of the class DukeSprite:
class DukeSprite:
# [...]
def contains_point(self, pt):
""" Return True if my sprite rectangle contains pt """
(my_x, my_y) = self.posn
my_width = 50
my_height = self.image.get_height()
(x, y) = pt
return ( x >= my_x and x < my_x + my_width and
y >= my_y and y < my_y + my_height)
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 2 years ago.
I recently started making Atari Breakout in Pygame. I encountered a problem that I don't know how to fix. Whenever I run my code, and the ball comes in contact with the block, the block that's colliding with it, doesn't disappear as it should, but the last block from the list. (I made a list of 21 pygame.Surfaces and iterate over it and blit every single one). Obviously, if the ball touches the last block from the list, the correct one disappears. Please can you help me?
This is my code:
import pygame, random, math
pygame.init()
screen = pygame.display.set_mode((800,600))
pygame.display.set_caption('Atari Breakout')
player = pygame.image.load('player copy.png')
ball = pygame.image.load('poland.png')
blocks = [pygame.image.load('block.png') for i in range(21)]
x, y = 25,25
blockx, blocky = [], []
for i in range(21):
if i % 7 == 0:
y += 52
x = 25
blockx.append(x)
blocky.append(y)
x += (50 + 64)
ballx, bally = 400, 300
balldx, balldy = 4,4
score = 0
score_font = pygame.font.Font('freesansbold.ttf', 32)
def show_score():
score_text = score_font.render('Score : {}'.format(score), True, (255,255,255))
screen.blit(score_text, (0,0))
def isCollision(x1,y1,x2,y2):
ballRect = ball.get_rect(topleft = (x1, y1))
playerRect = player.get_rect(topleft = (x2, y2))
return ballRect.colliderect(playerRect)
def isCollision2(x1,y1,x2,y2):
ballRect = ball.get_rect(topleft = (x1,y1))
blockRect = blocks[i].get_rect(topleft = (x2,y2))
return ballRect.colliderect(blockRect)
def blit_blocks():
for i in range(len(blocks)):
screen.blit(blocks[i], (blockx[i], blocky[i]))
running = True
while running:
if score >= 21:
running = False
screen.fill((0,0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pos = pygame.mouse.get_pos()
for i, block in enumerate(blocks):
if isCollision2(ballx, bally, blockx[i], blocky[i]):
balldy *= -1
score += 1
blocks.pop(i)
if isCollision(ballx, bally, pos[0], 525):
balldy *= -1
if bally <= 0:
balldy *= -1
if bally >= 570:
running = False
if ballx <= 0:
balldx *= -1
if ballx >= 770:
balldx *= -1
ballx += balldx
bally += balldy
screen.blit(player, (pos[0], 525))
screen.blit(ball, (ballx,bally))
blit_blocks()
show_score()
pygame.display.update()
What you actually do ist manipulate a list (by pop) while you iterate it. I recommend to travers a copy of the list (blocks[:]) and to manipulate the original list. e.g:
i = 0
for x, y, block in zip(blockx[:], blocky[:], blocks[:]):
if isCollision2(ballx, bally, x, y):
blockx.pop(i)
blocky.pop(i)
blocks.pop(i)
balldy *= -1
score += 1
else:
i += 1
Anyway, I recommend to create a class Block:
class Block():
def __init__(self, image, x, y)
self.image = image
self.x = x
self.y = y
image = pygame.image.load('block.png')
x, y = 25,25
blocks = []
for i in range(21)
if i % 7 == 0:
y += 52
x = 25
blocks.append(Block(image, x, y))
x += (50 + 64)
i = 0
for block in block[:]:
if isCollision2(ballx, bally, block.x, block.y):
balldy *= -1
score += 1
block.pop(i)
else:
i += 1
it is a bad practice to pop from a list while you are iterating over this will cause unexpected behaviour, here:
for i, block in enumerate(blocks):
if isCollision2(ballx, bally, blockx[i], blocky[i]):
balldy *= -1
score += 1
blocks.pop(i)
you can use:
new_blocks = []
for i, block in enumerate(blocks):
if isCollision2(ballx, bally, blockx[i], blocky[i]):
balldy *= -1
score += 1
else:
new_blocks.append(block)
blocks = new_blocks
Below is the code I wrote.
The object moves along a circular path when I constantly calculate its position and give the coordinates to the obj.rect.x and object.rect.y.
What I need to know is how to rotate the object by something like below.
obj.rect.x += incrementx
obj.rect.y += incrementy
I implemented this in my code bu then the motion becomes anything but circluar.
Please help.
The two images used are here.
http://s5.postimg.org/fs4adqqib/crate_B.png
http://s5.postimg.org/vevjr44ab/plt0.png
import sys, os, pygame
from math import sin,cos,pi, radians
from pygame.locals import *
from standard_object_creator import *
SCREENW = 800
SCREENH = 700
BLUE = (0, 50, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
PURPLE = (145, 0, 100)
YELLOW = (220,220, 0)
pygame.init()
FPSCLOCK = pygame.time.Clock()
FONT1= "data\Cookie-Regular.ttf"
if sys.platform == 'win32' or sys.platform == 'win64':
#os.environ['SDL_VIDEO_CENTERED'] = '2'# center of screen
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (10,30)#top left corner
SCREEN = pygame.display.set_mode((SCREENW, SCREENH))
## self, imagelist, posx, posy, speedx = 0, speedy = 0, value = 0
plat = pygame.image.load("grfx\plt0.png").convert_alpha()
box = pygame.image.load("grfx\crateB.png").convert_alpha()
FPS = 160 # frames per second
platforms = pygame.sprite.Group()
boxes = pygame.sprite.Group()
def maketext(msg,fontsize, colour = YELLOW, font = FONT1):
mafont = pygame.font.Font(font, fontsize)
matext = mafont.render(msg, True, colour)
matext = matext.convert_alpha()
return matext
box = object_factory ([box], 340, 50, 0, 1)
boxes.add(box)
center_x = 450 # x pos in relation to screen width
center_y = 400 # y pos in relation to screen height
radius = 200
angle = -90 #pi / 4 # starting angle 45 degrees
omega = .001 #Angular velocity
for x in xrange(6):
xpos = radius * cos(angle) #+ center_x #Starting position x
ypos = radius * sin(angle) #+ center_x #Startinh position y
obj = object_factory([plat], xpos, ypos)
obj.angle = angle
obj.omega = omega #angula velocity
obj.radius = radius
platforms.add(obj)
angle += 60
mouseposlist = []
all2gether = [platforms, boxes]
while True:
SCREEN.fill(BLACK)
## MOVE THE SPRITE IN A CIRCLE. Each object is placed by varying the step)
for obj in platforms:
obj.angle = obj.angle + obj.omega
## THE CODE BELOW WORKS
obj.rect.x = center_x + (cos(obj.angle) * obj.radius)
obj.rect.y = center_y + (sin(obj.angle) * obj.radius)
## How can I get the same thing to work in this way? by adding the rate of change to the box objects rect.x and rec.t? Why does this not work?
#obj.rect.x += obj.radius * obj.omega * cos(obj.angle)
#obj.rect.y -= obj.radius * obj.omega * sin(obj.angle)
pygame.draw.line(SCREEN, BLUE, (center_x, center_y), (obj.rect.x, obj.rect.y), 2)
for hp in boxes:
hp.rect.x += hp.speedx
hp.rect.y += hp.speedy
hp.move()
hp.collide(platforms)
for thing in all2gether:
thing.update()
thing.draw(SCREEN)
pygame.draw.line(SCREEN, BLUE, (0, SCREENH / 2), (SCREENW, SCREENH / 2), 2)
pygame.draw.line(SCREEN, BLUE, (SCREENW / 2, 0), (SCREENW / 2, SCREENH), 2)
pygame.display.update()
FPSCLOCK.tick(FPS)
##--------------------------------------------------------------
pygame.event.pump()
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
if event.button == 1:
pos = pygame.mouse.get_pos()
val = [pos[0], pos[1], 0, 0]
print val
mouseposlist.append(val)
elif event.button == 3 and mouseposlist != []:
mouseposlist.pop(-1)
if event.type == KEYDOWN and event.key == K_ESCAPE:
print mouseposlist
pygame.quit()
sys.exit()
pygame.time.wait(0)
Your solution for moving the sprite in a circle is the time evaluation of the positional equation. You need to calculate the angle as a function of time. x = r * cos (omega * time). your first solution is a loop on time, incrementing omega by the fractional angle that is provided by the angular velocity. To evaluate a position take the amount of time multiplied by the angular velocity....
I manged to solve my problem and would like to share it. The new code is given below.
This works with Python / Pygame
center_of_rotation_x = SCREENW/2
center_of_rotation_y = SCREENH/2
radius = 200
angle = radians(45) #pi/4 # starting angle 45 degrees
omega = 0.1 #Angular velocity
x = center_of_rotation_x + radius * cos(angle) #Starting position x
y = center_of_rotation_y - radius * sin(angle) #Starting position y
SCREEN.blit(star, (x, y)) # Draw current x,y
angle = angle + omega # New angle, we add angular velocity
x = x + radius * omega * cos(angle + pi / 2) # New x
y = y - radius * omega * sin(angle + pi / 2) # New y
The above code works as it is. But when applied as a class it works differently. I will ask that in another question
So I am trying to move this random jumble of two polygons, a circle, and a line across the screen, any direction, and when it reaches the end of the screen is is placed back on the screen and moves again. Simply put, I want to move those shapes across the screen. I cannot really figure out how, I am new to pygame so all this is a bit confusing but this is what I have so far.
import pygame, sys, time, random
from pygame.locals import *
pygame.init()
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption("Paint")
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
windowSurface.fill(WHITE)
info = pygame.display.Info()
sw = info.current_w
sh = info.current_h
x = y = 0
dx = 5
dy = 2
while True:
pygame.draw.polygon(windowSurface,BLUE,((0+x,250+y),(120+x,120+y),(55+x,55+y)))
pygame.draw.polygon(windowSurface,RED,((0+x,150+y),(85+x,85+y),(100+x,175+y),(0+x,150+y)))
pygame.draw.line(windowSurface,BLACK,(60+x,85+y), (120+x, 110+x), 6)
pygame.draw.circle(windowSurface, GREEN , (75+x,100+y), 13, 0)
x += dx
y += dy
if x - dx < 0 or x + dx > sw:
dx = -dx
if y - dy < 0 or y + dy > sh:
dy = -dy
pygame.display.update()
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
You probably want to clear the screen each time you redraw.
while True:
windowSurface.fill(WHITE) #This clears the screen on each redraw
pygame.draw.polygon(windowSurface,BLUE,((0+x,250+y),(120+x,120+y),(55+x,55+y)))
pygame.draw.polygon(windowSurface,RED,((0+x,150+y),(85+x,85+y),(100+x,175+y),(0+x,150+y)))
pygame.draw.line(windowSurface,BLACK,(60+x,85+y), (120+x, 110+y), 6)
pygame.draw.circle(windowSurface, GREEN , (75+x,100+y), 13, 0)
Also, look at the coordinates for the line. I have changed the endpoint to
(120+x, 110+y)
And if you change your edge detection to this your shapes will mostly stay in the window
if x < 0 or x > sw-120:
dx = -dx
x += dx
if y < -85 or y > sh-175:
dy = -dy
y += dy