How to make a delay in a keypress in pygame? - python

I am making a platformer and to make an easy version of gravity, I need to make it so when I jump(spacebar) after 2 seconds or so gravity turns back on. Basically 'ychange' changes the y axis position of the character per frame (if ychange = 3, it goes down 3 pixels per frame). It is always set to 6 so he falls 6 frames per frame until he hits something that has a collision, to make him jump ychange is set to -6 or -7, which makes him raise 6 or 7 pixels a frame.
My question is, how can I delay switching from ychange = -6 to ychange = 6 for a jump
Ex:
WHEN JUMP:
ychange = -6
DELAY FOR 2-3 SECONDS
ychange = 6
I have obviously tried time.sleep and pygame.time.wait but they literally pause the program until the wait is over which obviously is not good.
The code for the jump is:
while not gameExit:
for event in pygame.event.get(): # Binds
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
xchange -= 3
if event.key == pygame.K_d:
xchange += 3
if event.key == pygame.K_SPACE: # SPACEBAR/JUMP
ychange = -6
time.sleep(2)
ychange = 6
if event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_d:
xchange = 0
They "if event.key == pygame.K_SPACE" is the jump part, the rest are other binds.
That is an example of it with time.sleep command which pauses the whole program until the delay is finished.
Any ideas?
Restatement:
Problem: Delay pauses the program
Tried: time.sleep, pygame.time.wait
Question: How do I get a delay that will not pause the whole program

use pygame.time.get_ticks() to get the time when the player presses space and then set a boolean flag to true. Then get the time once a frame until 2 seconds have passed from the original time, at which point you set ychange to 6 and set the flag to false again.
Alternatively look up threading like one of the comments suggests.
Pseudocode:
flag = False; # do this before the game loop starts
if event.type == pygame.KEYUP:
# checking flag prevents resetting the timer
if flag == False and event.key == pygame.K_w:
starttime = pygame.time.get_ticks()
ychange = -6
flag = True
# wait 2000 milliseconds before executing this
if flag == True and pygame.time.get_ticks() - starttime >= 2000:
ychange = 6
flag = False

Related

A resetting powerup timer

Im learning how to use pygame from a youtube and now im done with the video ive taken the game further by adding new enemies altering speeds and background objects .i'd also like to take the game further by adding a feature that allows you to do a 'mega jump' to avoid a large amount of enemies but i want to make it so it can only be used once every 5 seconds and when you use it the 5 second timer resets.
i will link the code below
if event.type == pygame.KEYDOWN: #if any key pressed
if event.key == pygame.K_w: #checking for specific key
if player_rect.bottom > 299:
player_grav = -22.5
if event.key == pygame.K_e: #checking for specific key
if player_rect.bottom > 299:
player_grav -= 30 #here is where the jump should be but i have no idea what to do
Use pygame.time.get_ticks() to measure the time in milliseconds. Set the time when the mega jump may be performed and check if the current time is greater than this time:
next_mega_jump = 5000 # 5000 milliseconds == 5 seconds
run = True
while run:
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
if player_rect.bottom > 299:
player_grav = -22.5
print("jump")
if event.key == pygame.K_e:
if player_rect.bottom > 299 and current_time > next_mega_jump:
next_mega_jump = current_time + 5000
player_grav = -30
print("mega jump")

Countdown timer not starting at the given value

