how to prevent button spamming in pygame - python

basically, at the moment, in my game the user can shoot as many times as they wish... this defeats the objective of the game (it makes it too easy) as they can just spam bullets. I have a 'Shop' type function which enables the user to purchase upgrades, therefore the spamming defeats the purpose of the shop.
elif event.key == K_SPACE:
newBullet = {'rect': pygame.Rect(bulletX, bulletY, 10, 3), 'surface': bullet}
pistolSound.play()
bullets.append(newBullet)
This is the code, what can I add to it in order prevent the button mashing spam. I'm bad with the pygame time function, but I have a feeling that could be used.
Additionally, i would like to my zombies to spawn consistently instead of at the moment where it is quite random...
timer = clock.tick()
if waveNumber <= 2:
timer += 1
if timer == 2:
newZombie = {'rect':pygame.Rect(zombieX, zombieY, 75, 75), 'surface': zombie}
zombieSound.play()
zombies.append(newZombie)
timer = 0
How would I go about doing this? I think I'm using the time function wrong.
Many thanks, Will.

Limit the number of bullets to some small number:
elif event.key == K_SPACE:
if len(bullets) < 3:
newBullet = {'rect': pygame.Rect(bulletX, bulletY, 10, 3), 'surface': bullet}
pistolSound.play()
bullets.append(newBullet)

You should break your simulation and event handling into brief "turns" and allow the player to shoot only one bullet per turn (or even less).
The closest to your code example would be setting a "shot button pressed this turn" flag when the appropriate kind of event is processed, then after all events have been processed shooting at most once:
#each event
if event.key == K_SPACE:
playerShooting=True
...
#each turn
if playerShooting:
newBullet = {'rect': pygame.Rect(bulletX, bulletY, 10, 3), 'surface': bullet}
pistolSound.play()
bullets.append(newBullet)
playerShooting=False#clear to prepare for next turn
Zombies have a similar problem: you cannot predict the value of clock.tick(). Instead, spawn zombies at most once per turn (likely on a schedule, e.g. every fifth turn). The clock should be used to decide whether the main loop should proceed to game state update instead of waiting for further inputs, and to decide when to render frames.
For reference, a bad example and a good but incomplete example of PyGame main loops.

Related

Spawning Object issue

I am trying to get objects spawn on screen. But they don't seem to come up.
When exiting the game the sprites come up. It's only during the game you can't see any objects.
Exit = False
while not Exit:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == QUIT:
Exit = True
elif event.type == USEREVENT +1:
fireball.add(fire(screen, random.randint(50,1000), random.randint(50,800)))
fireball.update()
fireball.draw(screen)
pygame.display.flip()
Since you do not tell us, I'm going to assume that fireball is a sprite.Group and fire a child class of sprite.Sprite. From the little I can see, seems to be the correct guess.
So what you want is to create and add fire instances to the fireball group during the game.
You can achieve it by adding the following lines in the main loop before firefall.update():
newev = pygame.event.Event(USEREVENT+1)
pygame.event.post(newev)
This will create a custom event of type USEREVENT+1 which will be catch by the event checking loop next iteration, executing hence the line: fireball.add(fire(screen, random.randint(50,1000), random.randint(50,800)))
Maybe you do not want to create a new fire sprite each iteration. In that case you should add some flow control to skip those lines under some conditions.
For example, if you want a random approach, you can do:
if random.random() < 0.1:
newev = pygame.event.Event(USEREVENT+1)
pygame.event.post(newev)
In this case, each iteration of the main loop you have a 10% of probability to submit the custom event. Adjust the probability to your suits.
If instead you want to add a new fire sprite after a given amount of time, you need to measure the time with pygame.time.get_ticks(). If a given amount of time is passed, the event is submitted.
checktime = pygame.time.get_ticks() - reftime
if checktime > 5000: #5 seconds, the time is in milliseconds
reftime = pygame.time.get_ticks() #reset the reference time
newev = pygame.event.Event(USEREVENT+1)
pygame.event.post(newev)
And of course remember to define reftime = pygame.time.get_ticks() the first time before the main loop.
You can refer to this answer for another example on how to measure time with pygame.

ways of using midi events to make a player move in pygame

