So I'm making a game in PyGame and I made a system that spawns a bunch of trees and rocks. These are objects that call an update()
function where some necessary calculations for their position are made and they get blited to the screen. I'm trying to make a program that only really updates a foliage object when its visible on the screen, So I made a function that returns if its onscreen.
So I added an if
statement that runs this function and suddenly I get 600 FPS average but for some reason the player is moving very slowly and the animation goes very fast. I use a delta time system which might have something to do with it but I'm not certain.
here is the github
import pygame
from pygame.locals import *
from random import randint, choice
import time
screen_width = 800#int(1500/1.5)
screen_height = 800#int(1500/1.5)
flags = HWSURFACE|DOUBLEBUF|NOFRAME #| FULLSCREEN
screen = pygame.display.set_mode((screen_width, screen_height), flags, 1000000000)
fps = 0
clock = pygame.time.Clock()
last_time = time.time()
time_now = last_time
dt = 0
images = {
"players" : {
"base_right" : [
pygame.image.load("images/player/baseplr1.png").convert_alpha(),
pygame.image.load("images/player/baseplr2.png").convert_alpha(),
],
"base_left" : [
pygame.transform.flip(pygame.image.load("images/player/baseplr1.png").convert_alpha(),True, False),
pygame.transform.flip(pygame.image.load("images/player/baseplr2.png").convert_alpha(),True, False),
],
},
"enemies" : {
"simple" : [
pygame.image.load("images/enemies/zombie.png").convert_alpha()
]
},
"foliage" : {
"tree" : pygame.image.load("images/foliage/tree.png").convert_alpha() ,
"rock" : pygame.image.load("images/foliage/rock.png").convert_alpha() ,
},
"background" : pygame.transform.scale(pygame.image.load("background.png").convert_alpha(),(1000,1000)),
}
class Player(pygame.sprite.Sprite):
def __init__(self, pos, stats, skin, ori):
pygame.sprite.Sprite.__init__(self)
self.dmg = stats[0]
self.hp = stats[1]
self.speed = stats[2]
self.skin = skin
self.ori = ori
self.index_max = len(images["players"][self.skin "_" self.ori])
self.index = 0
self.image = images["players"][self.skin "_" self.ori][self.index]
self.count = False
self.timer = 0
self.anim_max = 16
self.rect = self.image.get_rect(center = pos)
self.x, self.y = self.rect.x, self.rect.y
def update(self):
self.movement()
self.animate()
def movement(self):
self.keys = pygame.key.get_pressed()
self.count = False
if self.keys[K_d]:
self.x = self.speed * dt
self.ori = "right"
self.count = True
if self.keys[K_a]:
self.x -= self.speed * dt
self.ori = "left"
self.count = True
if self.keys[K_s]:
self.y = self.speed* dt
self.count = True
if self.keys[K_w]:
self.y -= self.speed * dt
self.count = True
self.rect.x = self.x - main_cam.scroll[0]
self.rect.y = self.y - main_cam.scroll[1]
def animate(self):
if self.count:
self.timer = 1
if self.timer >= self.anim_max:
self.timer = 0
self.index = 1
if self.index == self.index_max:
self.index = 0
self.image = images["players"][self.skin "_" self.ori][self.index]
class Foliage:
def __init__(self, pos, type):
self.image = images["foliage"][type]
self.rect = self.image.get_rect()
self.x = pos[0]
self.y = pos[1]
def update(self):
self.rect.x = self.x - main_cam.scroll[0]
self.rect.y = self.y - main_cam.scroll[1]
screen.blit(self.image, (self.rect.x,self.rect.y))
def on_screen(self):
return self.rect.x > 0 and self.rect.x < screen_width and self.rect.y > 0 and self.rect.y < screen_width
class Camera:
def __init__(self, speed):
self.scroll = [5,5]
self.speed = speed
def move_on_command(self):
keys = pygame.key.get_pressed()
if keys[K_UP]:
self.scroll[1] -= self.speed * dt
if keys[K_DOWN]:
self.scroll[1] = self.speed * dt
if keys[K_RIGHT]:
self.scroll[0] = self.speed * dt
if keys[K_LEFT]:
self.scroll[0] -= self.speed * dt
def follow(self, obj, speed=12):
if (obj.x - self.scroll[0]) != screen.get_width()/2:
self.scroll[0] = ((obj.x - (self.scroll[0] screen.get_width()/2))/speed ) * dt
if obj.y - self.scroll[1] != screen.get_height()/2:
self.scroll[1] = ((obj.y - (self.scroll[1] screen.get_height()/2))/speed) * dt
class Folliage_Manager:
def __init__(self, multi=1):
self.foliage = []
self.foliage_amount = multi
def spawn_foliage(self, amount):
for i in range(int(amount*self.foliage_amount)):
cord = (randint(-10000,10000), randint(-10000, 100000))
e = randint(0,6)
if e > 4:
newfol = Foliage(cord, choice(["tree", "rock"]))
self.foliage.append(newfol)
# Instantiation of stuff
main_cam = Camera(1)
playerGroup = pygame.sprite.Group()
player = Player((500,500), [25, 100, 2], "base", "right")
playerGroup.add(player)
fm = Folliage_Manager(multi=0.8)
fm.spawn_foliage(30000)
# settings
def get_dt():
global last_time
dt = 0
time_now = time.time()
dt = time_now - last_time
last_time = time_now
return dt * 300
left_border = -10000
right_border = 10000
top_border = -10000
bottom_border = 10000
def spawnBackground(res):
global left_border, top_border, right_border, bottom_border
for i in range(int(-1000*res/2),int(1000*res/2), 1000):
for x in range(int(-1000*res/2),int(1000*res/2), 1000):
screen.blit(images["background"], (main_cam.scroll[0]*-1 i, main_cam.scroll[1]*-1 x))
#print(i)
def render(groups):
spawnBackground(10)
main_cam.follow(player, 60)
for fol in fm.foliage:
#if fol.rect.x > 0 and fol.rect.x < screen_width and fol.rect.y >0 and fol.rect.y > screen_height:
if fol.on_screen():
fol.update()
#print("PRINTING R")
for group in groups:
group.update()
group.draw(screen)
screen.blit(images["enemies"]["simple"][0], (500-main_cam.scroll[0],300-main_cam.scroll[1]))
pygame.event.set_allowed([QUIT,KEYDOWN])
fpse = []
run = True
while run:
clock.tick(fps)
dt = int(get_dt())
render([playerGroup])
for event in pygame.event.get():
if event.type == QUIT:
run = False
if event.type == KEYDOWN and event.key == K_ESCAPE:
run = False
fpse.append(int(clock.get_fps()))
pygame.display.update()
print(f"""
MAX FPS: {max(fpse)}
LEAST FPS: {min(fpse)}
AVERAGE FPS: {int(sum(fpse)/len(fpse))}
""")
quit()
CodePudding user response:
The main issue is that your code is using time.time()
which has a granularity of decimal seconds. It is multiplied by 300, but this looks like a fudge to speed it up??
It's better to use the PyGame function pygame.time.get_ticks()
for all in-game times. This function returns the number of milliseconds since your program started. So changing your get_dt()
:
def get_dt():
global last_time
dt = 0
time_now = pygame.time.get_ticks() # milliseconds since game start
dt = time_now - last_time
last_time = time_now
#return dt * 300
return dt
Makes the player move more quickly, and it fixes the occasional weird slowdowns I was seeing - maybe this is a rounding of the fractional seconds from time.time()
? (I did not investigate why.)
You player animation uses simple "counter" based frame control. This means the speed of animation is directly tied to the on-screen frame-rate. It might work out better if this animation rate is tied to the PyGame clock.
The approach taken is to store the time of the next frame change. Then the code looks at the current time, comparing it with the future-time. If that time is now, the animation frame is changed, and the future-time recalculated. I also changed Player.count
to Player.animating
(boolean).
class Player(pygame.sprite.Sprite):
def __init__(self, pos, stats, skin, ori):
pygame.sprite.Sprite.__init__(self)
# [...]
self.ANIMATION_RATE = 50 # milliseconds between animation updates
self.animating = False # is the animation running (replaces self.count)
# Future time of next animation frame
self.next_anim_time = pygame.time.get_ticks() self.ANIMATION_RATE
def movement(self):
self.keys = pygame.key.get_pressed()
self.animating = False
if self.keys[K_d]:
self.x = self.speed * dt
self.ori = "right"
self.animating = True
# [...] Rest of identical changes omitted for the sake of brevity
def animate(self):
if ( self.animating ):
time_now = pygame.time.get_ticks()
# Is it time to do change the animation frame
if ( time_now > self.next_anim_time ):
self.next_anim_time = time_now self.ANIMATION_RATE # set time for next frame
self.index = 1
if ( self.index >= self.index_max ):
self.index = 0
self.image = images["players"][self.skin "_" self.ori][self.index]
Note also that the animation function was changing the image every time (mostly back to the same thing), rather than when a change was actually needed.
Oh, and I never saw anything like a rock or leaf in testing. Maybe there's a further issue here, or they're meant to be rare items.