How to make definiton work? - python

Please see below examples:
import pygame
pygame.init()
x = 800
y = 600
programDisplay = pygame.display.set_mode((x,y))
pygame.display.set_caption('Title')
pygame.display.update()
programExit = False
while not programExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
programExit = True
pygame.quit()
quit()
Second Example:
import pygame
pygame.init()
x = 800
y = 600
programDisplay = pygame.display.set_mode((x,y))
pygame.display.set_caption('Title')
pygame.display.update()
programExit = False
def programQuit():
for event in pygame.event.get():
if event.type == pygame.QUIT:
programExit = True
while not programExit:
programQuit()
pygame.quit()
quit()
How to make definition from 2nd example work so the outcome is same as in first example?
Think it might be something to do with global and local variables but could not get it working.

Here, I fixed it
import pygame
pygame.init()
x = 800
y = 600
programDisplay = pygame.display.set_mode((x,y))
pygame.display.set_caption('Title')
pygame.display.update()
programExit = False
def checkForProgramQuit():
global programExit
for event in pygame.event.get():
if event.type == pygame.QUIT:
programExit = True
while not programExit:
checkForProgramQuit()
programDisplay.fill((255,255,255))
pygame.display.update()
pygame.quit()
quit()
the programExit variable you were modifying was local to the function.

Carcigenticate is quite right, but here are some notes on what's going on here and some practices that will avoid this in future.
programExit = False
def programQuit(programExit=False):
for event in pygame.event.get():
if event.type == pygame.QUIT:
programExit = True # Issue 1
while not programExit:
programQuit()
Issue 1 is that this assignment is creating a new variable in the scope of the function and setting it's value. It is not changing the value of the module level variable programExit.
A better way to do this is to have the function pass back it's result as a return value like this.
def programContinue():
for event in pygame.event.get():
if event.type == pygame.QUIT:
return False
return True
while programContinue():
pass
Also by reversing the boolean logic returned by the function, I think things become a bit clearer and we can get rid of the 'not'. Also expressing the while clause this way seems a bit clearer to me. The 'pass' statement could usefully be replaced with some logging or the display updates from C._'s answer.

Related

I have a function that detects if i press a key in pygame, but it only detects one key?

I have a function that detects key presses but, when i use the function and detect 'a' it detects but if i detect 'd' it doesnt detect it, but if i put the function that detects the key 'd' before the function that detects the key 'a' it detects 'd', why so?
here is my code:
keys = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','space','1','2','3','4','5','6','7','8','9','0']
pygame_keys = [pygame.K_a,pygame.K_b,pygame.K_c,pygame.K_d,pygame.K_e,pygame.K_f,pygame.K_g,pygame.K_h,pygame.K_i,pygame.K_j,pygame.K_k,pygame.K_l,pygame.K_m,pygame.K_n,pygame.K_o,pygame.K_p,pygame.K_q,pygame.K_r,pygame.K_s,pygame.K_t,pygame.K_u,pygame.K_v,pygame.K_w,pygame.K_x,pygame.K_y,pygame.K_z,pygame.K_SPACE,pygame.K_1,pygame.K_2,pygame.K_3,pygame.K_4,pygame.K_5,pygame.K_6,pygame.K_7,pygame.K_8,pygame.K_9,pygame.K_0]
def key_pressed(key_press,one_click =False):
global key_function_run
if one_click:
key_function_run = True
if not one_click:
if kb.is_pressed(key_press):
return True
if one_click:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
for i in range(len(keys)):
if event.type == pygame.KEYDOWN:
if key_press == keys[i]:
if event.key == pygame_keys[i]:
print(i)
return True
pass
And here is how i am using the function:
if x == 205:
player_lane = 2
if x == 60:
player_lane = 1
if x == 347:
player_lane = 3
#player movement
if peasy.key_pressed('a',True) and player_lane == 2:
x = 60
if peasy.key_pressed('a',True) and player_lane == 3:
x = 205
if peasy.key_pressed('d',True) and player_lane == 2:
x = 347
if peasy.key_pressed('d',True) and player_lane == 1:
x = 205
Make a dictionary from your lists:
key_dict = dict(zip(keys, pygame_keys))
And use pygame.key.get_pressed:
def key_hold(keys, key):
return keys[key_dict[key]]
keys = pygame.key.get_pressed()
is_a = key_hold(keys, 'a')
is_d = key_hold(keys, 'd')
If you want to use the KEYDOWN event, you have to be aware that pygame.event.get() get all the messages and remove them from the queue. See the documentation:
This will get all the messages and remove them from the queue. [...]
So you can call pygame.event.get only once per frame. Also see Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?. However you can use unicode property of the KEYDOWN event:
def key_down_event(event_list, c):
return any(e for e in event_list if e.type == pygame.KEYDOWN and e.unicode == c)
event_list = pygame.event.get()
is_a = key_down_event(event_list, 'a')
is_d = key_down_event(event_list, 'd')
If you want a function that detects both whether a key is held down or has been pressed, you need to count the frames of how long a key is pressed, for each key. Return True if the count for a key is 1 when you want to determine if the key was just pressed:
import pygame
key_count = {}
def key_pressed(keys, key, one_click):
pressed = keys[key]
key_count[key] = (key_count.get(key, 0) + 1) if pressed else 0
return key_count[key] == 1 if one_click else pressed
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
a_once = key_pressed(keys, pygame.K_a, True)
d_hold = key_pressed(keys, pygame.K_d, False)
if a_once:
print(f'key event: {pygame.key.name(pygame.K_a)}')
if d_hold:
print(f'key pressed: {pygame.key.name(pygame.K_d)}')
pygame.display.flip()
pygame.quit()
exit()
You shouldn't need to make a custom function for input handling, you can use something like this:
running = True
def main():
# Your normal game loop
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Gets a list of the currently pressed keys,
# this is updated because we put it in the while loop
kbd = pygame.key.get_pressed()
if kbd[pygame.K_a]:
print("I just pressed the A button!") # You can run here whatever ya want
Edit: I realized OP wanted to use pygame.KEYDOWN instead of pygame.key.get_pressed(), so in that case, you can use something like this
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
print("Pressed D!")
Hope I helped. If you have any questions, just leave a comment/reply/whatever!

