Problem with making Circle properly appear on screen in Pygame - python

I think my understanding of Pygame is a little bit weak. I would appreciate any help in general about the intricacies of the code (since this was given by the teacher) or simply how I can at least make the obstacle visible.
def draw(screen, background, boids, obstaclearray):
#redrawing the whole window
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
pygame.display.update(dirty)
Above is where I actually do the drawing and attempt to draw the circle.
The CircularObstacle class is a very simple class that looks like this:
import pygame
class CircularObstacle():
def __init__(self, x, y, radius): #MAYBE ADD A SIZE
self.x = x
self.y = y
self.radius = radius
The problem is that the circle only draws itself when the boids have went over it, which is really weird. I think it has to do with the way the pygame has been setup with and the Surfaces and everything, so below is all the code in main. Of course the obstacle does not work as intended, but I plan to fix that later, first I want to at least get a circle to show.
Below is my full code because I believe it is crucial to solving the issue:
import pygame
from pygame.locals import *
import argparse
import sys
from boid import Boid
from Obstacle import CircularObstacle
def add_boids(boids,num_boids):
for boid in range (num_boids):
boids.add(Boid())
def update(dt, boids):
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN:
mods = pygame.key.get_mods()
if event.key == pygame.K_q:
# quit
pygame.quit()
sys.exit(0)
elif event.key == pygame.K_UP:
# add boids
if mods & pygame.KMOD_SHIFT:
add_boids(boids, 100)
else:
add_boids(boids, 10)
elif event.key == pygame.K_DOWN:
# remove boids
if mods & pygame.KMOD_SHIFT:
boids.remove(boids.sprites()[:100])
else:
boids.remove(boids.sprites()[:10])
#ADD STUFF LIKE THE SLIDER AND STUFF
for b in boids:
b.update(dt, boids)
def draw(screen, background, boids, obstaclearray):
#redrawing the whole window
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
pygame.display.update(dirty)
default_boids = 0
default_geometry = "1000x1000"
# Initialise pygame.
pygame.init()
pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.KEYUP])
# keep a good framerate so the graphics are better
fps = 60.0
fpsClock = pygame.time.Clock()
# Set up pygamme window
window_width, window_height = 800,600
flags = DOUBLEBUF
screen = pygame.display.set_mode((window_width, window_height), flags)
screen.set_alpha(None)
background = pygame.Surface(screen.get_size()).convert()
background.fill(pygame.Color('black'))
boids = pygame.sprite.RenderUpdates()
add_boids(boids, default_boids)
obstaclearray = []
defaultcircleobstacle = CircularObstacle(200,200,13)
obstaclearray.append(defaultcircleobstacle)
#The "game loop"
dt = 1/fps # here dt means the amount of time elapsed since the last frame
#it seems like thie is a forever loop but in reality this is not since in the update method we provide functinality to quit the program
while True:
update(dt, boids)
draw(screen, background, boids, obstaclearray)
dt = fpsClock.tick(fps)

When you call pygame.display.update() you have 2 options. You can call it without any parameter. In this case the complete screen is updated.
pygame.display.update()
Or call it with a list of rectangular regions that need to be updated. In this case, only the rectangular areas will be updated.
pygame.display.update(rect_list)
You do the 2nd option, but the areas where the circles are drawn are not in the dirty list, therefore this regions are not updated.
pygame.display.update(dirty)
Either update the whole screen with pygame.display.update() or add the regions of the circles to the dirty list:
def draw(screen, background, boids, obstaclearray):
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
dirty_rect = pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
dirty.append(dirty_rect)
pygame.display.update(dirty)

Related

pygame.event.set_grab remains turned on after exception / crash and makes program "unkillable"

