How to move an image smoothly in pygame - python

I have searched other related questions on stack overflow but can't find the right solution.
Code Overview
basically I am trying to make a space invader game here at the bottom there is a space ship shooting down enemies and the enemies constantly move making the game hard and fun. while I am trying to make the enemies move, they paint lines which doesn't look good.
Code
# importing modules
import pygame
import random
# initialize pygame
pygame.init()
# creating a window
screen = pygame.display.set_mode((800, 600))
# Title and Icon
pygame.display.set_caption("Space Shooters")
icon = pygame.image.load('spaceship.png')
pygame.display.set_icon(icon)
# Player
player = pygame.image.load('spaceship2.png')
playerX = 370
playerY = 500
playerX_change = 0
# Enemy
enemyImg = pygame.image.load('enemy.png')
enemyX = random.randint(6, 730)
enemyY = random.randint(45, 150)
enemyX_change = 0.3
enemyY_change = 0
# Defining and drawing spaceship
def spaceship(x, y):
screen.blit(player, (x, y))
# Defining and drawing enemy
def enemy(x, y):
screen.blit(enemyImg, (x, y))
# making the game window run endlessly and game loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Keystroke check (right, left)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -0.25
if event.key == pygame.K_RIGHT:
playerX_change = 0.25
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# Changing X coordinates to move spaceship
playerX += playerX_change
if playerX <= 6:
playerX = 6
if playerX >= 730:
playerX = 730
enemyX += enemyX_change
if enemyX <= 6:
enemyX = 6
enemyX_change = 0.3
if enemyX >= 730:
enemyX = 730
enemyX_change = -0.3
spaceship(playerX, playerY)
enemy(enemyX, enemyY)
# updating display
pygame.display.update()
Expected Results vs Received Results
Expected Results: The enemy moves on the x axis at a constant speed and bounces back when a boundary has been hit. No trail should be formed.
Received Results: The enemy is moving as expected ,but painting a trail where it is and was going.

The display is redrawn in every frame, therefore the display must be cleared before drawing the objects in the scene:
while running:
# [...]
# clear the display
screen.fill(0) # <-- clear display
# draw the scene
spaceship(playerX, playerY)
enemy(enemyX, enemyY)
# update the display
pygame.display.update()
The typical PyGame application loop has to:
limit the frames per second to limit CPU usage with pygame.time.Clock.tick
handle the events by calling either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by calling either pygame.display.update() or pygame.display.flip()

Related

How to use time and make a timer in Pygame? [duplicate]

