How to animate drawings in pygame (movement) - python

I have been trying to animate drawing elements without success. I can animate imported images, but when I try to animate drawings generated by pygame they remain static.
Edit: By "animate" I mean "to move". As in making a circle move in x and y direction.
This is my code:
import pygame, sys
from pygame.locals import *
pygame.init()
FPS = 60
WIDTH = 600
HEIGHT = 500
fpsClock = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WIDTH, HEIGHT), 0, 32)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
ballx = WIDTH / 2
bally = HEIGHT / 2
ball_vel = [1, 1]
ball_pos =(ballx, bally)
RADIUS = 20
# Game Loop:
while True:
# Check for quit event
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Erase the screen (I have tried with and without this step)
DISPLAYSURF.fill(BLACK)
# Update circle position
ballx += ball_vel[0]
bally += ball_vel[1]
# Draw Circle (I have tried with and without locks/unlocks)
DISPLAYSURF.lock()
pygame.draw.circle(DISPLAYSURF, WHITE, ball_pos, RADIUS, 2)
DISPLAYSURF.unlock()
# Update the screen
pygame.display.update()
fpsClock.tick(FPS)
I've tried with and without locking/unlocking the display surface (as the documentation suggests). I've tried with and without erasing the screen before updating it (as some tutorials suggest). I just can't get it to work.
What am I doing wrong? How do you animate drawing elements?
Thanks for your time.

You are not updating the ball_pos tuple: you set it to the start coordinates:
ballx = WIDTH / 2
bally = HEIGHT / 2
ball_vel = [1, 1]
ball_pos =(ballx, bally)
You later update ballx and bally, but never set ball_pos again to ballx, and bally.
In the while loop, after setting the ballx and bally, do this:
ball_pos = (ballx,bally)

import pygame, sys
from pygame.locals import *
pygame.init()
FPS = 60
WIDTH = 600
HEIGHT = 500
fpsClock = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WIDTH, HEIGHT), 0, 32)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
ballx = WIDTH / 2
bally = HEIGHT / 2
ball_vel = [1, 1]
ball_pos =(ballx, bally)
RADIUS = 20
# Game Loop:
while True:
# Check for quit event
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Erase the screen (I have tried with and without this step)
DISPLAYSURF.fill(BLACK)
# Update circle position
ballx += ball_vel[0]
bally += ball_vel[1]
ball_pos =(ballx, bally)
# Draw Circle (I have tried with and without locks/unlocks)
pygame.draw.circle(DISPLAYSURF, WHITE, ball_pos, RADIUS, 2)
# Update the screen
pygame.display.flip()
fpsClock.tick(FPS)
flip = update()

Related

Pygame: Efficient way to call draw function in for loops [duplicate]

