I am currently making a fighting game and was wondering how command inputs could be added. I understand it is kind of irrelevant and many substitutes are possible, but it would be nice to use familiar fighting game inputs.
I currently have something like this:
keys = pygame.key.get_pressed()
if keys[pygame.K_DOWN]:
commandcount +=1
if commandcount > 0 and commandcount < 30 and keys[pygame.K_RIGHT] and keys[pygame.K_z]:
player1.projectile = True
The "commandcount" helps keep the window of action available until a certain amount of time.
The main problem with this is that you could still press the inputs in whatever order and the projectile would still come out.
Thanks
Try using the pygame.KEYDOWN events instead of pygame.key.get_pressed() so the order can be observed. Use a list to keep track of the order of these KEYDOWN events. When the order matches a specific combo then execute the move and reset the list. The list also gets reset after a certain amount of time with a combo. I made an example program with the combo down, right, z activating a fireball.
import pygame
# pygame setup
pygame.init()
# Open a window on the screen
width, height = 600, 600
screen = pygame.display.set_mode((width, height))
def main():
clock = pygame.time.Clock()
black = (0, 0, 0)
move_combo = []
frames_without_combo = 0
while True:
clock.tick(30) # number of loops per second
frames_without_combo += 1
if frames_without_combo > 30 or len(move_combo) > 2:
print("COMBO RESET")
frames_without_combo = 0
move_combo = []
screen.fill(black)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
move_combo.append(event.key) # Only keys for combos should be added
if move_combo == [pygame.K_DOWN, pygame.K_RIGHT, pygame.K_z]:
print("FIRE BALL")
frames_without_combo = 0
print(move_combo)
pygame.display.update()
main()
Related
This question already has an answer here:
Setting up an invisible boundary for my sprite
(1 answer)
Closed 11 months ago.
I am looking on how to keep my sprite within the set boundaries of a window in Pygame. Could anyone here please help me keep the car sprite within the lines at all times? Thanks! (please don't come to edit my question, actually help me!)
import pygame
pygame.init()
screen = pygame.display.set_mode((300,208))
pygame.display.set_caption("TinyRacer")
car = pygame.image.load("car.png")
bg = pygame.image.load("bg.png")
run = True
y = 84
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
y -= 16
if key[pygame.K_DOWN]:
y += 16
screen.fill((255,255,255))
screen.blit(car, (0,y))
screen.blit(bg,(0,0))
pygame.display.update()
pygame.quit()
I have tried following Techwithtim's tutorial on this, but to no avail.
If you would keep position and size in pygame.Rect() then you could use special functions to check collisions.
Or simply check
car_rect.top < screen_rect.top and screen_rect.bottom < car_rect.bottom
Or you could use contains to check if one rect is fully inside another.
I create green background which is smaller than window, and I use Rect() to check if car in inside this green region.
car_rect.y -= 16
if car_rect.top < bg_rect.top:
car_rect.top = bg_rect.top
I also use Rect() to put elements in center of screen.
car_rect.centerx = screen_rect.centerx
car_rect.centery = screen_rect.centery
The same way you can put other elements (i.e. text in center of button)
To make it simpler I use surfaces instead of images so everyone can simply copy and run it.
import pygame
pygame.init()
screen = pygame.display.set_mode((300,208))
screen_rect = screen.get_rect()
pygame.display.set_caption("TinyRacer")
#car = pygame.image.load("car.png")
car = pygame.Surface((20,10))
car.fill((255,0,0))
car_rect = car.get_rect()
car_rect.centerx = screen_rect.centerx
car_rect.centery = screen_rect.centery
#bg = pygame.image.load("bg.png")
bg = pygame.Surface((300,100))
bg.fill((0,255,0))
bg_rect = bg.get_rect()
bg_rect.centery = screen_rect.centery
clock = pygame.time.Clock()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
car_rect.y -= 16
if car_rect.top < bg_rect.top:
car_rect.top = bg_rect.top
if key[pygame.K_DOWN]:
car_rect.y += 16
if car_rect.bottom > bg_rect.bottom:
car_rect.bottom = bg_rect.bottom
screen.fill((255,255,255))
screen.blit(bg, bg_rect)
screen.blit(car, car_rect)
pygame.display.update()
clock.tick(25) # 25FPS (it gives 40ms delay)
pygame.quit()
I am trying to make a python window for my game from my laptop in pygame... however when I try to close the window I get an error message saying "not responding" im not sure why that if I thought i had done everything right.... the code is below any help is needed.
Thanks!
import pygame
from sys import exit
pygame.init()
screen = pygame.display.set_mode((800,400))
clock = pygame.time.Clock()
sky_surface = pygame.image.load("bg_desert.png").convert()
snail_surface = pygame.image.load("snailWalk1.png").convert_alpha()
player_surf = pygame.image.load("p1_walk01.png")
snail_x_pos = 600
while True:
pygame.time.set_timer(snail_x_pos, 100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
print("hello")
snail_x_pos -=4
if snail_x_pos < -100: snail_x_pos = 800
screen.blit(sky_surface,(0,0))
screen.blit(snail_surface,(snail_x_pos,350))
screen.blit(player_surf,(80, 200))
pygame.display.update()
clock.tick(60)
All problem makes
pygame.time.set_timer(snail_x_pos, 100)
which you run inside loop.
If I remove it then it closes without problem.
Every set_timer creates new timer which sends event every 100ms (again and again). If you run it in loop then you create hundreds timers.
You should run it only once - before loop - and it will repeatly send event which you can get in for-loop.
Problem is also that you use it in wrong way - you use snail position but it should user_id and time in milliseconds.
my_event_id = pygame.USEREVENT + 1
pygame.time.set_timer(my_event_id, 500) # 500ms = 0.5s
and later you can use it to move snail
elif event.type == my_event_id:
print('0.5 second')
snail_x_pos -= 4
Here my version with other changes.
I use Surfaces instead images so everyone can simply copy and run it.
I also use pygame.Rect to keep position and size - it has useful values (ie. .center to get/set center position) and functions (ie. to detect colisions). I use .left and .right to move snake to right side of window when it leaves window on left side.
import pygame
pygame.init()
screen = pygame.display.set_mode((800,400))
sky_surface = pygame.Surface((800, 100))
sky_surface.fill((0,0,255))
sky_surface_rect = sky_surface.get_rect()
snail_surface = pygame.Surface((100, 10))
snail_surface.fill((0,255,0))
snail_surface_rect = snail_surface.get_rect()
snail_surface_rect.x = 600
snail_surface_rect.y = 350
player_surf = pygame.Surface((10, 50))
player_surf.fill((255,0,0))
player_surf_rect = player_surf.get_rect()
player_surf_rect.x = 80
player_surf_rect.y = 200
clock = pygame.time.Clock()
my_event_id = pygame.USEREVENT + 1
pygame.time.set_timer(my_event_id, 500) # 500ms = 0.1s
while True:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
print("hello")
elif event.type == my_event_id:
print('0.5 second')
# move snake
snail_surface_rect.x -= 4
# use this event to slow down player
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]:
player_surf_rect.x -= 4
elif pressed[pygame.K_RIGHT]:
player_surf_rect.x += 4
# - updates -
if snail_surface_rect.right < 0:
snail_surface_rect.left = 800
# - draw -
screen.fill((0,0,0)) # clear screen
screen.blit(sky_surface, sky_surface_rect)
screen.blit(snail_surface, snail_surface_rect)
screen.blit(player_surf, player_surf_rect)
pygame.display.update()
clock.tick(60)
This question already has answers here:
Why is my pygame application loop not working properly?
(1 answer)
Why is my PyGame application not running at all?
(2 answers)
Closed 2 years ago.
I'm writing a program for school, but when I press a specific key to make my laser move across the screen, it disappears instantly. How can I make it move slowly, so the player can see it going from the left side to the right side of the screen ?
Here's the code (I wrote it again so I had only the part doing the laser moves)
#
import pygame, time
from pygame_functions import*
pygame.init()
win = pygame.display.set_mode((1440, 480))
laserImg = pygame.image.load('laser.png')
backgroundImg = pygame.image.load('bg.jpg')
laserX = 90
laserY = 400
clock = pygame.time.Clock()
run = True
while run:
clock.tick(14)
win.blit(backgroundImg, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
win.blit(laserImg, (laserX, laserY))
if event.type == pygame.KEYDOWN:
print("touche")
if keys[pygame.K_UP]:
while laserX < 1500:
laserX += 10
pygame.display.update()
pygame.quit
#
Thanks if you can help me
edit : idk why but there wasn't the line saying "while laserX < 1500"
Try something like this:
run = True
laser_moving=False
while run:
clock.tick(14)
win.blit(backgroundImg, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
win.blit(laserImg, (laserX, laserY))
if event.type == pygame.KEYDOWN:
print("touche")
if keys[pygame.K_UP]:
laser_moving=True
if laser_moving:
laser_x+=1
if laser_x > 1600:
laser_moving=False
Your issue is that you immediately move the laser outside the screen in your loop, without waiting for the next frame to be drawn in between. You need to exit your loop so the next frame can be drawn and the intermediate states of the laser are visible.
I currently have an object that has code to rotate it around its center and move it around (bby altering its pos values).
However I want to make it so that if up arrow is pressed, it will accelerate in the direction its facing and when its released it will decelerate again back to 0. In the code I use a dt value for the change in time.
I tried clocking the time when a button is pressed and released and use that as the dt value, using this method the dT value can be negative. I also think this wouldn't work because then the rocket would receive let's say a dT value of 1 sec and update its velocity to go really fast instead of having a smooth acceleration/deacceleration.
class Rocket:
def __init__(self, image, pos, angle):
self.image = pygame.image.load(image)
self.imageDimensions = self.image.get_rect()
self.angle = angle
self.pos = pos
self.center = (self.pos[0], self.pos[1])
self.velocity = [0, 0]
self.acceleration = [0, 0]
self.angularVelocity = 0
self.angularAcceleration = 0
self.isAccelerating = False
self.thrust = 50
def draw(self, surface):
rotatedImg = pygame.transform.rotate(self.image, self.angle)
rotatedImgDimensions = rotatedImg.get_rect()
#display image
surface.blit(rotatedImg, (self.pos[0] - rotatedImgDimensions.center[0], self.pos[1] - rotatedImgDimensions.center[1]))
def update(self, dt):
#update angle
self.angularVelocity += self.angularAcceleration * dt
self.angle += self.angularVelocity * dt
#if accelerating update the acceleration
if self.isAccelerating:
self.acceleration[0] -= self.thrust * math.sin(math.radians(self.angle))
self.acceleration[1] -= self.thrust * math.sin(math.radians(self.angle))
#update velocity
self.velocity[0] += self.acceleration[0] * dt
self.velocity[1] += self.acceleration[1] * dt
#update position
self.pos[0] += self.velocity[0] * dt
self.pos[1] += self.velocity[1] * dt
So in short I expect the rocket to accelerate forward when I press the up arrow, deaccelerate to 0 when I press the down arroy and rotate left and right when pressing arrow left and right.
Please note that the above class is in a different file named Objects.py
Thank you!!
Here is the rest of the code:
import pygame
from pygame.locals import *
import math
import Objects
#colors
WHITE = (255,255,255)
BLACK = (0,0,0)
TRANSPARENT = (0,0,0,0)
#size window
Width, Height = (1280,720)
#Main menu
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_SPACE:
intro = False
screen.fill(BLACK)
FPS = 30
#initialise pygame
pygame.init()
fpsClock = pygame.time.Clock()
screen = pygame.display.set_mode((Width, Height))
pygame.display.set_caption("Rockets and Missiles")
#Add players
Rocket1 = Objects.Rocket("rocket.png", [100, 100], 0) #start at pos 50,50
#run main menu first
game_intro()
run = True
while run:
screen.fill(BLACK)
Rocket1.draw(screen)
#event handler
pressed = pygame.key.get_pressed() #list with all pressed keys
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
Time0 = pygame.time.get_ticks()
if event.type == pygame.KEYUP:
Time1 = pygame.time.get_ticks()
if pressed[pygame.K_UP]:
dT = Time1 - Time0
print(dT)
Rocket1.update(dT)
pygame.display.update()
fpsClock.tick(FPS)
pygame.quit()
quit()
I don't think you have a very useful definition of dT. As far as I can tell you only call Rocket.update() when a key is pressed, the rockets need to update every frame with small dT if you want smooth motion. Without calling update() in your rocket class more consistently you will not get the nice motion you want.
I suggest something like this for your main loop:
dT = 1/FPS
while run:
screen.fill(BLACK)
Rocket1.draw(screen)
#event handler
pressed = pygame.key.get_pressed() #list with all pressed keys
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if pressed[pygame.K_UP]:
Rocket1.ChangeCurrentAcceleration()
Rocket1.update()
pygame.display.update()
fpsClock.tick(FPS)
For some new ChangeCurrentAcceleration() function that adds to the acceleration in your rocket, you can then change update() to assume that it already has the proper acceleration from thrust and calculates new velocity and position from there (maybe add a 0.95* multiplier to accel so it naturally slows down).
I do not think measuring the time the button is pressed is the correct approach. Each iteration of the main loop corresponds to a fixed amount of time, and to produce an animation you want to move the rocket by that fixed amount of time each iteration. So no need to calculate a dt the way you are doing. You already have it, and it's equal to 1/FPS.
What you want to do usually is to set some velocity / acceleration for you rocket. Now they are all 0, but you should set a fixed value different from zero: how much you want it to be faster, or how much faster do you want it to accelerate, when the key button is pressed.
And when the key button corresponding is pressed, call the update method to calculate the new position / angle based on that velocity / acceleration and then redraw the rocket, considering that the time passed is 1/FPS.
And you also need two method to update separately linear motion and rotation. The way is now, you cannot separate the movements based on different keys.
Thanks guys for helping out, i didnt realise this part :D
However i want to answer my own question for people visiting this page later on.
A perhaps better way of doing it is to get the time it took the software to go over each iteration and using that as dT. it would look like the following: BBefore the main loop:fpsClock = pygame.time.Clock()
The main loop:
while run:
screen.fill(BLACK)
Rocket1.draw(screen)
#draw missiles
for missile in Missiles:
missile.draw(screen)
#event handler
pressed = pygame.key.get_pressed() #list with all pressed keys
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if pressed[K_UP]:
Rocket1.acceleration = [0, -100]
if pressed[K_DOWN]:
Rocket1.acceleration = [0, 100]
if pressed[K_RCTRL]:
Missiles.append(Objects.Missile("missile.png", Rocket1.pos, Rocket1.angle))
dT = fpsClock.get_time()
Rocket1.update(dT/1000)
pygame.display.update()
fpsClock.tick(FPS)
I found a solution to make a sprite move when you hold a key down. The problem is that it forces writing ugly duplicated code. The current solution I found is:
for event in pygame.event.get():
if event.type == KEYDOWN:
keystate = pygame.key.get_pressed()
while keystate[K_RIGHT]:
screen.fill((255,255,255))
pygame.event.get()
for sprite in sprites:
rimage = sprite[1].getimage()
if sprite[2] is None:
x+=3
sprite[1].update(time)
screen.blit(rimage, (x,y))
if sprite[1].isfinished() == True:
sprite[1].reset()
last_dir = "right"
if x >= screen_width - rimage.get_width():
x = screen_width - rimage.get_width()
#update player sprite movement
#update player sprite animation
#update rest of game map
keystate = pygame.key.get_pressed()
time = pygame.time.get_ticks()
pygame.display.update()
The problem is that the while keystate block. It has to be repeated for each direction and the game world needs to be updated in each while block. That is five places where the same code needs to be duplicated....4 directions plus if a key is not pressed. I could wrap it in a function but I was wondering if there was a better way to handle holding down a button in pygame.
The usual way program in pygame is use the events to update the direction, then write the update position code outside events, that way you don't need replicated code.
clock = pygame.time.Clock()
direction = (0,0)
while True: # main loop
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_RIGHT:
direction = (3, 0)
elif event.key == K_LEFT:
direction = (-3, 0)
elif event.key == K_UP:
direction = (0, 3)
elif event.key == K_DOWN:
direction = (0, -3)
else:
print "Unrecognized key"
if event.type == KEYUP:
direction = (0, 0)
screen.fill((255,255,255))
for sprite in sprites:
rimage = sprite[1].getimage()
if sprite[2] is None:
# Check if new position is inside the screen
new_pos = x + direction[0], y + direction[1]
if new_pos[0] + rimage.get_width() < screen_width:
x = new_pos[0]
if new_pos[1] + rimage.get_height() < screen_height:
y = new_pos[1]
# Draw the sprite
sprite[1].update(time)
screen.blit(rimage, (x,y))
if sprite[1].isfinished() == True:
sprite[1].reset()
last_dir = direction
#update player sprite movement
#update player sprite animation
#update rest of game map
time = pygame.time.get_ticks()
pygame.display.update()
clock.tick(40) # Keep game running at 40 fps
If you want you can achieve the same with keystate, in such case you don't need to process key events.
Pygame suggests or implies the division of programs into 3 parts:
The event handling, updating and drawing.
As pmoleri already said, you simply change the direction of the movement.
In the update function, you should pass in a delta time parameter, to move all the sprites according to the time passed. It is quite important, since the other technique doesn't take into account the variable speed of the processor. Games in DOS have been made this way, so now we need emulators to artificially slow down the processor. The draw part simply draws all the sprites.
This way you have a clear division between these 3 distinc parts in games: player input, game logic(movement, collision, actions etc.) and drawing.
Additionally, pygame.key.get_pressed() can be used.
This returns a list of all the keys currently being held down.
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
# Move Up
if keys[pygame.K_a]:
# Move Down
... etc ...
This does not need to be in the event section, but probably where the player updates.
This can make the code less clunky and (possible) be more efficient
-Hope I helped!