I am creating a game with pygame and I am using pygame.event.set_grab(True) to keep cursor locked in the game window (it keeps all keyboard input in the window also), but when the program crashes (either because of syntax error or some other exception) the set_grab stays turned on and it is impossible to turn off the program afterwards. (fortunately I am using Linux so i can access console that overrides everything so i can turn it off manually)
I am wondering if it is possible to make some error handling which will turn it off (or kills the program) or if there is a better way to keep just mouse inputs in the window. (so it is possible to alt+f4)
import pygame
pygame.init()
size = (600, 700)
monitor=pygame.display.Info()
screen = pygame.display.set_mode(size)#pygame.FULLSCREEN)
pygame.display.set_caption("Meteor Galaxy 3")
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
pygame.event.set_grab(True) #this is turned on with the initialization
#(doesnt have to be) of the game
When the game crashes it transforms into the usual black window with the exception you cant do anything.
Thank you.
Edit:
The full code:
#coding: utf-8
import pygame
import random
random.randint(0,2)
#TODO:
#Vymyslieť systém na čakanie framov
#Upraviť zmenu rýchlosti hráčovho náboja
##Pygame init
pygame.init()
size = (600, 700)
possible_sizes=[[600,700],[900,1050],[1200,1400],[1800,2100]] #ASI 1200,1400 obrázky a potom downscale?? (ak vôbec zmena rozlisenia..)
monitor=pygame.display.Info()
screen = pygame.display.set_mode(size)#pygame.FULLSCREEN)
pygame.display.set_caption("Meteor Galaxy 3")
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
pygame.event.set_grab(True)
#<VARIABLES>
##<COLORS>
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
##</COLORS>
##<IMAGES> #"../meteo_data/img/"
bg1=[pygame.image.load("../meteo_data/img/bg1.png"),700-5770,True]
#backgrounds.append([pygame.image.load("img/bg2.png"),[600,1924]])
#backgrounds.append([pygame.image.load("img/bg3.png"),[600,1924]])
img_crosshair=pygame.image.load("../meteo_data/img/crosshair.png").convert()
#Ships
img_player=pygame.image.load("../meteo_data/img/ships/player.png").convert()
img_enemy1=pygame.image.load("../meteo_data/img/ships/enemy1.png").convert()
#Bullets
img_b1=pygame.image.load("../meteo_data/img/bullets/bullet.png").convert()
img_player.set_colorkey(BLACK)
img_enemy1.set_colorkey(BLACK)
img_crosshair.set_colorkey(BLACK)
##</IMAGES>
##<SOUNDS>
##</SOUNDS>
menu_game=1 #Nula pre menu , jedna pre hru?? , medzi nula a jedna ostatné??
esc_menu=False
fire=False
level=-1
level_ended=True
## def=0 def=0
##<BULLET TYPES> #[IMAGE,DAMAGE,SPEED,PENETRATION,relX,relY] /relX a relY vziať z relX a relY lode.
B_default=[img_b1,3,4,False,0,0]
##</BULLET TYPES>
##<SHIP TYPES> #[IMAGE,HEALTH,SPEED,RELOAD,X,Y,relX,relY,BULLET_TYPE] /relX a relY je pre bullet
##</SHIP TYPES>
##<LEVELS>
level1=[bg1]
master_level=[level1]
##</LEVELS>
#</VARIABLES>
#<FUNCTIONS>
##<SPRITES>
class bullet(pygame.sprite.Sprite):
def __init__(self,bullet_type):
pygame.sprite.Sprite.__init__(self)
self.image=bullet_type[0]
self.dmg=bullet_type[1]
self.speed=bullet_type[2]
self.penetration=bullet_type[3] ##Prestrelí viac ENIMÁKOV ? True/False
self.rect = self.image.get_rect()
self.rect.x=bullet_type[4] ##Vypočítať pri vystrelení (ship pos -/+ ship.bullet_x(y)) (pre každý typ lode zvlášť)
self.rect.y=bullet_type[5]
self.image.set_colorkey(BLACK)
class ship(pygame.sprite.Sprite):
def __init__(self,ship_type):
pygame.sprite.Sprite.__init__(self)
self.image=ship_type[0]
self.hp=ship_type[1]
self.speed=ship_type[2] ##0 Pre hráča
self.reload=ship_type[3] ##Rýchlosť streľby (koľko framov čakať) 0 = každý frame bullet
self.rect=self.image.get_rect()
self.rect.x=ship_type[4]
self.rect.y=ship_type[5]
self.bullet_x=ship_type[6]
self.bullet_y=ship_type[7]
self.b_type=ship_type[8]
self.image.set_colorkey(BLACK)
class barrier(pygame.sprite.Sprite):
def __init__(self,coord):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.Surface([700,40])
self.image.fill(WHITE)
self.rect=self.image.get_rect()
self.rect.x=coord[0]
self.rect.y=coord[1]
bullets=pygame.sprite.Group()
ships=pygame.sprite.Group()
barriers=pygame.sprite.Group()
player_b_type=B_default
player_b_type[2]=player_b_type[2]*(-1)
player=ship([img_player,100,0,10,279,650,15,3,player_b_type]) ##PLAYER SHIP
wait=player.reload
barrier_top=barrier([-50,-400])
barrier_bottom=barrier([-50,900])
barriers.add(barrier_top)
barriers.add(barrier_bottom)
##</SPRITES>
#</FUNCTIONS>
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button==1:
fire=True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button==1:
fire=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_ESCAPE:
if not esc_menu:
esc_menu=True
pygame.event.set_grab(False)
else:
esc_menu=False
pygame.event.set_grab(True)
coord=pygame.mouse.get_pos()
if menu_game==0:
screen.fill(WHITE) #BG
elif menu_game==1:
#GAME LOGIC
if level_ended:
level=level+1
bg1_y=master_level[level][0][1]
level_ended=False
bg1_y=bg1_y+2
player.rect.x=coord[0]-20
pygame.sprite.groupcollide(barriers,bullets,False,True)
pygame.sprite.groupcollide(barriers,ships,False,True)
if fire:
if wait==0:
bullet_modified=player.b_type
bullet_modified[4]=player.rect.x+player.bullet_x
bullet_modified[5]=player.rect.y+player.bullet_y
b=bullet(bullet_modified)
bullets.add(b)
wait=player.reload
else:
wait=wait-1
#RENDERING
screen.fill(BLACK)
screen.blit(master_level[level][0][0],[0,bg1_y]) #BG
screen.blit(player.image,player.rect)
for naboj in bullets:
screen.blit(naboj.image,naboj.rect)
naboj.rect.y=naboj.rect.y+naboj.speed
screen.blit(img_crosshair,[coord[0]-10,coord[1]-10])
pygame.display.flip() #FRAMY
clock.tick(60)
pygame.quit()
#NOTES:
# Dlzka lvl sa urci vyskou bg (5760 px == 48 sec - 1. lvl)
#189 -
#
So, the problem there is that if some exception happens in the middle of that code, pygame.quit() is never called.
All you have to do is to set a try ... fynaly block around your Pygame code, and put the pygame.quit() call on the finally block.
For that I suggest some reformatting which will also improve the modularity of your code, which is to enclose all that code you put on the module level inside a function.
So:
...
def main():
done = False
while not done:
...
for naboj in bullets:
...
screen.blit(img_crosshair,[coord[0]-10,coord[1]-10])
pygame.display.flip() #FRAMY
clock.tick(60)
try:
main()
finally:
pygame.quit()
In this way, any unhandled exception within the code run in main (or in any other function called from it), as well as well behaved main-loop termination, will immediately run the code within the finally block: Pygame is shut down, along with its event handling, and you get the error traceback on the terminal enabling you to fix the code.
update the finally hint is also essential for anyone making a game with pygame that uses fullscreen, regardless of event_grab.

