pygame: mysterious lack of sprite movement - python

For some reason, my Player sprite is not moving when I press the arrow keys.
It was moving before, but now it does not move at all, and I don't see any problems
with the code I'm using (plus, no exceptions are raised). Here is the relevant code
(tell me if I need to add more):
def move(self,up,down,right,left): #move function in Player class
if up and self.rect.y>0:
print'up'
self.rect.y-=self.speed
if down and self.rect.y<650:
print'down'
self.rect.y+=self.speed
if right and self.rect.x<650:
print'right'
self.rect.x+=self.speed
if left and self.rect.x>0:
print'left'
self.rect.x-=self.speed
...
#in while loop, under "for event in pygame.event.get():"
elif event.type == 2: #pygame.KEYDOWN
if event.key in range(273,277): #if the key is an arrow key
if event.key==273: #up
up=True
elif event.key==274: #down
down=True
elif event.key==275: #left
right=True
else: #right
left=True
Neither of the following answer my question:
This,
or this
I am on Windows 10, python 2.7.

Let's look at your block of code inside the for loop nested in the while loop.
Is the while loop infinite, or rather, does it stop when you don't want it to?
Also, are you calling the move function directly after the if statement, and after you do so, are you resetting the variables for the directions?
Like this:
#in while loop, under "for event in pygame.event.get():"
elif event.type == 2: #pygame.KEYDOWN
if event.key in range(273,277): #if the key is an arrow key
if event.key==273: #up
up=True
elif event.key==274: #down
down=True
elif event.key==275: #left
right=True
else: #right
left=True
move(up,down,left,right)
up,down,right,left=False,False,False,False
Hopefully, this helps you track down your mistakes.

Related

What all things happens inside pygame when I press a key? When to use pygame.event==KEYDOWN

I have been trying to code a small 2D game using python.
checkp_list[0]=head_pos
pressed_key= pygame.key.get_pressed()
if pressed_key[pygame.K_ESCAPE]:
running=False
if pressed_key[pygame.K_UP]:
if dir_ not in ["up", "down"]:
dir_= "up"
checkp_no= checkp_no+1
#head_pos_next=head_move(head_pos, dir_)
checkp_list.insert(checkp_no,head_pos)
log_file_obj.write("chekp_list {}, checkp_no {} after append dir {}\n".format(checkp_list,checkp_no,dir_))
if pressed_key[pygame.K_DOWN]:
if dir_ not in ["up", "down"]:
dir_= "down"
checkp_no= checkp_no+1
#head_pos_next = head_move(head_pos, dir_)
checkp_list.insert(checkp_no,head_pos)
log_file_obj.write("chekp_list {}, checkp_no {} after append dir {}\n".format(checkp_list,checkp_no,dir_))
if pressed_key[pygame.K_RIGHT]:
if dir_ not in ["left", "right"]:
dir_= "right"
checkp_no= checkp_no+1
#head_pos_next = head_move(head_pos, dir_)
checkp_list.insert(checkp_no,head_pos)
log_file_obj.write("chekp_list {}, checkp_no {} after append dir {}\n".format(checkp_list,checkp_no,dir_))
if pressed_key[pygame.K_LEFT]:
if dir_ not in ["left", "right"]:
dir_= "left"
checkp_no= checkp_no+1
#head_pos_next = head_move(head_pos, dir_)
checkp_list.insert(checkp_no,head_pos)
log_file_obj.write("chekp_list {}, checkp_no {} after append dir {}\n".format(checkp_list,checkp_no,dir_))
All this is running inside a while True: loop. Basically, whenever an arrow key is pressed, direction indicator dir_ changes and it'll add 1 to checkpoint number checkp_no and insert current head position(head_pos of the head object in checkp_no index position.
But unfortunately, all in the checkp_list points turns out to be the latest head_pos. The list checkp_list is an important factor to implement my logic.
chekp_list while start [[325, 791], [325, 791], [325, 791]],
this is the checkp_list when checkp_no is 2 and when while loop is starting another iteration (taken from log file created).
All the points where checkp_list is getting appended are above.
Please help me to identify the issue.
pygame.key.get_pressed() returns a list with the current states of a key. When a key is hold down, the state for the key is True, else it is False. Use pygame.key.get_pressed() to evaluate if a key is continuously pressed.
while True:
pressed_key= pygame.key.get_pressed()
if pressed_key[pygame.K_UP]:
# the code in this condition is executed as long UP is hold down
# [...]
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released.
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
# The following code is executed once, every time when ESC is pressed.
# [...]
if event.type == pygame.KEYUP:
if event.key == pygame.K_ESCAPE:
# The following code is executed once, every time when ESC is released.
# [...]

Pygame updating variable in a function

