Change the character image for a while in Python Pygame - python

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

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

How can I create a timer that will start at a certain time and wont interupt my program in pygame?

I am making this game where you use the w, a, s, d keys to move a ball. I am trying to make a timer that starts when you press "start game". The problem is, when I do something like time.sleep, it interrupts the movement of the ball. I want to render the timer in the top right corner of the screen and make it 1 minute (also I will make a conditional statement for when the timer stops so I would like that to be possible).
This is what I believe would be easiest, and fit your needs the best.
clock = pygame.time.Clock()
fps = 60 # Or whatever frame-rate you want to cap the game at.
time = 0
game_started = False
# This is the main loop.
while True:
dt = clock.tick(fps)
if game_started:
time += dt
if time >= 60000: # 60 seconds.
game_started = False
# Then handle, events, update/draw objects etc.
Just set game_started = True when you press the button and the time variable will start to increment in time. Then you can just draw the time variable to the screen however you like. If you don't want to draw it when it isn't running then just blit it when game_started is True.

How to use a while loop within a class in pygame?

I'm trying to make an instance class (Block) spawn when the mouse is clicked and fade out slowly, and i want to be able to spawn an infinite number of blocks that fade out provided that the mouse is clicked fast enough.
To do this, I want to initiate the fade() function when the instance is first spawned and set ticks to 255 and set the alpha to ticks.
However, when using a while loop, the function completes itself without updating the display because the program gets constrained to the while loop in the fade() function.
Can someone help me with calling the fade function 255 times per instance?
import pygame,sys,time,Blockm
from pygame.locals import *
black,white=(0,0,0),(255,255,255)
size=w,h=1400,800
screen=pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert_alpha()
self.rect = Block.sprite.get_rect(top=y, left=x)
self.fade()
def fade(self):
ticks=255
Block.sprite.set_alpha(ticks)
while ticks!=0:
ticks-=1
print(ticks)
while True:
blocks = []
mmraw=pygame.mouse.get_pos()
mmx,mmy=mmraw[0]-(pygame.image.load("Block.png").get_width())/2,mmraw[1]-(pygame.image.load("Block.png").get_width())/2
for event in pygame.event.get():
if event.type== pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
print('click')
blocks.append(Block(mmx,mmy))
for block in blocks:
screen.blit(block.sprite, block.rect)
print('trying to blit')
print(Block.sprite.get_offset())
pygame.display.update()
I have one solution for you that ought to accomplish what you want it to, but I concede that it does not answer the question exactly as asked.
The first and most hindering problems you may be having are that you need to use pygame.image.load("Block.png").convert() and not pygame.image.load("Block").convert_alpha(), since .convert_alpha() doesn't work with .set_alpha(..).
It's also possible that you aren't noticing when blocks fade in any solution because screen isn't being refreshed prior to update. New, faded blocks are being drawn over 'brighter' ones, producing no difference (overlapping blocks notwithstanding). I've added a stopgap to my solution code below that fills the screen with blue, but I imagine you'll want something different. It's marked with a comment.
What I would suggest is having each Block process its alpha locally during a call from the main loop in your for block in blocks: block before you blit it. This version of your code should give you the result you want, although it does it using just the main loop, rather than parallel loops like you were asking about...
import pygame,sys,time,Blockm
from pygame.locals import *
black,white=(0,0,0),(255,255,255)
size=w,h=1400,800
screen=pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert()
# ^ MODIFIED: .convert_alpha() won't recognize the effects of .set_alpha(), so use regular .convert() instead.
self.rect = Block.sprite.get_rect(top=y, left=x)
self.ticks = 255 # NEW: Set a local tick count.
# REMOVED `self.fade()`: This runs on main now.
def fade(self):
## REPURPOSED METHOD: Runs each frame that the Block is active. Called on main loop.
# MODIFIED BLOCK: Update local tick count before setting the class's sprite alpha.
self.ticks -= 1
Block.sprite.set_alpha(self.ticks) # MOVED, MODIFIED: uses local tick count for alpha.
print(self.ticks) # UPDATED: Using local ticks.
blocks = [] # MOVED: Make a list for the Blocks, but don't clear it on frame.
while True:
mmraw=pygame.mouse.get_pos()
mmx,mmy=mmraw[0]-(pygame.image.load("Block.png").get_width())/2,mmraw[1]-(pygame.image.load("Block.png").get_width())/2
# ^ There may be a tidier way of doing this. Described in solution body...
for event in pygame.event.get():
if event.type== pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
print('click')
blocks.append(Block(mmx,mmy))
screen.fill((22,22,222))
# ^ NEW: Fill the screen with some backdrop so that the erasure is obvious!
# If you don't do this, you'll be blitting faded images over brighter ones and there will be no real change!!
for block in blocks:
block.fade() # NEW: Update this block.
if block.ticks < 1: # NEW BLOCK: If the block has become invisible...
blocks.remove(block) # NEW: Expunge this invisible block.
continue # NEW: Immediately move on to the next block.
screen.blit(Block.sprite, block.rect)
print('trying to blit')
print(Block.sprite.get_offset())
pygame.display.update()
There's a small problem with it, which is that vanishing Blocks trigger a 'flicker' in other remaining Blocks (or at least the next one in blocks) and I'm not sure why or how.
While I was looking, I found a few other things you might want to consider:
...in class Block::
-Consider using sprite = pygame.image.load("Block.png").convert() instead of sprite = None. This way, you can use something like mmx,mmy = mmraw[0] - Block.sprite.get_rect.centerx, mmraw[1] - Block.sprite.get_rect().centery instead of loading the image for a moment, just to know its size. Since all Blocks use the same graphic, it shouldn't make a difference, and this way, you won't have to reacquire the offset if you change Block.sprite during runtime!
-Consider assigning a copy of Block's sprite to each instance instead of using the class's Surface. This will take up more processing power, but only momentarily if you use it as a return instead. For instance:
class Block(object):
...
def fade(self):
sprite = Block.sprite.copy()
sprite.set_alpha(self.ticks)
self.ticks -= 1
return sprite
...
while True: # main loop
...
for block in blocks:
screen.blit(block.fade(), block.rect) # Although there are more Pythonic ways of writing this.
Alternatively, you could use screen.blit(sprite, self.rect) in Block.fade() rather than on main and forego the return entirely. Because the alpha is set locally, it won't have to be reset every time fade runs, and Block's unbound sprite can stay fresh!
Anyway, I hope this solves your problem, even if it does not (exactly) answer your question!
You probably want to use threads. These allow you to run multiple things at once. For your code, change it like this:
from threading import Thread
import pygame, sys, time, Blockm
from pygame.locals import *
black = (0,) * 3
white = (255,) * 3
size = w, h = 1400, 800
screen = pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert_alpha()
self.rect = Block.sprite.get_rect(top=y, left=x)
Thread(target=self.fade) # Made this a Thread so it runs separately.
def fade(self):
for tick in range(255, 0, -1):
Block.sprite.set_alpha(tick)
print(tick)
def game():
while True:
# The contents of that while True loop at the end
def main():
Thread(target=Main)
if __name__ == "__main__":
main()
This also adds an entry point to your program, which was lacking. Also, Block.fade wouldn't actually do what you want, as you only set_alpha once. I fixed that with a for loop instead. Also, note that you can now just return to break out of the whole game.

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