How to stop drawing a specific rectangle pygame

My program is a 'Piano Hero' game in pygame which works in the same way as guitar hero except that it is for a computer keyboard and its based on playing the piano rather than the guitar. I am using a design similar to Synthesia for my interface where rectangles come down to a 'hitline' and you have to press the key at the right time.
My problem is that although the rectangles are drawing and working as intended at first, they do not seem to update so that the top ever stops. In other words, every note in the song is infinitely long.
I feel like this is probably where the error is although I am not 100% sure.
def Draw(self,hitLine):
if self.coords[2][1]<hitLine:
self.coords[0][1]+=2
self.coords[1][1]+=2
self.coords[2][1]+=2
self.coords[3][1]+=2
elif self.coords[2][1]>=hitLine and self.coords[0][1]<hitLine:
self.coords[0][1]+=2
self.coords[1][1]+=2
else:
self.drawing = False
pygame.draw.polygon(screen,BLUE,self.coords,0)
pygame.display.update()
This line is inside a while loop which just updates all of the rectangles in the song one at a time.
for z in notes:
if z.drawing:
z.Draw(hitLine)
I found you're question quite fun to work on and is very interesting!
Some items to consider.
It doesn't seem that there is any reason to use a "pygame polygon" for your Note objects which are clearly rectangles. In my code below I used "pygame Rect" objects.
You're main loop doesn't clear the screen every frame.
In your main loop you need to clear the screen every frame. In my code I used Rect objects. The Note stops drawing itself when it's top hits the hitLine.
import pygame
pygame.init()
gameScreen = pygame.display.set_mode((1100, 692))
hitLine = 500
class Note:
def __init__(self, rect):
self.rect = rect
self.drawing = True
def draw(self):
if self.rect.y < hitLine:
self.rect.y += 2
else:
self.drawing = False;
pygame.draw.rect(gameScreen, (0, 0, 255), self.rect, 0)
fNote = Note(pygame.Rect(500, -550, 80, 550))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameScreen.fill((0, 0, 0))
if fNote.drawing:
fNote.draw()
pygame.display.update()