This question already has answers here:
Countdown timer in Pygame
(8 answers)
Spawning multiple instances of the same object concurrently in python
(1 answer)
Closed 5 months ago.
I am new to pygame programming and I am making a game with the help of tutorials. I finished the tutorial and want to add a new feature to my game.
Game Overview
I have created a space invader game where there are enemies moving on the x-axis and slowly coming down. In bottom there is a space ship which can move on the x axis and can shoot bullets to destroy or kill the enemies. The enemies respawn after being killed and if an enemy reaches the bottom, it is game over
Code
# importing modules
import pygame
import random
import math
from pygame import mixer
# initialize pygame
pygame.init()
# creating a window
screen = pygame.display.set_mode((800, 600))
# Background Music
mixer.music.load('background.wav')
mixer.music.play(-1)
# Background
bg_img = pygame.image.load("background3.png")
bg = pygame.transform.scale(bg_img, (800, 600))
# Title and Icon
pygame.display.set_caption("Space Shooters")
icon = pygame.image.load('spaceship.png')
pygame.display.set_icon(icon)
# Player
player = pygame.image.load('spaceship2.png')
playerX = 370
playerY = 500
playerX_change = 0
# Enemy
enemyImg = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 6
for i in range(num_of_enemies):
enemyImg.append(pygame.image.load('enemy.png'))
enemyX.append(random.randint(6, 730))
enemyY.append(random.randint(45, 150))
enemyX_change.append(0.3)
enemyY_change.append(40)
# Bullet
# Ready - The bullet can't be seen
# Fire - The bullet has been shot
bulletImg = pygame.image.load('bullet.png')
bulletX = 0
bulletY = 480
bulletX_change = 0
bulletY_change = 1.5
bullet_state = "Ready"
# Score
score_value = 0
font = pygame.font.Font('freesansbold.ttf', 32)
textX = 10
textY = 10
# Game Over
over_font = pygame.font.Font('freesansbold.ttf', 64)
# Function for displaying text
def show_score(x, y):
score = font.render("Score : " + str(score_value), True, (255, 255, 255))
screen.blit(score, (x, y))
def game_over_text():
over_text = over_font.render("GAME OVER", True, (255, 255, 255))
screen.blit(over_text, (200, 250))
# Defining and drawing spaceship
def spaceship(x, y):
screen.blit(player, (x, y))
# Defining and drawing enemy
def enemy(x, y, i):
screen.blit(enemyImg[i], (x, y))
def fire_bullet(x, y):
global bullet_state
bullet_state = "Fire"
screen.blit(bulletImg, (x + 16, y + 10))
def Iscollision(enemyX, enemyY, bulletX, bulletY):
distance = math.sqrt((math.pow(enemyX - bulletX, 2)) + (math.pow(enemyY - bulletY, 2)))
if distance < 27:
return True
else:
return False
# making the game window run endlessly and game loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Keystroke check (right, left)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -0.3
if event.key == pygame.K_RIGHT:
playerX_change = 0.3
if event.key == pygame.K_SPACE:
# Checking whether bullet is there on screen, if not making it ready, if yes disabling spacebar
if bullet_state == "Ready":
# Get the current X cordinate of spaceship
bulletX = playerX
fire_bullet(bulletX, bulletY)
bullet_sound = mixer.Sound('laser.wav')
bullet_sound.play()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# Changing X coordinates to move spaceship
playerX += playerX_change
if playerX <= 6:
playerX = 6
elif playerX >= 730:
playerX = 730
screen.fill(0)
screen.blit(bg, (0, 0))
# Enemy Movement
for i in range(num_of_enemies):
# Game Over
if enemyY[i] > 440:
for j in range(num_of_enemies):
enemyY[j] = 2000
game_over_text()
break
for i in range(num_of_enemies):
enemyX[i] += enemyX_change[i]
if enemyX[i] <= 6:
enemyX_change[i] = 0.3
enemyY[i] += enemyY_change[i]
elif enemyX[i] >= 730:
enemyX_change[i] = -0.3
enemyY[i] += enemyY_change[i]
# Collision
collision = Iscollision(enemyX[i], enemyY[i], bulletX, bulletY)
# if collision and if collision is true are both the same
if collision:
collision_sound = mixer.Sound('explosion.wav')
collision_sound.play()
bulletY = 480
bullet_state = "Ready"
score_value += 1
enemyX[i] = random.randint(6, 730)
enemyY[i] = random.randint(45, 150)
enemy(enemyX[i], enemyY[i], i)
# Deleting each frame of enemy moving so that it runs smoothly
# Bullet Movement
if bulletY <= 0:
bulletY = 480
bullet_state = "Ready"
if bullet_state == "Fire":
fire_bullet(bulletX, bulletY)
bulletY -= bulletY_change
spaceship(playerX, playerY)
show_score(textX, textY)
# updating display
pygame.display.update()
New Feature
I want to add a feature that when a laser ( I have a picture) hits the spaceship, shot by the enemies, the spaceship can't move for 10 seconds. I have already tryed other stack overflow questions but they don't match my needs.
If you only have one cooldown to manage, a simple and clean way to do it is by using a custom event :
# Custom event
COOLDOWN_END = pg.USEREVENT
When you detect a collision you simply start a timer :
# Custom event
pg.time.set_timer(COOLDOWN_END, 1000, 1)
The first parameter is the event you are going to send to your event handler, the 2nd is the time in ms (so 1 sec here) and last one is the number of iteration. Here you just want to send the event once, so put 1.
The last step is to do catch this event in your handler and do what you want with it :
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == COOLDOWN_END:
print(evt)
# Your logic goes here
Let's put everything together :
(Here, I simply used this technique to have a COOLDOWN_END event 1 sec after I clicked)
# General imports
import pygame as pg
import sys
# Init
pg.init()
# Vars
screen = pg.display.set_mode((500, 500))
pg.display.set_caption("name")
# Clock
FPS = 60
clock = pg.time.Clock()
# Custom event
COOLDOWN_END = pg.USEREVENT
# Main functions
def update():
pass
def draw():
# Clear screen
screen.fill((0,0,0))
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == pg.KEYDOWN:
if evt.key == pg.K_ESCAPE:
exit()
if evt.type == pg.MOUSEBUTTONDOWN:
if evt.button == 1:
on_click()
if evt.type == COOLDOWN_END:
print(evt)
def on_click():
pg.time.set_timer(COOLDOWN_END, 1000, 1)
def exit():
pg.quit()
sys.exit()
# Main loop
if __name__ == '__main__':
while True:
handle_evt()
update()
draw()
clock.tick(FPS)
pg.display.flip()
You may use custom event with from pygame.locals import USEREVENT.
An alternative is to keep track of time of when you got hit using pygame.time.get_ticks() + some stun cooldown system, and applying only to the portion of the code involved with player movement.
stun_duration = 10000 # milliseconds
damaged_timestamp = -stun_duration # allows to get stunned starting from pygame.init() call
running = True
while running:
for event in pygame.event.get():
# preceding code
# check if beyond stun cooldown
if pygame.time.get_ticks() - damaged_timestamp > stun_duration:
damaged_timestamp = pygame.time.get_ticks()
# Keystroke check (right, left)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -0.3
if event.key == pygame.K_RIGHT:
playerX_change = 0.3
if event.key == pygame.K_SPACE:
# Checking whether bullet is there on screen, if not making it ready, if yes disabling spacebar
if bullet_state == "Ready":
# Get the current X cordinate of spaceship
bulletX = playerX
fire_bullet(bulletX, bulletY)
bullet_sound = mixer.Sound('laser.wav')
bullet_sound.play()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# Changing X coordinates to move spaceship
playerX += playerX_change
if playerX <= 6:
playerX = 6
elif playerX >= 730:
playerX = 730
# proceeding code