this is my game play function with the main loop. The issue is that my timer with the inclusion of clock.tick(60) counts down from 100 (the assigned value) but at a pace slower than seconds (about 2 seconds). Without clock.tick(60) the countdown starts at 100 then immediately jumps to 97 then continues at a pace of just a little longer than seconds. All i'm aware of is that apart from clock.tick(60), milliseconds += clock.tick(60) has an effect aswell. is there a way to ensure the countdown starts at 100 and has second intervals without negatively effecting the much needed fps? If any more code such is needed then i'm happy to submit it.
Note: I've removed some unnecessary details for this post hence the seemingly unused global variables
def Gameplay():
global P1_sped
global P1_speed
global P2_sped
global P2_speed
global Touch
global player1_Lives
global player2_Lives
global P1_score
global P2_score
global done
Player_1_turn = True
Player_2_turn = False
P1_Turns = 3
P2_Turns = 3
resetTimer = 100
clock = pygame.time.Clock()
seconds = 100
milliseconds = 0
P1_sped = P1_sped + 1
P1_speed = P1_speed - 1
print(P1_sped, "sped")
print(P1_speed, "speed")
Countdown = True
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Keydown movement bindings
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
P2.changespeed(P2_speed, 0)
if event.key == pygame.K_RIGHT:
P2.changespeed(P2_sped, 0)
if event.key == pygame.K_UP:
P2.changespeed(0, P2_speed)
if event.key == pygame.K_DOWN:
P2.changespeed(0, P2_sped)
if event.key == pygame.K_a:
P1.changespeed(P1_speed, 0)
if event.key == pygame.K_d:
P1.changespeed(P1_sped, 0)
if event.key == pygame.K_w:
P1.changespeed(0, P1_speed)
if event.key == pygame.K_s:
P1.changespeed(0, P1_sped)
# Keyup movement bindings
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
P2.changespeed(P2_sped, 0)
if event.key == pygame.K_RIGHT:
P2.changespeed(P2_speed, 0)
if event.key == pygame.K_UP:
P2.changespeed(0, P2_sped)
if event.key == pygame.K_DOWN:
P2.changespeed(0, P2_speed)
if event.key == pygame.K_a:
P1.changespeed(P1_sped, 0)
if event.key == pygame.K_d:
P1.changespeed(P1_speed, 0)
if event.key == pygame.K_w:
P1.changespeed(0, P1_sped)
if event.key == pygame.K_s:
P1.changespeed(0, P1_speed)
#milliseconds += clock.tick_busy_loop(60) #returns the time since the last time we called the function, and limits the frame rate to 60FPS
milliseconds += clock.tick(60)
# Applying an update to the sprites
all_sprite_list.update()
# Backgorund colour
screen.fill(D_BLUE)
# Applying the sprites
all_sprite_list.draw(screen)
# The countdown used before the game starts
if Countdown == True:
screen.blit(THREE, (SCREEN_WIDTH/2-53.5,SCREEN_HEIGHT/2-91.5))
pygame.display.update()
time.sleep(1)
screen.fill(D_BLUE)
all_sprite_list.draw(screen)
#pygame.draw.rect(screen, BLACK,(SCREEN_WIDTH/2-250,SCREEN_HEIGHT/2-250,500,500))
screen.blit(TWO, (SCREEN_WIDTH/2-52,SCREEN_HEIGHT/2-88.5))
pygame.display.update()
time.sleep(1)
screen.fill(D_BLUE)
all_sprite_list.draw(screen)
#pygame.draw.rect(screen, BLACK,(SCREEN_WIDTH/2-250,SCREEN_HEIGHT/2-250,500,500))
screen.blit(ONE, (SCREEN_WIDTH/2-39,SCREEN_HEIGHT/2-87))
pygame.display.update()
time.sleep(1)
Countdown = False
if milliseconds > 1000:
seconds -= 1
milliseconds -= 1000
if seconds == 0:
#Countdown = True
P1.reset(500, 500)
P2.reset(800, 500)
if Player_1_turn:
P1_score += 1000
P1_Turns -= 1
Countdown = True
seconds = resetTimer
if P1_Turns == 0:
Text("GameOver Player1", 100, WHITE, SCREEN_WIDTH/2, SCREEN_HEIGHT/2-350)
pygame.display.update()
time.sleep(1)
Player_2_turn = True
Player_1_turn = False
else:
#if Player_2_turn == True or Player_1_turn == False:
P2_score += 1000
P2_Turns -= 1
Countdown = True
seconds = resetTimer
if P2_Turns == 0:
Text("GameOver Player2", 100, WHITE, SCREEN_WIDTH/2, SCREEN_HEIGHT/2-350)
pygame.display.update()
time.sleep(1)
#GameEnd()
CharSel()
#print(P1_score, "P1 score")
#print(P2_score, "P2 score")
Text(seconds, 100, WHITE, SCREEN_WIDTH/2, SCREEN_HEIGHT/2-350)
pygame.display.flip()
# Used for fps
clock.tick(60)
StartScreen()
pygame.quit()
You call clock.tick(60) twice in your main loop. Once where you do milliseconds += clock.tick(60) and once at the end of the loop. If you read the docs, you will see this 'This method should be called once per frame. It will compute how many milliseconds have passed since the previous call. If you pass the optional framerate argument the function will delay to keep the game running slower than the given ticks per second'.
So if you call it twice per loop the effect will be to have the loop execute at half the expected frame rate. Since you are calling it with 60, that means that you frame rate is actually going to be 30 frames per second.
Also, the return is the amount of time since the last time it was called. Since you call it twice, the call at the top of the loop will return the time since the call at the bottom of the loop, not since the last call from the top of the loop. So it is only going to be counting about half the time in your game loop, which accounts for why you are trying to count 1 second worth but 2 seconds are elapsing.
You need to remove one of the two calls to clock.tick(60)
EDIT:
You have the Countdown == True section that uses up 3 seconds on initial startup. That countdown takes place between the first milliseconds += clock.tick(60) and the following clock.tick(60). Those 3 seconds get returned and then hidden by the clock.tick(60), whereas without that they will be returned as 3000 ms to the milliseconds += clock.tick(60) and so cause the time displayed to quickly drop down by those 3 seconds.
You could probably hide that time loss by adding a clock.tick() right after the Countdown = False inside the if Countdown == True block.