Can't click on image again, what's wrong with my pygame code?

Okay, I'am trying to create a Tom and Jerry game with the pygame library.
The game focuses on catching mice by clicking on them as they appear in their holes. The problem
is that sometimes a cat appears instead of a mouse and should the player erroneously click on the
cat (s)he looses all earned points, but the game continues.
The mouse is an image of a mouse and the cat is an image of an cat.
If you click on the mouse, you get mouse, otherwise the cat gets the points.
The code is a mess, that's because I don't know what I'am doing and just set an another event loop because then it works, because it runs after I create the mouse. It works to click on the mouse but then you click somewhere else and after that it's like you did not clicked on the mouse.
The mouse is created in a loop and is supposed to wait for 5 seconds and if you click on the mouse within these seconds then an appropriate message prints out in the console ,,Jerry clicked!" else "1 click". If you don't click on the mouse within 5 seconds a image covers the mouse so she disappears.
Now, what I'am trying to do right now is to print the message 1 click when the player does not click on anything but print 1 click jerry clicked when the player clicks on the mouse. I have a image of the mousehole and then I put the mouse on the mousehole, that is, on an another image.
This code works with one image at least:
pygame.init()
width=350;
height=400
screen = pygame.display.set_mode( (width, height ) )
pygame.display.set_caption('clicked on image')
redSquare = pygame.image.load("images/red-square.png").convert()
x = 20; # x coordnate of image
y = 30; # y coordinate of image
screen.blit(redSquare , ( x,y)) # paint to screen
pygame.display.flip() # paint screen one time
running = True
while (running):
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
# Set the x, y postions of the mouse click
x, y = event.pos
if redSquare.get_rect().collidepoint(x, y):
print('clicked on image')
#loop over, quite pygame
pygame.quit()
My problem is that, when I click on the mouse and then I don't click on the mouse I can't click on the mouse again at another position.
So what's wrong? What I'am doing wrong here?
Here is my code:
import pygame
from pygame import *
from random import *
init()
run = True
screen = (800,800)
screen = display.set_mode(screen)
xpos = 0
ypos = 0
mouseorcatxpos = 5
mouseorcatypos = 0
mousehole = image.load("mousehole.png").convert()
cat = image.load("tom.png")
jerry = image.load("jerry.png")
def makeholes():
global ypos
global xpos
for holey in range(1,9):
for holex in range(1,9):
screen.blit(mousehole,(xpos,ypos))
display.flip()
xpos += 100
ypos += 100
xpos = 0
def mouseorcat():
global xpos
mouseorcatxpos = 5
ypos = 0
for mousecaty in range(1,9):
pygame.event.pump()
for mousecatx in range(1,9):
randommouse = randint(1, 3)
randomcat = randint(1, 10)
if(randommouse == 2):
screen.blit(jerry, (mouseorcatxpos, ypos))
display.flip()
for event in pygame.event.get():
if (event.type == MOUSEBUTTONDOWN):
if jerry.get_rect().collidepoint(xpos, ypos) == False:
print("l clicked!")
x, y = event.pos
if jerry.get_rect().collidepoint(xpos, y):
print("JERRY CLICKED!!")
x, y = event.pos
print(x, y)
time.wait(5000)
#screen.blit(mousehole, (mouseorcatxpos - 5, ypos))
display.flip()
elif(randomcat == 2):
screen.blit(cat, (mouseorcatxpos, ypos))
display.flip()
time.wait(1500)
screen.blit(mousehole, (mouseorcatxpos-5, ypos))
display.flip()
mouseorcatxpos += 100
mouseorcatxpos = 0
ypos += 100
makeholes()
while run == True:
for event in pygame.event.get():
mouseorcat()
if event.type == QUIT:
run = False
I rewrote your game to show you how I would do it.
To keep track of the time and to limit the framerate I used a pygame.time.Clock and a timer variable. The clock returns the time in milliseconds since clock.tick was called the last time, which is used to increase the timer variable. The cat just replaces the mouse after two seconds and the mouse is set to a new position. I use pygame.Rects to store the positions, but you could also use lists or tuples.
import sys
import random
import pygame
pygame.init()
size = (800, 800)
screen = pygame.display.set_mode(size)
# Images replaced by pygame.Surface. Do that too
# in the future before you post your code.
mousehole = pygame.Surface((40, 40)).convert()
mousehole.fill(pygame.Color(30, 30, 30))
cat = pygame.Surface((40, 40)).convert()
cat.fill(pygame.Color(110, 110, 130))
jerry = pygame.Surface((40, 40)).convert()
jerry.fill(pygame.Color(190, 130, 0))
# Create the background image and blit the holes.
background = pygame.Surface(size).convert()
for holey in range(8):
for holex in range(8):
background.blit(mousehole, (holex*100, holey*100))
def new_position():
"""Return a random position between 0-700 in steps of 100."""
return (random.randrange(0, 701, 100), random.randrange(0, 701, 100))
def main():
fps = 30
clock = pygame.time.Clock()
jerry_rect = jerry.get_rect() # Stores jerry's position and size.
jerry_rect.topleft = new_position() # New random position.
# The cat is outside of the screen first.
cat_rect = cat.get_rect(topleft=(-100, -100))
points = 0
timer = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if jerry_rect.collidepoint(event.pos):
points += 1
print('Jerry caught! Points:', points)
timer = 0
jerry_rect.topleft = new_position()
else:
print('Missed. Points:', points)
# Run logic.
timer += clock.tick(fps) / 1000 # timer + seconds since last tick.
if timer > 2: # Cat catches mouse after 2 seconds.
cat_rect.topleft = jerry_rect.topleft
jerry_rect.topleft = new_position()
timer = 0
points = 0
print('Tom caught Jerry.')
# Draw.
# Clear the screen by blitting the bg.
screen.blit(background, (0, 0))
screen.blit(jerry, jerry_rect)
screen.blit(cat, cat_rect)
pygame.display.flip()
if __name__ == '__main__':
main()
pygame.quit()
sys.exit()
Side notes:
Don't use star imports (from module import *), because that can make code harder to read. If you want you can use from pygame.locals import *, if it's the only star import.
Don't use global variables, because they can make code harder to read, understand and maintain. Pass variables to functions as arguments and then return the result.
Update: Some notes about your program:
The first big problem is that your game has two event loops and the important one is deeply nested inside of two other for loops and a if. The event loop should be directly under the main while loop (one indentation level (when you have more experience you can put it into a function or class method)).
The two for loops seem to have the purpose to let the code run until randommouse or randomcat are 2. To run code until a condition is met is the purpose of a while loop. But in this case you should better just pick a random number and write the if/elif conditions so that they always apply. For example, you want a 2/3 chance for mouse and 1/3 for a cat,
random_number = random.randint(1, 3)
if random_number < 3:
print("2/3 probability. It's a mouse")
else:
print("1/3 probability. It's a cat")
Or use random.choice with a list:
>>> random.choice(['mouse', 'mouse', 'cat'])
'mouse'
time.wait(5000) shouldn't be used because the game just hangs in this time. You can't even close the window. Limit the framerate and get the time since the last tick with a pygame.time.Clock.
pygame.event.pump() is not needed.
If you call get_rect() without an argument, the rect is positioned at (0, 0).
if jerry.get_rect().collidepoint(xpos, y):
That's the reason why clicking on jerry only works in the top row, and because you use the global xpos here. Since xpos is 0, the whole top row counts as Jerry.
You can pass coordinates to get_rect like so (you can also use center or other args instead of topleft):
jerry_rect = jerry.get_rect(topleft=(50, 100))
I'm sorry but I don't think I can simply fix your code. I've tried it several times, but I always end up re-writing it completely.
I begin by extracting the event loop out of the two nested for loops, then remove these loops, create rects for the mouse and cat, fix the collision detection, add a timer and so on. Take a close look at my example and try to rewrite your game in a similar way, and keep asking questions if you don't understand something.

