Currently, I am stuck on trying to get my tank to move when the user presses "a" and "d". The lines involving pressing a key to move the tank seem correct and I believe should work. This is also my first time using one of these forums. Please provide feedback so I can improve. Thank you for your help.
I have asked my teacher and friends for help, but they are all wondering why the tank will not move. I also have searched over the internet and youtube for answers. A weird thing is that my friend and I directly copied a youtube video on user movement where the user can move a rectangle around. My friend can hold down "w","a","s",or "d" to move the rectangle, but I can not hold down "w","a","s",or "d" to move it but need to spam the button. What is weird is that when you move your mouse around, I can then hold down "w","a","s",or "d".
import pygame
from pygame.locals import *
import math
import random
width = 640
height = 480
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("2 Player Tanks")
def gameloop():
pygame.init()
time = pygame.time.get_ticks()
screen.fill(white)
tankx = 100
tanky = 100
tankwidth = 40
tankheight = 20
turretwidth = 5
wheelwidth = 5
tankmove = 5
def tank(x,y):
x = int(x)
y = int(y)
pygame.draw.circle(screen,black,(x,y),10)
pygame.draw.rect(screen,black,(x-tankheight,y,tankwidth,tankheight))
pygame.draw.line(screen,black,(x,y),(x-20,y-20), turretwidth)
startx = 15
for i in range(7):
pygame.draw.circle(screen,black,(x-startx,y+20),wheelwidth)
startx -= 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
keys= pygame.key.get_pressed()
if keys[pygame.K_a]:
tankx -= tankmove
if keys[pygame.K_d]:
tankx += tankmove
if keys[pygame.K_w]:
tanky -= tankmove
if keys[pygame.K_s]:
tanky += tankmove
tank(tankx,tanky)
pygame.display.update()
gameloop()
I want the player to be able to use "a" and "d" to move the tank horizontally.
The event loop is executed only when an event occurs. This means it is executed when a key is pressed or a key is released — however, when a key is held down, no event occurs and the event loop is not executed.
You've got to evaluate the key presses in the main loop (in scope of gameloop) rather than in the event loop:
e.g.
def gameloop():
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
# <--
keys= pygame.key.get_pressed()
if keys[pygame.K_a]:
tankx -= tankmove
if keys[pygame.K_d]:
tankx += tankmove
if keys[pygame.K_w]:
tanky -= tankmove
if keys[pygame.K_s]:
tanky += tankmove
tank(tankx,tanky)
pygame.display.update()
Note: pygame.key.get_pressed() returns the current states of the keys and the states are evaluated and updated when pygame.event.get() is called.
The position of the tank is reset at the begin of the frame, because the variables tankx and tanky are set at the begin of gameloop:
def gameloop():
#[...]
tankx = 100
tanky = 100
Define the variables in global scope, and use the global statement to access them.
Decrease the speed of the tank, because it would move very rapidly (tankmove = 1).
The pygame.init() should be called once only, at the begin of application.
e.g.
def gameloop():
global tankx, tanky, tankmove
tankwidth = 40
tankheight = 20
turretwidth = 5
wheelwidth = 5
time = pygame.time.get_ticks()
screen.fill(white)
# [...]
pygame.init()
tankx = 100
tanky = 100
tankmove = 1
run = True
while run:
gameloop()
I can move the tank with a randomized background, but the program keeps on drawing a new tank. To fix this, I added a screen.fill(white). That fixes the drawing problem, but now I have no background.
Don't draw the random background to the window. Create a pygame.Surface and draw the random background to the surface.
.blit the background surface to the screen in every frame:
background_surface = pygame.Surface((widht, height))
# draw background to "background_surface" rather then "screen"
# [...]
def gameloop():
# [...]
# blit background instead of screen.fill(white)
screen.blit(background_surface, (0, 0))
# [...]
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()
This question already has an answer here:
How to make a character jump in Pygame?
(1 answer)
Closed last year.
hello i am currently trying to make a jumping game in pygame a lot like the chrome dino game. i have made some simple code to draw a square and make it jump. i will my code dow n bellow. my problem is with the jumping part. whenever i press w wichis the the jump button the square jumps multiple times(usauly 2 times). good people of stack overflow please help a man in need.
here is my code
import pygame
pygame.init()
screen_width = 500
screen_height = 400
isJump = False
y = 350
x = 50
BLUE=(0,0,255)
run = True
screen = pygame.display.set_mode((screen_width, screen_height))
screen.fill((0,0,0))
pygame.display.set_caption("syoma n9ot intelent")
pygame.draw.rect(screen,BLUE,(x,y,50,50))
while run:
pygame.display.flip()
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
for a in range (1,250):
y -= .5
screen.fill((0,0,0))
pygame.draw.rect(screen,BLUE,(x,y,50,50))
pygame.display.flip()
for a in range (1,250):
y += .5
screen.fill((0,0,0))
pygame.draw.rect(screen,BLUE,(x,y,50,50))
pygame.display.flip()
if event.type == pygame.QUIT:
run = False
pygame.quit()
Use the KEYDOWN event instead of pygame.key.get_pressed().
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 or a step-by-step movement.
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. See pygame.time.Clock.tick():
This method should be called once per frame.
That means that the loop:
clock = pygame.time.Clock()
run = True
while run:
clock.tick(100)
runs 100 times per second.
Do not control the game with an extra loop in the application or event loop. Use the application loop. Use the variables isJump and jumpCount to control the jump. Set the variable isJump = True and jumpCount = 20 when w is started pressed. Decrement jumpCount in the application loop and change the y position of the player. Set isJump = False if jumpCount == -20:
Complete example:
import pygame
pygame.init()
screen_width = 500
screen_height = 400
isJump = False
jumpCount = 0
y = 350
x = 50
BLUE=(0,0,255)
run = True
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
pygame.display.set_caption("syoma n9ot intelent")
pygame.draw.rect(screen,BLUE,(x,y,50,50))
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
isJump = True
jumpCount = 20
if isJump:
if jumpCount > 0:
y -= 5
elif jumpCount <= 0:
y += 5
jumpCount -= 1
if jumpCount == -20:
isJump = False
screen.fill((0,0,0))
pygame.draw.rect(screen,BLUE,(x,y,50,50))
pygame.display.flip()
Struggling to make my 'cowboy' image move across the screen in all 4 directions. Nothing happens whenever I press the keys. Help please. This is based off the exercise questions in Python Crash Course.
import pygame
pygame.init()
screen = pygame.display.set_mode((500,500))
pygame.display.set_caption("Move the Cowboy")
cowboy = pygame.image.load('images/cowboy.bmp')
cowboy = pygame.transform.scale(cowboy, (50, 50))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill((240,237,207))
cowboy_rect = cowboy.get_rect()
screen_rect = screen.get_rect()
screen_center = screen_rect.center
cowboy_rect.center = screen_rect.center
cowboy_rect.x = cowboy_rect.width
cowboy_rect.y = cowboy_rect.height
cowboy_x = float(cowboy_rect.x)
cowboy_y = float(cowboy_rect.y)
screen.blit(cowboy, screen_center)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
cowboy_x -= 5
elif keys[pygame.K_RIGHT]:
cowboy_x += 5
elif keys[pygame.K_UP]:
cowboy_y -= 5
elif keys[pygame.K_DOWN]:
cowboy_y += 5
pygame.display.update()
pygame.quit()
You are putting the image at the middle of the screen with screen.blit(cowboy, screen_center). Change screen_center to cowboy_rect as this stores the position of the image.
Also take the cowboy_rect = cowboy.get_rect() out of the loop as this resets it back to 0.
You are then putting the position of cowboy_rect to the center of the screen by doing cowboy_rect.center = screen_rect.center, take that out of the loop so it only happens once, not every frame. Then its getting changed to 50,50 by cowboy_rect.x = cowboy_rect.width and cowboy_rect.y = cowboy_rect.height, so take that out.
Then at the keys, change cowboy_x -= 5 to cowboy_rect.x -= 5 and do the same for the others.
By putting this in: screen.blit(cowboy, screen_center), you are saying that this image will appear at the center of the screen. You want it to appear at (cowboy_x, cowboy_y), as these are the positionment variables you defined. Here is a working version of your code:
import pygame
pygame.init()
screen = pygame.display.set_mode((500,500))
pygame.display.set_caption("Move the Cowboy")
cowboy = pygame.image.load('images/cowboy.bmp')
cowboy = pygame.transform.scale(cowboy, (50, 50))
cowboy_x, cowboy_y = 0, 0
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill((240,237,207))
screen.blit(cowboy, (cowboy_x, cowboy_y))
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
cowboy_x -= 1
elif keys[pygame.K_RIGHT]:
cowboy_x += 1
elif keys[pygame.K_UP]:
cowboy_y -= 1
elif keys[pygame.K_DOWN]:
cowboy_y += 1
pygame.display.update()
pygame.quit()
I took the liberty of taking out some completely useless lines of code, and of bringing the stuff you didn't need to re-iterate out of the loop, to save processing space.
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!