Pygame project, screen doesn't update in one function, but it works within two other functions

My pygame project consists of several parts, including global map and towns. I use one Game class to contain necessary objects. All game is shown on one screen and works properly with global map and town (in_city function), it changes the screen and shows necessary information, but when I call another function (buy), it doesn't update screen.
def buy(self):
while self.running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.KEYDOWN:
return
self.screen.fill('BLACK')
pygame.display.update()
pygame.display.flip()
def in_city(self):
while self.running:
stop = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.MOUSEMOTION:
all_sprites.update(pygame.mouse.get_pos())
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
button_right.click(mouse_pos)
button_left.click(mouse_pos)
button_next.click(mouse_pos)
button_prev.click(mouse_pos)
if button_close.on_button(mouse_pos):
stop = True
if event.type == pygame.KEYDOWN:
self.buy()
if stop:
self.player.route = [(0, 0)]
self.player.next_move(self.maps)
self.camera.update(self.player)
return
screen.fill('WHITE')
city.render(screen)
current_text = city.enemies[city.current_enemy]
window.show(screen, current_text.get_text())
print(current_text.goods)
all_sprites.draw(screen)
pygame.display.update()
pygame.display.flip()`
Never run game loops recursively. Use the one application loop to draw the scene depending on states. You should never jump to another application loop in an event. The event should only change a state that is used to draw the scene. e.g.:
game_state = 'A'
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
game_state = 'B'
screen.fill('WHITE')
if game_state == 'A':
# draw scene A
elif game_state == 'B':
# draw scene B
pygame.display.flip()

My game won't work when I use the controls, how do I fix it?

I can not get the controls to work, I try to press escape to open a menu I made but it will not open and I do not know if I am checking for events correctly, is there a way I am SUPPOSED to do it?
I tried using the functions for checking for different keys and I went to the spread-sheet that displays all the event names so you can map them at pygame.org but it will not open when I use the escape or also known as:
elif event.type == pygame.K_ESCAPE:
Frame.blit('Textures/GUI/loom.png', (0,0))
Heres the full code:
import pygame
#Textures/Blocks/loom_side.png
pygame.init()
Screen = "None"
DB = 0
Width = 800
Height = 600
Frame = pygame.display.set_mode((Width,Height))
pygame.display.set_caption("HypoPixel")
FPS = pygame.time.Clock()
def Raycast(TTR, RayXPos, RayYPos, RaySizeX, RaySizeY):
RaycastThis = pygame.image.load(TTR)
RaycastThis = pygame.transform.scale(RaycastThis,(RaySizeX,RaySizeY))
Frame.blit(RaycastThis, (RayXPos, RayYPos))
Loop = True
Raycast('Textures/Screens/Skybox/Earth.png',0,0,800,600)
while Loop == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.K_ESCAPE:
Frame.blit('Textures/GUI/loom.png', (0,0))
pygame.display.update()
FPS.tick(60)
I expected to get the loom GUI that I made. Once I tried to press escape, nothing happened.
pygame.K_ESCAPE is not event type (see pygame.event), but it is a pygame.key.
First check if a key was pressed by comparing the event type to pygame.KEYDOWN:
event.type == pygame.KEYDOWN
Then check if the event.key, which cause the event, is the pygame.K_ESCAPE key:
event.key == pygame.K_ESCAPE
Furthermore, the parameter to Surface.blit() has to be a Surface object rather than a filename.
first load the image to a Surface, by pygame.image.load(), then blit the Surface:
sprite = pygame.image.load('Textures/GUI/loom.png')
Frame.blit(sprite, (0,0))
Of course the your Raycast function can be called to do so:
Raycast('Textures/GUI/loom.png',0,0,800,600)
Your code should look somehow like this:
while Loop == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
Raycast('Textures/GUI/loom.png',0,0,800,600)

Pygame event inside class not recognized

I have completed a game using pygame without any problems. Now I am trying to organise the code and add classes. However, I am having a problem with the event command.
I tried using pygame.event.poll() and pygame.event.get(), but neither helped.
class MainRun():
run = True
def Main(self):
#some code
while MainRun.run:
pygame.time.delay(35)
for event in pygame.event.get():
if event.type == pygame.QUIT:
MainRun.run = False
a.activate_skills()
class Player():
#code
def activate_skills(self):
if event.type == pygame.MOUSEBUTTONDOWN:
#some code
a = Player
main = MainRun().Main()
if event.type == pygame.MOUSEBUTTONDOWN: NameError: name 'event' is not defined
So how can I define the event? Please see what I have already tried.
You should only call pygame.event.get() once, as it will fetch all the events that have happened. For example:
a = pygame.event.get() # Contains the events that has happen.
for event in a:
if event.type == pygame.QUIT:
quit()
b = pygame.event.get() # Will probably contain nothing, as the code above took the events from the event queue.
for event in b:
if event.type == pygame.MOUSEBUTTONDOWN:
do_something()
do_some_calculation()
c = pygame.event.get() # Might contain something, if the user did something during the time it took to do the calculation.
for event in c:
if event.type == pygame.MOUSEBUTTONDOWN:
do_other_thing()
In the above example, it's likely do_something() will never be called, as the event queue has been cleared just before. do_other_thing() might be called, but that's only if the user pressed the button during the time it took to execute do_some_calculations(). If the user pressed before or after, the click event will have been cleared and lost.
So in your situation, you could do something like this:
class MainRun():
run = True
def Main(self):
#some code
while MainRun.run:
pygame.time.delay(35)
for event in pygame.event.get(): # ONLY CALLED HERE IN THE ENTIRE PROGRAM.
if event.type == pygame.QUIT:
MainRun.run = False
a.activate_skills(event) # MOVED THIS INTO THE FOR LOOP!
class Player():
#code
def activate_skills(self, event): # TAKING THE EVENT AS PARAMETER.
if event.type == pygame.MOUSEBUTTONDOWN:
#some code
a = Player
main = MainRun().Main()
Try this, hope it works for you :D
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
# your code

Multiple instances of pygame.event.get()

There are two for event in pygame.event.get(): instances. It doesn't work inside the second one. I guess that calling the function twice will not work. What should be done here? By the way this is a function to move a piece from square to square in a board game.
def movement_one(blit1,charac1,screen,squareblitter,boardcoord,placecheck_True):
global mouse1, mouse1_des
run1=True
while run1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse1 = pygame.mouse.get_pos()
for i in range(80):
if squareblitter[i].collidepoint(mouse1):
collided = i
break
print "wow"
print i
print collided in placecheck_True
if collided in placecheck_True:
#checks if the square is occupied or not
print "wiw"
for event in pygame.event.get():
print "wtf"
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse1_des = pygame.mouse.get_pos()
print "omg"
squareblitter[i]
pygame.display.update()
for i in range(80):
if squareblitter[i].collidepoint(mouse1_des):
screen.blit(placecheck_True[collided], squareblitter[i])
placecheck_True.update[i]=placecheck_True[collided]
pygame.display.update()
run1=False
break
else:
break
Calling pygame.event.get() again while handling an event from a previous call usually won't work. It's unclear from your code precisely what you're trying to accomplish. However, I suggest you rry moving the code for processing pygame.MOUSEBUTTONDOWN events when there's a collision that's currently in you inner for event loop to the outer one (and remove the inner for event loop altogether).
Here's what I mean (untested, of course):
def movement_one(blit1, charac1, screen, squareblitter, boardcoord, placecheck_True):
global mouse1, mouse1_des
run1 = True
while run1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse1 = pygame.mouse.get_pos()
for i in range(80):
if squareblitter[i].collidepoint(mouse1):
collided = i
break
print "wow"
print i
print collided in placecheck_True
if collided in placecheck_True:
mouse1_des = pygame.mouse.get_pos()
print "omg"
squareblitter[i]
pygame.display.update()
for i in range(80):
if squareblitter[i].collidepoint(mouse1_des):
screen.blit(placecheck_True[collided], squareblitter[i])
placecheck_True.update[i] = placecheck_True[collided]
pygame.display.update()
run1 = False # not sure you should do this...
break

Categories