Continuous object creation in pygame?

Okay, so I've researched this topic a bit. I'm creating a game in Python's Pygame, a replica of the famous "Raiden 2". My game loop is fairly similar to those I've seen around. What I'm trying to do is have the constructor create a bullet object (with my Bullet class) while the space bar is being held. However, the following code only creates a single bullet per keypress. Holding the button does nothing, just creates a single bullet.
while game.play is True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
b = Bullet(x, y)
bullet_group.add(b)
bullet_group.draw(screen)
Not sure where to go from here. Any help is welcome and appreciated.
This should work as you expect it:
addBullets = False
while game.play is True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
addBullets = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
addBullets = False
if addBullets:
b = Bullet(x, y)
bullet_group.add(b)
bullet_group.draw(screen)
You move the creation of bullets out of the handling of events and set there only a "flag" which is taken back if the key is released stopping creation of bullets.
And ... as mentioned in the comment by jsbueno this will work too:
while game.play is True:
for event in pygame.event.get():
pass # do something with events
if pygame.key.get_pressed()[pygame.K_SPACE]:
b = Bullet(x, y)
bullet_group.add(b)
I think the other answer is lacking an important detail. You most likely don't want to fire a bullet every frame, so you need to have some kind of timer. The simplest way is to count the frames, but then the game would be frame rate bound (better don't do it in this way).
firing = False
bullet_timer = 0
while game.play:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game.play = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
firing = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
firing = False
if bullet_timer > 0:
bullet_timer -= 1
elif firing: # Timer is less than 0 and we're firing.
bullet = Bullet()
bullet_group.add(bullet)
bullet_timer = 10 # Reset timer to 10 frames.
The frame rate independent variant is to use the time that pygame.Clock.tick returns (the time that has passed since it was called the last time) and subtract it from the timer variable. If the timer is <= 0 we're ready to fire.
clock = pygame.time.Clock()
firing = False
bullet_timer = 0
while game.play:
# `dt` is the passed time since the last tick in seconds.
dt = clock.tick(30) / 1000
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
firing = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
firing = False
if bullet_timer > 0:
bullet_timer -= dt
elif firing: # Timer is below 0 and we're firing.
bullet = Bullet()
bullet_group.add(bullet)
bullet_timer = .5 # Reset timer to a half second.

pygame moving left and right issue