Can anyone tell me why this png keeps creating a clone every time it hits a boundary?

So Im really new to pygame and trying to recreate space invaders. This is just the code for the enemy, can anyone tell me why it keep messing up whenever it hits a boundary? Im using pycharm to code this btw.
import pygame
screen = pygame.display.set_mode((800, 600))
pygame.init()
enemyIMG = pygame.image.load("ufo (2).png")
enemyX = 360
enemyY = 50
enemyY_change = 40
enemyX_change = 0.3
def enemy(x, y):
screen.blit(enemyIMG, (x, y))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
enemyX += enemyX_change
if enemyX <= 0:
enemyY += enemyY_change
enemyX_change = 0.3
elif enemyX >= 736:
enemyY += enemyY_change
enemyX_change = -0.3
enemy(enemyX, enemyY)
pygame.display.update()
I was trying to make the alien hit a boundary then go down 40 pixels, then go the other way. But instead, it created a clone of its self and stayed there while the other clone continued as normal.
The entire scene is redrawn in every frame. Therefor you have to clear the display in every frame:
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
enemyX += enemyX_change
if enemyX <= 0:
enemyY += enemyY_change
enemyX_change = 0.3
elif enemyX >= 736:
enemyY += enemyY_change
enemyX_change = -0.3
# clear display
screen.fill((0, 0, 0)) # <-- this is missing
# draw scene
enemy(enemyX, enemyY)
# update display
pygame.display.update()
The typical PyGame application loop has to:
limit the frames per second to limit CPU usage with pygame.time.Clock.tick
handle the events by calling either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by calling either pygame.display.update() or pygame.display.flip()

Why does "Game over" text not appear?

