I'm new to Pygame, and I just started working on a Pong Game. The following code hasn't been finished yet, but for some reason, the ball won't move. It only works if I set the "ball_draw = pygame..." inside the "draw_ball" method, instead of the initializing method. However, if I do that, I can't use the "wallCollision" method in the class because the "ball_draw" would be a local variable and not an attribute of the whole class and therefore, not accessible in other methods. How can I fix this? I appreciate your help.
import pygame, sys
pygame.init()
clock = pygame.time.Clock()
screen_width = 1280
screen_height = 960
bgColor = pygame.Color("grey12")
lightGrey = (200, 200, 200)
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Pong Game")
class BALL:
def __init__(self):
self.ball_width = 30
self.ball_height = 30
self.ball_x = screen_width/2 - self.ball_width
self.ball_y = screen_height/2 - self.ball_height
self.ball_speed_x = 7
self.ball_speed_y = 7
self.ball_draw = pygame.Rect(self.ball_x, self.ball_y, self.ball_width, self.ball_height)
def draw_ball(self):
pygame.draw.ellipse(screen, lightGrey, self.ball_draw)
def move_ball(self):
self.ball_x += self.ball_speed_x
self.ball_y += self.ball_speed_y
def wallCollision(self):
if self.ball_draw.bottom >= screen_height:
self.ball_speed_y *= -1
if self.ball_draw.top <= 0:
self.ball_speed_y *= -1
if self.ball_draw.left <= 0:
self.ball_speed_x *= -1
if self.ball_draw.right >= screen_width:
self.ball_speed_x *= -1
class PLAYER1:
def __init__(self):
self.player1_width = 10
self.player1_height = 140
self.player1_x = screen_width - 20
self.player1_y = screen_height/2 - 70
def draw_player1(self):
player1_draw = pygame.Rect(self.player1_x, self.player1_y, self.player1_width, self.player1_height)
pygame.draw.rect(screen, lightGrey, player1_draw)
def move_player1(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_UP] and self.player1_y >= 0:
self.player1_y -= 5
if keys[pygame.K_DOWN] and self.player1_y <= screen_height:
self.player1_y += 5
ball = BALL()
player1 = PLAYER1()
while True:
# Checking for events
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Drawing on screen
screen.fill(bgColor)
ball.draw_ball()
player1.draw_player1()
# Movement and Collisions
ball.move_ball()
player1.move_player1()
ball.wallCollision()
# Updating screen
pygame.display.flip()
# Defining 60 frames per second (FPS)
clock.tick(60)
You use the rectangle attribute self.ball_draw to draw the ball. Hence, if you move the ball and change the self.ball_x and self.ball_y attributes, you need to update the rectangle:
class BALL:
# [...]
def draw_ball(self):
pygame.draw.ellipse(screen, lightGrey, self.ball_draw)
def move_ball(self):
self.ball_x += self.ball_speed_x
self.ball_y += self.ball_speed_y
self.ball_draw.topleft = (self.ball_x, self.ball_y)
Related
So I am currently working on Ping with Pygame and I just can't figure out how I should do the collision with the paddle. I have one Pong Game without Classes where i did it like this
if paddle.colliderect(ecl):
bewegungx = abs(bewegungx)
ecl is the ball in this case and here is it with my classes. I currently have only one paddle since I wanna first figure the collision out and then do the rest.
Thank you in advance :)
import pygame
white = (255, 255, 255)
black = (0, 0, 0)
WIDTH = 400
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
FPS = 60
clock = pygame.time.Clock()
class Ball():
def __init__(self):
self.ball_w = 30
self.ball_h = 30
self.ball_x = 100
self.ball_y = 150
self.ball_speedx = 3
self.ball_speedy = 3
self.ball_draw = pygame.Rect(self.ball_x, self.ball_y, self.ball_w, self.ball_h)
def draw(self):
pygame.draw.rect(screen, white, self.ball_draw)
def move(self):
self.ball_x += self.ball_speedx
self.ball_y += self.ball_speedy
self.ball_draw.topleft = (self.ball_x, self.ball_y)
def wall_collision(self):
if self.ball_draw.top <=0:
self.ball_speedy = self.ball_speedy *-1
if self.ball_draw.right >= WIDTH:
self.ball_speedx = self.ball_speedx *-1
if self.ball_draw.left <=0:
self.ball_speedx = self.ball_speedx *-1
if self.ball_draw.bottom >= HEIGHT:
self.ball_speedy = self.ball_speedy *-1
class Paddle():
def __init__(self):
self.paddle_w = 30
self.paddle_h = 130
self.paddle_x = 10
self.paddle_y = 150
self.paddle_speed = 0
self.paddle_left = pygame.Rect(self.paddle_x, self.paddle_y, self.paddle_w, self.paddle_h)
def draw(self):
pygame.draw.rect(screen, white, self.paddle_left)
def move(self):
key = pygame.key.get_pressed()
if key[pygame.K_w] and self.paddle_left.top > 0:
self.paddle_left.y -=7
if key[pygame.K_s] and self.paddle_left.bottom < 600:
self.paddle_left.y += 7
ball = Ball()
paddle = Paddle()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
screen.fill(black)
#Ball Functionsw
ball.draw()
ball.move()
ball.wall_collision()
#Paddle Functions
paddle.draw()
paddle.move()
pygame.display.flip()
clock.tick(FPS)
When the Ball hits the Paddle, the direction of the Ball changes. Add a method paddle_left_collision to the Ball class:
class Ball():
# [...]
def paddle_left_collision(self, paddle_rect):
if self.ball_draw.colliderect(paddle_rect):
self.ball_speedx = abs(self.ball_speedx)
Call it in the applicaiton loop
while True:
# handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
# move objects and detect collisisons
ball.move()
paddle.move()
ball.wall_collision()
ball.paddle_left_collision(paddle.paddle_left)
# draw scene
screen.fill(black)
ball.draw()
paddle.draw()
pygame.display.flip()
clock.tick(FPS)
Note that if you have two paddles, change the method as follows:
def paddles_collision(self, paddle_left_rect, paddle_right_rect):
if self.ball_draw.colliderect(paddle_left_rect):
self.ball_speedx = abs(self.ball_speedx)
if self.ball_draw.colliderect(paddle_right_rect):
self.ball_speedx = -abs(self.ball_speedx)
See also How do I detect collision in pygame? and Sometimes the ball doesn't bounce off the paddle in pong game
I'm making a simple game in pygame, in which you're supposed to dodge or shoot the targets that descend through the screen. So far, I've created the ship and managed to set the movement of both the bullet and the ship, as for their key bindings. However, the bullet's x coordinate doesn't seem to change at all, even though I've defined in the init method in the bullet class that the bullet x coordinate is equal to the ship x coordinate, therefore, the bullet would always be leaving the ship's position. Instead, the bullet always leaves the middle of the screen. I can't find the issue. Any help would be very much appreciated
import pygame, sys
pygame.init()
clock = pygame.time.Clock()
screen_width = 600
screen_height = 800
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Space Race Game")
class ROCKET:
def __init__(self):
self.rocketImg = pygame.image.load("spaceship.png")
self.rocket_x = screen_width/2 - 32
self.rocket_y = screen_height/2 + 100
def draw_rocket(self):
screen.blit(self.rocketImg, (self.rocket_x, self.rocket_y))
def move_rocket(self):
key = pygame.key.get_pressed()
if key[pygame.K_LEFT] and self.rocket_x + 15 > 0:
self.rocket_x -= 5
if key[pygame.K_RIGHT] and self.rocket_x < screen_width - 40:
self.rocket_x += 5
class BULLET(ROCKET):
def __init__(self):
super().__init__()
self.bullet_width = 10
self.bullet_height = 20
self.bullet_x = self.rocket_x + 25
self.bullet_y = self.rocket_y
self.move = [0, 0]
self.bullet_speed = 5
self.bullet_rect = pygame.Rect(self.bullet_x, self.bullet_y, self.bullet_width, self.bullet_height)
def draw_bullet(self):
key = pygame.key.get_pressed()
if key[pygame.K_SPACE]:
self.bullet_x = self.rocket_x
self.move[1] = -1
self.bullet_y += self.move[1] * self.bullet_speed
self.bullet_rect.topleft = (self.bullet_x, self.bullet_y)
if self.bullet_y < self.rocket_y - 10:
pygame.draw.rect(screen, (0, 0, 0), self.bullet_rect)
if self.bullet_y < - 20:
self.bullet_y = self.rocket_y
self.move[1] = 0
rocket = ROCKET()
bullet = BULLET()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill((255, 255, 255))
rocket.draw_rocket()
rocket.move_rocket()
bullet.draw_bullet()
pygame.display.flip()
clock.tick(60)
You have to set the x-coordinate of the bullet by the current x-coordinate of the rocket.
Add an argument rocket to the method draw_bullet of the class BULLET. Make sure that you can only fire the bullet if the bullet has not yet been fired (self.move[1] == 0). Compute the center of the rocket and set the position of the bullet (rocket.rocket_x + self.rocketImg.get_width() // 2):
class BULLET(ROCKET):
# [...]
def draw_bullet(self, rocket):
key = pygame.key.get_pressed()
if key[pygame.K_SPACE] and self.move[1] == 0:
rocket_center_x = rocket.rocket_x + self.rocketImg.get_width() // 2
self.bullet_x = rocket_center_x - self.bullet_width // 2
self.move[1] = -1
# [...]
Passe the instance of ROCKET to the method draw_bullet:
while True:
# [...]
bullet.draw_bullet(rocket)
# [...]
If you want to fire multiple bullets, see the answers to the questions:
How can i shoot a bullet with space bar?
How do I stop more than 1 bullet firing at once?
1) In my code the problem is that my Platform underneath my player class are connected and when the player moves so does the platform and if so the player moves on the platform the entire time. Can someone tell me how to fix that? If you are wondering where to look for all the code and where it is going to be it is all the code here because I didn't Know what was causing the problem in my code.
import math
import os
import sys
# It is importing everything
import pygame
from pygame.locals import *
#The platform class is the problem I'm having
class Platform:
def __init__(self, size, x, y, length, color, velocity):
self.length = length
self.size = size
self.x = x
self.y = y
self.color = color
self.velocity = velocity
self.xVelocity = 0
# This is what the platform class has and what it does
def draw(self):
display = pygame.display.get_surface()
pygame.draw.rect(display, self.color, (int(self.x) - 80, int(self.y) + 225, int(self.x), int(self.y) - 45))
# This is def draw function is showing that how I want my Platform to look like
def do(self):
self.draw()
2) When my player reaches the "end" of the screen it stops but I want to make it a scrolling screen and nothing happens and then there is no game when the player moves and scroll by itself. After someone tells me how to fix my second problem then I want the Background to kill the player if the background goes past the player.
# The def do function is running def draw function
class Player:
def __init__(self, velocity, maxJumpRange, x, y, size):
self.falling = True
self.jumpCounter = 0
self.xVelocity = 0
self.y = y
self.x = x
self.jumping = False
self.velocity = velocity
self.maxJumpRange = maxJumpRange
self.jump_offset = 0
self.size = size
# The player class is making how the Player is going to look and what are his limits
def keys(self):
k = pygame.key.get_pressed()
# The def keys(self): is creating a variable for pygame.key.get_pressed() and underneath is a function to make the player move around
if k[K_LEFT]:
self.xVelocity = -self.velocity
elif k[K_RIGHT]:
self.xVelocity = self.velocity
else:
self.xVelocity = 0
if k[K_SPACE] or k[K_UP] and not self.jumping and not self.falling:
self.jumping = True
self.jumpCounter = 0
# The if k[K_Space] or k[K_UP] is making sure the player has a jump limit and can't continue jumping forever.
def move(self):
self.x += self.xVelocity
# This to make sure that the player can move while he is jumping.
if self.jumping:
self.y -= self.velocity
self.jumpCounter += 1
if self.jumpCounter == self.maxJumpRange:
self.jumping = False
self.falling = True
elif self.falling:
if self.y <= h - 10 <= self.y + self.velocity:
self.y = h - 10
self.falling = False
else:
self.y += self.velocity
def draw(self):
display = pygame.display.get_surface()
pygame.draw.circle(display, White, (int(self.x), int(self.y) - 25), 25, 0)
def do(self):
self.keys()
self.move()
self.draw()
# This Function is doing all of the Functions self.keys(), self.move(), self.draw()
def events():
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# This is to make sure that you can exit the game is needed
def keys(player):
keys = pygame.key.get_pressed()
if keys[K_UP] or keys[K_SPACE] and player.jumping == False and player.jump_offset == 0:
player.jumping = True
# The function here to make sure that the player can jump
def do_jumping(player):
jump_height = 25
# Def do_jumping(player): is to adjust the jump_height of the player
if player.jumping:
player.jump_offset += 1
if player.jump_offset >= jump_height:
player.jumping = False
elif player.jump_offset > 0 and player.jumping == False:
player.jump_offset -= 1
# The above is showing how the makes sure the player doesn't jump higher then the jump height
w = 576
h = 516
hw = w / 2
hh = h / 2
AREA = w * h
# The above is showing the width the height and Area
os.environ['SDL_VIDEO_WINDOW_POS'] = "50,50"
p = Player(hh, hw, 290, 250, 30)
# the above is showing what the graphics are
pygame.init()
Clock = pygame.time.Clock()
DS = pygame.display.set_mode((w, h)) # This is what the display size is
pygame.display.set_caption("Try to get point B")
FPS = 120
Black = (0, 0, 0, 255)
White = (255, 255, 255, 255)
pl = Platform(290, 250, 50, 70, White, 0)
Red = (255, 0, 0)
Solid_Fill = 0
# Bkgd stands for background
bkgd = pygame.image.load("scroll.png").convert() # scroll.png is the png im using to use as the background
bkgdWidth = bkgd.get_rect().size[0]
print(bkgdWidth)
bkgdHeight = bkgd.get_rect().size
stageWidth = bkgdWidth * 2
StagePosX = 0
startScollingPosx = hw
circleRadius = 25
circlePosX = circleRadius
playerPosX = circleRadius
playerPosY = 377
playerVelocityX = 0
playerVelocityY = 0
platformVelocityX = 0
platformVelocityY = 0
x = 0
# The above is showing the player Velocity, player position y and x and what the stage position is, Ignore platform
# velocity.
# What the while true loop is doing is to make sure that the background moves while the player moves
while True:
events()
k = pygame.key.get_pressed()
if k[K_RIGHT]:
playerVelocityX = 1
pl.xVelocity = 0
elif k[K_LEFT]:
playerVelocityX = -1
pl.xVelocity = 0
else:
playerVelocityX = 0
pl.xVelocity = 0
playerPosX += playerVelocityX
if playerPosX > stageWidth - circleRadius:
playerPosX = stageWidth - circleRadius
if playerPosX < circleRadius:
playerPosX = circleRadius
if playerPosX < startScollingPosx:
circlePosX = playerPosX
elif playerPosX > stageWidth - startScollingPosx:
circlePosX - stageWidth + w
else:
circlePosX = startScollingPosx
StagePosX -= playerVelocityX
# The code below show is working how to balance the Display size and rel x is the easier of saying that
rel_x = StagePosX % bkgdWidth
DS.blit(bkgd, (rel_x - bkgdWidth, 0))
if rel_x < w:
DS.blit(bkgd, (rel_x, 0))
events()
keys(p)
do_jumping(p)
pygame.draw.circle(DS, White, (math.floor(p.x), math.floor(p.y) - math.floor(p.jump_offset)), math.floor(p.size), math.floor(Solid_Fill))
platform_color = Red
pl.color = Red
pl.draw()
if p.jump_offset == 0:
pl.color = White
pl.do()
pygame.display.update()
Clock.tick(FPS)
DS.fill(Black)
3) Lastly sorry for the lack of good code and where to look around the code because I didn't Know what was causing the problem in my code so I had to put out all of it.
I edited your code a bit so fix the problems you had, I did change the image as i didn't have it, but i fixed jumping and standing on platform by making player constantly fall and check if the player collides with the platform. I also added the scrolling background. Got rid of the unnecessary code as mentioned in my comments. changed the player move to do the jump from there. changed the size in platform to a list so it has width and height. got rid of velocity too as platforms didn't move.
import math
import os
import sys
# It is importing everything
import pygame
from pygame.locals import *
class Platform:
def __init__(self, size, x, y, color):
#size is a list, this means it has width and height
self.size = size
self.x = x
self.y = y
self.color = color
# This is what the platform class has and what it does
def draw(self):
display = pygame.display.get_surface()
pygame.draw.rect(display, self.color, (int(self.x), int(self.y), self.size[0], self.size[1]))
# This is def draw function is showing that how I want my Platform to look like
def do(self):
self.draw()
# The def do function is running def draw function
class Player:
def __init__(self, velocity, maxJumpRange, x, y, size):
self.falling = True
self.jumpCounter = 0
self.xVelocity = 0
self.y = y
self.x = x
self.jumping = False
self.velocity = velocity
self.maxJumpRange = maxJumpRange
self.jump_offset = 0
self.size = size
self.TouchedGround = False
# The player class is making how the Player is going to look and what are his limits
def keys(self):
k = pygame.key.get_pressed()
# The def keys(self): is creating a variable for pygame.key.get_pressed() and underneath is a function to make the player move around
if k[K_LEFT]:
self.xVelocity = -self.velocity
elif k[K_RIGHT]:
self.xVelocity = self.velocity
else:
self.xVelocity = 0
if (k[K_SPACE] or k[K_UP]) and not self.jumping and self.TouchedGround:
self.jumping = True
self.jumpCounter = 0
self.TouchedGround = False
# The if k[K_Space] or k[K_UP] is making sure the player has a jump limit and can't continue jumping forever.
def move(self):
self.x += self.xVelocity
# if the player is jumping, change y value
if self.jumping:
self.y -= self.velocity
self.jumpCounter += 1
if self.jumpCounter == self.maxJumpRange:
self.jumping = False
self.falling = True
elif self.falling:
self.y += self.velocity
self.jumpCounter -= 1
def draw(self):
display = pygame.display.get_surface()
pygame.draw.circle(display, White, (int(self.x), int(self.y)), self.size)
def do(self):
self.keys()
self.move()
self.draw()
# This Function is doing all of the Functions self.keys(), self.move(), self.draw()
def events():
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
#window size
w = 576
h = 516
# The above is showing the width the height and Area
os.environ['SDL_VIDEO_WINDOW_POS'] = "50,50"
# the above is showing what the graphics are
#player
p = Player(1, 100, 290, 250, 30)
#start pygame
pygame.init()
Clock = pygame.time.Clock()
DS = pygame.display.set_mode((w, h)) # This is what the display size is
pygame.display.set_caption("Try to get point B")
#variables
FPS = 120
Black = (0, 0, 0, 255)
White = (255, 255, 255, 255)
Red = (255, 0, 0)
# Bkgd stands for background
bkgd = pygame.Surface((w,h)) # didnt have the image so i made it blue
bkgd.fill((0,0,255))
#platforms
pl = Platform([290,20], 250, 350, White)
#this is a list that holds all the platforms
platforms_list = [pl,Platform([200,20], 100, 450, White), Platform([200,20], 400, 250, White)]
#this is how much to scroll the background by
background_scroll = 0
# What the while true loop is doing is to make sure that the background moves while the player moves
while True:
events()
#blit the background, since the image is same size as window blit twice so when scrolls, you dont have blackness
DS.blit(bkgd, (-background_scroll, 0))
DS.blit(bkgd, (w-background_scroll, 0))
#check for x button clicked
events()
#update the player
p.do()
#update platforms and check for collision with player
platform_color = Red
for platform in platforms_list:
platform.color = platform_color
if p.jumping == 0:
platform.color = White
platform.do()
#if bottom of player is in the platform, move the player on top of the platform
if p.y + p.size > platform.y and p.y + p.size < platform.y + platform.size[1]:
if p.x > platform.x and p.x < platform.x + platform.size[0]:
p.y = platform.y - p.size
p.TouchedGround = True
#if the player reaches the side of the screen, move the background and platforms to make it look like it is moving
if p.x + p.size >= w:
p.x = w - p.size
background_scroll += 1
for platform in platforms_list:
platform.x -= 1
if background_scroll == w:
background_scroll = 0
#same but for the left
if p.x - p.size <= 0:
p.x = 0 + p.size
background_scroll -= 1
for platform in platforms_list:
platform.x += 1
if background_scroll == 0:
background_scroll = w
#update screen
pygame.display.update()
Clock.tick(FPS)
DS.fill(Black)
I'm trying to make a circle move from the middle of the screen to the top, and then back to the middle, and so on, using Pygame.
import pygame
import sys
pygame.init()
gameOver = False
speed = 5
clock = pygame.time.Clock()
fps = 20
class Screen:
largeur = 600
hauteur = 600
demiLargeur = int(largeur/2)
demiHauteur = int(hauteur/2)
screen = pygame.display.set_mode((Screen.largeur, Screen.hauteur))
class Couleurs:
bleu = (000,000,255)
noir = (000,000,000)
class Cercle:
diametre = 50
epaisseur = 5
posTop = [Screen.demiLargeur, Screen.demiHauteur-2*diametre]
class CirclePos:
top = [Cercle.posTop[0],Cercle.posTop[1]]
circleListTop = [CirclePos.top]
class DrawCircles:
def top (circleListTop):
for CirclePos.top in circleListTop:
pygame.draw.circle(screen, Couleurs.bleu, (CirclePos.top[0],CirclePos.top[1]),Cercle.diametre,Cercle.epaisseur)
class UpdateCirclesPositions:
def top(circleListTop):
for idx,CirclePos.top in enumerate(circleListTop):
if CirclePos.top[1] > Cercle.diametre :
CirclePos.top[1] -= speed
else:
circleListTop.pop(idx)
while not gameOver:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(Couleurs.noir)
clock.tick(fps)
DrawCircles.top(circleListTop)
UpdateCirclesPositions.top(circleListTop)
pygame.display.update()
I have this code so far, the idea being to make it move up, make it desappear, and then create another list to move circles from top to middle. I feel it's a bad idea.
Any idea ?
Thanks
As for me you have too much classes. In class Circle you should have its properties and methods update and draw.
When circle is at the top then it should change speed - speed = -speed. The same when it is in the middle. This way it may move all time. But it may need variable direction to check if it already changed direction because it can be in place where it may change speed all time (in every move).
import pygame
# --- constans ---
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
FPS = 20
SPEED = 5
# --- classes ---
class Colors:
blue = (000, 000, 255)
black = (000, 000, 000)
red = (255, 000, 000)
class Circle:
def __init__(self, x, y, size=50, thick=5, color=Colors.blue, speed=SPEED):
self.size = size
self.thick = thick
self.color = color
self.rect = pygame.Rect(0, 0, 2*size, 2*size)
self.rect.centerx = x
self.rect.centery = y
if speed >= 0:
self.direction = 'up'
else:
self.direction = 'down'
self.speed = speed
def draw(self, screen):
pygame.draw.circle(screen, self.color, self.rect.center, self.size, self.thick)
def update(self):
self.rect.y -= self.speed
if self.rect.top <= 0 and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
if self.rect.bottom >= screen_rect.centery and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
# --- main ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
circles = [
Circle(screen_rect.centerx, screen_rect.centery),
Circle(screen_rect.centerx, 0, speed=-SPEED, color=Colors.red)
]
clock = pygame.time.Clock()
game_over = False
while not game_over:
# --- events ----
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_over = True
# --- updates --- (without draws)
for item in circles:
item.update()
# --- draws --- (without updates)
screen.fill(Colors.black)
for item in circles:
item.draw(screen)
pygame.display.update()
clock.tick(FPS)
pygame.quit() # close window
I have a simple bouncing box window that was drawn with 'Pygame'. Everything seems to work properly, except for a little annoyance. It stutters constantly! I have no idea what could be causing the stutter. I thought it might be a delay, so I implemented a fixed time-step to allow the loop to catch up, but this had no effect.
#--- initialize pygame window ---#
import pygame
import time
pygame.init()
size = (1200,500)
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
fps = 60
#--- define color palette ---#
black = (0,0,0)
white = (255,255,255)
#--- define the player ---#
class player:
def __init__(self,screen,surface, color):
self.speed = 3
self.direction_x = 1
self.direction_y = 1
self.screen = screen
self.surface = surface
self.rect = self.surface.get_rect()
self.color = color
def set_pos(self, x,y):
self.rect.x = x
self.rect.y = y
def advance_pos(self):
screen_width, screen_height = screen.get_size()
if self.rect.x + self.rect.width > screen_width or player1.rect.x < 0:
player1.direction_x *= -1
player1.speed = 3
elif player1.rect.y + player1.rect.height > screen_height or player1.rect.y < 0:
player1.direction_y *= -1
player1.speed = 3
else:
player1.speed -= 0.001
self.rect.x += self.speed * self.direction_x
self.rect.y += self.speed * self.direction_y
def draw(self):
pygame.draw.rect(self.surface, self.color, [0,0,self.rect.width,self.rect.height])
def blit(self):
screen.blit(self.surface, self.rect)
player1 = player(screen, pygame.Surface((50,50)), white)
player1.set_pos(50,50)
player1.draw()
#--- define game variables ---#
previous = time.time() * 1000
lag = 0.0
background = black
done = False
#--- game ---#
while not done:
#--- update time step ---#
current = time.time() * 1000
elapsed = current - previous
lag += elapsed
previous = current
#--- process events ---#
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
break
if event.type == pygame.VIDEORESIZE:
screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
#--- update logic ---#
while True:
player1.advance_pos()
lag -= fps
if lag <= fps:
break
#--- draw to screen ---#
screen.fill(background)
player1.blit()
pygame.display.update()
pygame.time.Clock().tick(fps)
This is a rewrite of your code that uses opengl instead for the rendering. The major changes are as follows:
I used opengl immediate mode, which is out-of-date and deprecated, but is a lot easier to understand at first. Most of the gl calls are either in the player.draw() method or in the main loop.
I fixed the way the timer is done. Rather than doing just clock.tick(fps), I manually keep track of the amount of time that it takes to do all of the processing to the frame and add the appropriate millisecond delay to reach 60 fps. You can try that modification with your existing pygame code before migrating to opengl as that might be sufficient to remove most of the stutter.
import pygame
import time
from OpenGL.GL import *
class Player:
def __init__(self, screen, width, height, color):
self.x = 0
self.y = 0
self.speed = 3
self.direction_x = 1
self.direction_y = 1
self.screen = screen
self.width = width
self.height = height
self.color = color
def set_pos(self, x, y):
self.x = x
self.y = y
def advance_pos(self):
screen_width, screen_height = screen.get_size()
if self.x + self.width > screen_width or self.x < 0:
self.direction_x *= -1
self.speed = 3
elif self.y + self.height > screen_height or self.y < 0:
self.direction_y *= -1
self.speed = 3
else:
self.speed -= 0.001
self.x += self.speed * self.direction_x
self.y += self.speed * self.direction_y
def draw(self):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslate(self.x, self.y, 0)
glBegin(GL_QUADS)
glColor(*self.color)
glVertex(0, 0, 0)
glVertex(self.width, 0, 0)
glVertex(self.width, self.height, 0)
glVertex(0, self.height, 0)
glEnd()
if __name__ == "__main__":
pygame.init()
size = width, height = (550, 400)
screen = pygame.display.set_mode(size, pygame.RESIZABLE | pygame.DOUBLEBUF | pygame.OPENGL)
fps = 60
black = (0,0,0,255)
white = (255,255,255,255)
player1 = Player(screen, 50, 50, white)
player1.set_pos(50,50)
done = False
previous = time.time() * 1000
glClearColor(*black)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, width, height, 0, -1, 1)
clock = pygame.time.Clock()
while not done:
current = time.time() * 1000
elapsed = current - previous
previous = current
delay = 1000.0/fps - elapsed
delay = max(int(delay), 0)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
break
if event.type == pygame.VIDEORESIZE:
size = width, height = event.w, event.h
screen = pygame.display.set_mode(size, pygame.RESIZABLE | pygame.DOUBLEBUF | pygame.OPENGL)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, width, height, 0, -1, 1)
glViewport(0, 0, width, height)
#reset player movement and position to avoid glitches where player is trapped outside new window borders
player1.set_pos(50, 50)
player1.direction_x = 1
player1.direction_y = 1
player1.advance_pos()
glClear(GL_COLOR_BUFFER_BIT)
glClear(GL_DEPTH_BUFFER_BIT)
player1.draw()
pygame.display.flip()
pygame.time.delay(delay)
I created a clock object befor entering the "while not done" loop and had no more lags
#--- initialize pygame window ---#
import pygame
import time
pygame.init()
size = (1200,500)
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
fps = 60
#--- define color palette ---#
black = (0,0,0)
white = (255,255,255)
#--- define the player ---#
class player:
def __init__(self,screen,surface, color):
self.speed = 3
self.direction_x = 1
self.direction_y = 1
self.screen = screen
self.surface = surface
self.rect = self.surface.get_rect()
self.color = color
def set_pos(self, x,y):
self.rect.x = x
self.rect.y = y
def advance_pos(self):
screen_width, screen_height = screen.get_size()
if self.rect.x + self.rect.width > screen_width or player1.rect.x < 0:
player1.direction_x *= -1
player1.speed = 3
elif player1.rect.y + player1.rect.height > screen_height or player1.rect.y < 0:
player1.direction_y *= -1
player1.speed = 3
else:
player1.speed -= 0.001
self.rect.x += self.speed * self.direction_x
self.rect.y += self.speed * self.direction_y
def draw(self):
pygame.draw.rect(self.surface, self.color, [0,0,self.rect.width,self.rect.height])
def blit(self):
screen.blit(self.surface, self.rect)
player1 = player(screen, pygame.Surface((50,50)), white)
player1.set_pos(50,50)
player1.draw()
#--- define game variables ---#
previous = time.time() * 1000
lag = 0.0
background = black
done = False
clock=pygame.time.Clock()
#--- game ---#
while not done:
#--- update time step ---#
current = time.time() * 1000
elapsed = current - previous
lag += elapsed
previous = current
#--- process events ---#
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
break
if event.type == pygame.VIDEORESIZE:
screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
#--- update logic ---#
while True:
player1.advance_pos()
lag -= fps
if lag <= fps:
break
#--- draw to screen ---#
screen.fill(background)
player1.blit()
pygame.display.update()
clock.tick(fps)
pygame.quit()