Pygame collision with rectangles in list not working right - python

I'm making python pygame labyrinth game.
Moving works with moving walls, but not player because player never escapes the screen. Currently I'm working on moving on X axis, but something goes wrong. When player collides with left wall, it normally doesn't let it go more to the left. But when going to the right it slightly ignores collision and goes more to the right.
Reproducible example:
import pygame
import math
pygame.init()
surface = pygame.display.set_mode((1000,600))
clock = pygame.time.Clock()
surfrect = surface.get_rect()
player=pygame.Rect((0,0), (35,35))
player.center=surfrect.w/2,surfrect.h/2
touched = False
levelR=[]
control=pygame.Rect((0,0), (50,50))
controld=[]
yspeed,xspeed=0,0
for i in range(-1,2):
oxr=surfrect.w/2-75*3/2
oyr=surfrect.h/2-75*3/2+75*3*i
yr,xr=oyr,oxr
gf=[]
for y in '#-#n#-#n#-#'.split('n'):
for x in y:
if x=='#':
gf.append(pygame.Rect((xr, yr), (75,75)))
xr+=75
yr+=75
xr=oxr
levelR.append([gf,pygame.Rect((oxr,oyr),(75*3,75*3))])
while True:
for ev in pygame.event.get():
if ev.type == pygame.QUIT:
pygame.quit()
elif ev.type == pygame.MOUSEBUTTONDOWN:
touched = True
elif ev.type == pygame.MOUSEBUTTONUP:
touched = False
surface.fill((0,0,0))
for sd in levelR:pygame.draw.rect(surface,(35,35,35),sd[1])
pygame.draw.circle(surface, (200,200,200), player.center, player.width/2-player.width/19, player.width)
for sd in levelR:
for rect in sd[0]:
scale=0.6
recti=pygame.Rect((rect.x,rect.y), (rect.width*scale,rect.height*scale))
recti.center=rect.center
pygame.draw.rect(surface,(255,255,255),rect)
pygame.draw.rect(surface,(0,0,0),recti)
if touched and not controld:
controld=pygame.mouse.get_pos()
control.center=controld
elif touched and controld:
radius = 150
x,y=pygame.mouse.get_pos()
distance=math.dist((x, y),controld)
if distance < radius:
pos=(x, y)
else:
dx=x-controld[0]
dy=y-controld[1]
ratio=radius/distance
pos=controld[0]+ratio*dx, controld[1]+ratio*dy
control.center=pos
try:xmd=(x-controld[0])/abs(x-controld[0])
except:xmd=0
try:ymd=(y-controld[1])/abs(y-controld[1])
except:ymd=0
sr=0.02*(150/radius)
xspeed=xmd*abs(control.centerx-controld[0])*sr
yspeed=ymd*abs(control.centery-controld[1])*sr
collisiont=5
for sd in levelR:
for block in sd[0]:
block.x-=xspeed
sd[1].x-=xspeed
for rect in [i for sd in levelR for i in sd[0]]:
if player.colliderect(rect):
for sd in levelR:
for block in sd[0]:
block.x+=xspeed
sd[1].x+=xspeed
break
pygame.draw.circle(surface, (255,255,255), control.center, control.width,control.width)
pygame.draw.circle(surface, (255,255,255), controld, radius, 5)
elif not touched and controld:controld=()
pygame.display.flip()
clock.tick(60)
I have tried debugging the collision and discovered that the moving to the right when collides with walls it stops the player from moving more to the right but player slightly moves to the right.
I expected walls not moving when player collides with walls.
It happened that the moving to the right when player collides with wall doesn't stop it directly.

Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fractional part of the coordinates is lost when the position of the Rect object is changed. In your case this means that the movement to the left and right behaves differently and block.x += xspeed is not the inverse operation of block.x -= xspeed. Consider that, 5 - 0.1 is 4, but 4 + 0.1 is still 4.
You can solve the problem by testing the object for collisions before you actually move it, and not moving the object at all if a collision is detected:
collide = False
for rect in [i for sd in levelR for i in sd[0]]:
text_rect = rect.copy()
text_rect.x -= xspeed
if player.colliderect(text_rect):
collide = True
break
if not collide:
for sd in levelR:
for block in sd[0]:
block.x-=xspeed
sd[1].x-=xspeed

