Gravity in pygame [closed] - python

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
I'm making a platform game with pygame, and I would like to add gravity to it.
Right now I only have a picture which moves when I press the arrow keys, and my next step would be gravity. Here's my code:
import pygame, sys
from pygame.locals import *
pygame.init()
FPS = 30
fpsClock = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((400, 300), 0, 32)
pygame.display.set_caption("Jadatja")
WHITE = (255, 255, 255)
catImg = pygame.image.load("images/cat.png")
catx = 10
caty = 10
movingRight = False
movingDown = False
movingLeft = False
movingUp = False
while True: #main game loop
#update
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_RIGHT:
#catx += 5
movingRight = True
movingLeft = False
elif event.key == K_DOWN:
#caty += 5
movingDown = True
movingUp = False
elif event.key == K_LEFT:
#catx -= 5
movingLeft = True
movingRight = False
elif event.key == K_UP:
#caty -= 5
movingUp = True
movingDown = False
if event.type == KEYUP:
if event.key == K_RIGHT:
movingRight = False
if event.key == K_DOWN:
movingDown = False
if event.key == K_LEFT:
movingLeft = False
if event.key == K_UP:
movingUp = False
#actually make the player move
if movingRight == True:
catx += 5
if movingDown == True:
caty += 5
if movingLeft == True:
catx -= 5
if movingUp == True:
caty -= 5
#exit
for event in pygame.event.get():
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == QUIT:
pygame.quit()
sys.exit()
#draw
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(catImg, (catx, caty))
pygame.display.update()
fpsClock.tick(FPS)
I'm not 100% sure if this code is as smooth as I think it is, but I hope you guys can make something of it.
Thanks

