I'm fairly new to Python (and programming in general), and brand-new to Pygame.
My desired outcome is: if I'm holding the A key, the character moves left. If I continue holding the A key and additionally hold down the W key, the character moves up. If I release the W key but not the A key, the character continues left.
This is the main loop:
direction = 0 #0 left, 1 right, 2 up, 3 down
move = False
running = True
while running:
#cap framerate
clock.tick(FPS)
#locational updates
dx = 0
dy = 0
if move:
if direction == 0:
dx = -SPEED
if direction == 1:
dx = SPEED
if direction == 2:
dy = -SPEED
if direction == 3:
dy = SPEED
#draw background
screen.fill(GREY)
player.move(dx, dy)
player.update()
player.draw(screen)
#handle keypresses
keys = pygame.key.get_pressed()
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
direction = 0
move = True
elif keys[pygame.K_d] or keys[pygame.K_RIGHT]:
direction = 1
move = True
elif keys[pygame.K_w] or keys[pygame.K_UP]:
direction = 2
move = True
elif keys[pygame.K_s] or keys[pygame.K_DOWN]:
direction = 3
move = True
else:
move = False
#event handler
for event in pygame.event.get():
if event.type == QUIT:
running = False
#show all the things
pygame.display.update()
The actual outcome is: the S key can be overridden as desired, since it's at the bottom of the if statements. The W key can be overridden by any key except S, D can only be overridden by A, and A cannot be overridden at all.
I tried using KEYDOWN and KEYUP, but that was even further from what I wanted. I also tried changing the elifs to ifs, but that just reversed the hierarchy, which makes sense. I've also searched for the answer, but I haven't found anything which specifically relates to my problem.
I understand that the root of the problem is that Python reads the code line by line, but with my limited knowledge I don't know how to get around that. Also, I'm specifically trying to avoid diagonal movement. Thanks for any help you guys can give.
So with help from #Rabbid76, I found this solution, in case anyone else has this problem (though there's probably a better way to do it):
First, instead of using the direction and move variables, I used these two:
#save the last pressed key
last_key = None
#create a list of key presses
key_list = []
Second, this section now uses the last_key variable to determine direction:
running = True
while running:
#[...]
#locational updates
dx, dy = 0, 0
#check which key should be controlling movement
if last_key in [pygame.K_a, pygame.K_LEFT]:
dx = -SPEED
elif last_key in [pygame.K_d, pygame.K_RIGHT]:
dx = SPEED
elif last_key in [pygame.K_w, pygame.K_UP]:
dy = -SPEED
elif last_key in [pygame.K_s, pygame.K_DOWN]:
dy = SPEED
else:
dx, dy = 0, 0
Finally, my event handler now adds KEYDOWN events to the list stored in the key_list variable, then uses that list during KEYUP events to see if it should revert to a previous direction:
running = True
while running:
#[...]
for event in pygame.event.get():
#handle keypresses
if event.type == pygame.KEYDOWN:
if event.key in [pygame.K_a, pygame.K_LEFT]:
last_key = event.key
#avoid redundancy in list of key presses
if last_key in key_list:
key_list.remove(last_key)
#add the key to the list
key_list.append(last_key)
if event.key in [pygame.K_d, pygame.K_RIGHT]:
last_key = event.key
if last_key in key_list:
key_list.remove(last_key)
key_list.append(last_key)
if event.key in [pygame.K_w, pygame.K_UP]:
last_key = event.key
if last_key in key_list:
key_list.remove(last_key)
key_list.append(last_key)
if event.key in [pygame.K_s, pygame.K_DOWN]:
last_key = event.key
if last_key in key_list:
key_list.remove(last_key)
key_list.append(last_key)
if event.type == pygame.KEYUP:
if event.key == last_key: #this is important or else the character stops moving if you, say, hold right, then left, then release right
try:
#check the second-to-last key to see if it is still pressed
if pygame.key.get_pressed()[key_list[len(key_list) - 2]]:
#if so, that is now the key to use
last_key = key_list[len(key_list) - 2]
#check if third-to-last key is still pressed
elif pygame.key.get_pressed()[key_list[len(key_list) - 3]]:
last_key = key_list[len(key_list) - 3]
#check if fourth-to-last key is still pressed
elif pygame.key.get_pressed()[key_list[len(key_list) - 4]]:
last_key = key_list[len(key_list) - 4]
#this breaks if you mix arrows and wasd, but you could add more of the above checks to fix that if it's an issue
else:
last_key = None
except:
last_key = None
if event.type == QUIT:
running = False
You have to use the KEYDOW and KEYUP event. The last key pressed defines the direction of the movement. You need to implement a queue of hit keys.
Create a dictionary that maps keys to direction vectors and an empty direction queue:
direction_map = {
pygame.K_a: (-1, 0), pygame.K_LEFT: (-1, 0), pygame.K_d: (1, 0), pygame.K_RIGHT: (1, 0),
pygame.K_w: (0, -1), pygame.K_UP: (0, -1), pygame.K_s: (0, 1), pygame.K_DOWN: (0, 1)
}
direction_queue = []
Add or move a direction to the end of the queue in the 'KEYDOWN' event:
while run:
for event in pygame.event.get():
# [...]
if event.type == pygame.KEYDOWN:
new_direction = direction_map.get(event.key)
if new_direction in direction_queue:
direction_queue.remove(new_direction)
if new_direction:
direction_queue.append(new_direction)
Removes a direction vector from the queue in the 'KEYUP' event:
while run:
for event in pygame.event.get():
# [...]
if event.type == pygame.KEYUP:
release_direction = direction_map.get(event.key)
if release_direction in direction_queue:
direction_queue.remove(release_direction)
Set the movement depending on the last item in the queue:
while run:
# [...]
dx, dy = direction_queue[-1] if direction_queue else (0, 0)
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
rect = pygame.Rect(190, 190, 20, 20)
speed = 1
direction_map = {
pygame.K_a: (-1, 0), pygame.K_LEFT: (-1, 0), pygame.K_d: (1, 0), pygame.K_RIGHT: (1, 0),
pygame.K_w: (0, -1), pygame.K_UP: (0, -1), pygame.K_s: (0, 1), pygame.K_DOWN: (0, 1)
}
direction_queue = []
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
new_direction = direction_map.get(event.key)
if new_direction in direction_queue:
direction_queue.remove(new_direction)
if new_direction:
direction_queue.append(new_direction)
if event.type == pygame.KEYUP:
release_direction = direction_map.get(event.key)
if release_direction in direction_queue:
direction_queue.remove(release_direction)
dx, dy = direction_queue[-1] if direction_queue else (0, 0)
rect.x = (rect.x + dx * speed) % 400
rect.y = (rect.y + dy * speed) % 400
window.fill(0)
pygame.draw.rect(window, "red", rect)
pygame.display.flip()
pygame.quit()
exit()
Related
I was learning python and trying to make snake game using pygame library. I made a rectangle which moves using the pressing of keys but rather than moving one block it moves to the end of the block.
Whenever i press in any direction whether it be up , left, right, or down rather than moving 1 box size it moves to the end of the other direction
Could someone tell me why that happens.
import pygame
box_size = 50
num_box = 15
color1 = (169, 215, 81)
color2 = (169,250,81)
x = 0
y = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
break
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
if x < width - box_size:
x = x + box_size
if keys[pygame.K_LEFT]:
if x > 0:
x = x - box_size
if keys[pygame.K_UP]:
if y > 0:
y = y - box_size
if keys[pygame.K_DOWN]:
if y < height - box_size:
y = y + box_size
print(x,y)
for i in range(0,num_box):
if i %2 == 0:
for j in range(0,num_box):
if j%2 == 0:
pygame.draw.rect(win, color1, (box_size*j, box_size*i, box_size, box_size))
else:
pygame.draw.rect(win, color2, (box_size * j, box_size * i, box_size, box_size))
else:
for k in range(0,num_box):
if k%2 == 0:
pygame.draw.rect(win, color2, (box_size*k, box_size*i, box_size, box_size))
else:
pygame.draw.rect(win, color1, (box_size * k, box_size * i, box_size, box_size))
pygame.draw.rect(win, (0, 0, 250), (x, y, box_size, box_size))
# # rect(surface, color, (left, top, width, height))
pygame.display.update()
pass
See pygame.time.Clock.tick():
This method should be called once per frame.
Use pygame.time.Clock to control the frames per second and thus the game speed. The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time. e.g.:
clock = pygame.time.Clock()
while True:
clock.tick(100)
for event in pygame.event.get():
# [...]
If you want to move the object step by step you need to use KEYDOWN event instead of pygame.key.get_pressed():
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# INDENTATION
#-->|
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
if x < width - box_size:
x = x + box_size
if event.key == pygame.K_LEFT:
if x > 0:
x = x - box_size
if event.key == pygame.K_UP:
if y > 0:
y = y - box_size
if event.key == pygame.K_DOWN:
if y < height - box_size:
y = y + box_size
See How to get keyboard input in pygame? and How can I make a sprite move when key is held down.
pygame.key.get_pressed() returns a sequence with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
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. Use the keyboard events for a single action like jumping or spawning a bullet or a step-by-step movement.
For a school project I am building a recreation of Among Us in python with Pygame. I have already set up all the server and client side code and that's all working fine. I'm now in the process of making the camera follow the player. Only I can't get it to work.
My idea was: when a player moves, everything in his surroundings has to move in the opposite direction. But when you have a multiplayer game this doesn't work. Because then the other player moves as well which breaks the system.
If anyone has any idea how to make such a code, please let me know.
Thank you in advance
You don't have to move the background and object rects around the player, you can just move the player around and have a scroll offset value that keeps track of how much the blitted objects have to be offset. You don't apply the scroll value to the rect position because the rect position isn't relative to the window like the blit objects are. Here is an example of how you could achieve this.
import pygame, sys
clock = pygame.time.Clock()
from pygame.locals import *
pygame.init()
pygame.display.set_caption("Scrolling example")
WINDOW_SIZE = (600, 400)
screen = pygame.display.set_mode(WINDOW_SIZE, 0, 32)
scroll = [0, 0]
player = pygame.Rect(100, 100, 10, 10)
up = False
down = False
left = False
right = False
blocks = [pygame.Rect(250,250,50,50)]
while True:
screen.fill((0, 0, 0))
scroll[0] += (player.x - scroll[0] - (WINDOW_SIZE[0]/2)) // 20
scroll[1] += (player.y - scroll[1] - (WINDOW_SIZE[1]/2)) // 20
player_movement = [0, 0]
if right == True:
player_movement[0] += 2
if left == True:
player_movement[0] -= 2
if up == True:
player_movement[1] -= 2
if down == True:
player_movement[1] += 2
player.x += player_movement[0]
player.y += player_movement[1]
player_scroll_rect = player.copy()
player_scroll_rect.x -= scroll[0]
player_scroll_rect.y -= scroll[1]
pygame.draw.rect(screen, (255,255,255), player_scroll_rect)
for block in blocks:
scroll_block = block.copy()
scroll_block.x = scroll_block.x - scroll[0]
scroll_block.y = scroll_block.y - scroll[1]
pygame.draw.rect(screen, (0,0,255), scroll_block)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_RIGHT:
right = True
if event.key == K_LEFT:
left = True
if event.key == K_UP:
up = True
if event.key == K_DOWN:
down = True
if event.type == KEYUP:
if event.key == K_RIGHT:
right = False
if event.key == K_LEFT:
left = False
if event.key == K_UP:
up = False
if event.key == K_DOWN:
down = False
pygame.display.update()
clock.tick(60)
If you want a solution for using images, ask me.
Also, you can find more about scrolling here: https://www.youtube.com/watch?v=5q7tmIlXROg
while True:
pencere = pygame.display.set_mode((800,600))
pygame.display.set_caption("Oyun")
for olay in pygame.event.get():
if olay.type == pygame.QUIT:
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
y -= 3
if keys[pygame.K_DOWN]:
y += 3
if keys[pygame.K_LEFT]:
x -= 3
if keys[pygame.K_RIGHT]:
x += 3
pencere.fill(beyaz)
pygame.draw.rect(pencere, mavi, (x, y, 40, 60))
pygame.display.update()
When i press navigate buttons, the rectangle goes 3 pixel. But how would I make it so when I hold the keys down?
You are reinitialising the pygame window every time round your loop with set_mode. Apparently this resets keyboard input too. (I'm actually surprised this doesn't make the whole window flicker or have other obvious effects.) You should only call set_mode once and you should do it before your main loop.
You can do
import time
key_1 = pygame.key.get_pressed()
time.sleep(0.5)
key_2 = pygame.key.get_pressed()
if key_1 == key_2:
if key_1[pygame.K_UP]:
y -= 3
if key_1[pygame.K_DOWN]:
y += 3
if key_1[pygame.K_LEFT]:
x -= 3
if key_1[pygame.K_RIGHT]:
x += 3
So x and y are updated only if one of the navigation keys is hold down
I use a feature like this a lot in my pygame projects.
The basis of it is to set a variable to True if a key is pressed and false if it is released. When you call a function like move(), you will check if the movement variables are True or False.
If you pressed the right and left arrows at the same time, there would be no movement.
This is just some of the code I use. Since you are using up and down aswell, you should just add more event checks for up and down and the code should be good to go.
Code:
running = True
directions = {"right": False, "left": False}
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
directions['right'] = True
elif event.key == pygame.K_LEFT:
directions['left'] = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
directions['right'] = False
elif event.key == pygame.K_LEFT:
directions['left'] = False
if directions['right']:
x += 3
if directions['left']:
x -= 3
This question already has answers here:
Pygame - Mouse clicks not getting detected
(2 answers)
Closed 2 years ago.
While I was learning how to move images in pygame, when I held down a key(any one: up, down, left, right) to move my image(a racecar), the image only moved once, even though holding down the key should've made the image move move in that particular directions continuously, but it only moved one unit. The fix that I found was that the declaration of the variables used to change the original position (x_vary and y_vary, original position: x and y) were not be added in the main while loop of my game.
My question is why? Why does it make a difference where I declare x_vary and y_vary
Here is the unfixed code:
import pygame
pygame.init()
clock = pygame.time.Clock()
game_window = pygame.display.set_mode((800, 600))
game_icon = pygame.image.load('icon2.jpg')
game_caption = pygame.display.set_caption('Racecar Game')
x = 800 * 0.45
y = 600 * 0.08
racecar = pygame.image.load('racecar.png')
def car(x, y):
game_window.blit(racecar,(x,y))
game_run = False
while game_run == False:
x_vary = 0
y_vary = 0
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_run = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_vary = -10
elif event.key == pygame.K_RIGHT:
x_vary = +10
elif event.key == pygame.K_UP:
y_vary = -10
elif event.key == pygame.K_DOWN:
y_vary = +10
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN:
x_vary = 0
y_vary = 0
x += x_vary
y += y_vary
game_window.fill((128, 0, 0))
car(x, y)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
The keyboard events (pygame.KEYDOWN, pygame.KEYUP) occur only one time when a key is pressed, respectively released.
If x_vary = 0 and y_vary = 0 are declared in the main application loop, then they are set when a button is pressed, but they are reinitialized (0) immediately in the next frame. You've to presse a button again to set them.
If x_vary = 0 and y_vary = 0 are declared before the main application loop, then they keep the value, which is set when a button is pressed. If a button is released, then both are set 0.
Use pygame.key.get_pressed(), to evaluate if the state of a key is pressed. pygame.key.get_pressed() get the state of all keyboard buttons and a the state of a key is True as long the keyboard is pressed down. e.g.:
game_run = False
while game_run == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_run = True
keys = pygame.key.get_pressed()
x_vary, y_vary = 0, 0
if keys[pygame.K_LEFT]:
x_vary = -10
if keys[pygame.K_RIGHT]:
x_vary = +10
if keys[pygame.K_UP]:
y_vary = -10
if keys[pygame.K_DOWN]:
y_vary = +10
x += x_vary
y += y_vary
game_window.fill((128, 0, 0))
car(x, y)
pygame.display.update()
clock.tick(60)
I have started making something on pygame but I have encountered an issue when moving left or right. if I quickly change from pressing the right arrow key to pressing the left one and also let go of the right one the block just stops moving. this is my code
bg = "sky.jpg"
ms = "ms.png"
import pygame, sys
from pygame.locals import *
x,y = 0,0
movex,movey=0,0
pygame.init()
screen=pygame.display.set_mode((664,385),0,32)
background=pygame.image.load(bg).convert()
mouse_c=pygame.image.load(ms).convert_alpha()
m = 0
pygame.event.pump()
while 1:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type==KEYDOWN:
if event.key==K_LEFT:
movex =-0.5
m = m + 1
if event.key==K_RIGHT:
movex=+0.5
m = m + 1
elif event.type == KEYUP:
if event.key==K_LEFT and not event.key==K_RIGHT:
movex = 0
if event.key==K_RIGHT and not event.key==K_LEFT:
movex =0
x+=movex
y=200
screen.blit(background, (0,0))
screen.blit(mouse_c,(x,y))
pygame.display.update()
is there a way I can change this so if the right arrow key is pressed and the left arrow key is released that it will go right instead of stopping?
P.S
I am still learning pygame and am very new to the module. I'm sorry if this seems like a stupid question but i couldn't find any answers to it.
Your problem is that when you test the KEYDOWN events with
if event.key==K_LEFT and not event.key==K_RIGHT:
you always get True, because when event.key==K_LEFT is True,
it also always is not event.key==K_RIGHT (because the key of the event is K_LEFT after all).
My approach to this kind of problem is to separate
the intent from the action. So, for the key
events, I would simply keep track of what action
is supposed to happen, like this:
moveLeft = False
moveRight = False
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT: moveLeft = True
if event.key == K_RIGHT: moveRight = True
elif event.type == KEYUP:
if event.key == K_LEFT: moveLeft = False
if event.key == K_RIGHT: moveRight = False
Then, in the "main" part of the loop, you can
take action based on the input, such as:
while True:
for event in pygame.event.get():
...
if moveLeft : x -= 0.5
if moveRight : x += 0.5
the problem is that you have overlapping key features; If you hold down first right and then left xmove is first set to 1 and then changes to -1. But then you release one of the keys and it resets xmove to 0 even though you are still holding the other key. What you want to do is create booleans for each key. Here is an example:
demo.py:
import pygame
window = pygame.display.set_mode((800, 600))
rightPressed = False
leftPressed = False
white = 255, 255, 255
black = 0, 0, 0
x = 250
xmove = 0
while True:
window.fill(white)
pygame.draw.rect(window, black, (x, 300, 100, 100))
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
rightPressed = True
if event.key == pygame.K_LEFT:
leftPressed = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
rightPressed = False
if event.key == pygame.K_LEFT:
leftPressed = False
xmove = 0
if rightPressed:
xmove = 1
if leftPressed:
xmove = -1
x += xmove
pygame.display.flip()
One way could be to create a queue that keeps track of the button that was pressed last. If we press the right arrow key we'll put the velocity first in the list, and if we then press the left arrow key we put the new velocity first in the list. So the button that was pressed last will always be first in the list. Then we just remove the button from the list when we release it.
import pygame
pygame.init()
screen = pygame.display.set_mode((720, 480))
clock = pygame.time.Clock()
FPS = 30
rect = pygame.Rect((350, 220), (32, 32)) # Often used to track the position of an object in pygame.
image = pygame.Surface((32, 32)) # Images are Surfaces, so here I create an 'image' from scratch since I don't have your image.
image.fill(pygame.Color('white')) # I fill the image with a white color.
velocity = [0, 0] # This is the current velocity.
speed = 200 # This is the speed the player will move in (pixels per second).
dx = [] # This will be our queue. It'll keep track of the horizontal movement.
while True:
dt = clock.tick(FPS) / 1000.0 # This will give me the time in seconds between each loop.
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
dx.insert(0, -speed)
elif event.key == pygame.K_RIGHT:
dx.insert(0, speed)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
dx.remove(-speed)
elif event.key == pygame.K_RIGHT:
dx.remove(speed)
if dx: # If there are elements in the list.
rect.x += dx[0] * dt
screen.fill((0, 0, 0))
screen.blit(image, rect)
pygame.display.update()
# print dx # Uncomment to see what's happening.
You should of course put everything in neat functions and maybe create a Player class.
I know my answer is pretty late but im new to Pygame to and for beginner like me doing code like some previous answer is easy to understand but i have a solution to.I didn`t use the keydown line code, instead i just put the moving event code nested in the main game while loop, im bad at english so i give you guy an example code.
enter code here
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.K_ESCAPE:
run = False
win.blit(bg, (0, 0))
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]:
x -= 5
if pressed[pygame.K_RIGHT]:
x += 5
if pressed[pygame.K_UP]:
y -= 5
if pressed[pygame.K_DOWN]:
y += 5
win.blit(image,(x,y))
pygame.display.update()
pygame.quit()
This will make the image move rapidly without repeating pushing the key, at long the code just in the main while loop with out inside any other loop.