How to play a bump sound in PyGame when the player hits the edge of the window without having the sound to continually play? - python-3.x

I am trying to make an if statement in PyGame to have sound affect playing when the player hits the edge of the screen, but I'm apperently making a mistake in my if statement; in another word, I check the condition in a wrong way becasue I don't want my soud affect to continually play when the user is at the edge of the screen, I want the sound to play once when the player is at the edge. I also don't my player to silde off the screen.
I have made a vatiable that handles the playing command in a variable called 'bump_sound':
bump_sound = pygame.mixer.Sound("bump.wav")
And this is what my if statement look like:
# These if statements check to make sure the player won't slide off the screen.
# They also check if the player hits the edge; if so, play the bump sound.
if self.rect.x >= 650:
self.rect.x = 650
bump_sound.play()
if self.rect.x >= 650:
self.rect.x = 650
if self.rect.x < 0:
self.rect.x = 0
bump_sound.play()
if self.rect.y > 325:
self.rect.y = 325
bump_sound.play()
if self.rect.y < 0:
self.rect.y = 0
bump_sound.play()
Thanksfully, the player won't slide off the screen; however, the sound keeps playing.

You have to use > instead of >=:
if self.rect.x > 650:
self.rect.x = 650
bump_sound.play()
Another way is to use Boolean variables. One that indicates if the player hits the edge and one that indicates if the player hit the edge in the previous frame. The sound is played only if the player touched the edge but did not hit it in the previous frame:
def __init__(self):
# [...]
self.previous_hit = False
self.hit = False
def your_function(self):
self.previous_hit = self.hit
if self.rect.x >= 650:
self.rect.x = 650
self.hit = True
if self.rect.x <= 0:
self.rect.x = 0
self.hit = True
if self.rect.y >= 325:
self.rect.y = 325
self.hit = True
if self.rect.y <= 0:
self.rect.y = 0
self.hit = True
if self.hit and not self.previous_hit:
bump_sound.play()

Related

Space Invaders bug pygame

