Countdown timer not starting at the given value - python

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.

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")

PYGAME - create clock and do action at intervals

I'm making a game where a moving cube at the top of the screen will fire a cube down the screen at certain intervals. How would I do this. For example, I want it so that every 1 second the moving cube will fire a projectile down the screen towards the player icon and when it reaches past the screen, it will respawn where the moving cube is and be able to fire again.
This is what I have so far.
import pygame
pygame.init()
screen = pygame.display.set_mode((280, 800))
pygame.display.set_caption("Cube Run")
icon = pygame.image.load("cube.png")
pygame.display.set_icon(icon)
player_icon = pygame.image.load("cursor.png")
player_x = 128
player_y = 750
player_x_change = 0
cube_1 = pygame.image.load("rectangle.png")
cube1_x = 128
cube1_y = 0
cube1_x_change = 0.8
cube_fire = pygame.image.load("rectangle.png")
cube_fire_x = 0
cube_fire_y = 0
cube_y_change = 1.5
cube_fire_state = "ready"
def player(player_x, player_y):
screen.blit(player_icon, (player_x, player_y))
def cube(cube1_x, cube1_y):
screen.blit(cube_1, (cube1_x, cube1_y))
def cube_enemy(cube_fire_x, cube_fire_y):
screen.blit(cube_fire, (cube_fire_x, cube_fire_y))
running = True
while running:
screen.fill((255, 255, 255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player_x_change += 0.7
if event.key == pygame.K_LEFT:
player_x_change -= 0.7
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or pygame.K_LEFT:
player_x_change = 0
player_x += player_x_change
if player_x < 0:
player_x = 0
elif player_x > 280-32:
player_x = 280-32
cube1_x += cube1_x_change
if cube1_x > 248:
cube1_x_change = -1
cube1_x += cube1_x_change
elif cube1_x < 0:
cube1_x_change = 1
cube1_x += cube1_x_change
cube_fire_x += cube1_x
cube_enemy(cube_fire_x, cube_fire_y)
player(player_x, player_y)
cube(cube1_x, cube1_y)
pygame.display.update()
You can register events with pygame.time.set_timer. Create a new event and set how many milliseconds should pass before it's fired. This event will then appear at the set intervall.
FIRE_EVENT = pygame.USEREVENT + 1 # This is just a integer.
OTHER_EVENT = pygame.USEREVENT + 2 # This is how you define more events.
pygame.time.set_timer(FIRE_EVENT, 1000) # 1000 milliseconds is 1 seconds.
Then in your event loop, you check for this event and do whatever you want.
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == FIRE_EVENT: # Will appear once every second.
make_square_fire()
When you want to disable the event, just set the interval to 0.
pygame.time.set_timer(FIRE_EVENT, 0)
In your code, you don't include a time manager of any kind – which means your code will run as fast as possible, an you can't really control how fast that will be, it will really depend on the machine it is working and on the CPU load, among other things.
Basically, you want to purposely wait within your program just the right amount of time so you can dynamically adapt to the execution speed. You could implement this by yourself (it isn't that hard, and there are plenty of tutorials out there), but to take a first glance of it, you could use pygame.Clock:
first, create a clock with clock = pygame.Clock().
Then, within your main loop, call eta = clock.tick(FPS), where FPS represents the target frame rate you want your application to run (you can simply fix it to 60 at the start of your program if don't really know what value you want it to be), and the eta variable measures the time elapsed (in milliseconds) since last tick call.
Next, to have something happen, say, every second, just keep a counter:
counter = 1000 # in ms
clock = pygame.Clock()
while True:
# do what you want
eta = clock.tick(FPS)
counter -= eta
if counter < 0:
# trigger the event
counter += 1000
# don't set it directly like
# counter = 1000
# to keep track of margin

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.

How to make a delay in a keypress in pygame?

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

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