I am currently attempting to programm a small game. An enemy is supposed to chase the player, the actual chasing is not implemented yet. I still need to figure out how to do that, but that's not the question.
I have made it so that the player returns to the start point once they collide with the enemy. In addition to that, a text 'Game over' is supposed to appear. The function for that is called at the end of the game loop and while the text appeared briefly(it actually only appeared once, I have tried it multiple times), it does not stay. I was planing on making it appear and then disappear after a few seconds so that the player can play again, but I'm not sure why it disappears instantly.
If this is the wrong place to post this, please tell me, I will delete this post. This is my code, would be amazing if somebody could help me out:
import pygame #imports pygame
import math
pygame.init()
#screen
screen = pygame.display.set_mode((800,600)) #width and height
#title and icon
pygame.display.set_caption("The Great Chase") #changes title
icon = pygame.image.load('game-controller.png')
pygame.display.set_icon(icon) #setting icon
#player
playerImg = pygame.image.load('scary-monster.png')
playerX = 370
playerY = 480
playerX_change = 0
playerY_change = 0
#enemy
enemyImg = pygame.image.load('caterpillar.png')
enemyX = 370
enemyY = 50
enemyX_change = 0
enemyY_change = 0
#GAME OVER
game_over_font = pygame.font.Font('Bubblegum.ttf',64)
def game_over_text():
game_over_text = game_over_font.render("GAME OVER", True, (255, 0, 0))
screen.blit(game_over_text, (200, 250))
def player(x, y): #function for player
screen.blit(playerImg, (x, y)) #draws image of player, blit -> means to draw
def enemy(x, y): #function for enemy
screen.blit(enemyImg,(x,y))
def isCollision(playerX, playerY, enemyX, enemyY):
distance = math.sqrt((math.pow(playerX-enemyX,2)) + (math.pow(playerY - enemyY,2)))
if distance < 25:
return True
else:
return False
def chase():
distance = math.sqrt((math.pow(playerX-enemyX,2)) + (math.pow(playerY - enemyY,2)))
#Game loop
running = True
while running:
screen.fill((255,255,0)) #0-255 RGB-> red, green & blue
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# if keystroke check whether right or left or up or down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -0.2
if event.key == pygame.K_RIGHT:
playerX_change = 0.2
if event.key == pygame.K_UP:
playerY_change = -0.2
if event.key == pygame.K_DOWN:
playerY_change = 0.2
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
playerX += playerX_change
playerY += playerY_change
#creating borders so that player won't leave screen
if playerX <=0:
playerX = 0
elif playerX >=768:
playerX = 768
if playerY <= 0:
playerY = 0
elif playerY >=568:
playerY = 568
#collision
collision = isCollision(playerX, playerY, enemyX, enemyY)
if collision:
playerY = 480
game_over_text()
player(playerX, playerY)
enemy(enemyX, enemyY)
pygame.display.update()
The collision only occurs for a moment and the game over text is only shown when the object collides. If you want to persist the text, set a gameover variable when the collision is detected and display the text based on the state of the variable:
gameover = False
running = True
while running:
# [...]
collision = isCollision(playerX, playerY, enemyX, enemyY)
if collision:
playerY = 480
gameover = True
player(playerX, playerY)
enemy(enemyX, enemyY)
if gameover:
game_over_text()
pygame.display.update()
Note, collision is set in every frame depending on the position of the objects. However, gameover is set once when a collision occurs and then maintains its state.

Why can't I shoot with pygame?