After finally beeing able to make multiple enemies in the game I confronted myself with the most annoying bug ever. I have multiple enemies, a space ship and I can even shoot bullets, but right after I added the enemies to the game and followed the exact same steps as show in this video, the background is gone for some reason, what made the speed of the player character, of the enemies and the bullet went like crazy. Plus, the sprites are duplicating themselves and wont go away, and the only think that I can think is "What the hell am I doing wrong??" because I feel like I can't even follow the steps and copy the code from a video (btw I'm not receiving any error messages). Anyways, here's the full game code.
import pygame
import random
import math
# Initiate pygame
pygame.init()
# Display the game window
screen = pygame.display.set_mode((800, 600))
# Title and Icon
pygame.display.set_caption('Space Invaders')
icon = pygame.image.load('icon.png')
pygame.display.set_icon(icon)
# Background
background = pygame.image.load('background.png')
# Player
playerSprite = pygame.image.load('player.png')
playerX = 370
playerY = 480
playerX_change = 0
# Enemies
enemySprite = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 6
for i in range(num_of_enemies):
enemySprite.append(pygame.image.load('enemy.png'))
enemyX.append(random.randint(0, 736))
enemyY.append(random.randint(50, 150))
enemyX_change.append(4)
enemyY_change.append(40)
# Bullet
bulletSprite = pygame.image.load('bullet.png')
bulletX = 0
bulletY = 480
bulletY_change = 10
# When bullet_state = 'ready', you can't see the bullet on the screen
# When bullet_state = 'fire', the bullet is currently moving
bullet_state = 'ready'
score = 0
def player(x, y):
screen.blit(playerSprite, (x, y))
def enemy(x, y, i):
screen.blit(enemySprite[i], (x, y))
def fire_bullet(x, y):
global bullet_state
bullet_state = 'fire'
screen.blit(bulletSprite, (x + 16, y + 10))
def isCollision(enemyX, enemyY, bulletX, bulletY):
distance = math.sqrt((math.pow(enemyX - bulletX, 2)) + (math.pow(enemyY - bulletY, 2)))
if distance < 27:
return True
else:
return False
# Game Loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If a key is pressed, check if it's the right or left arrow key
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -5
if event.key == pygame.K_RIGHT:
playerX_change = 5
if event.key == pygame.K_SPACE:
if bullet_state == 'ready':
bulletX = playerX
fire_bullet(playerX, bulletY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# Player movement
playerX += playerX_change
# Prevents the player from going off the border
if playerX <= 0:
playerX = 0
elif playerX >= 736:
playerX = 736
# Enemies movement
for i in range(num_of_enemies):
enemyX[i] += enemyX_change[i]
if enemyX[i] <= 0:
enemyX_change[i] = 4
enemyY[i] += enemyX_change[i]
elif enemyX[i] >= 736:
enemyX_change[i] = -4
enemyY[i] += enemyY_change[i]
# Collision
collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
if collision:
bulletY = 480
bullet_state = 'ready'
score += 1
print(score)
enemyX[i] = random.randint(0, 736)
enemyY[i] = random.randint(50, 150)
enemy(enemyX[i], enemyY[i], i)
# Bullet movement
if bulletY <= 0:
bulletY = 480
bullet_state = 'ready'
if bullet_state == 'fire':
fire_bullet(bulletX, bulletY)
bulletY -= bulletY_change
player(playerX, playerY)
pygame.display.update()
the background is gone for some reason
Of course, because the background is never blit to screen.
the sprites are duplicating themselves and wont go away,
That's because you don't draw the background.
blit the background in the main application loop, before the objects of the scene are drawn:
while running:
# [...]
screen.blit(background, (0, 0))
# Enemies movement
for i in range(num_of_enemies):
# [...]
the speed of the player character, of the enemies and the bullet went like crazy.
Use pygame.time.Clock() to control the frames per second.
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
# [...]
Another (less sophisticated) possibility, which is probably use in the tutorial is to use pygame.time.delay():
running = True
while running:
pygame.time.delay(10)
# [...]

Pygame assistance [duplicate]

I am making a game in pygame 1.9.2.
It's a faily simple game in which a ship moves between five columns of bad guys who attack by moving slowly downward. I am attempting to make it so that the ship moves left and right with the left and right arrow keys. Here is my code:
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
location-=1
if location==-1:
location=0
if keys[K_RIGHT]:
location+=1
if location==5:
location=4
It works too well. The ship moves too fast. It is near impossible to have it move only one location, left or right. How can i make it so the ship only moves once every time the key is pressed?
You can get the events from pygame and then watch out for the KEYDOWN event, instead of looking at the keys returned by get_pressed()(which gives you keys that are currently pressed down, whereas the KEYDOWN event shows you which keys were pressed down on that frame).
What's happening with your code right now is that if your game is rendering at 30fps, and you hold down the left arrow key for half a second, you're updating the location 15 times.
events = pygame.event.get()
for event in events:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
location -= 1
if event.key == pygame.K_RIGHT:
location += 1
To support continuous movement while a key is being held down, you would have to establish some sort of limitation, either based on a forced maximum frame rate of the game loop or by a counter which only allows you to move every so many ticks of the loop.
move_ticker = 0
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
if move_ticker == 0:
move_ticker = 10
location -= 1
if location == -1:
location = 0
if keys[K_RIGHT]:
if move_ticker == 0:
move_ticker = 10
location+=1
if location == 5:
location = 4
Then somewhere during the game loop you would do something like this:
if move_ticker > 0:
move_ticker -= 1
This would only let you move once every 10 frames (so if you move, the ticker gets set to 10, and after 10 frames it will allow you to move again)
pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is 1, otherwise 0. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement:
while True:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= speed
if keys[pygame.K_RIGHT]:
x += speed
if keys[pygame.K_UP]:
y -= speed
if keys[pygame.K_DOWN]:
y += speed
This code can be simplified by subtracting "left" from "right" and "up" from "down":
while True:
keys = pygame.key.get_pressed()
x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * speed
y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * speed
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action or movement:
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x -= speed
if event.key == pygame.K_RIGHT:
x += speed
if event.key == pygame.K_UP:
y -= speed
if event.key == pygame.K_DOWN:
y += speed
See also Key and Keyboard event
Minimal example of continuous movement: replit.com/#Rabbid76/PyGame-ContinuousMovement
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
rect = pygame.Rect(0, 0, 20, 20)
rect.center = window.get_rect().center
vel = 5
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
print(pygame.key.name(event.key))
keys = pygame.key.get_pressed()
rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel
rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * vel
rect.centerx = rect.centerx % window.get_width()
rect.centery = rect.centery % window.get_height()
window.fill(0)
pygame.draw.rect(window, (255, 0, 0), rect)
pygame.display.flip()
pygame.quit()
exit()
Minimal example for a single action: replit.com/#Rabbid76/PyGame-ShootBullet
import pygame
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
tank_surf = pygame.Surface((60, 40), pygame.SRCALPHA)
pygame.draw.rect(tank_surf, (0, 96, 0), (0, 00, 50, 40))
pygame.draw.rect(tank_surf, (0, 128, 0), (10, 10, 30, 20))
pygame.draw.rect(tank_surf, (32, 32, 96), (20, 16, 40, 8))
tank_rect = tank_surf.get_rect(midleft = (20, window.get_height() // 2))
bullet_surf = pygame.Surface((10, 10), pygame.SRCALPHA)
pygame.draw.circle(bullet_surf, (64, 64, 62), bullet_surf.get_rect().center, bullet_surf.get_width() // 2)
bullet_list = []
run = True
while run:
clock.tick(60)
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
bullet_list.insert(0, tank_rect.midright)
for i, bullet_pos in enumerate(bullet_list):
bullet_list[i] = bullet_pos[0] + 5, bullet_pos[1]
if bullet_surf.get_rect(center = bullet_pos).left > window.get_width():
del bullet_list[i:]
break
window.fill((224, 192, 160))
window.blit(tank_surf, tank_rect)
for bullet_pos in bullet_list:
window.blit(bullet_surf, bullet_surf.get_rect(center = bullet_pos))
pygame.display.flip()
pygame.quit()
exit()
import pygame
pygame.init()
pygame.display.set_mode()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); #sys.exit() if sys is imported
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_0:
print("Hey, you pressed the key, '0'!")
if event.key == pygame.K_1:
print("Doing whatever")
In note that K_0 and K_1 aren't the only keys, to see all of them, see pygame documentation, otherwise, hit tab after typing in
pygame.
(note the . after pygame) into an idle program. Note that the K must be capital. Also note that if you don't give pygame a display size (pass no args), then it will auto-use the size of the computer screen/monitor. Happy coding!
I think you can use:
pygame.time.delay(delayTime)
in which delayTime is in milliseconds.
Put it before events.
Try this:
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
if count == 10:
location-=1
count=0
else:
count +=1
if location==-1:
location=0
if keys[K_RIGHT]:
if count == 10:
location+=1
count=0
else:
count +=1
if location==5:
location=4
This will mean you only move 1/10 of the time. If it still moves to fast you could try increasing the value you set "count" too.
The reason behind this is that the pygame window operates at 60 fps (frames per second) and when you press the key for just like 1 sec it updates 60 frames as per the loop of the event block.
clock = pygame.time.Clock()
flag = true
while flag :
clock.tick(60)
Note that if you have animation in your project then the number of images will define the number of values in tick(). Let's say you have a character and it requires 20 sets images for walking and jumping then you have to make tick(20) to move the character the right way.
Just fyi, if you're trying to ensure the ship doesn't go off of the screen with
location-=1
if location==-1:
location=0
you can probably better use
location -= 1
location = max(0, location)
This way if it skips -1 your program doesn't break
make something like this, but based on time delay. i call my function first time immediately and then lunch timer, and while button is pressed i call it every button_press_delta seconds
from time import time
before main loop:
button_press_delta = 0.2
right_button_pressed = 0
while not done:
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
if not right_button_pressed:
call_my_function()
right_button_pressed = 1
right_button_pressed_time_start = time()
if right_button_pressed:
right_button_pressed_time = (
time() - right_button_pressed_time_start)
if right_button_pressed_time > button_press_delta:
call_my_function()
right_button_pressed_time_start = time()
else:
right_button_pressed = 0
You should use clock.tick(10) as stated in the docs.
all of the answers above are too complexicated i would just change the variables by 0.1 instead of 1
this makes the ship 10 times slower
if that is still too fast change the variables by 0.01
this makes the ship 100 times slower
try this
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
location -= 0.1 #or 0.01
if location==-1:
location=0
if keys[K_RIGHT]:
location += 0.1 #or 0.01
if location==5:
location=4
To slow down your game, use pygame.clock.tick(10)

Differentiate between key pressed just once and key held [pygame]

So in Pygame, I am trying to create this bat/ball game where the ball comes from the top of the screen (like a meteor) at various speeds, and the bat is near the bottom of the screen that can be moved left/right. Depending on the key pressed (direction the ball goes) and if there is a hit (i.e collision), the ball goes in a certain direction. For example, if you press the "d" key then the ball goes to the right side (provided there was a hit/collision).
Now everything seems to be working fine, however, what I actually want is the player timing the key press WHEN the ball is near the bat. At the moment, I can do that, but also if I keep holding the "d" key from much earlier, the ball still hits the bat. This is definitely not what I want, as it defeats the purpose of timing and having different speeds for the ball. I understand why this is happening as pygame.key.get_pressed() doesn't seem to differentiate between one key press and hold, and considers them one event. So, essentially, I would like to only let the collision happen if the key was pressed as the ball passed the bat and not while it was held from the very start. Here is the Ball class which contains the main code.
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = meteor_img
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
self.radius = int(self.rect.width * .85 / 2)
#pygame.draw.circle(self.image, RED, self.rect.center, self.radius)
self.rect.x = WIDTH /2 + 10
self.rect.y = HEIGHT - 770
self.speedy = random.randint(7, 14)
self.speedx = 0.3
self.j = 0
def update(self):
if self.rect.y < HEIGHT - 450 and self.j!= 1:
Ball.old_ball(self)
self.rect.x -= self.speedx
self.rect.y += self.speedy
#print (self.rect.x, self.rect.y)
elif self.j !=1:
Ball.change_ball(self)
self.rect.x += self.speedx
self.rect.y += self.speedy
#print (self.rect.x, self.rect.y)
else:
Ball.old_ball(self)
self.rect.x += self.speedx
self.rect.y += self.speedy
#print (self.rect.y)
shot_played = pygame.key.get_pressed()
hits = pygame.sprite.spritecollide(player, mobs, False, pygame.sprite.collide_circle)
#print (hits)
if hits and shot_played[pygame.K_e]:
#print ("Perfect Timing")
self.speedx = random.uniform(1, 5)
self.speedy = -random.uniform(2, 4)
self.j = 1
elif hits and shot_played[pygame.K_d]:
#print ("Perfect Timing")
self.speedx = random.uniform(6, 10)
self.speedy = -random.uniform(-2, 2)
self.j = 1
elif hits and shot_played[pygame.K_a]: #Leg Side
#print ("Perfect Timing")
self.speedx = -random.uniform(6, 10)
self.speedy = -random.uniform(-2, 2)
self.j = 1
if self.rect.top > HEIGHT + 10 or self.rect.left < -25 or self.rect.right > WIDTH + 20 or self.rect.y < 10:
self.rect.x = WIDTH /2+10
self.rect.y = HEIGHT - 770
self.speedy = random.randint(7, 14)
self.speedx = 0.3
self.j = 0
time.sleep(1)
#self.speedy = random.randrange(1, 8)
def change_ball(self):
self.image = ball_pitched_img
self.image.set_colorkey(WHITE)
def old_ball(self):
self.image = meteor_img
self.image.set_colorkey(WHITE)
You could just use an event loop instead of the state checking. When you press a key, pygame adds a pygame.KEYDOWN event to the queue once and a pygame.KEYUP event when the key is released. Calling pygame.event.get() empties the queue and returns a list of events over which you can iterate with a for loop to handle one event after the other.
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
done = False
while not done:
# for each event in the event queue.
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
print('d pressed')
# Check if the bat collides with the ball here.
screen.fill((30, 30, 30))
pygame.display.flip()
clock.tick(60)

Object follows coordinates (pygame)

I have an object that needs to follow 4 points given by the coordinates in self.list. When I execute the code, I can see that the checks are passed and it augments the search coordinates to the next point but the object on screen only goes to the last one.
Working in Python 3 and in pygame.
How can I fix this?
class Enemy:
def __init__(self, image):
super().__init__()
self.image = pygame.image.load(image)
self.rect = self.image.get_rect()
self.rect.x = 1280
self.rect.y = randrange(abs(720 - self.rect.height))
self.pattern = 2
self.list = [(1100,360),(900,180),(700,360),(900,540)]
self.tuple_dest = self.list[0]
self.i = 0
self.p=False
def move(self, player):
if self.tuple_dest[0] <= self.rect.x:
self.rect.x -= 1
elif self.tuple_dest[0] >= self.rect.x:
self.rect.x += 1
if self.tuple_dest[1] <= self.rect.y:
self.rect.y -= 1
elif self.tuple_dest[1] >= self.rect.y:
self.rect.y += 1
#check if arrived
print(self.p)
if self.tuple_dest[0] == self.list[self.i][0] and self.tuple_dest[1] == self.list[self.i][1] and self.p == False:
self.p = True
if self.i < (len(self.list)-1) and self.p==True:
print(self.p)
self.i += 1
print(self.i)
self.tuple_dest = self.list[self.i]
self.p = False
This is the problematic line:
if self.tuple_dest[0] == self.list[self.i][0] and self.tuple_dest[1] == self.list[self.i][1] and self.p == False:
You're just checking if the coordinates in the tuple are equal to the same tuple in the self.list, so the condition is immediately True.
You need to check if the current target coordinates are equal to the self.rect coordinates:
tx, ty = self.tuple_dest
if tx == self.rect.x and ty == self.rect.y and not self.p:
self.p = True

Arrow keys not moving pygame.surface diagonally when two keys are pressed

I am using python 3, on Pycharm CE, on a MacBook Air. I have previously and am currently working on basic games that use pygame. For some reason, when I am holding the down key to go down, then press the right key, it moves the player diagonally down to the right (which is what I like). However, when I move right, and then simultaneously press the up key, it does not do this behavior. It is not limited to just right/down keys, its just if I am moving the player down/up and simultaneously press left/right, then it moves diagonally. However if I am moving the player right/left and simultaneously press up/down, then it doesn't move diagonally.
I think it has something to do with the way pycharm, pygame, or python processes keyboard input, but I am not certain. Anyone else having these issues/annoyances?
def key_pressed(self):
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_RIGHT]:
self.vx = 500
elif key_pressed[pygame.K_LEFT]:
self.vx = -500
elif key_pressed[pygame.K_UP]:
self.vy = -500
elif key_pressed[pygame.K_DOWN]:
self.vy = 500
else:
self.vy = 0
self.vx = 0
def update(self):
self.rect.x += self.vx * self.game.dt
self.rect.y += self.vy * self.game.dt
self.key_pressed()
I think the answer posted above almost gets you there- the problem I think is that you only reset the vx and vy values if no keys are pressed. It seems less than ideal but having two separate blocks may solve the problem. In the above posted code the "else" only gets activated if the last "if" is true.
def key_pressed(self):
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_RIGHT] or key_pressed[pygame.K_LEFT]:
if key_pressed[pygame.K_RIGHT]:
self.vx = 500
if key_pressed[pygame.K_LEFT]:
self.vx = -500
else:
self.vx = 0
if key_pressed[pygame.K_UP] or key_pressed[pygame.K_DOWN]:
if key_pressed[pygame.K_DOWN]:
self.vy = 500
if key_pressed[pygame.K_UP]:
self.vy = -500
else:
self.vy = 0
Try this out and let us know if it fixes the behavior you're seeing.
Try replacing the elif statements with if statements, so then it won't skip over any of the key presses:
if key_pressed[pygame.K_RIGHT]:
self.vx = 500
if key_pressed[pygame.K_LEFT]:
self.vx = -500
if key_pressed[pygame.K_UP]:
self.vy = -500
if key_pressed[pygame.K_DOWN]:
self.vy = 500
else:
self.vy = 0
self.vx = 0

Resources