Related

Implementing a collision detect feature between a Rect object and a ball in pygame [duplicate]

This question already has an answer here:
Sometimes the ball doesn't bounce off the paddle in pong game
(1 answer)
Closed 2 years ago.
import pygame, random, time
# main function where we call all other functions, start the game loop, quit pygame and clean up the window. Inside we create a game object, display surface, and start the game loop by calling the play method on the game object. There is also a set caption with the title of the game.
def main():
pygame.init()
size =(500,400)
surface=pygame.display.set_mode(size)
pygame.display.set_caption('Pong v2')
game = Game(surface)
game.play()
pygame.quit()
# This is where we define the class game
class Game():
# an object in this class represents a complete game
# here we initialize a game. self is the game that is initialized surface is the display window surface object we also set default values for continuing the game and closing the window. we also define what fps we are running the game at, and define the velocity color position and radius of the ball
def __init__(self,surface):
# defining surface, fps, background color
self.surface=surface
self.FPS=120
self.bg_color=pygame.Color('black')
screen_width = surface.get_width()
screen_height = surface.get_height()
# defining ball attributes
ball_radius=10
ball_pos = [random.randint(ball_radius, screen_width-ball_radius),
random.randint(ball_radius, screen_height-ball_radius)]
ball_color=pygame.Color('white')
ball_velocity=[2,1]
self.ball=Ball(ball_pos,ball_radius,ball_color,ball_velocity,surface)
# defining paddle attributes
rect_left=[50,450]
rect_top=225
rect_height=60
rect_width=10
self.Paddle1=Rect(rect_left[0],rect_top,rect_width,rect_height,surface)
self.Paddle2=Rect(rect_left[1],rect_top,rect_width,rect_height,surface)
self.game_Clock=pygame.time.Clock()
self.close_clicked=False
self.continue_game=True
self.score1=0
self.score2=0
self.frame_counter=0
def play(self):
# game is played until player clicks close
while not self.close_clicked:
self.handle_events()
self.draw()
# if nothing sets this to false the game will continue to update
if self.continue_game:
self.update()
self.game_Clock.tick(self.FPS)
# score is drawn onto the screen (unimportant this is just playing with a feature for the next version), we define color font background etc of the score message and update score upon points being scored
def draw_score(self):
font_color = pygame.Color("white")
font_bg = pygame.Color("black")
font = pygame.font.SysFont("arial", 18)
text_img = font.render("Score for Player 1: " + str(self.score1) + ' Score for Player 2: ' + str(self.score2), True, font_color, font_bg)
text_pos = (0,0)
self.surface.blit(text_img, text_pos)
# ball, surface, score, and two paddles are drawn, pygame also updates this drawing once per frame
def draw(self):
self.surface.fill(self.bg_color)
self.draw_score()
#pygame.draw.rect(self.surface,pygame.Color('blue'),(50,225,10,50))
#pygame.draw.rect(self.surface,pygame.Color('red'),(450,225,10,50))
self.Paddle1.draw()
self.Paddle2.draw()
self.ball.draw()
pygame.display.update()
# score value set to default of 0 we tell ball to move and add 1 to frame counter upon each update. update game object for the next frame
def update(self):
self.ball.move()
self.score=0
self.frame_counter+=self.frame_counter+1
# here we setup an event loop and figure out if the action to end the game has been done
def handle_events(self):
events=pygame.event.get()
for event in events:
if event.type== pygame.QUIT:
self.close_clicked=True
# user defined class ball
class Ball:
# self is the ball to intialize. color/center/radius are defined for the ball that is initialized
def __init__(self,center,radius,color,velocity,surface):
self.center=center
self.radius=radius
self.color=color
self.velocity=velocity
self.surface=surface
# screen size is determined and edge of ball is checked that it is not touching the edge. if it is touching the edge it bounces and reverses velocity
def move(self):
screen_width=self.surface.get_width()
screen_height=self.surface.get_height()
screen_size=(screen_width,screen_height)
for i in range(0,len(self.center)):
self.center[i]+=self.velocity[i]
if (self.center[i]<=0 + self.radius or self.center[i]>=screen_size[i] - self.radius):
self.velocity[i]=-self.velocity[i]
# ball is drawn
def draw(self):
pygame.draw.circle(self.surface,self.color,self.center,self.radius)
class Rect:
def __init__(self,left,top,width,height,surface):
#self.left=left
#self.top=top
#self.width=width
#self.height=height
self.surface=surface
self.rect=pygame.Rect(left,top,width,height)
def draw(self):
pygame.draw.rect(self.surface,pygame.Color('red'),self.rect)
def collide(self):
if pygame.Rect.collide(x,y) == True:
return True
else:
return False
main()
Above is my work so far basically its supposed to be the retro arcade game pong where the ball bounces off of the edges of the paddles and if it doesn't the opposite side scores a point upon it hitting the edge of the window. So specifically this part of the project requires me to make the ball bounce off of the front of the paddles and I'm confused as to how to do this. My idea originally was to use the collidepoint method inside of the class Rect that if it returns true would reverse the balls velocity. However, I don't have access to the centre coordinates of the ball inside of the class or inside of the method play in the class game where I intended to make this work on the specific instances of ball and paddle1,paddle2 so I don't know how to do this.
Evaluate if the ball hits the left paddle at the right, respectively the right paddle at the left in Game.update.
If the ball hits the paddle the the score can be incremented:
class Game():
# [...]
def update(self):
self.ball.move()
# evaluate if Ball hits the left paddle (Paddle1) at the right
if self.ball.velocity[0] < 0 and self.Paddle1.rect.top <= self.ball.center[1] <= self.Paddle1.rect.bottom:
if self.Paddle1.rect.right <= self.ball.center[0] <= self.Paddle1.rect.right + self.ball.radius:
self.ball.velocity[0] = -self.ball.velocity[0]
self.ball.center[0] = self.Paddle1.rect.right + self.ball.radius
self.score1 += 1
# evaluate if Ball hits the right paddle (Paddle2) at the left
if self.ball.velocity[0] > 0 and self.Paddle2.rect.top <= self.ball.center[1] <= self.Paddle2.rect.bottom:
if self.Paddle2.rect.left >= self.ball.center[0] >= self.Paddle2.rect.left - self.ball.radius:
self.ball.velocity[0] = -self.ball.velocity[0]
self.ball.center[0] = self.Paddle2.rect.left - self.ball.radius
self.score2 += 1
Get the state of the keys by pygame.key.get_pressed() and change the position of the paddles is Game.handle_events.
e.g. Move the left paddle by w / s and the right paddle by UP / DOWN:
class Game():
# [...]
def handle_events(self):
events=pygame.event.get()
for event in events:
if event.type== pygame.QUIT:
self.close_clicked=True
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.Paddle1.rect.top = max(0, self.Paddle1.rect.top - 3)
if keys[pygame.K_s]:
self.Paddle1.rect.bottom = min(self.surface.get_height(), self.Paddle1.rect.bottom + 3)
if keys[pygame.K_UP]:
self.Paddle2.rect.top = max(0, self.Paddle2.rect.top - 3)
if keys[pygame.K_DOWN]:
self.Paddle2.rect.bottom = min(self.surface.get_height(), self.Paddle2.rect.bottom + 3)