I have made some little games with the pygame module and it had been fun. However, right now i´m trying to move the character with a piano via midi using pygame.midi. there´s the problem. I did it but not in the way i wanted to because
the character moves sometimes and other it doesn´t. I think i know where is the problem: If i understand correctly, the pygame.event.get() function "catch" the events always, it does not matter when (inside the main loop of the game), so i think that with my code this is not happening, i mean, i believe that the player not always do what is supposed to do because the midi input has to be in a specific "time" not as pygame.event.get(). I looked into the pygame documentation and there seems to be a function that does the trick: pygame.midi.midis2events(), but i don´t know how to use it.The question is just:
How can i move the character through the piano via midi with pygame in order that the character moves everytime?
import pygame as pg, pygame.midi
WIDTH = 800
HEIGHT = 600
FPS = 60
BLACK = (0,0,0)
BLUE = (0,0,255)
pg.init()
pg.midi.init()
screen = pg.display.set_mode((WIDTH,HEIGHT))
pg.display.set_caption('STACKOVERFLOW_EXAMPLE_MIDI')
clock = pg.time.Clock()
running = True
inp = pg.midi.Input(1)
x = WIDTH//2
y = HEIGHT//2
speedx = 0
midi_list = []
while running:
clock.tick(FPS)
if inp.poll():
midi_value = inp.read(1000)[0][0][1]
if midi_value==57:
midi_list.append(midi_value)
if len(midi_list)==1:
speedx = -1
else:
speedx = 0
midi_list = []
if midi_value == 63:
running = False
x = x+speedx
screen.fill(BLUE)
pg.draw.rect(screen,BLACK,[x,y,50,60])
pg.display.update()
pg.display.flip()
pg.quit()
I need that the player moves to the left (or to the right) when the piano´s key A3 (a specific note) is being pressed and that the player stops moving when
the key has been released. In this particular fragment of the code, but also to do things like press a piano´s key an shoot one bullet regardless of whether is being pressed or not, in short, to do the same things i can do with the keyboard events.
ok! i found and answer that works well for the time being:
if inp.poll():
s(0.1)
midi_values = inp.read(1000)
for midi in midi_values:
midi_value = midi[0][1]
#Movement to the left
if midi_value==57:
midi_list.append(midi_value)
if len(midi_list)==1:
speedx = -50
Modifying the code in order to iterate the list midi_values gives me the correct input-output response without the inestability which i experienced. Also i did introduce a sleep(0.1) delay and works well without to much cpu use. I found the answer here:
https://www.reddit.com/r/learnpython/comments/36dxu9/python_whileloop_cpu_demanding/
The only problem which i find now is that the pygame window stops with the message no responde (doesn´t respond or something similar) and it stops working.
For example the cube which i´m moving with the keys of the piano doesn´t do anything but the key to close the program does work fine!!

pygame simple game lagging

hi i am beginner in programming and pygame
and i was trying to make game like this
http://dan-ball.jp/en/javagame/dust/
and i started by making this simple code
import pygame
from pygame.locals import*
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (0,25)
pygame.init()
screen=pygame.display.set_mode((1360,705))
clock = pygame.time.Clock()
boxs=[]
while True :
screen.fill((0,0,0))
for event in pygame.event.get():
if event.type==QUIT :
pygame.quit()
quit()
if event.type== pygame.MOUSEBUTTONDOWN:
if event.button == 1:
mous=pygame.math.Vector2(event.pos[0]-4,event.pos[1]-4)
boxs.append(mous)
if event.type== pygame.MOUSEBUTTONDOWN:
if event.button == 3:
print (len(boxs))
for i in range(len(boxs)):
if ((boxs[i])[1])>=685:
pass
else:
(boxs[i])[1]=(boxs[i])[1]+2
for v in range (len(boxs)):
if v==i:
pass
else :
if (int((boxs[i])[1]))+4 >= (int((boxs[v])[1])) and (int((boxs[i])[1])) <= (int((boxs[v])[1]))+4 and (int((boxs[i])[0]))+4 >= (int((boxs[v])[0])) and (int((boxs[i])[0])) <= (int((boxs[v])[0]))+4:
(boxs[i])[1]=(boxs[i])[1]-2
break
pygame.draw.rect(screen,(250,250,250),((boxs[i])[0],(boxs[i])[1], 4, 4))
pygame.display.update()
clock.tick(60)
it works very good as start but my problem is that when the boxes became more the 350 or 400 the game will be lagging too much i dont know what iam doing wrong
and sorry if i was asking too much bad questions but thank you so much i have learned so much from you guys
I notice that you're doing this:
if ((boxs[i])[1])>=685:
pass
That means that you're never deleting boxes that go off the bottom of the screen, so as time goes by, your box list is getting larger and larger, which is going to eventually lead to lag.
You're also using Pygame vectors for your list, and then not taking advantage of them. For example, instead of:
(boxs[i])[1]=(boxs[i])[1]+2
you can say:
boxs[i].y += 2
Finally, you are not looping in a Pythonic way. In Python, you almost never need to loop with an index variable. Instead, you should be doing something like this:
for box in boxs:
if box.y >= 685:
pass
else:
box.y += 2
for other in boxs:
if other == box:
pass
and so on...
Finally, you should be using pygame Rect() objects, because then you can use the builtin pygame collision functions to easily handle your collisions.
http://www.pygame.org/docs/ref/rect.html