My Python program crashes when I press the exit button. It works fine otherwise. I am using Pygame module. The code is attached below

I am reading data from a file. Basically, those are coordinates where I want my ball to appear after every iteration. The code is working fine except for the fact that the output window 'Trial 1' crashes as soon as I press the exit button. This problem wasn't there before I added for t in range (np.size(T)):; however I require that. Please suggest some possible changes in the code to get rid of the problem.
import numpy as np
import pygame
pygame.init()
T = np.loadtxt('xy_shm1.txt', usecols=range(0,1))
Xcor = np.loadtxt('xy_shm1.txt', usecols=range(1,2))
Ycor = np.loadtxt('xy_shm1.txt', usecols=range(2,3))
clock = pygame.time.Clock()
background_colour = (255,255,255)
(width, height) = (800, 800)
class Particle():
def __init__(self, xy, size):
self.x, self.y = xy
self.size = size
self.colour = (0, 0, 255)
self.thickness = 1
def display(self):
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
def move(self):
self.x = Xcor[t] + 400
self.y = Ycor[t] + 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Trial 1')
number_of_particles = 1
my_particles = []
for n in range(number_of_particles):
size = 5
x = Xcor[0] + 400
y = Ycor[0] + 400
particle = Particle((x, y), size)
my_particles.append(particle)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for t in range(np.size(T)):
screen.fill(background_colour)
for particle in my_particles:
particle.move()
particle.display()
pygame.display.flip()
clock.tick(60)
pygame.quit()
The main problem is that you are trying to draw multiple frames within a frame. The frame loop should look like this:
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Draw one frame here
clock.tick(60) # If the game runs faster than 60fps, wait here
Note that in each iteration of the while loop, only one frame is drawn.
In your current code however, you start the loop, check the events once,
and then you draw a frame for each item in your list, without checking the events again.
This most likely causes the QUIT event to be missed, and the operating system intervening because the game seemingly is not responding.
In general, your code is quite messy. I suggest that you read some tutorials on pygame, or you will run into all sorts of similar problems. See for example: http://programarcadegames.com/python_examples/f.php?file=bouncing_rectangle.py