Pygame, I want to make it play the sound

How to make this work. I want it to play audio once the rectangle cross the center of the screen.
import pygame
from pygame import*
pygame.init()
mixer.init()
win=pygame.display.set_mode((600,00))
pygame.display.set_caption("bla")
x=20
y=20
width=10
height=10
run=True
while run:
pygame.time.delay(10)
for event in pygame.event.get():
if event == pygame.QUIT:
run= False
keys = pygame.key.get_pressed()
if keys [pygame.K_LEFT] :
x-=4
if keys [pygame.K_RIGHT]:
x+=4
if keys [pygame.K_UP]:
y-=4
if keys [pygame.K_DOWN]:
y+=4
if 350 < x < 450 and 350 < y < 450:
mixer.music.load('music.wav')
mixer.music.play(0)
win.fill((0,0,0))
pygame.draw.circle(win,(255,0,0),(300,300),(10))
pygame.draw.rect(win,(255,0,0),(x,y,width,height))
pygame.display.update()
I expect the sound will play when the rectangle cross the center of the window. But instead nothing happen
First, you have a typo:
win=pygame.display.set_mode((600,00))
# ^^^^
I guess should be:
win=pygame.display.set_mode((600,600))
To test you code, you may add a gray rectangle area to show where the cursor should enter to start the music, so you can be sure where you have to place the cursor.
win.fill((0,0,0))
pygame.draw.rect(win, (100, 100, 100), (350, 350, 100, 100)) #this is the rectagle where the music starts
pygame.draw.circle(win,(255,0,0),(300,300),(10))
pygame.draw.rect(win,(255,0,0),(x,y,width,height))
pygame.display.update()
Actually is not really at the center of the screen, so maybe you need to adjust the coordinates. Reduce x and y by 100 to center it in a 600x600 screen.
Note also that until the cursor remains in the area you will not hear anything because actually you start the music each iteration. Sort of rewinding the music each iteration.
But when you exit the area, it will start. To make it start when you enter in the square, check if the music is already playing and start it only if nothing is playing:
if 350 < x < 450 and 350 < y < 450:
if not mixer.music.get_busy():
mixer.music.load('music.wav')
mixer.music.play(0)
pygame.mixer.music.get_busy() checks if the music is playing.