The problem is that when I made it without the run of functions it worked fine, however it seems to now always reset back to 0. I have missed off other bits of code, but it will load the first function then when it comes out of the second function it resets back to 0.
if letter == "G":
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_g and x > 192 and x <250:
score += 1
texts(score,gameDisplay)
def texts(score,gameDisplay):
font=pygame.font.Font(None,100)
scoretext=font.render(str(score), 1,(204,0,204))
gameDisplay.blit(scoretext, (313, 617))
return score
do a score=0 before the for loop.
if letter == "G":
score=0
for event in...
EDIT:
you should initialize the score somewhere outside of the game loop

How to get an input from user in Pygame and save it as a variable? [duplicate]

This question already has an answer here:
How to get text input from user in Pygame? [duplicate]
(1 answer)
Closed 5 years ago.
I want to take input from the user in the game (e.g. their name), then put it on the screen.
I tried modules (including InputBox), but none of them are working. They just displayed my text on screen.
I want to save that input to a variable. Is there any way to do this?
Example:
font1 = pygame.font.SysFont("None", 30)
score = 0
text = font1.render("{}".format(score), True,(255,255,255))
...
...
if sneakeattheapple:
score += 1
text = font1.render("{}".format(score), True,(255,255,255))
...
...
screen.blit(text,(275,6))
This is going to put the score variable on the screen. But score is already defined, I want to do this with a variable given by user.
EDIT: Let me be more clear. In Python we can do this:
x = input("Do you want to do this? (y/n): ")
if x == "y":
#do something
if x == "n":
#do something
This is what I want to do in Pygame.
There's nothing baked into Pygame for this. You will either need to use a 3rd-party GUI library, or build it yourself. Example: if the textbox has focus, take all keydown events, and append to a string. Each frame, draw a box, then draw the string on top.
Building a simple one shouldn't be that hard, but if you want a more full-featured one, it will likely be easier to use a library.
I am currently assuming this function worked with your program is successful and raises no errors. Here is a function in the link you gave us:
def ask(screen, question):
"ask(screen, question) -> answer"
pygame.font.init()
current_string = []
display_box(screen, question + ": " + string.join(current_string,""))
while 1:
inkey = get_key()
if inkey == K_BACKSPACE:
current_string = current_string[0:-1]
elif inkey == K_RETURN:
break
elif inkey == K_MINUS:
current_string.append("_")
elif inkey <= 127:
current_string.append(chr(inkey))
display_box(screen, question + ": " + string.join(current_string,""))
return string.join(current_string,"")
It looks like this is how you get input from a user with a pygame screen right? Let's look at line 4 of this function:
current_string = []
The stuff the user types in is stored in this list. Assuming you know how to take a string and put it on the screen, you could save the string like this:
string_input = ''.join(current_string)
If you can make a similar function (if this one doesn't work), you can do the same thing! Just save the first item in the list holding the string in a variable as shown above. If you have any problems, please comment so I can edit my answer. To the next part now. You can simply activate this function at any time. You might want to activate when something happens. An example is when the snake eats the apple. You probaly have a function for that I believe. You can make a variable like this :
Eat = 0
And put in that function. When that variable is equal to 0. Nothing really activate the other function. When the snake eats the apple, reset the variable to 1 and then activate the function like this:
if Eat = 0:
someclassname.ask()
You do this with many other occasions. I hope this is clearer and more helpful!
Have you tried getting key event's and then putting them together to form something? You could put something like this inside of your main game loop.
input = ''
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
playing = False
if event.key == pygame.K_w:
input = input + "w"
if event.key == pygame.K_s:
input = input + "s"
if event.key == pygame.K_a:
input = input + "a"
Then you can check for when the user is done with input(button/enter key), and finalize the variable.
With this you might run into a problem where the key is held longer so you get 3 w's for only pressing the button once. If this is the case you can either 1. not allow another input until a certain time has passed(.25 s maybe?) or 2. use pygame.KEYUP instead of pygame.KEYDOWN and exclusively check for strokes.
Here is a possible solution, it may be really useful for future visitors that are also suffering with this problem like me. Which is;
First making a text-render function
def text1(word,x,y):
font = pygame.font.SysFont(None, 25)
text = font.render("{}".format(word), True, RED)
return screen.blit(text,(x,y))
Then a function works like input;
def inpt():
word=""
text1("Please enter your name: ",300,400) #example asking name
pygame.display.flip()
done = True
while done:
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
word+=str(chr(event.key))
if event.key == pygame.K_b:
word+=chr(event.key)
if event.key == pygame.K_c:
word+=chr(event.key)
if event.key == pygame.K_d:
word+=chr(event.key)
if event.key == pygame.K_RETURN:
done=False
#events...
return text1(word,700,30)
As you see, this function catching keyboard events, it has its own while loop also, it's important. I break the loop when pressing Enter button which is if event.key == pygame.K_RETURN: done=False . Then returning our word with text1 function, displaying. Other events can be set of course opinion-based, like space button for a gap etc.
Then, actually we have to make an intro function and we will ask the user's name in there for example, when intro function is done, name goes to set on the screen until game is over.
def game_intro():
intro=True
while intro:
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
quit()
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_RETURN:
intro=False
inpt() #Here we are calling our function
screen.fill(white)
pygame.display.update()
clock.tick(15)
See that we break this loop with the Enter button again. So we will ask the user our question in intro, then we will set it on the screen, top-right corner for example.

pygame issue: sprite running over trees

I'm creating a game where you can explore a terrain, always random & computer generated. The player, which is a sprite, is supposed to only run on the grass blocks, and it shouldn't be able to run into trees. In other words, the player can only run on grass.
However, despite hours of debugging, the player still can run on trees, which is plenty annoying.
import pygame, player # the module where the Player class is defined
import block # the module where the Block class is defined
...
#setting varibles, player, ...
run = True
clock = pygame.time.Clock()
create_world() # create the map of the world
while run:
clock.tick(30)
hit = pygame.sprite.spritecollide(player, blocks, False)
location = player.rect.centerx # defined to use as return spot in case of tree
# player is player.Player instance, blocks is group of all the blocks
# the block.Block class has an attribute called 'type'; grass = 'block'
# tree = 'tree'
for e in pygame.event.get():
if e.type == pygame.QUIT:
run = False
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_UP:
speed = [-30, 0] # the varible to update player's location
if hit[0].type == tree: # if hit tree
player.rect.centerx = location
...
if e.type == pygame.KEYUP:
reset_speed() # speed = [0, 0], to prevent constant movement
player.update(speed) # update the player
reset_speed() # not really useful
render_world() # blit the world & player
pygame.quit()
The if hit[0].type == 'tree': ... thing should've worked, but it didn't. Why?
Any help is appreciated.
So, to help yourself the standard handling in a loop is to move first, then handle collisions, this way you are privvy to both the previous position and the collided position.
As such in your current loop the player may be hitting a tree but is simply moving to the same position.
read through the loop step by step and try this out:
e.g.
loop N:
hit returns []
location is set to (posN)
speed is set
player moves to (posN+1)
loopN+1:
hit returns [tree]
location is set to (posN+1)
speed is set
player is reset to "location", but that is just (posN+1)
player moves to (posN+2)
and so on and so on. From this hopefully you can see that it -is- working, just not as you expected, i could dig up the old phrase about computers always being right and whatnot
Solution:
Rewrite this slightly and consider handling movement first then checking collision second inside the loop, and this will become easier to understand.

How to efficiently hold a key in Pygame?

I've found two related questions:
Pygame hold key down causes an infinite loop
pygame - on hold button down
But I want to be specific. How to?
while not done:
for e in event.get():
if e.type == KEYDOWN:
keys = key.get_pressed()
if e.type == QUIT or keys[K_ESCAPE]:
done = True
if keys[K_DOWN]:
print "DOWN"
When I press the down arrow, it prints, but it prints just once. If I want to print it another time, I need to press it again.
If I use the while keyword instead,
while keys[K_DOWN]:
print "DOWN"
I get an infinite loop for some obscure reason.
This logical alternative is also useless:
if ((e.type == KEYDOWN) and keys[K_DOWN]):
print "DOWN"
And there is this other one that somehow cleans the events and you can use while:
while not done:
for e in event.get():
if e.type == KEYDOWN:
keys = key.get_pressed()
if e.type == QUIT or keys[K_ESCAPE]:
done = True
while keys[K_DOWN]:
print "DOWN"
event.get()
keys = key.get_pressed()
But you press the down key for less than one second and it prints thousands of times. (Moving a player would be impossible, and adjusting clock for this does not seem to be the right way to deal with it (And I've tried and I've failed miserably.)).
To press and execute the block thousands of times is useless. What I want, is to press the key and keep going with the action while I don't release it, within the defined game clock speed.
Don't mix up event.get() and key.get_pressed().
If you press or release a key, and event is put into the event queue, which you query with event.get(). Do this if you're actually interested if a key was pressed down physically or released (these are the actual keyboard events. Note that KEYDOWN get's added multiple time to the queue depending on the key-repeat settings).
Also, there's no need to query the state of all keys while handling a KEYDOWN event, since you already know which key is pressed down by checking event.key
If you're interested in if a key is hold down (and ignoring the key-repeat, which you probably want), then you should simply use key.get_pressed(). Using a bunch of flags is just unnecessary and will clutter up your code.
So your code could simplified to:
while not done:
keys = key.get_pressed()
if keys[K_DOWN]:
print "DOWN"
for e in event.get():
pass # proceed other events.
# always call event.get() or event.poll() in the main loop
I am not familiar with Pygame, but, as I see, the program in it should have an event-based architecture. Unless you get the incoming events and process them, nothing happens. That's why your simple loop becomes infinite: it just does not process events.
while keys[K_DOWN]: # Nobody updates the keys, no events are processed
print "DOWN"
Then concerning the get_pressed() call. What it returns is a list of keys. So, you are trying to just loop until the key is released. That's a problem. According to this, pygame.event.get() returns immediately even if there are no events in the queue. The call to get() means: my code still has what to do, but I don't want to block the events, so please process the pending events before I continue. If your code is just waiting for an event, that means it has nothing to do.
The function to WAIT (without blocking the inner loop of Pygame) for an event is pygame.event.wait() (the logic is: I have nothing to do in my code until something happens). However, if you use it, you will have to get information about keys pressed or released from the event itself, not from get_pressed().
Here is an example from the comments to the doc page:
for event in pygame.event.get() :
if event.type == pygame.KEYDOWN :
if event.key == pygame.K_SPACE :
print "Space bar pressed down." #Here you should put you program in the mode associated with the pressed SPACE key
elif event.key == pygame.K_ESCAPE :
print "Escape key pressed down."
elif event.type == pygame.KEYUP :
if event.key == pygame.K_SPACE :
print "Space bar released."
elif event.key == pygame.K_ESCAPE :
print "Escape key released." #Time to change the mode again
I like to take a slightly different approach to this problem. Instead of checking if the key is pressed and taking some action when it is, set a flag on key down and unset it on key up. Then in the function to update the player's position, check the flag and update accordingly. The following pseudo-Python explains what I'm getting at:
if down_key_pressed:
down_flag = True
elif down_key_released:
down_flag = False
elif right_key_pressed:
etc...
This should be done in a separate loop that takes the player's input. Then in update_player_position() you can do:
if down_flag:
move_player_down()
elif right_flag:
move_player_right()
This example assumes four-directional movement, but you could extend it to eight-directional fairly easily by just using if down_flag and right_flag instead.
You can get keydown event repeatedly if you use pygame.key.set_repeat(# millisecond) to set the time limitation for each key event. Quote: when the keyboard repeat is enabled, keys that are held down will generate multiple pygame.KEYDOWN events. The delay is the number of milliseconds before the first repeated pygame.KEYDOWN will be sent. After that another pygame.KEYDOWN will be sent every interval milliseconds. If no arguments are passed the key repeat is disabled. When pygame is initialized the key repeat is disabled. please see following link for detail http://www.pygame.org/docs/ref/key.html#pygame.key.set_repeat
I am using a different approach on holding down a key that I am using in the specific task of moving an object left or right.
I do not care about the computer knowing that a key is actually held down.
When I press a key, a variable that I define for that key (EG: left arrow) is set to True.
Until that key is unpressed (with the event pygame.KEYUP) the "movement to left" is performed.
Dominik's solution is the perfect one (separating event.get() from the keyboard ones). It works perfectly! Finally, no more problems with pygame's input.
Flags:
flag = False # The flag is essential.
while not done:
for e in event.get(): # At each frame it loops through all possible events.
keys = key.get_pressed() # It gets the states of all keyboard keys.
if e.type == QUIT:
done = True
if e.type == KEYDOWN: # If the type is KEYDOWN (DIFFERENT FROM "HELD").
if keys[K_DOWN]: # And if the key is K_DOWN:
flag = True # The flag is set to true.
elif e.type == KEYUP: # The very opposite.
if keys[K_DOWN]:
flag = False
if flag == True: # DON'T USE "while flag == true", it'll crash everything. At every frame, it'll check if the flag is true up there. It's important to remember that the KEYDOWN worked ONLY ONCE, and it was enough to set that flag. So, at every frame, THE FLAG is checked, NOT THE KEYDOWN STATE. Every "player movement" while a key is being held MUST be done through flags.
print "DOWN"
This is basically How I did it, well Go to www.cswonders.com, and it might help you . Go to level 3, then 3.6, and go through the tutorial. I learned it from there. Here is my code.
def update(self):
self.speedx = 0
self.speedy = 0
# If left or right key is pressed, move left or right
pressed_key = pygame.key.get_pressed()
if pressed_key[pygame.K_LEFT]:
self.speedx = -10
if pressed_key[pygame.K_RIGHT]:
self.speedx = 10
if pressed_key[pygame.K_UP]:
self.speedy = -10
if pressed_key[pygame.K_DOWN]:
self.speedy = 10
self.rect.x += self.speedx
self.rect.y += self.speedy
# No further move if off screen
if self.rect.right > SCREEN_WIDTH:
self.rect.right = SCREEN_WIDTH
if self.rect.left < 0:
self.rect.left = 0

Categories

Resources