Why am I getting this error: "Invalid rectstyle argument"

I am currently making a game in python where a ball hits a group of boxes. I have loaded an image of a grass that I wanted the boxes to stand on, the grass image is 600 by 151 pixels. So far I have all the boxes and the ball appearing perfectly however, What is not working currently is when I try to load the image and display it so that the boxes can stand on the grass image instead of falling off.
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk as pm
from pymunk import Vec2d
import sys
from random import randint
def to_pygame(p):
"""Small hack to convert pymunk to pygame coordinates"""
return int(p[0]), int(-p[1]+600)
def add_box(space, size, pos, mass=1.0):
# pos is a Vec2d object
points = [(-size, -size), (-size, size), (size,size), (size, -size)]
moment = pm.moment_for_poly(int(mass), points, (0,0))
body = pm.Body(mass, moment)
body.position = pos
shape = pm.Poly(body, points, (0,0))
shape.friction = 1
space.add(body,shape)
return shape
def draw_box(screen, box):
ps = box.get_points()
ps.append(ps[0])
ps = map(to_pygame, ps)
pygame.draw.polygon(screen, THECOLORS["blue"], ps, 3)
def main():
pygame.init()
screen = pygame.display.set_mode((600, 600))
pygame.display.set_caption("Piling boxes")
clock = pygame.time.Clock()
boxes = []
x = 170
y = 120
space = pm.Space()
space.gravity = (0.0, -100.0)
### ground
body = pm.Body()
shape = pm.Segment(body, (150,100), (450,100), .0)
shape.friction = 4.4
space.add(shape)
for i in range(5):
# what is the mass of the box?
mass = randint(1,3)
# what size is the box?
size = randint(10, 35)
# calculate its position & hold it in a Vec2d
#x = x + size + 10
y = y + size
pos = Vec2d(x,y)
box = add_box(space, size, pos, mass)
boxes.append(box)
while 1:
clock.tick(15)
space.step(1/30.0)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit(0)
screen.fill(THECOLORS["white"])
test_image = pygame.image.load("grass.png")
screen.blit(test_image, to_pygame((450,100)), to_pygame((150,100)),3)
for i in range(len(boxes)):
draw_box(screen, boxes[i])
pygame.display.flip()
if __name__ == '__main__':
main()
No traceback or error log so it is a bit hard to identify the point of the error, an educated guess would be that you are passing Rectstyle arguments incorrectly somewhere. Please see the documentation for pygame.Rect on how to pass Rectstyle args correctly.
pygame.Rect(left, top, width, height): return Rect
pygame.Rect((left, top), (width, height)): return Rect
pygame.Rect(object): return Rect
The problem is
screen.blit(test_image, to_pygame((450,100)), to_pygame((150,100)),3)
To blit a surface to to screen, a valid blit is:
screen.blit(test_image, (450,100))
So you want:
screen.blit(test_image, to_pygame((450,100)))
Note: The extra args make me think maybe you're trying to use a source rect argument, to use a tileset. But in that case you wouldn't convert world to screen coords twice. Just on destination, not source.
If you do want the spritesheet/tileset, then out How do you select a sprite image from a sprite sheet in python? and Pygame.org/cookbook/SpriteSheet
The third argument in blit is the source area argument, should be a rectangle, not coordinates:
screen.blit(test_image, to_pygame((450,100)), to_pygame((150,100)),3)
Perhaps you want this:
screen.blit(test_image, to_pygame((450,100)), pygame.Rect((0,0), (150,100)), 3)
The problem is because your call to screen.blit is incorrect. See here.
The call could be
screen.blit(test_image, to_pygame((450,100)))
or perhaps if you wanted to use the area argument,
screen.blit(test_image, to_pygame((450,100)), pygame.Rect((0,0), (150,100)), 3)
Also, you are loading guess.png in every loop, instead move it out of the while loop, your code will perform better.

Categories