There is a tutorial for creating a bouncing ball which I think might be helpful to you.
Now, to add gravity to that simulation, you'd simply add some extra speed in the y-direction every time through the loop:
speed[1] += gravity
What you end up with is kind of goofy however, since the image quickly descends below the bottom of the window never to be seen again :)
The next step is therefore to clip the position of the ball so it must remain in the window:
import os
import sys, pygame
pygame.init()
size = width, height = 320, 240
speed = [1, 1]
black = 0, 0, 0
gravity = 0.1
screen = pygame.display.set_mode(size)
image_file = os.path.expanduser("~/pybin/pygame_examples/data/ball.png")
ball = pygame.image.load(image_file)
ballrect = ball.get_rect()
def clip(val, minval, maxval):
return min(max(val, minval), maxval)
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
speed[1] += gravity
ballrect = ballrect.move(speed)
if ballrect.left < 0 or ballrect.right > width:
speed[0] = -speed[0]
if ballrect.top < 0 or ballrect.bottom > height:
speed[1] = -speed[1]
# clip the position to remain in the window
ballrect.left = clip(ballrect.left, 0, width)
ballrect.right = clip(ballrect.right, 0, width)
ballrect.top = clip(ballrect.top, 0, height)
ballrect.bottom = clip(ballrect.bottom, 0, height)
screen.fill(black)
screen.blit(ball, ballrect)
pygame.display.flip()
Okay, now you can incorporate that in your current code and you'll be off and running. However, there are some things you can do to make your code more organized and less repetitive.
For example, consider the massive if...then blocks that follow
for event in pygame.event.get():
You could rewrite it as something like:
delta = {
pygame.K_LEFT: (-20, 0),
pygame.K_RIGHT: (+20, 0),
pygame.K_UP: (0, -20),
pygame.K_DOWN: (0, +20),
}
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
deltax, deltay = delta.get(event.key, (0, 0))
ball.speed[0] += deltax
ball.speed[1] += deltay
You could also benefit from putting all the logic associated with the movement of your image into a class:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.speed = [0, 0]
area = pygame.display.get_surface().get_rect()
self.width, self.height = area.width, area.height
def update(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > self.width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > self.height:
self.speed[1] = -self.speed[1]
self.rect.left = clip(self.rect.left, 0, self.width)
self.rect.right = clip(self.rect.right, 0, self.width)
self.rect.top = clip(self.rect.top, 0, self.height)
self.rect.bottom = clip(self.rect.bottom, 0, self.height)
Notice the update method is very similar to the code presented by the tutorial. One of the nice things about creating a Ball class is that the rest of your program does not need to know much about how a Ball moves. All the logic is in Ball.update. Moreover, it makes it easy to instantiate many balls. And you could create other classes (airplanes, birds, paddles, etc.) that move differently too and add them to your simulation relatively painlessly.
So, putting it all together, you would end up with something like this:
"""
http://stackoverflow.com/a/15459868/190597 (unutbu)
Based on http://www.pygame.org/docs/tut/intro/intro.html
Draws a red ball bouncing around in the window.
Pressing the arrow keys moves the ball
"""
import sys
import pygame
import os
image_file = os.path.expanduser("~/pybin/pygame_examples/data/ball.png")
delta = {
pygame.K_LEFT: (-20, 0),
pygame.K_RIGHT: (+20, 0),
pygame.K_UP: (0, -20),
pygame.K_DOWN: (0, +20),
}
gravity = +1
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.speed = [0, 0]
area = pygame.display.get_surface().get_rect()
self.width, self.height = area.width, area.height
def update(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > self.width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > self.height:
self.speed[1] = -self.speed[1]
self.rect.left = clip(self.rect.left, 0, self.width)
self.rect.right = clip(self.rect.right, 0, self.width)
self.rect.top = clip(self.rect.top, 0, self.height)
self.rect.bottom = clip(self.rect.bottom, 0, self.height)
def clip(val, minval, maxval):
return min(max(val, minval), maxval)
class Main(object):
def __init__(self):
self.setup()
def setup(self):
pygame.init()
size = (self.width, self.height) = (640,360)
self.screen = pygame.display.set_mode(size, 0, 32)
self.ball = Ball()
self.setup_background()
def setup_background(self):
self.background = pygame.Surface(self.screen.get_size())
self.background = self.background.convert()
self.background.fill((0, 0, 0))
self.screen.blit(self.background, (0, 0))
pygame.display.flip()
def draw(self):
self.screen.blit(self.background, (0, 0))
self.screen.blit(self.ball.image, self.ball.rect)
pygame.display.flip()
def event_loop(self):
ball = self.ball
friction = 1
while True:
for event in pygame.event.get():
if ((event.type == pygame.QUIT) or
(event.type == pygame.KEYDOWN and
event.key == pygame.K_ESCAPE)):
sys.exit()
elif event.type == pygame.KEYDOWN:
deltax, deltay = delta.get(event.key, (0, 0))
ball.speed[0] += deltax
ball.speed[1] += deltay
friction = 1
elif event.type == pygame.KEYUP:
friction = 0.99
ball.speed = [friction*s for s in ball.speed]
ball.speed[1] += gravity
ball.update()
self.draw()
pygame.time.delay(10)
if __name__ == '__main__':
app = Main()
app.event_loop()

Related

Replaying the main loop with a few changes pygame

I have been trying to make a Fangame for Undertale with pygame but i am stuck. This is my first project. I want to replay the main loop with a few changes, for example i want the knife movement to be different, but i dont know how to do it. I have tried with attack2 to replay everything but it doesnt work, for example the player can't move and the knife is also not moving. I also tried defining the main loop but then i can't change anything. Please comment if you can help me. This is the code i have:
import math
import pygame
from pygame import mixer
pygame.init()
screen = pygame.display.set_mode((1000, 800))
# FPS
FPS = 60
clock = pygame.time.Clock()
# Title and icon
pygame.display.set_caption("CharaFight")
icon = pygame.image.load('Chara.png')
pygame.display.set_icon(icon)
# images
fight_button = pygame.image.load('Fight.png')
fight_button2 = pygame.image.load('Fight2.png')
hp_bar1 = pygame.image.load('hp_bar.png')
hp_bar2 = pygame.image.load('hp_bar2.png')
# music and sounds
background_muziek = mixer.music
damage_sound = mixer.Sound('Undertale Sound Effect - Taking Damage.wav')
background_muziek.load('Prepare for Battle!.wav')
background_muziek.set_volume(0.4)
background_muziek.play(-1)
# backgrounds
background = pygame.image.load("Background1.png")
background2 = pygame.image.load("Background2.png")
# Player
playerImg = pygame.image.load("Soul.png")
PlayerX = 450
PlayerY = 550
PlayerX_change = 0
PlayerY_change = 0
HP = 0
# Enemy
enemyImg = pygame.image.load("Chara2.png")
charaImg = pygame.image.load("Chara3.png")
EnemyX = 390
EnemyY = 50
EnemyX_change = 0
EnemyY_change = 0
# Knife
knifeImg = pygame.image.load("knife.png")
KnifeX = 350
KnifeY = 580
KnifeX_change = 5
KnifeY_change = 0
Knife_state = "ready"
# Game over text
over_font = pygame.font.Font('8-BIT WONDER.TTF', 64)
det_font = pygame.font.Font('8-BIT WONDER.TTF', 30)
emy_font = pygame.font.Font('Determination_text.ttf', 45)
def game_over_text():
over_text = over_font.render("GAME OVER", True, (255, 255, 255))
det_text = det_font.render(" Stay Determined ", True, (255, 255, 255))
screen.blit(over_text, (230, 250))
screen.blit(det_text, (280, 400))
def enemy_text():
chara_text = emy_font.render(" * Give me your SOUL! ", True, (255, 255, 255))
screen.blit(chara_text, (150, 500))
def fade(width, height):
fade1 = pygame.Surface((width, height))
fade1.fill((0, 0, 0))
for alpha in range(0, 150):
fade1.set_alpha(alpha)
screen.blit(fade1, (0, 0))
pygame.display.update()
pygame.time.delay(5)
def player(x, y):
screen.blit(playerImg, (x, y))
def enemy(x, y):
screen.blit(enemyImg, (x, y))
def fire_knife(x, y):
global Knife_state
Knife_state = "fire"
screen.blit(knifeImg, (x, y))
def isCollision(PlayerX, PlayerY, KnifeX, KnifeY):
distance = math.sqrt(math.pow(PlayerX - KnifeX, 2) + (math.pow(PlayerY - 37 - KnifeY, 2)))
if distance < 27:
return True
else:
return False
# Here is my attempt to replay the main loop
def attack2():
screen.fill((0, 0, 0))
player(PlayerX, PlayerY)
enemy(EnemyX, EnemyY)
fire_knife(KnifeY, KnifeX)
screen.blit(background, (100, 300))
pygame.display.flip()
# main loop
running = True
while running:
screen.fill((0, 0, 0))
screen.blit(background, (100, 300))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Movement
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
PlayerX_change = -2.5
if event.key == pygame.K_RIGHT:
PlayerX_change = 2.5
if event.key == pygame.K_UP:
PlayerY_change = -2.5
if event.key == pygame.K_DOWN:
PlayerY_change = 2.5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
PlayerX_change = 0
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
PlayerY_change = 0
# Boundaries for player
PlayerY += PlayerY_change
if PlayerY >= 700:
PlayerY = 700
elif PlayerY <= 520:
PlayerY = 520
PlayerX += PlayerX_change
if PlayerX <= 370:
PlayerX = 370
elif PlayerX >= 570:
PlayerX = 570
# Knife movement
if KnifeX >= 550:
KnifeX = 340
KnifeY += 20
Knife_state = "ready"
if KnifeY >= 680:
KnifeY = 480
Knife_state = "ready"
if Knife_state == "fire":
fire_knife(KnifeX, KnifeY)
KnifeX += KnifeX_change
# collision and game over screen
collision = isCollision(PlayerX, PlayerY, KnifeX, KnifeY)
if collision:
Knife_state = "ready"
damage_sound.set_volume(1.5)
damage_sound.play()
HP += 1
if HP > 100:
fade(1000, 800)
screen.fill((0, 0, 0))
game_over_text()
background_muziek.stop()
game_over_sound = mixer.music
game_over_sound.load('Undertale Game Over Theme.wav')
game_over_sound.play(-1)
pygame.display.flip()
pygame.time.wait(5000)
while True:
if pygame.key.get_pressed():
running = True
pygame.quit()
# Fight screen
if pygame.time.get_ticks() > 21700:
fade(1000, 800)
screen.fill((0, 0, 0))
enemy_text()
screen.blit(background2, (140, 250))
screen.blit(charaImg, (390, 50))
screen.blit(fight_button, (330, 630))
screen.blit(hp_bar1, (440, 350))
pygame.display.flip()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
screen.blit(fight_button2, (330, 630))
screen.blit(hp_bar2, (440, 350))
damage_sound.set_volume(10)
damage_sound.play()
pygame.display.flip()
pygame.time.wait(5000)
attack2()------------------> here i try to replay the main loop but i cant move and the knife is also not moving.
if pygame.key.get_pressed():
running = True
clock.tick(FPS)
player(PlayerX, PlayerY)
enemy(EnemyX, EnemyY)
fire_knife(KnifeX, KnifeY)
pygame.display.update()
You can use a class like this to animate stuff:
import pygame
from os import listdir, path
def load_animations(folder_path: str) -> tuple:
animation = listdir(folder_path)
animation = tuple(pygame.image.load(path.join(folder_path, frame)).convert_alpha() for frame in animation)
return animation
class Animation:
def __init__(self, FPS: int, animation_folder: str, speed: float, pos: tuple):
self.animation = load_animations(animation_folder)
self.speed = speed
self.FPS = FPS
self.current_frame = 0
self.image = None
self.rect = None
self.pos = pos
def get_frame(self) -> pygame.Surface:
self.current_frame += self.speed * 10 / self.FPS
if self.current_frame >= len(self.animation):
return
return self.animation[int(self.current_frame)]
def play(self, screen: pygame.Surface):
self.image = self.get_frame()
if not self.image:
return
self.rect = self.image.get_rect(center=self.pos)
screen.blit(self.image, self.rect)
And to use this you would do something like:
animation = Animation(60, "path to the folder", 1, (100, 100))
# Put this inside the main loop.
animation.play(# Put the display surface in here)
And you should make a draw function for your whole game and put this code inside of the draw function (This function should handle all the graphics of your game).

I need to be able to pause the whole game, but only my car sprite pauses when I press the p key on my keyboard

I implemented a pause feature in my game in the while loop at the end of my code, but the problem is that only my car sprite pauses. I have tried a bunch of different ways setting up my pause code but none of the ways I arrange it seems to pause the whole game.
I want to be able to pause my whole game when I press the p key on my keyboard, not just my car sprite. Can someone please tell me how I can fix this code? The code that will pause the whole game will be nice. Please, the rearranged code that will pause the whole game will help. Also I don't just want the answer, I want a quick explanation of why only my car sprite would pause instead of the whole game.
My code:
import random
import pygame
import pygame.freetype
pygame.init()
#screen settings
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("AutoPilot")
screen.fill((255, 255, 255))
#fps
FPS = 120
clock = pygame.time.Clock()
#load images
bg = pygame.image.load('background/street.png').convert_alpha() # background
bullets = pygame.image.load('car/bullet.png').convert_alpha()
debris_img = pygame.image.load('debris/cement.png')
#define game variables
shoot = False
#player class
class Player(pygame.sprite.Sprite):
def __init__(self, scale, speed):
pygame.sprite.Sprite.__init__(self)
self.bullet = pygame.image.load('car/bullet.png').convert_alpha()
self.bullet_list = []
self.speed = speed
#self.x = x
#self.y = y
self.moving = True
self.frame = 0
self.flip = False
self.direction = 0
self.score = 0
#load car
self.images = []
img = pygame.image.load('car/car.png').convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.update_time = pygame.time.get_ticks()
self.movingLeft = False
self.movingRight = False
self.rect.x = 465
self.rect.y = 325
#draw car to screen
def draw(self):
screen.blit(self.image, (self.rect.centerx, self.rect.centery))
#move car
def move(self):
#reset the movement variables
dx = 0
dy = 0
#moving variables
if self.movingLeft and self.rect.x > 33:
dx -= self.speed
self.flip = True
self.direction = -1
if self.movingRight and self.rect.x < 900:
dx += self.speed
self.flip = False
self.direction = 1
#update rectangle position
self.rect.x += dx
self.rect.y += dy
#shoot
def shoot(self):
bullet = Bullet(self.rect.centerx + 18, self.rect.y + 30, self.direction)
bullet_group.add(bullet)
#check collision
def collision(self, debris_group):
for debris in debris_group:
if debris.health > 0 and pygame.sprite.spritecollide(debris, bullet_group, True):
debris.health -= 1
if debris.health <= 0:
self.score += 1
#player stats/score
def stats(self):
myfont = pygame.font.SysFont('comicsans', 30)
scoretext = myfont.render("Score: " + str(self.score), 1, (0,0,0))
screen.blit(scoretext, (100,10))
#bullet class
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.speed = 5
self.image = bullets
self.rect = self.image.get_rect()
self.rect.center = (x,y)
self.direction = direction
def update(self):
self.rect.centery -= self.speed
#check if bullet has gone off screen
if self.rect.centery < 1:
self.kill()
#debris class
class Debris(pygame.sprite.Sprite):
def __init__(self,scale,speed):
pygame.sprite.Sprite.__init__(self)
self.scale = scale
self.x = random.randrange(100,800)
self.speed_y = 10
self.y = 15
self.speed = speed
self.vy = 0
self.on_ground = True
self.move = True
self.health = 4
self.max_health = self.health
self.alive = True
self.velocity = random.randrange(1,2)
self.speed_x = random.randrange(-3,3)
self.moving_down = True
self.is_destroyed = False
#load debris
self.image = debris_img
self.rect = self.image.get_rect()
self.rect.x = random.randrange(100, 800)
self.rect.y = random.randrange(-150, -100)
self.rect.center = (self.x,self.y)
#load explosion
self.img_explosion_00 = pygame.image.load('explosion/0.png').convert_alpha()
self.img_explosion_00 = pygame.transform.scale(self.img_explosion_00, (self.img_explosion_00.get_width() * 2,
self.img_explosion_00.get_height() * 2))
self.img_explosion_01 = pygame.image.load('explosion/1.png').convert_alpha()
self.img_explosion_01 = pygame.transform.scale(self.img_explosion_01, (self.img_explosion_01.get_width() * 2,
self.img_explosion_01.get_height() * 2))
self.img_explosion_02 = pygame.image.load('explosion/2.png').convert_alpha()
self.img_explosion_02 = pygame.transform.scale(self.img_explosion_02, (self.img_explosion_02.get_width() * 2,
self.img_explosion_02.get_height() * 2))
self.img_explosion_03 = pygame.image.load('explosion/3.png').convert_alpha()
self.img_explosion_03 = pygame.transform.scale(self.img_explosion_03, (self.img_explosion_03.get_width() * 2,
self.img_explosion_03.get_height() * 2))
#explosion list
self.anim_explosion = [self.img_explosion_00,
self.img_explosion_01,
self.img_explosion_02,
self.img_explosion_03]
self.anim_index = 0
self.frame_len = 20 #frames before explosion animation disappears
#spawn new debris
def spawn_new_debris(self):
self.rect.x = random.randrange(100, 800)
self.rect.y = random.randrange(-150, -100)
self.velocity = random.randrange(1, 2)
self.speed_x = random.randrange(-3, 3)
#respawn debris when they go of the screen
def boundaries(self):
if self.rect.left > WIDTH + 10 or self.rect.right < -10 or self.rect.top > HEIGHT + 10:
self.spawn_new_debris()
#update image
def update(self):
self.rect.y += self.velocity
self.rect.x += self.speed_x
self.boundaries()
if self.health <= 0:
max_index = len(self.anim_explosion) - 1
if self.anim_index > max_index:
self.kill()
else:
if self.frame_len == 0:
self.image = self.anim_explosion[self.anim_index]
self.anim_index += 1
self.frame_len = 20
else:
self.frame_len -= 1
#make debris fall down
def falldown(self):
self.rect.centery += self.velocity
if self.moving_down and self.rect.y > 350:
self.kill()
######################CAR/DEBRIS##########################
player = Player(1,5)
##########################################################
#groups
bullet_group = pygame.sprite.Group()
debris_group = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
for x in range(100):
d = Debris(1, 5)
debris_group.add(d)
all_sprites.add(d)
#game runs here
run = True
paused = False
while run:
#draw street
screen.blit(bg, [0, 0])
#update groups
bullet_group.update()
bullet_group.draw(screen)
debris_group.update()
debris_group.draw(screen)
#draw car
player.draw()
player.move()
player.collision(debris_group)
player.stats()
#update all sprites
all_sprites.update()
all_sprites.draw(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#pause game
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
paused = not paused
if paused == True:
continue
#check if key is down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.key == pygame.K_a:
player.movingLeft = True
if event.key == pygame.K_d:
player.movingRight = True
if event.key == pygame.K_SPACE:
player.shoot()
shoot = True
#check if key is up
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movingLeft = False
if event.key == pygame.K_d:
player.movingRight = False
#update the display
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
Move your
for event in pygame.event.get():
right below the while loop, and move your
if paused:
continue
below the for loop, like so:
run = True
paused = False
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#pause game
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
paused = not paused
#check if key is down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.key == pygame.K_a:
player.movingLeft = True
if event.key == pygame.K_d:
player.movingRight = True
if event.key == pygame.K_SPACE:
player.shoot()
shoot = True
#check if key is up
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movingLeft = False
if event.key == pygame.K_d:
player.movingRight = False
if paused:
continue
#draw street
screen.blit(bg, [0, 0])
#update groups
bullet_group.update()
bullet_group.draw(screen)
debris_group.update()
debris_group.draw(screen)
#draw car
player.draw()
player.move()
player.collision(debris_group)
player.stats()
#update all sprites
all_sprites.update()
all_sprites.draw(screen)
#update the display
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
The reason only your car sprite paused when you pressed the q key is because, as you can see in your original code here:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#pause game
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
paused = not paused
if paused == True:
continue
you put the
if paused == True:
continue
inside the for loop, not the while loop, so the lines that move the car would be blocked, but the rest of the code within the while loop will still get executed.

Shooting tank bullet in python/pygame

I'm working on python project right now using pygame library and I need help with this project I make. I want my tank to shoot bullets and so those bullets ricochet from the walls. What is the best way possible to do this?
I'm sorry that my code looks so messy, I've been watching different youtube tutorials and they all do differently.
Here is my code
import pygame
pygame.init()
# ======================= Variables =======================
# ------------------------ Screen -------------------------
screenWidth = 1060
screenHeight = 798
screenSize = (screenWidth, screenHeight)
display = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption("Tank game")
bg = pygame.image.load("sprites/background/background1.png")
bg = pygame.transform.scale(bg, (screenWidth, screenHeight))
# ------------------------ Player -------------------------
# ------------------------ Enemy -------------------------
# ----------------------- Other ---------------------------
red = (155, 0, 0)
clock = pygame.time.Clock()
fps = 60
# ========================= Clases ========================
class player(pygame.sprite.Sprite):
def __init__(self, location, angle, vel, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/player/player_tank.png")
self.x = x
self.y = y
self.vel = vel
self.angle = angle
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
if self.angle == 360:
self.angle = 0
def rotate(self):
rot_image = pygame.transform.rotate(self.image, self.angle)
rot_rect = rot_image.get_rect(center=self.rect.center)
return rot_image, rot_rect
def moving_after_angle_change(self):
x = round(math.cos(math.radians(self.angle + 90)), 1) * self.vel
y = round(math.sin(math.radians(self.angle - 90)), 1) * self.vel
return x, y
class enemy(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, end):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/enemy/enemy_tank.png")
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x, self.y, self.end]
self.vel = 5
def draw(self, display):
self.move()
display.blit(self.image, (self.x, self.y))
def move(self):
if self.vel > 0:
pass
# Bullet
bullet = pygame.image.load("sprites/bullet/bullet.png")
bullet = pygame.transform.scale(bullet, (16, 16))
bullet_x = 0
bullet_y = 480
bullet_x_change = 0
bullet_y_change = 10
bullet_state = 'ready'
# ======================= Functions =======================
def redrawGameWindow():
display.blit(bg, (0, 0))
display.blit(player_tank.image, player_tank.rect)
display.blit(enemy_tank.image, (enemy_tank.x, enemy_tank.y))
#display.blit(bullet, (player_tank.x + 160, player_tank.y + 100))
pygame.display.flip()
def fireBullet(x, y):
global bullet_state
bullet_state = 'fire'
display.blit(bullet, (x + 16, y + 10))
player_location = [70, 570]
player_angle = 270
player_angle_change = 0
player_vel = 0
player_x_change = 0
player_y_change = 0
player_tank_x_change_store = 0
player_tank_y_change_store = 0
run = True
while run:
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
player_vel = 2
elif event.key == pygame.K_DOWN:
player_vel = -2
elif event.key == pygame.K_LEFT:
player_angle_change = 2
elif event.key == pygame.K_RIGHT:
player_angle_change = -2
if event.key == pygame.K_SPACE:
display.blit(bullet, (player_tank.x + 160, player_tank.y + 100))
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
player_vel = 0
elif event.key == pygame.K_DOWN:
player_vel = 0
elif event.key == pygame.K_LEFT:
player_angle_change = 0
elif event.key == pygame.K_RIGHT:
player_angle_change = 0
player_angle += player_angle_change
player_tank = player(player_location, player_angle, player_vel, player_x_change, player_y_change)
enemy_tank = enemy(800, 170, 64, 64, 700)
player_tank.image, player_tank.rect = player_tank.rotate()
player_tank.x_change, player_tank.y_change = player_tank.moving_after_angle_change()
player_tank_x_change_store += player_tank.x_change
player_tank_y_change_store += player_tank.y_change
player_tank.rect.centerx += player_tank_x_change_store
player_tank.rect.centery += player_tank_y_change_store
# Bullet movement
if bullet_state == "fire":
fireBullet(player_tank.x, bullet_y)
bullet_y -= bullet_y_change
redrawGameWindow()
pygame.quit()
quit()
One way to do this is to make the bullets velocity flip when it hits a wall. Now I'm assuming the walls are the edge of the screen so what you need to do is in the loop, check if the bullet is about to hit a wall (the edge of the screen) and if it is, flip its x_change and y_change. So something like this:
if bullet_y <= 0:
bullet_y_change *= -1
bullet_y = 0
if bullet_y >= screenHeight:
bullet_y_change *= -1
bullet_y = screenHeight
if bullet_x <= 0:
bullet_x_change *= -1
bullet_x = 0
if bullet_x >= screenWidth:
bullet_x_change *= 1
bullet_x = screenWidth
Here's a full example, based on an old answer:
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill((0, 0, 0))
self.image.set_colorkey((0, 0, 0))
pygame.draw.polygon(self.image, pygame.Color('dodgerblue'), ((0, 0), (32, 16), (0, 32)))
self.org_image = self.image.copy()
self.angle = 0
self.direction = pygame.Vector2(1, 0)
self.rect = self.image.get_rect(center=(200, 200))
self.pos = pygame.Vector2(self.rect.center)
def update(self, events, dt):
for e in events:
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
self.groups()[0].add(Projectile(self.rect.center, self.direction.normalize()))
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]:
self.angle += 3
if pressed[pygame.K_d]:
self.angle -= 3
self.direction = pygame.Vector2(1, 0).rotate(-self.angle)
self.image = pygame.transform.rotate(self.org_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
class Projectile(pygame.sprite.Sprite):
def __init__(self, pos, direction):
super().__init__()
self.image = pygame.Surface((8, 8))
self.image.fill((0, 0, 0))
self.image.set_colorkey((0, 0, 0))
pygame.draw.circle(self.image, pygame.Color('orange'), (4, 4), 4)
self.rect = self.image.get_rect(center=pos)
self.direction = direction
self.pos = pygame.Vector2(self.rect.center)
self.lives = 15
def update(self, events, dt):
# Bounding box of the screen
screen_r = pygame.display.get_surface().get_rect()
# where we would move next
next_pos = self.pos + self.direction * dt
# we hit a hall
if not screen_r.contains(self.rect):
# after 15 hits, destroy self
self.lives -= 1
if self.lives == 0:
return self.kill()
# horizontal reflection
if next_pos.x > screen_r.right or next_pos.x < screen_r.left:
self.direction.x *= -1
# vertical reflection
if next_pos.y > screen_r.bottom or next_pos.y < screen_r.top:
self.direction.y *= -1
# move after applying reflection
next_pos = self.pos + self.direction * dt
# set the new position
self.pos = next_pos
self.rect.center = self.pos
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
sprites = pygame.sprite.Group(Player())
clock = pygame.time.Clock()
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
sprites.update(events, dt)
screen.fill((30, 30, 30))
sprites.draw(screen)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
See how using the Vector2 class makes this kind of tasks very easy to solve.
Also, look at the proper usage of the Sprite class. Each Sprite minds its own business: the Player class handles rotating the player and creating projectiles, Projectile handles moving the projectiles and bouncing of the wall.

How to use time.Time() to control projectile fire rate

I'm trying to use time.Time() to control the fire rate of my projectiles, but after putting in the time logic, my player can no longer shoot any projectiles at all. How can I make it so that my player only fires once every half a second or so?
The following is a workable example;
main game module
import pygame
from constants import *
from player import Player
from Projectile import Projectiles
import time
pygame.init()
screen = pygame.display.set_mode([500, 500])
pygame.display.set_caption('Labyrinth')
# Spawn player
player = Player(50, 50)
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(player)
projectile_list = pygame.sprite.Group()
clock = pygame.time.Clock()
done = False
# ----- Event Loop
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == ord('a'):
player.changespeed(-3, 0)
elif event.key == ord('d'):
player.changespeed(3, 0)
elif event.key == ord('w'):
player.changespeed(0, -3)
elif event.key == ord('s'):
player.changespeed(0, 3)
elif event.key == pygame.K_LEFT:
projectile = Projectiles(player.rect.x, player.rect.y, -5, 0)
elif event.key == pygame.K_RIGHT:
projectile = Projectiles(player.rect.x, player.rect.y, 5, 0)
elif event.key == pygame.K_UP:
projectile = Projectiles(player.rect.x, player.rect.y, 0, -5)
elif event.key == pygame.K_DOWN:
projectile = Projectiles(player.rect.x, player.rect.y, 0, 5)
try:
if projectile:
if time.Time() - last_shot > player.fire_rate:
projectile_list.add(projectile)
last_shot = time.Time()
except:
pass
elif event.type == pygame.KEYUP:
if event.key == ord('a'):
player.changespeed(3, 0)
elif event.key == ord('d'):
player.changespeed(-3, 0)
elif event.key == ord('w'):
player.changespeed(0, 3)
elif event.key == ord('s'):
player.changespeed(0, -3)
# ----- Game Logic
all_sprites_list.update()
projectile_list.update()
screen.fill(GREEN)
all_sprites_list.draw(screen)
projectile_list.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
player module
from constants import *
import pygame
import time
from datetime import datetime, timedelta
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface([15, 15])
self.image.fill(BLACK)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.fire_rate = 1
self.change_x = 0
self.change_y = 0
def changespeed(self, x, y):
self.change_x += x
self.change_y += y
def update(self):
self.rect.x += self.change_x
self.rect.y += self.change_y
projectile module
import pygame
from constants import *
class Projectiles(pygame.sprite.Sprite):
def __init__(self, x, y, x_speed, y_speed):
super().__init__()
self.image = pygame.Surface([4, 4])
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.x_speed = x_speed
self.y_speed = y_speed
def update(self):
self.rect.x += self.x_speed
self.rect.y += self.y_speed
constants module defines a few colours. Any help is much appreciated!
Well, strictly speaking, the answer is that you have time.Time() in a try block, and it should be time.time() (all lowercase). This throws an exception, so any time time.Time() - last_shot > player.fire_rate is evaluated, the try block isn't run. This would have been more apparent if you had caught the exception and thrown an error message rather than just passing.
There likely are further issues, but a more in-depth analysis of your code is difficult, as I don't see you initializing last_shot anywhere, nor player.fire_rate. The "minimal complete example" standard isn't just for the answerers, but for the asker as well. Basic debugging in isolating the issue as much as possible.
As another note, your while loop appears to contain nothing but a for loop, making it redundant. If you're doing that to just have the program run through the loop over and over again until the appropriate time, then time.sleep() would be probably be a better way to do that.
A try with a bare except is an anti-pattern and should almost never be used, because it hides all errors in the try clause and makes the code hard to debug. I'd remove it and rearrange the event handling code.
When an arrow key gets pressed, only change the velocity vector depending on the key instead of creating new projectile instances. Then check again if any of the arrow keys were pressed and if the timer is finished, create a projectile instance and pass the current velocity vector.
import pygame as pg
from pygame.math import Vector2
BULLET_IMG = pg.Surface((9, 9))
BULLET_IMG.fill(pg.Color('aquamarine2'))
class Projectile(pg.sprite.Sprite):
def __init__(self, pos, vel):
super().__init__()
self.image = BULLET_IMG
self.rect = self.image.get_rect(center=pos)
self.vel = Vector2(vel)
def update(self):
self.rect.move_ip(self.vel)
def main():
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
projectiles = pg.sprite.Group()
previous_time = pg.time.get_ticks()
speed = 12
vel = Vector2(speed, 0)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
# Only change the velocity vector.
if event.key == pg.K_LEFT:
vel = Vector2(-speed, 0)
elif event.key == pg.K_RIGHT:
vel = Vector2(speed, 0)
elif event.key == pg.K_UP:
vel = Vector2(0, -speed)
elif event.key == pg.K_DOWN:
vel = Vector2(0, speed)
# If an arrow keys was pressed, create a new projectile.
if event.key in (pg.K_LEFT, pg.K_RIGHT, pg.K_UP, pg.K_DOWN):
current_time = pg.time.get_ticks()
# We're ready to fire when 500 ms have passed.
if current_time - previous_time > 500:
previous_time = current_time
# Now create the projectile instance and pass
# the position and the velocity.
projectiles.add(Projectile(pg.mouse.get_pos(), vel))
projectiles.update()
screen.fill((30, 30, 30))
projectiles.draw(screen)
pg.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
pg.quit()

pygame slowing down an object with KEYUP?

making a simple game in pygame. have imported an image for my charactater and can move him around freely. I was wondering how to make the character slowly stop when the;
event.type == KEYUP:
as in, When I release the key the character will take say, 50 pixels from when the key has been released to the character slowly stopping.
I have tried a few different things but nothing seems to work. below is my code. any help would be great =]
bg="bg.jpg"
staticCharacter="toon.png"
import pygame, sys, time
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((640,360),0,32)
background=pygame.image.load(bg).convert()
staticToon = pygame.image.load(characterMid).convert_alpha()
x = 0
y = 0
movex = 0
movey = 0
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:
movex = -0.20
elif event.key == K_RIGHT:
movex = +0.20
elif event.key == K_UP:
movey = -0.20
elif event.key == K_DOWN:
movey = +0.20
if event.type == KEYUP:
if event.key == K_LEFT:
movex = 0
elif event.key == K_RIGHT:
movex = 0
elif event.key == K_UP:
movey = 0
elif event.key == K_DOWN:
movey = 0
x += movex
y += movey
if x > 560:
x = 560
if y > 280:
y = 280
if x < 0:
x = 0
if y < 0:
y = 0
screen.blit(background,(0,0))
screen.blit(staticToon,(x,y))
pygame.display.update()
distance = speed*time
Instead of augmenting the position (through movex or movey), you want to augment the speed:
delta = {
pygame.K_LEFT: (-20, 0),
pygame.K_RIGHT: (+20, 0),
pygame.K_UP: (0, -20),
pygame.K_DOWN: (0, +20),
}
...
elif event.type == pygame.KEYDOWN:
deltax, deltay = delta.get(event.key, (0, 0))
ball.speed[0] += deltax
ball.speed[1] += deltay
Now, to slow down the object (a ball in my example below), add some friction:
ball.speed[0] *= 0.95
ball.speed[1] *= 0.95
This diminishes the speed by a little bit each time through the event loop.
Move the object a distance equal to speed * time. To make things simple, let's just take change in time to be 1.
self.rect = self.rect.move(self.speed)
Here is a runnable example: (just add a correct path for staticCharacter.)
import sys
import pygame
import os
'''
Based on http://www.pygame.org/docs/tut/intro/intro.html
Draws a red ball bouncing around in the window.
Pressing the arrow keys moves the ball
'''
staticCharacter = "toon.png"
delta = {
pygame.K_LEFT: (-20, 0),
pygame.K_RIGHT: (+20, 0),
pygame.K_UP: (0, -20),
pygame.K_DOWN: (0, +20),
}
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(staticCharacter)
self.rect = self.image.get_rect()
self.speed = [2, 2]
self.area = pygame.display.get_surface().get_rect()
def update(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > self.area.width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > self.area.height:
self.speed[1] = -self.speed[1]
class Main(object):
def __init__(self):
self.setup()
def setup(self):
pygame.init()
size = (self.width, self.height) = (640,360)
self.black = (0, 0, 0)
self.screen = pygame.display.set_mode(size, 0, 32)
self.ball = Ball()
self.setup_background()
def setup_background(self):
self.background = pygame.Surface(self.screen.get_size())
self.background = self.background.convert()
self.background.fill(self.black)
self.screen.blit(self.background, (0, 0))
pygame.display.flip()
def draw(self):
self.screen.blit(self.background, (0, 0))
self.screen.blit(self.ball.image, self.ball.rect)
pygame.display.flip()
def event_loop(self):
ball = self.ball
while True:
for event in pygame.event.get():
if ((event.type == pygame.QUIT) or
(event.type == pygame.KEYDOWN and
event.key == pygame.K_ESCAPE)):
sys.exit()
elif event.type == pygame.KEYDOWN:
deltax, deltay = delta.get(event.key, (0, 0))
ball.speed[0] += deltax
ball.speed[1] += deltay
ball.speed = [0.99*s for s in ball.speed]
ball.update()
self.draw()
pygame.time.delay(10)
if __name__ == '__main__':
app = Main()
app.event_loop()
Normally, character movement is made to be independent from the machine speed. In your example, you do not know how many times per second the While loop goes, it is dependent on the computer speed. So firstly, you should use the pygame Clock to calculate the time difference between each call to move().
myclock = pygame.Clock()
delta = myclock.tick()
when you have the time difference, you can use a distance equation such as this:
position += speed * direction * delta
delta is in ms, so you need to set your speed accordingly. The direction would be -1 or +1 depending on the current key pressed. A keyup would set direction to 0.
Now back to the question. You would have to change the above a bit differently.
A keyup would subtract a small value from direction.
Then check if direction isn't 1 or -1 and then subtract a value from your direction. You would do this every loop. This way it will get smaller and smaller, and it will appear that your character is slowing down. It is good to also multiply the value that you subtract by delta so it will be deacceleration will constant on every computer.
My advice:
Go to the following website: http://karpathy.ca/phyces/tutorial1.php
Download pygamehelper and startercode (I promise it will make your life easier).
FPS is built-in so you don't need to make the adjustments within your movement logic.
Review some of the other tutorials if you want further clarification and use the vector library if you want some extra snaz.

Categories

Resources