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
Related
I want to implement smooth motion that accelerates up to max speed and then decelerates slowly if no keys are pressed, similar to the motion of the space ship in asteroid. Here is my current code for the movement:
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
y_change = 0
accel_x = 0
accel_y = 0
max_speed = 6
sign = lambda a: (a > 0) - (a < 0)
crashed = False
while not crashed:
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
# Set the acceleration value.
if event.key == pygame.K_LEFT:
print('left')
accel_x = -.2
if event.key == pygame.K_RIGHT:
print('right')
accel_x = .2
if event.key == pygame.K_DOWN:
print('down')
accel_y = .2
if event.key == pygame.K_UP:
print('up')
accel_y = -.2
if event.type == pygame.KEYUP:
if event.key in (pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN):
print('key up')
accel_x = 0
accel_y = 0
x_change += accel_x # Accelerate.
y_change += accel_y
if abs(x_change) >= max_speed: # If max_speed is exceeded.
# Normalize the x_change and multiply it with the max_speed.
x_change = sign(x_change) * max_speed
if abs(y_change) >= max_speed: # If max_speed is exceeded.
# Normalize the y_change and multiply it with the max_speed.
y_change = sign(y_change) * max_speed
# Decelerate if no key is pressed.
if accel_x == 0:
x_change *= 0.98
if accel_y == 0:
y_change *= 0.98
x += x_change # Move the object.
y += y_change
display.fill(GRAY)
pygame.draw.rect(display, (0, 120, 250), (x, y, 20, 40))
pygame.display.update()
clock.tick(60)
pygame.quit()
When pressing the arrow keys individually the motion works perfectly, however if pressing 2 keys in rapid succession, the second key is registered but there is no motion from the object. I've tried making a list of keys using pygame.key.get_pressed() and comparing the current motion with the key pressed in the list, however this didn't solve the problem.
The major issue in your code is, that when you press and LEFT, then press and hold RIGHT and finally release LEFT, the resulting states of accel_x and accel_y are both 0, because the last event that happend was pygame.KEYUP.
I recommend to evaluate the states of pygame.key.get_pressed() rather than to use the key events:
If UP is pressed and DOWN is not pressed, the increment y_change up to its maximum (y_change = max(y_change-0.2, -max_speed)).
If UP is not pressed and DOWN is pressed, the decrement y_change down to its minimum (y_change = min(y_change+0.2, max_speed)).
If both keys are pressed simultaneously or no one of them is pressed, then decrease the speed (y_change *= 0.98).
Apply the same principle to x_change, when the keys LEFT and/or RIGHT are pressed:
crashed = False
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
keys = pygame.key.get_pressed()
# handle left and right movement
if keys[pygame.K_LEFT] and not keys[pygame.K_RIGHT]:
x_change = max(x_change-0.2, -max_speed)
elif keys[pygame.K_RIGHT] and not keys[pygame.K_LEFT]:
x_change = min(x_change+0.2, max_speed)
else:
x_change *= 0.98
# handle up and down movement
if keys[pygame.K_UP] and not keys[pygame.K_DOWN]:
y_change = max(y_change-0.2, -max_speed)
elif keys[pygame.K_DOWN] and not keys[pygame.K_UP]:
y_change = min(y_change+0.2, max_speed)
else:
y_change *= 0.98
x += x_change # Move the object.
y += y_change
display.fill(GRAY)
pygame.draw.rect(display, (0, 120, 250), (x, y, 20, 40))
pygame.display.update()
clock.tick(60)
I am trying to create a game in which you can move a character with arrow keys. When moving left or right, I want the character (an image) to flip and point left/right accordingly. The original image/character is pointing left. But I cannot get the character to flip, please help me, thanks.
import pygame
pygame.init()#initiate pygame
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
display_width = 1200
display_height = 800
display = pygame.display.set_mode((display_width,display_height))
characterimg = pygame.image.load(r'/Users/ye57324/Desktop/Make/coding/python/characterimg.png')
def soldier(x,y):
display.blit(characterimg, (x,y))
x = (display_width * 0.45)
y = (display_height * 0.1)
pygame.display.set_caption('Game')
clock = pygame.time.Clock()#game clock
flip_right = False
x_change = 0
y_change = 0
start = True
while start:
for event in pygame.event.get():
if event.type == pygame.QUIT:
start = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change += -5
if flip_right == True:
pygame.transform.flip(characterimg, True, False)
flip_right = False
elif event.key == pygame.K_RIGHT:
x_change += 5
if flip_right == False:
pygame.transform.flip(characterimg, True, False)
flip_right = True
elif event.key == pygame.K_UP:
y_change += -5
elif event.key == pygame.K_DOWN:
y_change += 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
x_change += 5
elif event.key == pygame.K_RIGHT:
x_change += -5
elif event.key == pygame.K_UP:
y_change += 5
elif event.key == pygame.K_DOWN:
y_change += -5
x += x_change
y += y_change
display.fill(white)
soldier(x,y)
pygame.display.update()
clock.tick(60)#fps
Pygame's doc-page about pygame.transform.flip states
Flipping a Surface is non-destructive and returns a new Surface with the same dimensions.
This means that the surface you pass to that method remains unaffected. You have to keep the return-value around in order to see any effect.
In practice, this means that you should replace
pygame.transform.flip(characterimg, True, False)
with
characterimg = pygame.transform.flip(characterimg, True, False)
So that characterimg point to the new, flipped version of the image.
Note: Performance wise, this is not a very good method of doing things. Each time you call pygame.transform.flip, pygame has to allocated new memory for the new surface, go over each pixel of the original surface and copy it over to the new surface, transforming its position in the process. You'd better flip the image once at the beginning of your program, so that you have a variable characterimg_left and characterimg_right and then just assign those to your characterimg variable.
I'm making a small test game on pygame where you need to fly a plane around the screen without touching the boundaries of the window, or the game will close down. Before I added the boundaries' code, I was able to make the plane's sprite change when it flew left or right, tilting in the respective direction. Now, although the "collision boundaries" code has nothing to do with the sprites code, the plane only remains in it's one, initial sprite and does not change (it still moves and the boundary code works fine, but the sprite itself wont change).
here's my code:
def game_loop():
x = display_width * 0.45
y = display_height * 0.8
x_change = 0
accel_x = 0
y_change = 0
accel_y = 0
max_speed_x = 2.5
max_speed_y = 2.5
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
elif event.type == pygame.KEYDOWN:
if event.key in (pygame.K_LEFT, pygame.K_a):
accel_x = -.2
img = pygame.image.load('PocketFlyer3.png')
img = pygame.transform.scale(img, (32, 32))
elif event.key in (pygame.K_RIGHT, pygame.K_d):
accel_x = .2
img = pygame.image.load('PocketFlyer2.png')
img = pygame.transform.scale(img, (32, 32))
if event.key in (pygame.K_UP, pygame.K_w):
accel_y = -.2
elif event.key in (pygame.K_DOWN, pygame.K_s):
accel_y = .2
elif event.type == pygame.KEYUP:
if event.key in (pygame.K_LEFT, pygame.K_RIGHT, pygame.K_a, pygame.K_d):
accel_x = 0
img = pygame.image.load('PocketFlyer1.png')
img = pygame.transform.scale(img, (32, 32))
if event.key in (pygame.K_UP, pygame.K_DOWN, pygame.K_w, pygame.K_s):
accel_y = 0
#accel script for X
x_change += accel_x
if abs(x_change) >= max_speed_x:
x_change = x_change/abs(x_change) * max_speed_x
if accel_x == 0:
x_change *= 0.92
x += x_change
#accel script for Y
y_change += accel_y
if abs(y_change) >= max_speed_y:
y_change = y_change/abs(y_change) * max_speed_y
if accel_y == 0:
y_change *= 0.92
y += y_change
display.fill(skyblue)
plane(x,y)
if x > display_width - plane_width or x < 0 or y > display_width - plane_width or y < 0:
gameExit = True
pygame.display.update()
clock.tick(60)
the width of the plane and the game's window is defined before this extract; nothing too important has been left out of this small extract either.
(the slight difference in the indentation of the code isn't the issue, only something that's occurred whilst posting this question)
Your boundary test is wrong (checks both x and y against width and ignores height), should be:
if (x > display_width - plane_width) \
or (x < 0) \
or (y > display_height - plane_height) \
or (y < 0):
I'm trying to learn python so I'm messing around with pygame. I'm a complete beginner.
so far I have made it so that I can control an image, moving it in 2d with the arrow keys.
However, I drew the image facing left and it is always facing left and I want to make it face the direction it's moving. I managed to make it rotate 180 degrees when I press right, but that was everytime I press right so I kept flipping the wrong way.
I need it to face right when moving right and left when moving left.
Image attached.
Thank you
import pygame
import time
pygame.init()
display_width = 1000
display_height = 800
black=(0,0,0)
white=(255,255,255)
gamedisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('metal gear python')
clock = pygame.time.Clock()
snakeimg= pygame.image.load('snake.png')
snake_width = 96
snake_height= 79
def snake(x,y):
gamedisplay.blit(snakeimg, (x,y))
discovered = False
while not discovered:
for event in pygame.event.get():
if event.type == pygame.QUIT:
discovered = TRUE
print (event)
x = (display_width * 0.45)
y = (display_height * 0.8)
x_change=0
y_change=0
snake_speed=0
gameExit= False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
print(event)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -3
elif event.key == pygame.K_RIGHT:
x_change = 3
elif event.key == pygame.K_RIGHT:
x_change = 3
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
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
y_change = -3
elif event.key == pygame.K_DOWN:
y_change = 3
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
y_change = 0
y += y_change
gamedisplay.fill(white)
snake(x,y)
if x > display_width-snake_width or x < 0:
gameExit=True
if y > display_height-snake_height or y < 0:
True
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
Mainly i change the snake function, so that it computes the correct rotation and then rotates the image. Also I change a little bit of the logic, especially at event handling so it is easier to read
import pygame
import time
pygame.init()
display_width = 1000
display_height = 800
black = (0, 0, 0)
white = (255, 255, 255)
gamedisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption('metal gear python')
clock = pygame.time.Clock()
snakeimg = pygame.image.load('snake.png')
snake_width = 96
snake_height = 79
def snake(x, y):
if x_change == 0 and y_change == 0:
rotation = -90
elif x_change > 0 and y_change == 0:
rotation = 180
elif x_change < 0 and y_change == 0:
rotation = 0
elif x_change == 0 and y_change > 0:
rotation = 90
elif x_change == 0 and y_change < 0:
rotation = -90
elif x_change < 0 and y_change < 0:
rotation = -45
elif x_change < 0 and y_change > 0:
rotation = 45
elif x_change > 0 and y_change < 0:
rotation = -135
elif x_change > 0 and y_change > 0:
rotation = 135
gamedisplay.blit(pygame.transform.rotate(snakeimg, rotation), (x, y))
discovered = False
while not discovered:
for event in pygame.event.get():
if event.type == pygame.QUIT:
discovered = True
print(event)
x = (display_width * 0.45)
y = (display_height * 0.8)
x_change = 0
y_change = 0
snake_speed = 0
gameExit = False
while not gameExit:
for event in pygame.event.get():
print(event)
if event.type == pygame.QUIT:
gameExit = True
discovered = True # so you can actually quit the game
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -3
elif event.key == pygame.K_RIGHT:
x_change = 3
elif event.key == pygame.K_UP:
y_change = -3
elif event.key == pygame.K_DOWN:
y_change = 3
elif event.type == pygame.KEYUP:
if event.key in (pygame.K_LEFT, pygame.K_RIGHT):
x_change = 0
elif event.key in (pygame.K_UP, pygame.K_DOWN):
y_change = 0
x += x_change
y += y_change
gamedisplay.fill(white)
snake(x, y)
if x > display_width - snake_width or x < 0:
gameExit = True
if y > display_height - snake_height or y < 0:
gameExit = True
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
And still i have the question: Why 2 game loops inside each other?
How would I get some mechanics for falling when in an empty space, many answers on the internet said to add gravity but I couldn't understand how they did that they just showed me a bunch of equations.
Also, how would I set an image as my background?
Here's my source code:
import pygame
pygame.init()
display_width = 2560
display_height = 1440
white = (255,255,255)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('RGB')
clock = pygame.time.Clock()
filler = pygame.image.load('filleraftergimp.png')
def fill(x,y):
gameDisplay.blit(filler,(x,y))
x = (display_width * 0.45)
y = (display_height * 0.8)
x_change = 0
y_change = 0
diedorgameover = False
while not diedorgameover:
for event in pygame.event.get():
if event.type == pygame.QUIT:
diedorgameover = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
x_change = -5
elif event.key == pygame.K_d:
x_change = 5
elif event.key == pygame.K_s:
y_change = 5
elif event.key == pygame.K_w:
y_change = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_d:
x_change = 0
if event.key == pygame.K_s or event.key == pygame.K_w:
y_change = 0
x += x_change
y += y_change
gameDisplay.fill(white)
fill(x,y)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
To implement gravity in your game (as in a 2D platformer), you can just increase the y_change variable each frame, so that you move the object a bit faster downwards each time. Take a look at this example:
import pygame as pg
pg.init()
LIGHTBLUE = pg.Color('lightskyblue2')
DARKBLUE = pg.Color(11, 8, 69)
display = pg.display.set_mode((800, 600))
width, height = display.get_size()
clock = pg.time.Clock()
player_image = pg.Surface((30, 60))
player_image.fill(DARKBLUE)
x = width * 0.45
y = 0
x_change = 0
y_change = 0
on_ground = False
# A constant value that you add to the y_change each frame.
GRAVITY = .3
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_a:
x_change = -5
elif event.key == pg.K_d:
x_change = 5
elif event.key == pg.K_s:
y_change = 5
elif event.key == pg.K_w:
if on_ground: # Only jump if the player is on_ground.
y_change = -12
on_ground = False
elif event.type == pg.KEYUP:
if event.key == pg.K_a and x_change < 0:
x_change = 0
elif event.key == pg.K_d and x_change > 0:
x_change = 0
# Add the GRAVITY value to y_change, so that
# the object moves faster each frame.
y_change += GRAVITY
x += x_change
y += y_change
# Stop the object when it's near the bottom of the screen.
if y >= height - 130:
y = height - 130
y_change = 0
on_ground = True
# Draw everything.
display.fill(LIGHTBLUE)
pg.draw.line(display, (0, 0, 0), (0, height-70), (width, height-70))
display.blit(player_image, (x, y))
pg.display.update()
clock.tick(60)
pg.quit()