How to increase points when a rect collides with another rect and the left mouse button is clicked?

So I am making a game where the player has to click on a moving rect (a tennis ball) to get points. So far I have created the tennis ball and some other balls, and I have got them moving randomly round the screen and bouncing off the edges. I have also made a rect called target which moves with the mouse. I would like the game to add 10 points every time the player clicks on the tennis ball (with the target) and deduct a life if they click elsewhere on the screen. Any help would be greatly appreciated!
Thanks :)
import pygame
import sys
from random import *
from pygame.locals import *
#game
pygame.init()
running=True
screen_width=800
screen_height=600
screen=pygame.display.set_mode([screen_width,screen_height])
background_colour = (0,0,150)
screen.fill(background_colour)
clock=pygame.time.Clock()
game_sound='pumpup.wav'
pygame.mixer.init()
points=0
lives=3
#balls
x= randint(20,40)
y= randint(20,40)
ball=pygame.image.load ('ball_t.png')
ball_movement=[x,y]#amount ball moves each time (x,y)
ball_location=[100,100]#x,y co-ordinates for start position
ball_rect=ball.get_rect()#gets rectangle for ball
x2= randint(5,15)
y2= randint(5,15)
ball2_movement=[x2,y2]#amount ball moves each time (x,y)
ball2_location=[10,10]#x,y co-ordinates for start position
ball2=pygame.image.load ('ball_tt.png')
ball2_rect=ball2.get_rect()#gets rectangle for ball
x3= randint(10,40)
y3= randint(10,30)
ball3_movement=[x3,y3]#amount ball moves each time (x,y)
ball3_location=[10,100]#x,y co-ordinates for start position
ball3=pygame.image.load ('ball_b.png')
ball3_rect=ball3.get_rect()#gets rectangle for ball
x4= randint(10,30)
y4= randint(10,30)
ball4_movement=[x4,y4]#amount ball moves each time (x,y)
ball4_location=[200,100]#x,y co-ordinates for start position
ball4=pygame.image.load ('ball_bs.png')
ball4_rect=ball4.get_rect()#gets rectangle for ball
#target
target_location=[200,100]#x,y co-ordinates for start position
target=pygame.image.load ('Target.png')
target_rect=target.get_rect()#gets rectangle for ball
target_rect.center=target_location
#score
font=pygame.font.Font(None, 50)#default font
score="Score: {}".format(points)
score_text=font.render(score,1,(0,200,250))
text_pos=[10,10] #position on screen
#lives
font=pygame.font.Font(None, 50)#default font
livest="Lives: {}".format(lives)
lives_text=font.render(livest,1,(0,200,250))
textl_pos=[650,10]#position on screen
#show initial screen
screen.blit(ball, ball_rect)#draws ball on its rectangle
screen.blit(ball2, ball2_rect)
screen.blit(ball3, ball3_rect)
screen.blit(ball4, ball4_rect)
screen.blit(target, target_rect)
screen.blit(score_text, text_pos)
screen.blit(lives_text, textl_pos)
pygame.display.flip() #displays screen
while running: #event loop
clock.tick(50) #won't run at more than 60 frames per second
screen.fill([0,0,150])#clears screen
for event in pygame.event.get():
if event.type==pygame.QUIT:
running=False
elif event.type==pygame.MOUSEMOTION:
target_rect.centerx=event.pos[0] #moves target with mouse
target_rect.centery=event.pos[1]
if event.type == pygame.MOUSEBUTTONDOWN and target_rect.colliderect(ball2_rect):
points=100
if ball_rect.left<0 or ball_rect.right>screen_width: #check whether ball is off screen (x)
ball_movement[0]=-ball_movement[0]#change direction on x axis
if ball_rect.top<0 or ball_rect.bottom>screen_height: #check whether ball is off screen (y)
ball_movement[1]=-ball_movement[1] #change direction on y axis
if ball2_rect.left<0 or ball2_rect.right>screen_width: #check whether ball is off screen (x)
ball2_movement[0]=-ball2_movement[0]#change direction on x axis
if ball2_rect.top<0 or ball2_rect.bottom>screen_height: #check whether ball is off screen (y)
ball2_movement[1]=-ball2_movement[1] #change direction on y axis
if ball3_rect.left<0 or ball3_rect.right>screen_width: #check whether ball is off screen (x)
ball3_movement[0]=-ball3_movement[0]
if ball3_rect.top<0 or ball3_rect.bottom>screen_height: #check whether ball is off screen (y)
ball3_movement[1]=-ball3_movement[1] #change direction on y axis
if ball4_rect.left<0 or ball4_rect.right>screen_width: #check whether ball is off screen (x)
ball4_movement[0]=-ball4_movement[0]
if ball4_rect.top<0 or ball4_rect.bottom>screen_height: #check whether ball is off screen (y)
ball4_movement[1]=-ball4_movement[1] #change direction on y axis
if ball_rect.top > screen_height and lives >0:
lives=lives-1
livest="Lives: {}".format(lives)
lives_text=font.render(livest,1,(0,0,0))
pygame.time.delay(500)#pause
ball_rect.topleft=[10,0] #reinstate ball
if lives ==0:
pygame.display.flip()
pygame.time.delay(2500)#pause
running=False
ball_rect=ball_rect.move(ball_movement)#move ball
ball2_rect=ball2_rect.move(ball2_movement)
ball3_rect=ball3_rect.move(ball3_movement)
ball4_rect=ball4_rect.move(ball4_movement)
screen.blit(ball, ball_rect)#redraws ball
screen.blit(ball2, ball2_rect)
screen.blit(ball3, ball3_rect)
screen.blit(ball4, ball4_rect)
screen.blit(target, target_rect)
screen.blit(score_text, text_pos)
screen.blit(lives_text, textl_pos)
pygame.display.flip() #displays screen
pygame.quit() #quits game
I would recommend not creating a ball 4 different times. Instead, create a ball class and make 4 ball objects. Try this
class ball:
def __init__(self, x, y, image, location):
self.x = x
self.y = y
self.img = image
self.movement = [x, y]
self.rect = self.img.get_rect()
self.rect.topleft = location
Then, you can get rid of all of this:
x= randint(20,40)
y= randint(20,40)
ball=pygame.image.load ('ball_t.png')
ball_movement=[x,y]#amount ball moves each time (x,y)
ball_location=[100,100]#x,y co-ordinates for start position
ball_rect=ball.get_rect()#gets rectangle for ball
x2= randint(5,15)... etc.
and replace it all with this:
balls = []
balls.append(ball(randint(20, 40), randint(20, 40), 'ball_t.png', [100, 100])
balls.append(ball(randint(5, 15), randint(5, 15), 'ball_tt.png', [10, 10])
balls.append(ball(randint(10, 40), randint(10, 30), 'ball_b.png', [10, 100])
balls.append(ball(randint(10, 30), randint(10, 30), 'ball_bs.png', [200, 100])
Then, take this part:
if ball_rect.left<0 or ball_rect.right>screen_width: #check whether ball is off screen (x)
ball_movement[0]=-ball_movement[0]#change direction on x axis
if ball_rect.top<0 or ball_rect.bottom>screen_height: #check whether ball is off screen (y)
ball_movement[1]=-ball_movement[1] #change direction on y axis
if ball2... etc.
and replace it with a method of ball.
class ball:
def __init__():
blah blah blah init
def is_touching_wall(self):
if self.rect.left < 0 or self.rect.right > screen_width:
self.movement[0] -= self.movement[0]
if self.rect.top < 0 or self.rect.bottom > screen_height:
self.movement[1] -= self.movement[1]
and then in the main function:
for ball in balls:
ball.is_touching_wall()
And then lastly, add one more method to the ball class:
def update(self):
self.rect = self.rect.move(self.movement)
screen.blit(self.img, self.rect)
and replace this part:
ball_rect=ball_rect.move(ball_movement)#move ball
ball2_rect=ball2_rect.move(ball2_movement)
ball3_rect=ball3_rect.move(ball3_movement)
ball4_rect=ball4_rect.move(ball4_movement)
screen.blit(ball, ball_rect)#redraws ball
screen.blit(ball2, ball2_rect)
screen.blit(ball3, ball3_rect)
screen.blit(ball4, ball4_rect)
with this:
for ball in balls:
ball.update()
Lastly, two more recommendations.
Don't post questions on SE saying, "Here's my code. Fix it." You never specified what you tried to fix the problem, where in your code the problem is, or even exactly what your question was. Also, try to only post the smallest part of your code that will replicate your problem. Fix your code, and add more information to your question, and then people will be a lot more willing to help you with the target.
Pay attention to formatting. Read PEP-8. Add spaces inbetween arguments and inbetween comparisons. Indent better. Import this. etc...

Different colors for shapes through iterations in Python / Pygame?

I'm new to stackoverflow, but was hoping for a little insight from more advanced programmers. I am switching majors to Computer Science next semester and am taking an intro class learning some beginner's Python programming. I have already finished the program below (the assignment was to make a program that draws ovals on the window surface by filling in some of the professor's code, not too bad at all) but I wanted to add a little something extra: As you can see, I have the color of the ovals set to be random, but it stays the same until the program is restarted entirely i.e. all of the ovals are that particular color for the length of the program. With the code written the way it is, I can't figure out a way to get the color to change for each oval. Keep in mind, this is all for kicks, but if anyone's feeling especially helpful or creative, I'm curious to see what you have to say. Let me know if I can expound on anything. Thanks!
import pygame, random, sys
WINDOWWIDTH = 700
WINDOWHEIGHT = 700
BACKGROUNDCOLOR = (150,160,100)
#A different color every run
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
pygame.init()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption("Mobile Ovals")
#The draw variable is used later to indicate the mouse is still pressed
ovals = []
completedOvals = []
finished = False
draw = False
startXY = (-1, -1)
while not finished:
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYUP and
event.key == pygame.K_ESCAPE):
finished = True
elif event.type == pygame.KEYDOWN:
pressed = pygame.key.get_pressed()
if pressed[pygame.K_F4] and (pressed[pygame.K_LALT] or
pressed[pygame.K_RALT]):
finished = True
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
for oval in ovals:
completedOvals.append (oval)
if draw == True:
del ovals [:]
#The above function ensures only one oval is onscreen at any given time
endXY = event.pos
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
ovals.append (pygame.Rect (left, top, width, height))
windowSurface.fill(BACKGROUNDCOLOR)
for oval in ovals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, oval)
for completedOval in completedOvals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, completedOval)
pygame.display.update()
pygame.quit()
Your problem is quite simple. You set OVAL_COLOR once. But every time you make reference to the variable OVAL_COLOR, you're not creating a new random color, you're re-using the RGB color that was randomly generated when you created the variable.
Now, the way your program is structured, you maintain a list of all complete ovals that you're re-drawing every time the draw variable is set to true. If you place the OVAL_COLOR variable inside the for loop, you will update the color with every mouse movement, changing the color of the oval being drawn, as well as the color of all the old ovals being re-drawn.
The solution to have a new random oval color is to set the variable OVAL_COLOR when the mouse button goes down. That way, the oval color won't change as you drag the mouse to adjust the oval. But, given the current structure of the program, you'll need to save the oval colors assigned to completed ovals, or you'll still have the oval color change each time.
When the mouse button is pressed down, we want a new random color for our circle. Generate a random value, which will be used every time the circle is re-drawn.
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
draw = True
When the mouse button is released, save the coordinates for the oval, along with the color that it was drawn with.
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
# print len(ovals) # (always ==1)
completedOvals.append ((ovals[-1], OVAL_COLOR))
When we iterate through these completed ovals, draw them with the same color each time.
for (completedOval, color) in completedOvals:
pygame.draw.ellipse(windowSurface, color, completedOval)
Create a simple Oval() class, that contains it's color, and size.
import pygame
from pygame.locals import *
class Oval(object):
"""handle, and draw basic ovals. stores Rect() and Color()"""
def __init__(self, startXY, endXY):
self.color = Color(random.randint(0,255), random.randint(0,255), random.randint(0,255))
self.rect = Rect(0,0,1,1)
self.coord_to_oval(startXY, endXY)
def draw(self):
pygame.draw.ellipse(windowSurface, self.color, self.rect)
def coord_to_oval(self, startXY, endXY):
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
self.rect = Rect(left, top, width, height)
# main loop
while not finished:
for event in pygame.event.get():
# events, and creation:
# ... your other events here ...
elif event.type == MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type ==MOUSEBUTTONUP:
# on mouseup, create instance.
endXY = event.pos
oval_new = Oval(startXY, endXY)
completedOvals.append(oval_new)
# draw them:
for oval in ovals:
oval.draw()
for oval in completedOvals:
oval.draw()
I mostly left out your non-completed ovals. Was that to show the size before clicking?

Single player 'pong' game

I am just starting out learning pygame and livewires, and I'm trying to make a single-player pong game, where you just hit the ball, and it bounces around until it passes your paddle (located on the left side of the screen and controlled by the mouse), which makes you lose. I have the basic code, but the ball doesn't stay on the screen, it just flickers and doesn't remain constant. Also, the paddle does not move with the mouse. I'm sure I'm missing something simple, but I just can't figure it out. Help please! Here's what I have:
from livewires import games
import random
games.init(screen_width=640, screen_height=480, fps=50)
class Paddle(games.Sprite):
image=games.load_image("paddle.bmp")
def __init__(self, x=10):
super(Paddle, self).__init__(image=Paddle.image,
y=games.mouse.y,
left=10)
self.score=games.Text(value=0, size=25, top=5, right=games.screen.width - 10)
games.screen.add(self.score)
def update(self):
self.y=games.mouse.y
if self.top<0:
self.top=0
if self.bottom>games.screen.height:
self.bottom=games.screen.height
self.check_collide()
def check_collide(self):
for ball in self.overlapping_sprites:
self.score.value+=1
ball.handle_collide()
class Ball(games.Sprite):
image=games.load_image("ball.bmp")
speed=5
def __init__(self, x=90, y=90):
super(Ball, self).__init__(image=Ball.image,
x=x, y=y,
dx=Ball.speed, dy=Ball.speed)
def update(self):
if self.right>games.screen.width:
self.dx=-self.dx
if self.bottom>games.screen.height or self.top<0:
self.dy=-self.dy
if self.left<0:
self.end_game()
self.destroy()
def handle_collide(self):
self.dx=-self.dx
def end_game(self):
end_message=games.Message(value="Game Over",
size=90,
x=games.screen.width/2,
y=games.screen.height/2,
lifetime=250,
after_death=games.screen.quit)
games.screen.add(end_message)
def main():
background_image=games.load_image("background.bmp", transparent=False)
games.screen.background=background_image
paddle_image=games.load_image("paddle.bmp")
the_paddle=games.Sprite(image=paddle_image,
x=10,
y=games.mouse.y)
games.screen.add(the_paddle)
ball_image=games.load_image("ball.bmp")
the_ball=games.Sprite(image=ball_image,
x=630,
y=200,
dx=2,
dy=2)
games.screen.add(the_ball)
games.mouse.is_visible=False
games.screen.event_grab=True
games.screen.mainloop()
main()
I can't help you because you did not post the complete code here. At least, I do not see where you're updating the positions of the sprites (self.x += self.dx somewhere?) and updating the draw to screen. You're also not utilising your classes in the main() function.
That said, I'm seeing
def __init__(self, x=10):
and inside the constructor you never used the x variable. That worries me, too.
Consider using the Paddle and Ball class as a Sprite, like the following:
if __name__ == '__main__':
background_image = games.load_image("background.bmp", transparent=False)
games.screen.background = background_image
the_paddle = Puddle()
games.screen.add(the_paddle)
the_ball = Ball()
games.screen.add(the_ball)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
Note I've taken the liberty to make your code read more Pythonic. I have never used livewires, however, so my code may not function. But it should point you to the right direction. Good luck!
Why are you using livewires? You can use only pygame for a pong game.
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480)) # window size
pygame.display.set_caption("Simple pong") # window title
# this is a rect that contains the ball
# at the beginning it is set in the center of the screen
ball_rect = pygame.Rect((312, 232), (16, 16))
# speed of the ball (x, y)
ball_speed = [4, 4]
# this contains your paddle
# vertically centered on the left side
paddle_rect = pygame.Rect((8, 200), (8, 80))
# 1 point if you hit the ball
# -5 point if you miss the ball
score = 0
# load the font for displaying the score
font = pygame.font.Font(None, 30)
# mainloop
while True:
# event handler
for event in pygame.event.get():
# quit event => close the game
if event.type == pygame.QUIT:
exit(0)
# control the paddle with the mouse
elif event.type == pygame.MOUSEMOTION:
paddle_rect.centery = event.pos[1]
# correct paddle position if it's going out of window
if paddle_rect.top < 0:
paddle_rect.top = 0
elif paddle_rect.bottom >= 480:
paddle_rect.bottom = 480
# this test if up or down keys are pressed
# if yes move the paddle
if pygame.key.get_pressed()[pygame.K_UP] and paddle_rect.top > 0:
paddle_rect.top -= 5
elif pygame.key.get_pressed()[pygame.K_DOWN] and paddle_rect.bottom < 480:
paddle_rect.top += 5
# update ball position
# this move the ball
ball_rect.left += ball_speed[0]
ball_rect.top += ball_speed[1]
# these two if block control if the ball is going out on the screen
# if it's going it reverse speed to simulate a bounce
if ball_rect.top <= 0 or ball_rect.bottom >= 480:
ball_speed[1] = -ball_speed[1]
if ball_rect.right >= 640:
ball_speed[0] = -ball_speed[0]
# this control if the ball touched the left side
elif ball_rect.left <= 0:
score -= 5
# reset the ball to the center
ball_rect = pygame.Rect((312, 232), (16, 16))
# test if the ball is hit by the paddle
# if yes reverse speed and add a point
if paddle_rect.colliderect(ball_rect):
ball_speed[0] = -ball_speed[0]
score += 1
# clear screen
screen.fill((255, 255, 255))
# draw the ball, the paddle and the score
pygame.draw.rect(screen, (0, 0, 0), paddle_rect) # paddle
pygame.draw.circle(screen, (0, 0, 0), ball_rect.center, ball_rect.width/2) # ball
score_text = font.render(str(score), True, (0, 0, 0))
screen.blit(score_text, (320-font.size(str(score))[0]/2, 5)) # score
# update screen and wait 20 milliseconds
pygame.display.flip()
pygame.time.delay(20)

Categories

Resources