I've been trying to make something cool with Python and Pygame for fun. I know a thing or two about Python in general but I'm quite a beginner with Pygame.
So the problem is: I have created a movable player and a moving enemy. I want to make the player shoot when I press the spacebar. I loaded a .png image, defined a function for shooting, and made so that the bullet keeps moving once shot. For some reason when I call the function, it just does nothing. It doesn't even give an error. I know I can shoot only one bullet with the current code and whatnot, but I would like to just get the current code working as a start.
"""
player and enemy functions are defined above and work well, I didn't include
them in this post for the sake of saving everyone's time
"""
# bullet
bullet_pic = pygame.image.load("bullet.png")
bullet_pic_reverse = pygame.image.load("bullet reverse.png")
bullet_state = "nope"
def shoot(pic, x, y):
global bullet_state
bullet_state = "jes"
screen.blit(pic, (x, y + 30))
player_dir = player_pic # direction of the player (left or right)
enemy_dir = enemy_pic # direction of the enemy
enemyX_change = 2.5
running = True
while running:
screen.blit(back, (0, 0)) # background picture
player(player_dir, playerX, playerY)
enemy(enemy_dir, enemyX, enemyY)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == K_ESCAPE:
running = False
elif event.type == pygame.KEYDOWN:
# shooting
if event.key == K_SPACE:
if player_dir == player_pic:
bulletX = playerX + 100
bulletX_change = 20
bullet_dir = bullet_pic
else:
bulletX = playerX
bulletX_change = -20
bullet_dir = bullet_pic_reverse
shoot(bullet_dir, bulletX, playerY)
# player movement
hold = pygame.key.get_pressed()
if hold[K_LEFT] and playerX > 0:
playerX -= 7
player_dir = player_pic_reverse
if hold[K_RIGHT] and playerX < 1100:
playerX += 7
player_dir = player_pic
# enemy movement
enemyX += enemyX_change
if enemyX >= 1125:
enemyX_change = -2.5
enemy_dir = enemy_pic_reverse
elif enemyX <= 900:
enemyX_change = 2.5
enemy_dir = enemy_pic
# bullet constant movement
if bullet_state == "jes":
shoot(bullet_dir, bulletX, playerY)
bulletX += bulletX_change
I think the problem is with pygame.display.update(), you should put it always in the end of while running: loop. If you update display before calling shoot() background picture will be over bullet picture and you won't see it.
Another thing, you write K_ESCAPE instead of pygame.K_ESCAPE same with spacebar key. Does it work correctly for you? I didn't do anything with pygame for some time, maybe it's fine, but check it :)

sprite won't move left or right in pygame

I am trying to move a sprite (called Player) to the left or right by pressing the arrow keys. The movement to the right or left would be made by changing the center of the rectangle of the sprite. As you might be able to see, I am trying to add/subtract 8 from the sprite's x coordinates in order to move it right or left. However, the sprite doesn't move when I hit an arrow key. How can I fix this?
import pygame
import random
import time
import pygame as pg
# set the width and height of the window
width = 800
height = 370
groundThickness = 30
pheight = 50
pwidth = 50
playerx = 0+pwidth
playery = height-groundThickness-pheight/2
fps = 30
# define colors
white = (255, 255, 255)
black = (0, 0, 0)
red = (5, 35, 231)
# initialize pygame
pygame.init()
# initialize pygame sounds
pygame.mixer.init()
# create window
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("my game")
clock = pygame.time.Clock()
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((pwidth, pheight))
self.image.fill(red)
self.rect = self.image.get_rect()
self.rect.center = (playerx, playery)
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
# Game loop
running = True
x_change = 0
while running:
# keep loop running at the right speed
clock.tick(fps)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
print("left")
x_change = -8
elif event.key == pygame.K_RIGHT:
print("right")
x_change = 8
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
print(x_change)
playerx += x_change
all_sprites.update()
#ground
pygame.draw.rect(screen, (0,255,0), ((0, height-groundThickness), (width, groundThickness)))
# Update
all_sprites.update()
# Draw / render
all_sprites.draw(screen)
pygame.display.update()
# AFTER drawing everything, flip the display
pygame.display.flip()
pygame.quit()
You've never altered the position of the sprite player.
How do you expect it to move?
All you've done is to change the value of your local variable playerx, but you've never pushed that change back into the sprite object.
Start by adding the middle line below:
playerx += x_change
player.rect.center = (playerx, playery)
all_sprites.update()
See how that alters things? Can you take it from there?
Filling the screen with your background color or image will also be needed to keep your screen updated with the current position of the moving sprite. The following command will be added at the top of your draw/render section of the game loop:
screen.fill(black)

Categories

Resources