Change the character image for a while in Python Pygame

I am struggling with a problem I can't solve.
I want to change the image of my character when the enemy hurts it.
In pseudocode, it would be like this:
*If enemy collides -> player close eyes and get red;*
*After 0.50 seg -> player gets normal back again*
I tried to do it with Clock and Timers but it is very difficult. I only get changing the image, but not getting it back.
Any ideas?
Thank you!
I would assume it's as easy as this. pygame.time.set_timer(x, y) basically creates an x event on the event stack every y milliseconds.
# Main game loop
while True:
# display stuff and other parts of your game
# replace this with whatever detection you have for collision
if enemycollide:
player.setSprite(1)
pygame.time.set_timer(14, 500) # 500 milliseconds = .5 seconds
# event handling
for event in pygame.event.get():
if event.type == 14:
player.setSprite(0)
pygame.time.set_timer(14, 0) # Disable the timer

Do something every x (milli)seconds in pygame

I'm learing Python and Pygame, and my first thing I'm making is a simple Snake game. I'm trying to make it so that the snake moves once every 0.25 seconds. Here is the part of my code that loops:
while True:
check_for_quit()
clear_screen()
draw_snake()
draw_food()
check_for_direction_change()
move_snake() #How do I make it so that this loop runs at normal speed, but move_snake() only executes once every 0.25 seconds?
pygame.display.update()
I want all of the other function to run normally, but move_snake() to only occur once every 0.25 seconds. I've looked it up and found a few answers but they all seem too complicated for someone who's making their first ever Python script.
Would it be possible to actually get an example of what my code should look like rather than just telling me which function I need to use? Thanks!
There are several approaches, like keeping track of the system time or using a Clock and counting ticks.
But the simplest way is to use the event queue and creating an event every x ms, using pygame.time.set_timer():
pygame.time.set_timer()
repeatedly create an event on the event queue
set_timer(eventid, milliseconds) -> None
Set an event type to appear on the event queue every given number of milliseconds. The first event will not appear until the amount of time has passed.
Every event type can have a separate timer attached to it. It is best to use the value between pygame.USEREVENT and pygame.NUMEVENTS.
To disable the timer for an event, set the milliseconds argument to 0.
Here's a small, running example where the snake moves every 250 ms:
import pygame
pygame.init()
screen = pygame.display.set_mode((300, 300))
player, dir, size = pygame.Rect(100,100,20,20), (0, 0), 20
MOVEEVENT, t, trail = pygame.USEREVENT+1, 250, []
pygame.time.set_timer(MOVEEVENT, t)
while True:
keys = pygame.key.get_pressed()
if keys[pygame.K_w]: dir = 0, -1
if keys[pygame.K_a]: dir = -1, 0
if keys[pygame.K_s]: dir = 0, 1
if keys[pygame.K_d]: dir = 1, 0
if pygame.event.get(pygame.QUIT): break
for e in pygame.event.get():
if e.type == MOVEEVENT: # is called every 't' milliseconds
trail.append(player.inflate((-10, -10)))
trail = trail[-5:]
player.move_ip(*[v*size for v in dir])
screen.fill((0,120,0))
for t in trail:
pygame.draw.rect(screen, (255,0,0), t)
pygame.draw.rect(screen, (255,0,0), player)
pygame.display.flip()
Use the Clock module of Pygame to keep track of time. Specifically the method tick of the Clock class will report to you the number of milliseconds since the last time you called tick. Therefore you can call tick once at the beginning (or at the end) of every iteration in your game loop and store its return value in a variable called dt. Then use dt to update your time-dependent game state variables.
time_elapsed_since_last_action = 0
clock = pygame.time.Clock()
while True: # game loop
# the following method returns the time since its last call in milliseconds
# it is good practice to store it in a variable called 'dt'
dt = clock.tick()
time_elapsed_since_last_action += dt
# dt is measured in milliseconds, therefore 250 ms = 0.25 seconds
if time_elapsed_since_last_action > 250:
snake.action() # move the snake here
time_elapsed_since_last_action = 0 # reset it to 0 so you can count again

Categories

Resources