Pygame, I want to make it play the sound - python

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.

Related

Pygame collision with rectangles in list not working right

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

use pygame to create animation of an enlarged radius of circle failed

i want to use python to create an animation of an enlarged raidus of a circle.
i use two ways to accomplish this, one way is success, the another is failed.
i want to know why, is there anyone can help.
version 1:
import pygame
global DISPLAYSURF, FPSCLOCK
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWNWIDTH, WINDOWNHEIGHT))
radius = 1
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
radius = radius + 1
pygame.draw.circle(DISPLAYSURF, RED, (100, 100), radius)
pygame.display.update()
FPSCLOCK.tick(1)
version2:
import pygame
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWNWIDTH, WINDOWNHEIGHT))
for radius in range(0, 100, 2):
pygame.draw.circle(DISPLAYSURF, RED, (100, 100), radius)
pygame.display.update()
FPSCLOCK.tick(1)
while True:
just_wait = True
the version is what i want , the version2 can not work
i run the code on macOs 10.13.1 High Sierra with python3 , why!!!
I think you put code with wrong indentions and it should be
for radius in range(0, 100, 2):
pygame.draw.circle(DISPLAYSURF, RED, (100, 100), radius)
pygame.display.update()
FPSCLOCK.tick(1)
while True:
just_wait = True
And it means you run while True loop inside for loop.
But while True loop is endless loop - you never leave it.
So it never go back to draw new circle and update screen.
You can put print() in code and you will see it.
First version is preferred version because it not only draw animation but it checks events so you can leave program in any time.
On some systems if you will not use pygame.event.get(): to get events from system then system will think that program hung up and system can kill program.

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...

pygame - Real time 2x scale low resolution

I'm writing an arcade game with traditional resolution 240 x 320 (vertical screen)
I need to render that to a modern display in real time.
This means that I need it to double (1 pixel = 4 on output) or even triple (1 pixel = 9)
I can't simply double scale the sprites because the game movement won't scale with them. (movement won't "snap" to visual scale)
Currently I have a game window that is 480 x 640 pixels.
I'm blitting all game sprites to a 240 x 320 surface, double scaling it and outputting that surface to the window with pygame. The game has slowed down far too much now.
How can all these emulators do nice double scale and triple scales with big clean pixels and pygame not? I thought SDL would be better at 2D rasterization.
Here is the code I currently have:
import pygame
import sys
import random
from Bullet import Bullet
bullets = []
pygame.init()
fps_clock = pygame.time.Clock()
# Our final window layer
window = pygame.display.set_mode((480, 640))
# This is the layer that gets scaled
render_layer = pygame.Surface((240, 320))
red = (255, 0, 0)
white = (255, 255, 255)
dkred =(127, 0, 0)
counter = 0;
# Sprite resources
bullet_sprite = pygame.image.load("shot1.png")
bullet_sprite2 = pygame.image.load("shot2.png")
while True:
render_layer.fill(dkred)
for i in bullets:
i.tick()
if i.sprite == "bullet_sprite1":
render_layer.blit(bullet_sprite, (i.x - 12, i.y -12))
else:
render_layer.blit(bullet_sprite2, (i.x - 12, i.y -12))
pygame.transform.scale2x(render_layer, window)
if i.x < 0 or i.y < 0 or i.x > 240 or i.y > 320:
i.dead = True
bullets = [x for x in bullets if x.dead == False]
counter += 3.33
for i in range(10):
if i % 2 == 0:
bullets.append(Bullet(120,120,360.0/10*i - counter, 3, -1,
sprite = "bullet_sprite1"))
else:
bullets.append(Bullet(120,120,360.0/10*i - counter, 3, -1,
sprite = "bullet_sprite2"))
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit()
sys.exit()
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_ESCAPE:
pygame.event.post(pygame.event.Event(pygame.QUIT))
pygame.display.update()
fps_clock.tick(60)
I found that pygame.transform.scale2x() is in a for loop. Try just using it before pygame.display.update(). If there is more than one bullet, then I see how it could get laggy quickly.

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?

Categories

Resources