Reputation: 649
I'm trying to make a clone of google chrome dinosaur game in Python pygame
. I don't have much experience with pygame
. I managed to create all of the dinosaur sprite animations (walking, jumping and bending) and now I'm trying to add the obstacles. So what I want to do is when the user's score surpasses 35, obstacles will start being randomly generated. The problem is that when the score surpasses 35, obstacles appear on top of each other, some appear and disappear constantly and it just looks bad and very laggy. How can I render the obstacles more smoothly? The relevant parts of the code:
import pygame
import random
import os
from threading import Thread
pygame.init()
w, h = pygame.display.get_surface().get_size()
imgX = w
def generate_obstacles():
global imgX
obstacles_tuple = (obstacle1, obstacle2, obstacle3, obstacle4, obstacle5)
chosen_obstacle = random.choice(obstacles_tuple)
try:
if WINDOW.get_width() >= imgX > 0:
WINDOW.blit(chosen_obstacle, (imgX, 323))
else:
imgX = w
except pygame.error:
quit()
pygame.display.update()
def draw():
WINDOW.fill("#FFFFFF")
pygame.display.update()
r1, r2 = 5000, 7000
def timer(score):
global r1, r2
timer_running = True
while timer_running:
rand_time = random.randint(r1, r2)
pygame.time.delay(rand_time)
generate_obstacles()
if r1 >= 500 and r2 >= 500:
if score % 100 == 0:
r1 -= 20
r2 -= 20
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT or keys[pygame.K_ESCAPE]:
break
game_intro(True)
run = True
my_sprite = Dino()
speed = 5
while run:
pygame.time.delay(100)
bgX -= speed
bgX2 -= speed
imgX -= speed
if bgX < floor.get_width() * -1:
bgX = floor.get_width()
if bgX2 < floor.get_width() * -1:
bgX2 = floor.get_width()
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT or keys[pygame.K_ESCAPE]:
with open("highscore.txt", "w") as f:
f.write(str(my_sprite.high_score))
run = False
pygame.quit()
quit()
if event.type == pygame.USEREVENT+1:
my_sprite.fall()
if (keys[pygame.K_UP] or keys[pygame.K_SPACE]) and not my_sprite.is_jumping:
my_sprite.jump()
if keys[pygame.K_DOWN]:
my_sprite.bend()
draw()
my_sprite.walk()
if my_sprite.score >= 35:
Thread(target=lambda: timer(my_sprite.score)).start()
I also made a video so you can get a better understanding of what the problem is.
Full code:
import pygame
import random
import os
from threading import Thread
pygame.init()
WINDOW = pygame.display.set_mode((500, 500))
pygame.display.set_caption("Dinosaur Game")
walk1_image = pygame.image.load("images/walk1.png")
walk1 = pygame.transform.scale(walk1_image, (64, 64))
bend1_image = pygame.image.load("images/bend1.png")
bend1 = pygame.transform.scale(bend1_image, (64, 64))
bend2_image = pygame.image.load("images/bend2.png")
bend2 = pygame.transform.scale(bend2_image, (64, 64))
walk2_image = pygame.image.load("images/walk2.png")
walk2 = pygame.transform.scale(walk2_image, (64, 64))
die_image = pygame.image.load("images/die.png")
die = pygame.transform.scale(die_image, (64, 64))
jump_image = pygame.image.load("images/jump.png")
jump = pygame.transform.scale(jump_image, (64, 64))
images_list = [walk1, walk2, die, jump, bend1]
floor_image = pygame.image.load("obstacles/floor-0.png").convert()
floor = pygame.transform.scale(floor_image, (500, 100))
bgX = 0
bgX2 = floor.get_width()
in_main = False
obstacle1_image = pygame.image.load("obstacles/obstacle1.png")
obstacle1 = pygame.transform.scale(obstacle1_image, (78, 78))
obstacle2_image = pygame.image.load("obstacles/obstacle2.png")
obstacle2 = pygame.transform.scale(obstacle2_image, (156, 78))
obstacle3_image = pygame.image.load("obstacles/obstacle3.png")
obstacle3 = pygame.transform.scale(obstacle3_image, (64, 64))
obstacle4_image = pygame.image.load("obstacles/obstacle4.png")
obstacle4 = pygame.transform.scale(obstacle4_image, (128, 64))
obstacle5_image = pygame.image.load("obstacles/obstacle5.png")
obstacle5 = pygame.transform.scale(obstacle5_image, (192, 64))
def game_intro(intro):
while intro:
WINDOW.fill("#FFFFFF")
my_font = pygame.font.SysFont("comicsans", 40)
label = my_font.render("Press space to play", True, (105, 105, 105))
WINDOW.blit(jump, (125, 200))
WINDOW.blit(label, (125, 300))
my_font = pygame.font.SysFont("comicsans", 20)
label2 = my_font.render("Made by: Roni", True, (0, 0, 0))
WINDOW.blit(label2, (10, 480))
pygame.display.update()
for event2 in pygame.event.get():
keys2 = pygame.key.get_pressed()
if event2.type == pygame.QUIT or keys2[pygame.K_ESCAPE]:
intro = False
pygame.quit()
quit()
if keys2[pygame.K_SPACE]:
if not os.path.isfile("highscore.txt"):
f = open("highscore.txt", "w")
f.write("0")
f.close()
intro = False
return
class Dino(pygame.sprite.Sprite):
def __init__(self):
super(Dino, self).__init__()
self.images = [walk1, walk2]
self.index = 0
self.image = self.images[self.index]
self.x = 200
self.y = 323
self.is_falling = False
self.is_jumping = False
self.is_bend = False
self.is_down = False
self.score = 0
self.high_score = 0
def walk(self):
self.update_score()
WINDOW.blit(floor, (bgX, 300))
WINDOW.blit(floor, (bgX2, 300))
self.score += 1
if self.is_jumping:
WINDOW.blit(jump, (self.x, self.y))
elif self.is_bend:
bend_images = [bend1, bend2]
keys3 = pygame.key.get_pressed()
self.index += 1
if self.index >= len(self.images):
self.index = 0
bend = bend_images[self.index]
if keys3[pygame.K_DOWN]:
WINDOW.blit(bend, (self.x, self.y))
else:
self.is_bend = False
self.walk()
else:
self.index += 1
if self.index >= len(self.images):
self.index = 0
self.image = self.images[self.index]
WINDOW.blit(self.image, (self.x, self.y))
pygame.display.update()
def jump(self):
self.is_jumping = True
for i in range(3):
self.y -= 20
self.walk()
pygame.time.delay(10)
WINDOW.fill("#FFFFFF")
self.is_falling = True
pygame.time.set_timer(pygame.USEREVENT+1, 1000)
def fall(self):
if self.is_falling:
while self.y != 323:
self.y += 20
self.walk()
WINDOW.fill("#FFFFFF")
self.is_falling = False
self.is_jumping = False
pygame.display.update()
def bend(self):
self.is_bend = True
self.walk()
def update_score(self):
my_font = pygame.font.SysFont("comicsans", 40)
zeros = 5 - len(str(self.score))
str_score = "0" * zeros + str(self.score)
label = my_font.render(str_score, True, (0, 0, 0))
WINDOW.blit(label, (410, 0))
with open("highscore.txt", "r") as f:
self.high_score = int(f.read())
if self.high_score <= self.score:
self.high_score = self.score
zeros2 = 5 - len(str(self.high_score))
str_score2 = "0" * zeros2 + str(self.high_score)
label2 = my_font.render("HI: " + str_score2, True, (0, 0, 0))
WINDOW.blit(label2, (250, 0))
pygame.display.update()
w, h = pygame.display.get_surface().get_size()
imgX = w
def generate_obstacles():
global imgX
obstacles_tuple = (obstacle1, obstacle2, obstacle3, obstacle4, obstacle5)
chosen_obstacle = random.choice(obstacles_tuple)
try:
if WINDOW.get_width() >= imgX > 0:
WINDOW.blit(chosen_obstacle, (imgX, 323))
else:
imgX = w
except pygame.error:
quit()
pygame.display.update()
def draw():
WINDOW.fill("#FFFFFF")
pygame.display.update()
r1, r2 = 5000, 7000
def timer(score):
global r1, r2
timer_running = True
while timer_running:
rand_time = random.randint(r1, r2)
pygame.time.delay(rand_time)
generate_obstacles()
if r1 >= 500 and r2 >= 500:
if score % 100 == 0:
r1 -= 20
r2 -= 20
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT or keys[pygame.K_ESCAPE]:
break
game_intro(True)
run = True
my_sprite = Dino()
speed = 5
while run:
pygame.time.delay(100)
bgX -= speed
bgX2 -= speed
imgX -= speed
if bgX < floor.get_width() * -1:
bgX = floor.get_width()
if bgX2 < floor.get_width() * -1:
bgX2 = floor.get_width()
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT or keys[pygame.K_ESCAPE]:
with open("highscore.txt", "w") as f:
f.write(str(my_sprite.high_score))
run = False
pygame.quit()
quit()
if event.type == pygame.USEREVENT+1:
my_sprite.fall()
if (keys[pygame.K_UP] or keys[pygame.K_SPACE]) and not my_sprite.is_jumping:
my_sprite.jump()
if keys[pygame.K_DOWN]:
my_sprite.bend()
draw()
my_sprite.walk()
if my_sprite.score >= 35:
Thread(target=lambda: timer(my_sprite.score)).start()
The problem was solved thanks to @qouify. You can look at his answer to see what solved the problem. Full updated code:
import pygame
import random
import os
from threading import Thread
pygame.init()
WINDOW = pygame.display.set_mode((500, 500))
pygame.display.set_caption("Dinosaur Game")
walk1_image = pygame.image.load("Dino/walk1.png")
walk1 = pygame.transform.scale(walk1_image, (64, 64))
bend1_image = pygame.image.load("Dino/bend1.png")
bend1 = pygame.transform.scale(bend1_image, (64, 64))
bend2_image = pygame.image.load("Dino/bend2.png")
bend2 = pygame.transform.scale(bend2_image, (64, 64))
walk2_image = pygame.image.load("Dino/walk2.png")
walk2 = pygame.transform.scale(walk2_image, (64, 64))
die_image = pygame.image.load("Dino/die.png")
die = pygame.transform.scale(die_image, (64, 64))
jump_image = pygame.image.load("Dino/jump.png")
jump = pygame.transform.scale(jump_image, (64, 64))
images_list = [walk1, walk2, die, jump, bend1]
floor_image = pygame.image.load("Others/floor-0.png").convert()
floor = pygame.transform.scale(floor_image, (500, 100))
bgX = 0
bgX2 = floor.get_width()
in_main = False
obstacle1_image = pygame.image.load("Obstacles/obstacle1.png")
obstacle1 = pygame.transform.scale(obstacle1_image, (78, 78))
obstacle2_image = pygame.image.load("Obstacles/obstacle2.png")
obstacle2 = pygame.transform.scale(obstacle2_image, (156, 78))
obstacle3_image = pygame.image.load("Obstacles/obstacle3.png")
obstacle3 = pygame.transform.scale(obstacle3_image, (64, 64))
obstacle4_image = pygame.image.load("Obstacles/obstacle4.png")
obstacle4 = pygame.transform.scale(obstacle4_image, (128, 64))
obstacle5_image = pygame.image.load("Obstacles/obstacle5.png")
obstacle5 = pygame.transform.scale(obstacle5_image, (192, 64))
def game_intro(intro):
while intro:
WINDOW.fill("#FFFFFF")
my_font = pygame.font.SysFont("comicsans", 40)
label = my_font.render("Press space to play", True, (105, 105, 105))
WINDOW.blit(jump, (125, 200))
WINDOW.blit(label, (125, 300))
my_font = pygame.font.SysFont("comicsans", 20)
label2 = my_font.render("Made by: Roni Meirom", True, (0, 0, 0))
WINDOW.blit(label2, (10, 480))
pygame.display.update()
for event2 in pygame.event.get():
keys2 = pygame.key.get_pressed()
if event2.type == pygame.QUIT or keys2[pygame.K_ESCAPE]:
intro = False
pygame.quit()
quit()
if keys2[pygame.K_SPACE]:
if not os.path.isfile("highscore.txt"):
f = open("highscore.txt", "w")
f.write("0")
f.close()
intro = False
return
class Dino(pygame.sprite.Sprite):
def __init__(self):
super(Dino, self).__init__()
self.images = [walk1, walk2]
self.index = 0
self.image = self.images[self.index]
self.x = 200
self.y = 323
self.is_falling = False
self.is_jumping = False
self.is_bend = False
self.is_down = False
self.score = 0
self.high_score = 0
def walk(self):
self.update_score()
WINDOW.blit(floor, (bgX, 300))
WINDOW.blit(floor, (bgX2, 300))
self.score += 1
if self.is_jumping:
WINDOW.blit(jump, (self.x, self.y))
elif self.is_bend:
bend_images = [bend1, bend2]
keys3 = pygame.key.get_pressed()
self.index += 1
if self.index >= len(self.images):
self.index = 0
bend = bend_images[self.index]
if keys3[pygame.K_DOWN]:
WINDOW.blit(bend, (self.x, self.y))
else:
self.is_bend = False
self.walk()
else:
self.index += 1
if self.index >= len(self.images):
self.index = 0
self.image = self.images[self.index]
WINDOW.blit(self.image, (self.x, self.y))
def jump(self):
self.is_jumping = True
for i in range(3):
self.y -= 20
self.walk()
pygame.time.delay(10)
WINDOW.fill("#FFFFFF")
self.is_falling = True
pygame.time.set_timer(pygame.USEREVENT+1, 1000)
def fall(self):
if self.is_falling:
self.y += 20
if self.y != 323:
self.y = 323
self.is_falling = False
self.is_jumping = False
def bend(self):
self.is_bend = True
self.walk()
def update_score(self):
my_font = pygame.font.SysFont("comicsans", 40)
zeros = 5 - len(str(self.score))
str_score = "0" * zeros + str(self.score)
label = my_font.render(str_score, True, (0, 0, 0))
WINDOW.blit(label, (410, 0))
with open("highscore.txt", "r") as f:
self.high_score = int(f.read())
if self.high_score <= self.score:
self.high_score = self.score
zeros2 = 5 - len(str(self.high_score))
str_score2 = "0" * zeros2 + str(self.high_score)
label2 = my_font.render("HI: " + str_score2, True, (0, 0, 0))
WINDOW.blit(label2, (250, 0))
w, h = pygame.display.get_surface().get_size()
imgX = w
def generate_obstacles():
global imgX
obstacles_tuple = (obstacle1, obstacle2, obstacle3, obstacle4, obstacle5)
chosen_obstacle = random.choice(obstacles_tuple)
try:
if WINDOW.get_width() >= imgX > 0:
WINDOW.blit(chosen_obstacle, (imgX, 323))
else:
imgX = w
except pygame.error:
quit()
r1, r2 = 5000, 7000
def timer(score):
global r1, r2
timer_running = True
while timer_running:
rand_time = random.randint(r1, r2)
pygame.time.delay(rand_time)
generate_obstacles()
if r1 >= 500 and r2 >= 500:
if score % 100 == 0:
r1 -= 20
r2 -= 20
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT or keys[pygame.K_ESCAPE]:
break
game_intro(True)
run = True
my_sprite = Dino()
speed = 5
while run:
bgX -= speed
bgX2 -= speed
imgX -= speed
if bgX < floor.get_width() * -1:
bgX = floor.get_width()
if bgX2 < floor.get_width() * -1:
bgX2 = floor.get_width()
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT or keys[pygame.K_ESCAPE]:
with open("highscore.txt", "w") as f:
f.write(str(my_sprite.high_score))
run = False
pygame.quit()
quit()
if event.type == pygame.USEREVENT+1:
my_sprite.fall()
if (keys[pygame.K_UP] or keys[pygame.K_SPACE]) and not my_sprite.is_jumping:
my_sprite.jump()
if keys[pygame.K_DOWN]:
my_sprite.bend()
WINDOW.fill("#FFFFFF")
my_sprite.walk()
if my_sprite.score >= 35:
Thread(target=lambda: timer(my_sprite.score)).start()
pygame.time.delay(100)
pygame.display.update()
Upvotes: 0
Views: 1476
Reputation: 3920
It suspect that one issue with your code is that pygame.display.update()
can be called concurrently by:
draw
function ;draw_obstacles
function.This may explain why some obstacles appear and disappear very quickly. Maybe, just removing the pygame.display.update()
in function draw_obstacles
could fix that.
Another issue that may explain the lagging is related to your condition to launch the thread. It seems to me that, as soon as the score exceeds 35, you will launch another thread at each iteration of the main loop, which means a lot of threads. So, unless there is in your code some condition that can prevent that, you should add one.
[EDIT] After watching your full code I noticed a few issues.
First, as already mentionned, you should do pygame.display.update()
only once in your main loop. Same for WINDOW.fill
that you have
put have several places in your code. That's what causes the effect
of objects appearing and disappearing very quickly.
Your program (and every pygame program) should have the following structure.
while run:
process_events_and_change_objects_status()
WINDOW.fill("#FFFFFF")
redraw_everything()
pygame.display.update()
wait()
And there should not be any other call to WINDOW.fill
or
pygame.display.update()
anywhere else.
Second, looking at the fall
method:
def fall(self):
if self.is_falling:
while self.y != 323:
self.y += 20
self.walk()
WINDOW.fill("#FFFFFF")
self.is_falling = False
self.is_jumping = False
pygame.display.update()
Here you're trying to do two things at the same time: update the
object status and redraw everything with self.walk
. That's bad
because, for example, if there are currently obstacles on the screen,
they won't be drawned since self.walk
does not care about obstacles. Moreover this means that events cannot be processed while the dinosaur is falling.
So you have to change it to only update the object status with
something like that.
def fall(self):
if self.is_falling:
self.y += 20
if self.y != 323:
self.is_falling = False
self.is_jumping = False
Then redrawing will be managed by self.walk
in the main loop. Same
for bend
and jump
.
So I think that you need to modify your program structure in order to clearly separate in your code the drawing part from the processing part (processing events and updating the dinosaur status). Otherwise if you mix both you will face the kind of problems you are facing now and your code will be impossible to maintain and evolve.
Upvotes: 1