I have started making something on pygame but I have encountered an issue when moving left or right. if I quickly change from pressing the right arrow key to pressing the left one and also let go of the right one the block just stops moving. this is my code
bg = "sky.jpg"
ms = "ms.png"
import pygame, sys
from pygame.locals import *
x,y = 0,0
movex,movey=0,0
pygame.init()
screen=pygame.display.set_mode((664,385),0,32)
background=pygame.image.load(bg).convert()
mouse_c=pygame.image.load(ms).convert_alpha()
m = 0
pygame.event.pump()
while 1:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type==KEYDOWN:
if event.key==K_LEFT:
movex =-0.5
m = m + 1
if event.key==K_RIGHT:
movex=+0.5
m = m + 1
elif event.type == KEYUP:
if event.key==K_LEFT and not event.key==K_RIGHT:
movex = 0
if event.key==K_RIGHT and not event.key==K_LEFT:
movex =0
x+=movex
y=200
screen.blit(background, (0,0))
screen.blit(mouse_c,(x,y))
pygame.display.update()
is there a way I can change this so if the right arrow key is pressed and the left arrow key is released that it will go right instead of stopping?
P.S
I am still learning pygame and am very new to the module. I'm sorry if this seems like a stupid question but i couldn't find any answers to it.
Your problem is that when you test the KEYDOWN events with
if event.key==K_LEFT and not event.key==K_RIGHT:
you always get True, because when event.key==K_LEFT is True,
it also always is not event.key==K_RIGHT (because the key of the event is K_LEFT after all).
My approach to this kind of problem is to separate
the intent from the action. So, for the key
events, I would simply keep track of what action
is supposed to happen, like this:
moveLeft = False
moveRight = False
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT: moveLeft = True
if event.key == K_RIGHT: moveRight = True
elif event.type == KEYUP:
if event.key == K_LEFT: moveLeft = False
if event.key == K_RIGHT: moveRight = False
Then, in the "main" part of the loop, you can
take action based on the input, such as:
while True:
for event in pygame.event.get():
...
if moveLeft : x -= 0.5
if moveRight : x += 0.5
the problem is that you have overlapping key features; If you hold down first right and then left xmove is first set to 1 and then changes to -1. But then you release one of the keys and it resets xmove to 0 even though you are still holding the other key. What you want to do is create booleans for each key. Here is an example:
demo.py:
import pygame
window = pygame.display.set_mode((800, 600))
rightPressed = False
leftPressed = False
white = 255, 255, 255
black = 0, 0, 0
x = 250
xmove = 0
while True:
window.fill(white)
pygame.draw.rect(window, black, (x, 300, 100, 100))
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
rightPressed = True
if event.key == pygame.K_LEFT:
leftPressed = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
rightPressed = False
if event.key == pygame.K_LEFT:
leftPressed = False
xmove = 0
if rightPressed:
xmove = 1
if leftPressed:
xmove = -1
x += xmove
pygame.display.flip()
One way could be to create a queue that keeps track of the button that was pressed last. If we press the right arrow key we'll put the velocity first in the list, and if we then press the left arrow key we put the new velocity first in the list. So the button that was pressed last will always be first in the list. Then we just remove the button from the list when we release it.
import pygame
pygame.init()
screen = pygame.display.set_mode((720, 480))
clock = pygame.time.Clock()
FPS = 30
rect = pygame.Rect((350, 220), (32, 32)) # Often used to track the position of an object in pygame.
image = pygame.Surface((32, 32)) # Images are Surfaces, so here I create an 'image' from scratch since I don't have your image.
image.fill(pygame.Color('white')) # I fill the image with a white color.
velocity = [0, 0] # This is the current velocity.
speed = 200 # This is the speed the player will move in (pixels per second).
dx = [] # This will be our queue. It'll keep track of the horizontal movement.
while True:
dt = clock.tick(FPS) / 1000.0 # This will give me the time in seconds between each loop.
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
dx.insert(0, -speed)
elif event.key == pygame.K_RIGHT:
dx.insert(0, speed)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
dx.remove(-speed)
elif event.key == pygame.K_RIGHT:
dx.remove(speed)
if dx: # If there are elements in the list.
rect.x += dx[0] * dt
screen.fill((0, 0, 0))
screen.blit(image, rect)
pygame.display.update()
# print dx # Uncomment to see what's happening.
You should of course put everything in neat functions and maybe create a Player class.
I know my answer is pretty late but im new to Pygame to and for beginner like me doing code like some previous answer is easy to understand but i have a solution to.I didn`t use the keydown line code, instead i just put the moving event code nested in the main game while loop, im bad at english so i give you guy an example code.
enter code here
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.K_ESCAPE:
run = False
win.blit(bg, (0, 0))
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]:
x -= 5
if pressed[pygame.K_RIGHT]:
x += 5
if pressed[pygame.K_UP]:
y -= 5
if pressed[pygame.K_DOWN]:
y += 5
win.blit(image,(x,y))
pygame.display.update()
pygame.quit()
This will make the image move rapidly without repeating pushing the key, at long the code just in the main while loop with out inside any other loop.

Character only maintains movement while mouse is moving on screen?

My while loops only maintains the movement for the sprite while the cursor is moving inside of the screen. I've tried reorganizing some of the screen.blits and display.update() and display.flip(). I can't seem to figure out why the character stops after a change in one pixel instead of continuing like it I intended.
background_image = 'Terrain_Grass_First.png'
import pygame, sys
from pygame.locals import *
pygame.init()
pygame.display.set_caption('Hans')
screen_width = 600
screen_height = 400
screen = pygame.display.set_mode((screen_width, screen_height),0,32)
pygame.mouse.set_visible(False)
sprite = pygame.image.load('Hans_front_still.png').convert_alpha()
x,y = (0,0)
movex, movey = (0,0)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_w:
y = -1
elif event.key == K_a:
x = -1
elif event.key == K_s:
y = +1
elif event.key == K_d:
x = +1
elif event.type == KEYUP:
if event.key == K_w:
y = 0
elif event.key == K_a:
x = 0
elif event.key == K_s:
y = 0
elif event.key == K_d:
x = 0
movex += x
movey += y
screen.fill((0,0,0))
screen.blit(sprite,(movex,movey))
pygame.display.flip()
Your loop blocks on pygame.event.get.
You don't schedule any events of your own.
So, you do nothing until the OS has an event (mouse move, redraw, etc.) to give you.
And, even if you fixed that, you're calling movex += x once per event. So, when the OS is throwing a lot of events at you, your sprite will go zipping madly across the screen, but when the events are coming more slowly, it will crawl along. That's almost never what you want.
An easy fix for both problems is to just schedule your own events. For example, with pygame.time.set_timer(), you can make sure you get an event every, say, 250 milliseconds, and you can only move the sprite on those events. For example:
timer_event = pygame.USEREVENT + 1
pygame.time.set_timer(timer_event, 250)
while True:
for event in pygame.event.get():
# ...
elif event.type == timer_event:
movex += x
movey += y
Another alternative is to design your game with a fixed framerate.
A trivial example looks like this:
FRAME_TIME = 50 # 50ms = 20fps
next_frame_time = pygame.time.get_ticks() + FRAMES
while True:
while True:
event = pygame.event.poll()
if event.type == pygame.NOEVENT:
break
elif # ...
pygame.display.flip()
now = pygame.time.get_ticks()
if now < next_frame_time:
pygame.time.wait(next_frame_time - now)
next_frame_time += FRAMES
Now you can just move every 5th frame.
A realistic example has to deal with missed frames, too many events in the queue, choose between wait and delay appropriately, etc.
But I'd go with the event-driven version with a timer instead of a fixed-framerate version in most cases, especially if you're already going down that line.
The only problem is your Indentation!
The fifth and sixth lines from the bottom have wrong indentation, and you need to delete them.
These two lines:
movex += x
movey += y
should be:
movex += x
movey += y
And it works

Categories