I was coded a simple pygame grid window. but pygame window start lag after that.
Here is that simple code👇
import pygame
import random
pygame.init()
pygame.font.init()
screen_width = 500
screen_height = screen_width
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption("Snake GaMe By Akila")
def drawGrid():
grid_list = []
blockSize = 25
for x in range(screen_width):
for y in range(screen_height):
rect = pygame.Rect(x*blockSize, y*blockSize, blockSize, blockSize)
pygame.draw.rect(screen, (255,255,255), rect, 1)
running = True
while running:
screen.fill((0,0,0))
drawGrid()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.update()
I was tried changing the call snippet of drawGrid() function position.
To improve the performance, do not construct the grind in every frame.
Create a pygame.Surface with the size of the window and draw the grid on this surface:
grid_surf = pygame.Surface((screen_width,screen_height))
drawGrid(grid_surf)
This surface is the back ground for your scene. blit it at the begin of every frame:
screen.blit(grid_surf, (0, 0))
Example code:
import pygame
import random
pygame.init()
pygame.font.init()
screen_width = 500
screen_height = screen_width
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption("Snake GaMe By Akila")
def drawGrid(surf):
grid_list = []
blockSize = 25
for x in range(screen_width):
for y in range(screen_height):
rect = pygame.Rect(x*blockSize, y*blockSize, blockSize, blockSize)
pygame.draw.rect(surf, (255,255,255), rect, 1)
grid_surf = pygame.Surface((screen_width,screen_height))
drawGrid(grid_surf)
running = True
while running:
screen.blit(grid_surf, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.update()

Text keeps blinking in pygame

so I am making a code where there is a character(A rectangle) and and Balloon(A circle) and the character has to catch the balloons before it hits the ground. Everything worked fine until I tried making the game look a little better and used the blit function to add an image. For some reason my text keeps buffering now.
Code:
import random
import pygame
from pygame.locals import *
pygame.init()
#Variables
white = (255, 255, 255)
blue = (70,130,180)
black = (0,0,0)
red = (255,0,0)
x = 400
y = 450
score = 0
cooly = 100
randomx = random.randint(100,800)
clock = pygame.time.Clock()
#screen stuff
screenwidth = 800
screenheight = 600
screen = pygame.display.set_mode((screenwidth, screenheight))
pygame.display.set_caption("Balloon Game!")
#end of screen stuff
#Initializing font
def show_text(msg, x, y, color,size):
fontobj= pygame.font.SysFont("freesans", size)
msgobj = fontobj.render(msg,False,color)
screen.blit(msgobj,(x, y))
#Game loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
# Draw Character
screen.fill(blue)
character = pygame.draw.rect(screen, red, (x, y, 60, 60))
#End of Drawing Character
#Draw Balloons
balloon = pygame.draw.circle(screen, (black), (randomx, cooly), 30, 30)
cooly = cooly + 2
# Making Arrow Keys
keyPressed = pygame.key.get_pressed()
if keyPressed[pygame.K_LEFT]:
x -= 3
if keyPressed[pygame.K_RIGHT]:
x += 3
#Drawing Cool Stuff
Cloud = pygame.image.load('Cloud.png')
screen.blit(Cloud,(100,75))
pygame.display.update()
#End of making arrow keys
show_text("Score:",130,0,black,50)
show_text(str(score),250,0,black,50)
#Collision
if balloon.colliderect(character):
randomx = random.randint(100,800)
cooly = 100
score = score + 1
#end of collision
#Ending
if cooly >= 600:
screen.fill(blue)
show_text("Game Over!", 200, 250, black, 100)
show_text("Score :", 200, 350, black, 75)
show_text(str(score), 400, 350, black, 75)
#Polishing
if score == score + 5:
cooly += 1
x += 3
pygame.display.update()
clock.tick(250)
The problem is caused by multiple calls to pygame.display.update(). An update of the display at the end of the application loop is sufficient. Multiple calls to pygame.display.update() or pygame.display.flip() cause flickering.
Remove all calls to pygame.display.update() from your code, but call it once at the end of the application loop.
Do not create the font object and do not load the images in the application loop. This are very expensive operatios, because the filed have to be read and interpreted:
fontobj= pygame.font.SysFont("freesans", size) #<-- INSERT
def show_text(msg, x, y, color,size):
# fontobj= pygame.font.SysFont("freesans", size) <-- DELET
msgobj = fontobj.render(msg,False,color)
screen.blit(msgobj,(x, y))
Cloud = pygame.image.load('Cloud.png') #<-- INSERT
# [...]
while True:
# [...]
# Cloud = pygame.image.load('Cloud.png') <-- DELETE
screen.blit(Cloud,(100,75))
# pygame.display.update() <-- DELETE
# [...]
pygame.display.update()
clock.tick(250)

cs50 minesweeper algorithm sometimes stuck in forever loop [duplicate]

I was coded a simple pygame grid window. but pygame window start lag after that.
Here is that simple code👇
import pygame
import random
pygame.init()
pygame.font.init()
screen_width = 500
screen_height = screen_width
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption("Snake GaMe By Akila")
def drawGrid():
grid_list = []
blockSize = 25
for x in range(screen_width):
for y in range(screen_height):
rect = pygame.Rect(x*blockSize, y*blockSize, blockSize, blockSize)
pygame.draw.rect(screen, (255,255,255), rect, 1)
running = True
while running:
screen.fill((0,0,0))
drawGrid()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.update()
I was tried changing the call snippet of drawGrid() function position.
To improve the performance, do not construct the grind in every frame.
Create a pygame.Surface with the size of the window and draw the grid on this surface:
grid_surf = pygame.Surface((screen_width,screen_height))
drawGrid(grid_surf)
This surface is the back ground for your scene. blit it at the begin of every frame:
screen.blit(grid_surf, (0, 0))
Example code:
import pygame
import random
pygame.init()
pygame.font.init()
screen_width = 500
screen_height = screen_width
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption("Snake GaMe By Akila")
def drawGrid(surf):
grid_list = []
blockSize = 25
for x in range(screen_width):
for y in range(screen_height):
rect = pygame.Rect(x*blockSize, y*blockSize, blockSize, blockSize)
pygame.draw.rect(surf, (255,255,255), rect, 1)
grid_surf = pygame.Surface((screen_width,screen_height))
drawGrid(grid_surf)
running = True
while running:
screen.blit(grid_surf, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.update()

opygame: trying to bouce a ball, but it gets stuck

import pygame
Red = 255, 0, 0
Black= 0,0,0
rectXpos = 2
rectypos = 2
speed = 2
screenedgex = 500
pygame.init()
window = pygame.display.set_mode(size=(500, 500))
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.update()
window.fill(Black)
square = pygame.draw.rect(window, Red, [rectXpos, rectypos, 50, 50],2)
rectXpos += 2
if rectXpos < 500:
rectXpos -= 2
clock.tick(60)
print(rectXpos)`enter code here`
so what am i doing wrong? i tried making a if statment to stop the ball and reverse it but it keeps the ball at the edge of the window
This is complete code, I separated the x and y bounces, so you can use either one, also updated the code a bit more, plus some extra formatting.
# Imports
import pygame
# Vars
Red = 255, 0, 0
Black= 0,0,0
rectXpos = 2
rectYpos = 2
rect_width = 50
rect_height = 50
screen_width = 500
screen_height = 500
block_x_direction = 1
block_y_direction = 1
# Setup Code
pygame.init()
window = pygame.display.set_mode(size=(screen_width, screen_height))
clock = pygame.time.Clock()
running = True
# Game Loop
########################################################
while running:
# Event Loop
########################################################
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Game Code - Update
########################################################
# Game Code - Update - Rect X Bounce
if rectXpos + (rect_width)>= screen_width:
block_x_direction = block_x_direction * -1
rectXpos += 2 * block_x_direction
# Game Code - Update - Rect Y Bounce
if rectYpos + (rect_height)>= screen_height:
block_y_direction = block_y_direction * -1
rectYpos += 2 * block_y_direction
# - Tick Game
clock.tick(60)
# Game Code - Render
########################################################
window.fill(Black)
square = pygame.draw.rect(window, Red, [rectXpos, rectYpos, rect_width, rect_height],2)
pygame.display.update()
# Game Code - Debug Code
########################################################
print(clock.tick)
I assume you want to move rectangle to and fro when mouse is moving.
There are 2 things you are doing wrong here:
1. correct this:
if rectXpos > 500: as you have to decrease X when it will reach 500
2. when reach rectXpos 501 it should change its direction till it reach rectXpos 0
but you have decreased position till it is greater than 500 so it will then stuck in between 499 to 501
correct code:
import pygame
Red = 255, 0, 0
Black= 0,0,0
rectXpos = 2
rectypos = 2
speed = 2
screenedgex = 500
pygame.init()
window = pygame.display.set_mode(size=(500, 500))
clock = pygame.time.Clock()
running = True
k=1 #here is k used to indicate direction
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.update()
window.fill(Black)
square = pygame.draw.rect(window, Red, [rectXpos, rectypos, 50, 50],2)
rectXpos += 2*k #here is addition of 2 in given direction
if (rectXpos > 500) or (rectXpos < 0): #here is condition to change direction
k=-k
clock.tick(60)
print(rectXpos)
You should add speed to position and when you touch border then you should change speed to -speed.
You could also use pygame.Rect() to keep position and size - it has properties .left and .right (and other) which can be very useful. And you can use Rect to draw pygame.draw.rect() (or to check collision with other Rect)
import pygame
# --- constants --- (UPPER_CASE_NAMES)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
WIDTH = 500
HEIGHT = 500
# --- main ---
speed = 10
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
item = pygame.Rect(0, 0, 50, 50)
clock = pygame.time.Clock()
running = True
while running:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# - updates - (without draws)
item.x += speed
if item.right >= WIDTH:
speed = -speed
if item.left <= 0:
speed = -speed
# - draws - (without updates)
window.fill(BLACK)
pygame.draw.rect(window, RED, item, 2)
pygame.display.update()
clock.tick(60)
# - end -
pygame.quit()

Pygame Transformation: rotate shape on key press

I'm fairly new to Pygame, and can't seem to find a solid answer on this. I have a shape, specifically an ellipse, that I want to rotate both left and right. The key bind would be a and d, as the arrow keys are already binded to move left and right on an x,y axis.
I know that it involves pygame.transform.rotate, however I can't seem to implement this right.
def main():
#Setup basic variables and methods for pygame
pygame.init()
windowWidth = 800
windowHeight = 700
fps = 45
clock = pygame.time.Clock()
gameWindow = pygame.display.set_mode((windowWidth, windowHeight))
surface = pygame.Surface((50,50))
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
shipX = windowWidth/2
shipY = windowWidth/2
shipSpeed = 4
while(True):
pygame.draw.ellipse(gameWindow, WHITE, (shipX, shipY, 20, 30))
#Monitor the FPS of the game
clock.tick(fps)
for event in pygame.event.get():
# ________________________________________
if event.type == pygame.QUIT:
gameExit()
rotate = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP] and shipY > shipSpeed: shipY -= shipSpeed
if pressed[pygame.K_DOWN] and shipY < windowHeight - shipSpeed - 20: shipY += shipSpeed
if pressed[pygame.K_LEFT] and shipX > shipSpeed:shipX -= shipSpeed
if pressed[pygame.K_RIGHT] and shipX < windowWidth - shipSpeed - 20: shipX += shipSpeed
if pressed[ord('a')]: rotate = pygame.transform.rotate(surface, -20)
if pressed[ord('d')]: rotate = pygame.transform.rotate(surface, 20)
gameWindow.fill(BLACK)
# 'flip' display - always after drawing...
pygame.display.flip()
The expected result is that the shape will change it's angle, and then move accordingly.
Again, I'm very new to pygame, so any detailed help would be appreciated.
Your problem is that you draw the ellipse directly on the screen, but you should draw your ellipse on another Surface.
Then you can rotate that new Surface with pygame.transform.rotate.
Here's a simple example:
import pygame
import random
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
surface = pygame.Surface((100, 200))
surface.set_colorkey((2, 3, 4))
surface.fill((2, 3, 4))
rect = surface.get_rect(center=(100, 100))
pygame.draw.ellipse(surface, pygame.Color('white'), (0, 0, 100, 200))
angle = 0
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]: rect.move_ip(0, -5)
if pressed[pygame.K_DOWN]: rect.move_ip(0, 5)
if pressed[pygame.K_LEFT]: rect.move_ip(-5, 0)
if pressed[pygame.K_RIGHT]: rect.move_ip(5, 0)
if pressed[pygame.K_a]: angle += 1
if pressed[pygame.K_d]: angle -= 1
rotated = pygame.transform.rotate(surface, angle)
rect = rotated.get_rect(center=rect.center)
rect.clamp_ip(screen_rect)
screen.fill(pygame.Color('dodgerblue'))
screen.blit(rotated, rect.topleft)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Note that I use a Rect to store the position of the object because it's easy then to rotate the Surface around its center (by setting its center attribute), and to ensure the Surface does not go outside the screen (by using clamp_ip).
Also, it's important to always rotate the source Surface, and not the already rotated Surface. Otherwise, you'll get distortions.
Note that we have three things here: an image, a position, and some behaviour logic. Whenever you see these things together, consider putting them together into a class. Pygame already offers a nice class for this, called Sprite.
Here's the same example, but Sprite-based:
import pygame
import random
class Thingy(pygame.sprite.Sprite):
def __init__(self, area):
super().__init__()
# image is what get's painted on the screen
self.image = pygame.Surface((100, 200))
self.image.set_colorkey((2, 3, 4))
self.image.fill((2, 3, 4))
pygame.draw.ellipse(self.image, pygame.Color('white'), (0, 0, 100, 200))
# we keep a reference to the original image
# since we use that for rotation to prevent distortions
self.original = self.image.copy()
# rect is used to determine the position of a sprite on the screen
# the Rect class also offers a lot of useful functions
self.rect = self.image.get_rect(center=(100, 100))
self.angle = 0
self.area = area
def update(self, events, dt):
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]: self.rect.move_ip(0, -5)
if pressed[pygame.K_DOWN]: self.rect.move_ip(0, 5)
if pressed[pygame.K_LEFT]: self.rect.move_ip(-5, 0)
if pressed[pygame.K_RIGHT]: self.rect.move_ip(5, 0)
if pressed[pygame.K_a]: self.angle += 1
if pressed[pygame.K_d]: self.angle -= 1
# let's rotate the image, but ensure that we keep the center position
# so it doesn't "jump around"
self.image = pygame.transform.rotate(self.original, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
self.rect.clamp_ip(self.area)
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
sprites = pygame.sprite.Group(Thingy(screen_rect))
dt = 0
while True:
# nice clean main loop
# all game logic goes into the sprites
# handle "global" events
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
# update all sprites
sprites.update(events, dt)
# draw everything
screen.fill(pygame.Color('dodgerblue'))
sprites.draw(screen)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
You should make a class for your object:
class myRect(pygame.Surface):
def __init__(self, parent, xpos, ypos, width, height):
super(myRect, self).__init__(width, height)
self.xpos = xpos
self.ypos = ypos
self.parent = parent
def update(self, parent):
parent.blit(self, (self.xpos, self.ypos))
def rotate(self, angle):
#(your rotation code goes here)

Categories