I am coding a top-down shooter game, and was recently trying to figure out a way to continuously move my character while holding down a key. I was searching the internet to find a way, and found pygame.key.get_pressed(), this worked great until I tried to create borders so you couldn't escape the sides of the screen. I have basically worked out the problem being while your holding down the movement keys, pygame can't check the x and y coordinates of your player, therefore it allows the player to move out of bounds even when you add something like if x >= 0:.
I am asking if there is a way to create a border for the sides of the screen, while holding down a movement button with pygame.key.get_pressed(), or if there is a good alternative for moving.
Some of the code :
if eventHandler == "LEVEL1":
# SEE IF PLAYER QUIT AND PROCEED TO QUIT
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
if event.type == pygame.KEYDOWN:
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
movingUP = True
if keys[pygame.K_DOWN]:
movingDOWN = True
if keys[pygame.K_LEFT]:
movingLEFT = True
if keys[pygame.K_RIGHT]:
movingRIGHT = True
if event.type == pygame.KEYUP:
movingUP = False
movingDOWN = False
movingLEFT = False
movingRIGHT = False
x_change = 0
y_change = 0
if movingUP == True:
y_change -= 5
if movingDOWN == True:
y_change += 5
if movingLEFT == True:
x_change -= 5
if movingRIGHT == True:
x_change += 5
# UPDATES EVERY TIME GAME LOOPS
pygame.display.update()
clock.tick(60)
x += x_change
y += y_change
if eventHandler == "LEVEL1":
w.blit(level1map, (0, 0))
w.blit(player, (x, y))
pygame.display.update()
I have already tried adding (with 16 being the border)
if x >= 16:
x_change = 0
movingLEFT = 0
and so on with all of the directions. I tried this for basically anywhere it would make sense in the code. I have also tried putting
if keys[pygame.K_LEFT] and x >= 16:
movingLEFT = True
"and variable >= border" everywhere it might make a border, but still no results.
Any way to create a border that works or an alternative for moving?
[...] while your holding down the movement keys, pygame can't check the x and y coordinates of your player, [...]
No.
While your holding down the movement keys, no event occurs. There is a single pygame.KEYDOWN event when the key is pressed and a single pygame.KEYUP event when the key is released.
But there are no continuously events, when a key is pressed.
It is perfect suitable to get the pressed key in the event loop by (pygame.key.get_pressed()) and to change the state of the control variables movingUP, movingDOWN, movingLEFT movingRIGHT. The values which are returned by pygame.key.get_pressed() are updated when the event is handled by pygame.event.get().
But the bounds check has to be done in every frame, every time when the position (x, y) was changed. It has to bed done in the main game loop, rather than the event loop:
maxX = # screenWidth - playerWidth
maxY = # screenHeight - playerHeight
while loop:
if eventHandler == "LEVEL1":
# SEE IF PLAYER QUIT AND PROCEED TO QUIT
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
if event.type == pygame.KEYDOWN:
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
movingUP = True
# [...]
if movingUP == True:
y_change -= 5
# [...]
x += x_change
y += y_change
if x < 0:
x = 0
x_change = 0
elif x > maxX
x = maxX
x_change = 0
if y < 0:
y = 0
y_change = 0
elif y > maxY
y = maxY
y_change = 0
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.
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)
Trying to make imported csv of rects in pygame iterate through a list of tuples in a for loop, in main game loop, in order to add collision with the rects in the list. Only the last rect in the list has collision meaning that only the last tuple's parameters are taken into account in the game loop.
while gameExit == False:
for event in pygame.event.get():
if event.type == QUIT:
gameExit = True
#Gets keypresses and moves the player accordingly
if event.type == pygame.KEYDOWN:
#print(str(event.key))
if event.key == pygame.K_LEFT:
xChange = -playersize/2
yChange = 0
#print(str(x)+", "+str(y))
elif event.key == pygame.K_RIGHT:
xChange = playersize/2
yChange = 0
#print(str(x)+", "+str(y))
elif event.key == pygame.K_UP:
yChange = -playersize/2
xChange = 0
#print(str(x)+", "+str(y))
elif event.key == pygame.K_DOWN:
yChange = playersize/2
xChange = 0
"""
#Make player stop moving after keyup
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or pygame.K_RIGHT:
xChange = 0
if event.key == pygame.K_UP or pygame.K_DOWN:
yChange = 0
"""
#if player goes out of bounds then move them to the other side of the screen
if pacX > width or pacX < 0 or pacY > height or pacY < 0:
if pacX > width: pacX = 0
if pacX < 0: pacX = width - playersize
if pacY > height: pacY = 0
if pacY < 0: pacY = height - playersize
#is the movement selected going to intersect with a boundary?
#THIS IS THE FOR LOOP REFERENCED ABOVE
for coord in xy:
x = float(coord[0])
y = float(coord[1])
rectwidth = float(coord[2])
rectheight = float(coord[3])
if pacX+xChange > round(x-playersize) and pacX+xChange < round(x+rectwidth) and pacY+yChange > round(y-playersize) and pacY+yChange < round(y+rectheight):
touching = True
else:
touching = False
#if not touching a boundary then allow move
if not touching:
pacX += xChange
pacY += yChange
elif touching == True:
pass
#print("Sorry, that's out of bounds...")
#draw everything on the display
DISPLAY.fill(grey)
pygame.draw.rect(DISPLAY,yellow,[pacX,pacY,playersize,playersize])
buildlevel(xy,DISPLAY,darkBlue)
pygame.display.update()
clock.tick(fps)
pygame.quit()
sys.exit()
CSV FILE:
(Each of these are the coordinate and dimensions parameters for the pygame.draw.rect() method in pygame. Each row is stored as one tuple in the xy list.)
100 100 100 100
200 200 100 100
300 300 100 100
300 300 100 100
400 400 100 100
If we reduce your example to pseudo-(python)-code, it looks something like this:
for block in blocks:
if collide(player, block):
touching = True
else:
touching = False
If you now step trough this, either in your mind or with a debugger, you realize the touching gets set every time the loop-body executes, overwriting the previous value. To fix this, only set touching = True in the loop body.
for block in blocks:
if collide(player, block):
touching = True
But now touching might have not been initialized. This can be fixed by setting touching = False before the loop:
touching = False
for block in blocks:
if collide(player, block):
touching = True
I want to make a script in python/pygame that will make a character speed up as you hold a movement button (so, when I hold the left key, the character will accelerate to a specific speed and then the speed will even out, when I release the key it will slowly decrease in speed until stopping fully.)
at the moment, my code will recognize that the character must speed up, however the changes in speed only occur every time I press down and release the key (e.g: first key press will have the speed at 1, second press will be two, all the way up to the fifth or sixth press where it's at max speed, before resetting.)
I want to write my code so it means the character will accelerate whilst the key is being held down and not require multiple presses.
here is the movement section (with a few more random bits that were around it) of my code so far:
x = (display_width * 0.45)
y = display_height * 0.8
x_change = 0
negx = 0
posx = 0
bun_speed = 0
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
posx = 0
if negx != -5:
negx = negx - 1
x_change = negx
elif negx == -5:
negx = 0
x_change = -5
elif event.key == pygame.K_RIGHT:
negx = 0
if posx != 5:
posx = posx + 1
x_change = posx
elif posx == 5:
posx = 0
x_change = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
x_change = 0
elif event.key == pygame.K_RIGHT:
x_change = 0
x += x_change
display.fill(white)
bunny(x,y)
pygame.display.update()
clock.tick(60)
the two variables negx and posx decrease and increase respectively depending on the key pressed. pressing the key assigned to one variable resets the other to zero, so when that variable is next called when the opposite button is pressed it will accelerate from zero. Once either variable reaches the max speed, it resets for when the button is released meaning the character can accelerate once more.
If there's any way to make this work (as well as neaten up the code, if you're able to) then guidance as to how to get it to function would be much appreciated.
Define a variable for the acceleration (accel_x in the example), set it to the desired value in the event loop and add it to the x_change every frame to accelerate. To limit the x_change to the maximum speed, you have to normalize it and multiply it by the max_speed.
To decelerate the object, you can multiply x_change with a value below 1 when no key is pressed.
import pygame
pygame.init()
display = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
GRAY = pygame.Color('gray12')
display_width, display_height = display.get_size()
x = display_width * 0.45
y = display_height * 0.8
x_change = 0
accel_x = 0
max_speed = 6
crashed = False
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
elif event.type == pygame.KEYDOWN:
# Set the acceleration value.
if event.key == pygame.K_LEFT:
accel_x = -.2
elif event.key == pygame.K_RIGHT:
accel_x = .2
elif event.type == pygame.KEYUP:
if event.key in (pygame.K_LEFT, pygame.K_RIGHT):
accel_x = 0
x_change += accel_x # Accelerate.
if abs(x_change) >= max_speed: # If max_speed is exceeded.
# Normalize the x_change and multiply it with the max_speed.
x_change = x_change/abs(x_change) * max_speed
# Decelerate if no key is pressed.
if accel_x == 0:
x_change *= 0.92
x += x_change # Move the object.
display.fill(GRAY)
pygame.draw.rect(display, (0, 120, 250), (x, y, 20, 40))
pygame.display.update()
clock.tick(60)
pygame.quit()
I found another way of accelerating the character. Instead of changing distance, change time. I defined a variable which goes inside pygame.time.delay() and the delay is something I change. This is better because the character does not look like glitching when moving from one place to another.
import pygame
pygame.init()
win = pygame.display.set_mode((500, 500))
pygame.display.set_caption("ACCELERATE")
def main():
k = True
thita = 40
x = 250
y = 400
while k:
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
thita = 40
if event.key == pygame.K_RIGHT:
thita = 40
if event.type == pygame.QUIT:
k = False
if keys[pygame.K_LEFT]:
x -= 4
pygame.time.delay(thita)
if thita > 12:
thita -= 1
if keys[pygame.K_RIGHT]:
x += 4
pygame.time.delay(thita)
if thita > 11:
thita -= 1
pygame.draw.rect(win, (255, 0, 0), (x, y, 10, 10))
pygame.display.update()
win.fill((0, 0, 0))
main()
You see the thita goes back to its original value as soon as we lift the key.
Kevin, what you have mentioned is a key point, but it is a very easy fix, you just need to replace decelerating code with something that checks whether the x_change is positive or negative and then adding or subtracting once you have figured it out.
For example his code is:
if accel_x == 0:
x_change *= 0.92
replace this with something along the lines of:
if accel_x == 0:
if x_change > 0:
x_change -= 0.2
if x_change < 0.2:
x_change = 0
elif x_change < 0:
x_change += 0.2
if x_change > -0.2:
x_change = 0