So I began creating a game a couple months ago using pygame, it has a top view similar to Starcraft, Age of empires etc... I have written around 1320 lines of code to create the basis of my game; however, I am experiencing issues with frame rate when blitting images and believe this is because I cannot use accelerated graphics with pygame. The way I am currently blitting images is by blitting all images ahead of time on a surface which I then subsurface to create a blit image of my entire screen. Is there a more effective way that i should be utilizing?
So my assumption is that would be huge mess to look through and I do not want to waste your guy's time. Essentially any time I blit a surface the size of my screen my framerate drops by ~20 frames, is there a way I can avoid this in pygame?
##PYGAME INITATE##
import pygame, os
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()
_W,_H = pygame.display.Info().current_w, pygame.display.Info().current_h
flags = pygame.DOUBLEBUF | pygame.HWSURFACE
gameDisplay = pygame.display.set_mode((_W,_H),pygame.FULLSCREEN ) ## CREATES SCREEN YOU DISPLAY ON ##
gameDisplay.fill((0,0,0)) ## FILL COLOR OF SCREEN ##
pygame.display.set_caption("Dope Game") ## SETS NAME ##
gameClock = pygame.time.Clock() ## CLOCK OF THE GAME ##
import math
import os
import random
import copy
SQRT = math.sqrt
PI = math.pi
cos = math.cos
sin = math.sin
## REPEATABLE FUNCTIONS ##
def loadScale(file,command,sizeX,sizeY):
temp = pygame.image.load(file)
tempInfo = temp.get_rect()
tempInfo1,tempInfo2,tempInfo3,tempInfo4 = temp.get_rect()
tempInfo3 = int(tempInfo3)
tempInfo4 = int(tempInfo4)
if (command == "ratio"):
tempInfo3 = tempInfo3*sizeX
tempInfo4 = tempInfo4*sizeY
temp = pygame.transform.scale(temp,(int(tempInfo3),int(tempInfo4) ) )
elif (command == "size"):
temp = pygame.transform.scale(temp, (sizeX,sizeY) )
return(temp)
## NON GAME RELATED CLASSES ##
class EnterFrame():
def __init__(self,frameReset,function,parse,reset):
self.frameReset = frameReset
self.currentFrame = frameReset
self.function = function
self.parse = parse
self.reset = reset
if (self.reset != "onComplete"):
self.reset = (reset-1)
enterFrameTable.append(self)
def step(self,enterFrameTable):
if (self.currentFrame == 0):
self.function(self.parse)
if (self.reset != "onComplete"):
if (self.reset > 0):
self.currentFrame = self.frameReset
self.reset = self.reset-1
else:
enterFrameTable.remove(self)
del self
else:
self.currentFrame = self.frameReset
else:
self.currentFrame = self.currentFrame-1
class PlayerCreation():
def __init__(self):
self.x = _W
self.y = _H
self.view = [1600,1600]
self.viewShift = []
self.viewChangeSpeed = 25
def moveView(self,key):
add = EnterFrame(0,self.moveViewAction,key,"onComplete")
self.viewShift.append([add,key])
def moveViewAction(self,key):
if (key == "up"):
self.view[1] = self.view[1]-self.viewChangeSpeed
Map.recenterView()
if (self.view[1] < 0):
self.view[1] = 0
elif (key == "right"):
self.view[0] = self.view[0]+self.viewChangeSpeed
Map.recenterView()
if (self.view[0] > Map.tileSize*4):
self.view[0] = Map.tileSize*4
elif (key == "down"):
self.view[1] = self.view[1]+self.viewChangeSpeed
Map.recenterView()
if (self.view[1] > Map.tileSize*4):
self.view[1] = Map.tileSize*4
elif (key == "left"):
self.view[0] = self.view[0]-self.viewChangeSpeed
Map.recenterView()
if (self.view[0] < 0):
self.view[0] = 0
def endMoveView(self,key):
for i in range(len(self.viewShift)-1,-1,-1 ):
if (self.viewShift[i][1] == key):
enterFrameTable.remove(self.viewShift[i][0])
del self.viewShift[i]
class ImageCreation():
def __init__(self,name,image,type,hitBox):
self.name = name
self.image = image
self.type = type
self.hitBox = hitBox
self.rect = self.image.get_rect()
if (self.hitBox != "none"):
self.shiftX = hitBox[0][0]
self.shiftY = hitBox[0][1]
for i in range(1,len(hitBox) ):
if (hitBox[i][0] < self.shiftX):
self.shiftX = hitBox[i][0]
if (hitBox[i][1] < self.shiftY):
self.shiftY = hitBox[i][1]
else:
self.shiftX = self.rect[2]/2
self.shiftY = self.rect[3]/2
imageTable.append(self)
def draw(self,x,y):
image = self.image
self.blit = gameDisplay.blit(image,(x,y) )
class MapCreation():
def __init__(self):
self.tileSize = 800
self.size = self.tileSize*10
self.tiles = []
self.loadedTiles = []
self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32)
self.centerTile = [5,5]
self.drawPoint = [(_W-self.tileSize)/2,(_H-self.tileSize)/2]
self.amount = round(self.size/self.tileSize)
backGround = loadScale("Grass.png","size",self.tileSize,self.tileSize)
for i in range(0,self.amount):
for u in range(0,self.amount):
image = copy.copy(backGround)
newTile = Tile(image,[u*self.tileSize,i*self.tileSize])
self.tiles.append(newTile)
info = imageFind("House.png")
tile = self.tiles[55]
imageObject(info,tile,[240,50])
self.loadTiles("center")
def recenterView(self):
if (Player.view[0] > 3*self.tileSize):
self.centerTile[0] = self.centerTile[0]+1
Player.view[0] = Player.view[0]-self.tileSize
self.loadTiles("right")
print("right")
elif (Player.view[0] < 1*self.tileSize):
self.centerTile[0] = self.centerTile[0]-1
Player.view[0] = Player.view[0]+self.tileSize
self.loadTiles("center")
print("center")
if (Player.view[1] > 3*self.tileSize):
self.centerTile[1] = self.centerTile[1]+1
Player.view[1] = Player.view[1]-self.tileSize
self.loadTiles("center")
print("center")
elif (Player.view[1] < 1*self.tileSize):
self.centerTile[1] = self.centerTile[1]-1
Player.view[1] = Player.view[1]+self.tileSize
self.loadTiles("center")
print("center")
def loadTiles(self,load):
tileIndex = self.centerTile[0]+self.centerTile[1]*self.amount
if (load == "center"):
self.loadedTiles = []
for i in range(-2,3):
for u in range(-2,3):
loadTile = tileIndex + i*self.amount + u
self.loadedTiles.append(self.tiles[loadTile])
self.tiles[loadTile].loaded = [u+2,i+2]
self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32)
for i in range(0,len(self.loadedTiles) ):
sx = self.loadedTiles[i].loaded[0]*self.tileSize
sy = self.loadedTiles[i].loaded[1]*self.tileSize
self.surface.blit(self.loadedTiles[i].buttomLayer,(sx,sy) )
for i in range(0,len(self.loadedTiles) ):
sx = self.loadedTiles[i].loaded[0]*(self.tileSize+2)
sy = self.loadedTiles[i].loaded[1]*(self.tileSize+2)
self.surface.blit(self.loadedTiles[i].middleLayer,(sx,sy) )
for i in range(0,len(self.loadedTiles) ):
sx = self.loadedTiles[i].loaded[0]*(self.tileSize+2)
sy = self.loadedTiles[i].loaded[1]*(self.tileSize+2)
self.surface.blit(self.loadedTiles[i].topLayer,(sx,sy) )
elif (load == "right"):
self.loadedTiles = []
for i in range(-2,3):
for u in range(-2,3):
loadTile = tileIndex + i*self.amount + u
self.loadedTiles.append(self.tiles[loadTile])
self.tiles[loadTile].loaded = [u+2,i+2]
## OLD METHOD THAT WASNT WORKING ##
##subSurf = self.surface.subsurface(self.tileSize,0,self.tileSize*1,self.tileSize*5)
##self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32)
##self.surface.blit(subSurf,(0,0) )
## NEW METHOD ##
self.surface.scroll(dx=-self.tileSize*1,dy=0)
def draw(self):
global Player
image = self.surface.subsurface(Player.view[0],Player.view[1],self.tileSize,self.tileSize)
image = pygame.transform.scale(image,(self.tileSize,self.tileSize) )
gameDisplay.blit(image,((_W-self.tileSize)/2,(_H-self.tileSize)/2) )
image = pygame.transform.scale(self.surface,(300,300) )
gameDisplay.blit(image,(0,0 ) )
class Tile():
def __init__(self,image,coords):
transparentSurface = pygame.Surface([1000,1000], pygame.SRCALPHA, 32)
self.x = coords[0]
self.y = coords[1]
self.loaded = False
self.buttomLayer = image
self.middleLayer = copy.copy(transparentSurface)
self.topLayer = transparentSurface
class imageObject():
def __init__(self,info,tile,coords):
self.info = info
self.image = info.image
self.rect = self.image.get_rect()
self.x = coords[0]
self.y = coords[1]
self.hitBox = []
if (self.info.hitBox != "none"):
for i in range(0,len(self.info.hitBox) ):
self.hitBox.append([self.info.hitBox[i][0]+self.x,self.info.hitBox[i][1]+self.y])
#self.object = createObject(self.hitBox,True,"none")
if (info.type == "background"):
tile.buttomLayer.blit(self.image,(self.x,self.y) )
if (info.type == "object"):
tile.middleLayer.blit(self.image,(self.x,self.y) )
if (info.type == "object alphas"):
tile.topLayer.blit(self.image,(self.x,self.y) )
def imageFind(name):
for i in range(0,len(imageTable) ):
if (name == imageTable[i].name):
return(imageTable[i])
return("none")
def imageLoad(types):
if (types == "basic"):
image = loadScale("House.png","ratio",1,1)
basicHouse = ImageCreation("House.png",image,"object",[[190, 375], [350, 375], [350, 235], [190, 235]])
image = loadScale("Grass.png","ratio",1,1)
grass = ImageCreation("Grass.png",image,"background","none")
def enterFrameHandle(enterFrameTable):
for i in range(len(enterFrameTable)-1,-1,-1 ):
enterFrameTable[i].step(enterFrameTable)
def EventHandle(event):
global Player
if (event.type == pygame.QUIT):
endGame()
elif(event.type == pygame.KEYDOWN):
key = (pygame.key.name(event.key) )
if (key == "escape"):
endGame()
elif (key == "up" or key == "right" or key == "down" or key == "left"):
Player.moveView(key)
elif(event.type == pygame.KEYUP):
key = (pygame.key.name(event.key) )
if (key == "up" or key == "right" or key == "down" or key == "left"):
Player.endMoveView(key)
def endGame():
global QuitGame
QuitGame = True
def mainLoop():
## GLOBALS ##
global QuitGame
QuitGame = False
global Player
Player = PlayerCreation()
## MAIN TABLES ##
global enterFrameTable
enterFrameTable = []
global basicObjectTable
basicObjectTable = []
## TEMP TABLES ##
global imageTable
imageTable = []
## START UP LOOPS ##
imageLoad("basic")
global Map
Map = MapCreation()
## Temporary ##
while (QuitGame == False):
enterFrameHandle(enterFrameTable)
for event in pygame.event.get():
EventHandle(event)
Map.draw()
pygame.display.update() ## updates the screen ##
gameDisplay.fill([0,0,0]) ## Clears screen for next frame ##
gameClock.tick(64) ## The FPS ##
fps = gameClock.get_fps()
if (fps < 64 and fps != 0):
fps = gameClock.get_fps()
print("FPS HAS DROPPED TOO LOW DOWN TO",fps)
runGameNow = True
mainLoop()
pygame.quit()
quit()
Issue with path finder is due to picking up on itteration lines when finding nearest point.
The problem is quite straight forward really.
Your view the amount of lines as disease and the cause must be Pygame. Usually, or at least in almost any case - if you're not a god at using the language and library, the problem is probably not the language or the library. Because odds are you are no wear near to pushing the limits of what the two can deliver for you.
One dead give-away of this fact is, if you look at what your eyes see on screen, you'll quickly notice that whenever your background is about to loop around - that's when the glitch/frame drop occur.
Obviously, this COULD be a cause of the library doing something suspicious.. if it was the library that did the actual loop-around.
But in your code, this is a implementation you've done yourself.
So the best bet is to start looking there.
Just to be extremely confident in where the delay occurs - we can have a look at the profiler that comes with Python (cProfiler).
python -m cProfile -o sample_data.pyprof awesome_game.py
pyprof2calltree -i sample_data.pyprof -k
The result would show something along the lines of:
From this we can see that a lot of processing time goes to pygame.Surface and enterFrameHandle. And I bet that pygame.Surface is called within enterFrameHandle somewhere down the line.
So the best bet is to start at the end of the eventFrameHandle chain.
And in this breakdown, that's loadTiles.
And straight off the bat, a lot of warning signs in here.
There's at least four loops in here.. and loops are bad, because those take processing time.
I added debug information by simply doing:
def loadTiles
if load == "center":
print('Loading center')
elif load == "right":
print('Loading right')
Apparently center gets triggered whenever the glitch occurs.
So that narrows it down a little further. So i added timers around each for loop.
Loop one: takes 0.0 seconds
Loop two: takes 0.29 seconds
Loop three: takes 0.5 seconds
Loop four: takes 0.6 seconds
All in all, these are EXTREMELY bad for you, since they directly impact the render sequence. So why is this? Lets break it down even further.
We'll start off with the first loop that takes up 0.3 seconds:
for i in range(0,len(self.loadedTiles) ):
sx = self.loadedTiles[i].loaded[0]*self.tileSize
sy = self.loadedTiles[i].loaded[1]*self.tileSize
self.surface.blit(self.loadedTiles[i].buttomLayer,(sx,sy) )
So len(self.loadedTiles) will be 25.
That means this loop has to iterate 25 times per render cycle where loadTiles("center") is called.
Each cycle takes almost exactly 0.01 seconds which amounts up to 0.25 seconds per full loop. It's not terrible, well it is.. If you want 60 FPS out of your game, no loop or function call can take more than 0.0166 seconds in total.
So we've already overrun our desired FPS target. So we gotta knock a few milliseconds off this if we wanna get anywhere.
The loop in itself is not that bad, i mean 25 iterations, a modern PC can do that in less time than time() can measure. So, it all points to self.surface.blit().. Which from experience, it sure is.
This also correlate with the fact that our graph above spends 30% of the total CPU time in pygame.surface.blit.. So here's our first thief.
Looking at the rest of the loops, they are essentially the same, except with bigger numbers in the math which takes slight longer to calculate, hence probably the time difference in the loops.
So, what can we do to shrink down .blit times?
Well we can stop calling blit in every loop iteration, and just move the sprite objects positions and then blit them one by one.
But sensei, that's almost the same thing?
Well yes my soon to be ninja turtle, it is.. That's why, we'll move the sprites in to batches/groups, and render the group. Thus, we can change the positions (fast operation), and because we moved the rendering outside of the loops, we can also convert them into a group object and render the group.
First, we'll convert your objects into pygame.sprite.Sprite, these are clever little objects that contain both collision detection, moving arounds etc.
self.grass = pygame.sprite.Sprite()
self.grass.image = pygame.image.load("Grass.png")
self.grass.rect = pygame.Rect(0, 0, 60, 60)
And you add this into a rendering group (a batch):
self.main_group = pygame.sprite.Group(self.grass)
Bam, and instead of updating 25 sprites, now you can do:
self.main_group.draw(gameDisplay)
And bam, SPEED!
Now, your code is NOT designed for this.
So this will take a while to fix and correct this design flaw.
Because this would take hours for me in order to keep your original code as close as possible, i ignored this and reworked your entire MapCreation and modified your PlayerCreation and how you move the map around (I changed from trying to recenter each time, it's a nice idea but far to complex to implement 1PM rewriting someone else's code).. So.. Here's the new approach to your problem:
import pygame, os
from time import time
from collections import OrderedDict
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()
_W,_H = pygame.display.Info().current_w, pygame.display.Info().current_h
flags = pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE
gameDisplay = pygame.display.set_mode((500, 500))
gameDisplay.fill((0,0,0)) ## FILL COLOR OF SCREEN ##
pygame.display.set_caption("Dope Game") ## SETS NAME ##
gameClock = pygame.time.Clock() ## CLOCK OF THE GAME ##
import math
import os
import random
import copy
SQRT = math.sqrt
PI = math.pi
cos = math.cos
sin = math.sin
## REPEATABLE FUNCTIONS ##
def loadScale(file,command,sizeX,sizeY):
temp = pygame.image.load(file)
tempInfo = temp.get_rect()
tempInfo1,tempInfo2,tempInfo3,tempInfo4 = temp.get_rect()
tempInfo3 = int(tempInfo3)
tempInfo4 = int(tempInfo4)
if (command == "ratio"):
tempInfo3 = tempInfo3*sizeX
tempInfo4 = tempInfo4*sizeY
temp = pygame.transform.scale(temp,(int(tempInfo3),int(tempInfo4) ) )
elif (command == "size"):
temp = pygame.transform.scale(temp, (sizeX,sizeY) )
return(temp)
## NON GAME RELATED CLASSES ##
class EnterFrame():
def __init__(self,frameReset,function,parse,reset):
self.frameReset = frameReset
self.currentFrame = frameReset
self.function = function
self.parse = parse
self.reset = reset
if (self.reset != "onComplete"):
self.reset = (reset-1)
enterFrameTable.append(self)
def step(self,enterFrameTable):
if (self.currentFrame == 0):
self.function(self.parse)
if (self.reset != "onComplete"):
if (self.reset > 0):
self.currentFrame = self.frameReset
self.reset = self.reset-1
else:
enterFrameTable.remove(self)
del self
else:
self.currentFrame = self.frameReset
else:
self.currentFrame = self.currentFrame-1
class PlayerCreation():
def __init__(self):
self.x = _W
self.y = _H
self.view = [1600,1600]
self.viewShift = []
self.viewChangeSpeed = 25
def moveView(self,key):
add = EnterFrame(0,self.moveViewAction,key,"onComplete")
self.viewShift.append([add,key])
def moveViewAction(self,key):
if (key == "up"):
self.view[1] = self.view[1]-self.viewChangeSpeed
Map.move_tile(0, 1) # Player moves up, so the tiles should move down -> (0, 1) == (x, y)
if (self.view[1] < 0):
self.view[1] = 0
elif (key == "right"):
self.view[0] = self.view[0]+self.viewChangeSpeed
Map.move_tile(-1, 0)
if (self.view[0] > Map.tileSize*4):
self.view[0] = Map.tileSize*4
elif (key == "down"):
self.view[1] = self.view[1]+self.viewChangeSpeed
Map.move_tile(0, -1)
if (self.view[1] > Map.tileSize*4):
self.view[1] = Map.tileSize*4
elif (key == "left"):
self.view[0] = self.view[0]-self.viewChangeSpeed
Map.move_tile(1, 0)
if (self.view[0] < 0):
self.view[0] = 0
def endMoveView(self,key):
for i in range(len(self.viewShift)-1,-1,-1 ):
if (self.viewShift[i][1] == key):
enterFrameTable.remove(self.viewShift[i][0])
del self.viewShift[i]
class ImageCreation():
def __init__(self,name,image,type,hitBox):
self.name = name
self.image = image
self.type = type
self.hitBox = hitBox
self.rect = self.image.get_rect()
if (self.hitBox != "none"):
self.shiftX = hitBox[0][0]
self.shiftY = hitBox[0][1]
for i in range(1,len(hitBox) ):
if (hitBox[i][0] < self.shiftX):
self.shiftX = hitBox[i][0]
if (hitBox[i][1] < self.shiftY):
self.shiftY = hitBox[i][1]
else:
self.shiftX = self.rect[2]/2
self.shiftY = self.rect[3]/2
imageTable.append(self)
def draw(self,x,y):
image = self.image
self.blit = gameDisplay.blit(image,(x,y) )
class MapCreation():
def __init__(self):
self.tileSize = 800
self.size = self.tileSize*10
self.tiles = []
self.centerTile = [5,5]
self.amount = round(self.size/self.tileSize)
self.sprites = OrderedDict()
self.grass_image = pygame.image.load("Grass.png")
self.grass_group = pygame.sprite.Group()
for x in range(0, self.amount*60, 60): ## 10*60, but we step 60 pixels, so in the end, this will be 10 steps.
for y in range(0, self.amount*60, 60): ## Which is the same as `for x in range(self.amount)` but we scale it up
## to give us pixels instead of just the ammount.
index = len(self.sprites) # -- Generate a index for this sprite. Used for access later (to update pos for instace)
## == Create the sprite, add a image to it and define a position and size.
self.sprites[index] = pygame.sprite.Sprite()
self.sprites[index].image = self.grass_image
self.sprites[index].rect = pygame.Rect(x, y, 60, 60)
## == Then add the sprite to the grass group.
self.grass_group.add(self.sprites[index])
def move_tile(self, dx, dy):
for index in self.sprites:
x, y, width, height = self.sprites[index].rect
## == this is how you move the sprites:d
self.sprites[index].rect = pygame.Rect(x+dx, y+dy, 60, 60)
def draw(self):
self.grass_group.update()
self.grass_group.draw(gameDisplay)
class Tile():
def __init__(self,image,coords):
transparentSurface = pygame.Surface([1000,1000], pygame.SRCALPHA, 32)
self.x = coords[0]
self.y = coords[1]
self.loaded = False
self.buttomLayer = image
self.middleLayer = copy.copy(transparentSurface)
self.topLayer = transparentSurface
class imageObject():
def __init__(self,info,tile,coords):
self.info = info
self.image = info.image
self.rect = self.image.get_rect()
self.x = coords[0]
self.y = coords[1]
self.hitBox = []
if (self.info.hitBox != "none"):
for i in range(0,len(self.info.hitBox) ):
self.hitBox.append([self.info.hitBox[i][0]+self.x,self.info.hitBox[i][1]+self.y])
#self.object = createObject(self.hitBox,True,"none")
if (info.type == "background"):
tile.buttomLayer.blit(self.image,(self.x,self.y) )
if (info.type == "object"):
tile.middleLayer.blit(self.image,(self.x,self.y) )
if (info.type == "object alphas"):
tile.topLayer.blit(self.image,(self.x,self.y) )
def imageFind(name):
for i in range(0,len(imageTable) ):
if (name == imageTable[i].name):
return(imageTable[i])
return("none")
def imageLoad(types):
if (types == "basic"):
image = loadScale("House.png","ratio",1,1)
basicHouse = ImageCreation("House.png",image,"object",[[190, 375], [350, 375], [350, 235], [190, 235]])
image = loadScale("Grass.png","ratio",1,1)
grass = ImageCreation("Grass.png",image,"background","none")
def enterFrameHandle(enterFrameTable):
for i in range(len(enterFrameTable)-1,-1,-1 ):
enterFrameTable[i].step(enterFrameTable)
def EventHandle(event):
global Player
if (event.type == pygame.QUIT):
endGame()
elif(event.type == pygame.KEYDOWN):
key = (pygame.key.name(event.key) )
if (key == "escape"):
endGame()
elif (key == "up" or key == "right" or key == "down" or key == "left"):
Player.moveView(key)
elif(event.type == pygame.KEYUP):
key = (pygame.key.name(event.key) )
if (key == "up" or key == "right" or key == "down" or key == "left"):
Player.endMoveView(key)
def endGame():
global QuitGame
QuitGame = True
def mainLoop():
## GLOBALS ##
global QuitGame
QuitGame = False
global Player
Player = PlayerCreation()
## MAIN TABLES ##
global enterFrameTable
enterFrameTable = []
global basicObjectTable
basicObjectTable = []
## TEMP TABLES ##
global imageTable
imageTable = []
## START UP LOOPS ##
imageLoad("basic")
global Map
Map = MapCreation()
## Temporary ##
while (QuitGame == False):
enterFrameHandle(enterFrameTable)
for event in pygame.event.get():
EventHandle(event)
pygame.display.update() ## updates the screen ##
gameDisplay.fill([0,0,0]) ## Clears screen for next frame ##
#Map.draw()
Map.grass_group.update()
Map.grass_group.draw(gameDisplay)
gameClock.tick(64) ## The FPS ##
fps = gameClock.get_fps()
if (fps < 64 and fps != 0):
fps = gameClock.get_fps()
print("FPS HAS DROPPED TOO LOW DOWN TO",fps)
pygame.display.flip()
runGameNow = True
mainLoop()
pygame.quit()
quit()
This code rarely drop down in FPS.
And when it does, it drops down to ~63 FPS because I moved the window around (that's a taxing thing to do because all the OpenGL references need to compensate for the new window position and events that triggers within Pygame (resize events, move events etc are taxing, but occurs once and not every render loop.. so it's acceptable).
Conclusion
It's rarely the library's fault for slow performance.
Stop thinking as a developer for a second, and watch the screen and see if you can find clues to as where the fault may lie. In this case a obvious glitch occurred every time the tiles rotated around the screen edge.. That's a clue! Not a single programming skill needed to visually see this bug.
Use a profiler and debug where your code spends most of the time.
Try to see how others have solved game designs.. You're on the right track by putting everything in classes, but that mostly helps your readability of the code - try to see what's in other peoples classes and how they use Pygame calls.
All in all, it's quite a fun and nice code you got going.
It's just not made for performance and it's hard to code in afterwards.. so maybe it's not such a bad idea to start all over? But keep your old code base for reference and avoid the obvious traps:
loops in the render sequence
rendering objects one by one, use batches instead
Only do loops to update positions/stats, not graphics!
Last note on the topic: Graphic cards are huge power houses. They can calculate a few million operations a second if not more. And each time you blit something to the screen, the graphics card needs to interrupt it's calculations and memory allocation to flip around buffers and update what you see on the screen.. And that's slow work because it's not calculations, it's operational tasks (which usually includes waiting for queues and signals).. So instead of throwing hundreds of blit's a second to the graphics card, throw math operations at it by sending it textures (math data describing how things look).. and then call blit (draw, in this case) once and let the graphics card take all the math you threw at it - do one big logical operation - and presto.. x100 the speed already.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am trying to learn the pygame module and I keep getting 'TypeError: argument 1 must be pygame.Surface, not str' when running it after reorganizing my code and rearranging it into classes, the code below shows the class if it is all that is needed, I have also included the full code if the first bit of code doesn't work.
class player(object):
def __init__(self,x,y,width,height,jH):
self.x= x # x coordinate of the character
self.y = y # y coordinate of the character
self.w = width # width of the character
self.h = height # Height of the character
self.v = 20 # Speed of the character
self.jump = False # Will determine whether the character is jumping
self.jumpC = jH # Jump variable
self.direction = "left"
self.isWalking = 0
def redraw(self,wind):
global number
global previousnumber
global selectedimage
if previousnumber == number:
selectedimage = walkUp[0]
previousnumber = number
window.blit(selectedimage,(self.x,self.y))
Above is the class, the full code is below if it is needed.
import pygame # importing module
pygame.init() # initiates pygame
window = pygame.display.set_mode((500,500)) # creates a 500x500px window
pygame.display.set_caption("PYGAME TEST") # sets caption, self explanatory
class player(object):
def __init__(self,x,y,width,height,jH):
self.x= x # x coordinate of the character
self.y = y # y coordinate of the character
self.w = width # width of the character
self.h = height # Height of the character
self.v = 20 # Speed of the character
self.jump = False # Will determine whether the character is jumping
self.jumpC = jH # Jump variable
self.direction = "left"
self.isWalking = 0
def redraw(self,wind):
global number
global previousnumber
global selectedimage
if previousnumber == number:
selectedimage = walkUp[0]
previousnumber = number
window.blit(selectedimage,(self.x,self.y))
run = True # variable used in a loop, its true if the program is still running.
walkRight = [pygame.image.load('right1.png'), pygame.image.load('right2.png'), pygame.image.load('right3.png')]
walkLeft = [pygame.image.load('left1.png'), pygame.image.load('left2.png'), pygame.image.load('left3.png')]
walkUp = [pygame.image.load('front1.png').convert(), pygame.image.load('front2.png'), pygame.image.load('front3.png')]
walkDown = [pygame.image.load('back1.png'), pygame.image.load('back2.png'), pygame.image.load('back3.png')]
bg = pygame.image.load('bg.jpg')
clock = pygame.time.Clock()
number = 0
selectedimage = walkUp[number]
previousnumber = -2
def spriteupdate(d,a,man):
global number
if man.direction != d:
man.direction = d
changed(0,a,man)
else:
number+= 1
if number>2:
number = 0
changed(number,a)
else:
changed(number,a,troop)
def changed(numb, direction,man):
global number
global selectedimage
number = numb
selectedimage = man.direction[number]
def update(man):
troop.redraw(window)
window.blit(bg,(0,0))
pygame.display.update()
troop = player(250,250,10,10,10)
while run:
clock.tick(15)# FPS
for event in pygame.event.get():
if event.type == pygame.QUIT: # if event quits, presses the x button
run = False # breaks loop
keys = pygame.key.get_pressed() # gets the keys pressed
if keys[pygame.K_LEFT]:# pressed left arrow, x coordinate changes by - speed
#direction, array
spriteupdate("left",walkLeft,troop)
if troop.x <= -10:
pass
else:
troop.x-=troop.v
if keys[pygame.K_RIGHT]: # similar here
spriteupdate("right", walkRight,troop)
if troop.x >= 470:
pass
else:
troop.x+=troop.v
if not(troop.jump): # Only works if the character isn't jumping
if keys[pygame.K_SPACE]: # The character is jumping. changes variable.
troop.jump = True
if keys[pygame.K_UP]: # same here
spriteupdate("down",walkDown,troop)
if troop.y <= -10:
pass
else:
troop.y-=troop.v
if keys[pygame.K_DOWN]: # same here
spriteupdate("up",walkUp,troop)
if troop.y >= 470:
pass
else:
troop.y+=troop.v
else:
if troop.jumpC >= -10: # jumpC is already bigger than -10, this is called
num = 1 # number, will be set to -1 if jumpC becomes a negative number
if troop.jumpC < 0:
num = -1 # there.
troop.y-= (troop.jumpC ** 2) * 0.5 * num # y subtracted (goes up) by the square of jumpC * half * num
troop.jumpC -= 1 # Subtracts 1
else:
troop.jump = False # If Jump c is smaller than -10, jump has finished.
troop.jumpC = 10 # reset variable
update(troop)
pygame.quit()
OUTPUT:
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File line 107, in <module>
update(troop)
File line 56, in update
troop.redraw(window)
File , line 25, in redraw
window.blit(selectedimage,(self.x,self.y))
TypeError: argument 1 must be pygame.Surface, not str
This output is above, hope that helps.
On line 53 selectedimage is changed from a surface to a string with :
selectedimage = man.direction[number]
I see one problem right away. In the player.redraw method, wind is the argument passed, but in the body of the method you use window. Try changing the argument:
def redraw(self, window):
I have recently written this code where fruits and other things fall from the top of the screen and the frog at the bottom has to try and catch them. The collision detection is not working and neither is the falling images as they seem to just fall and get stuck at the top of the screen. This is most of my code as I cannot seem to work out which area the actual error is in:
import pygame, sys, time, random
from pygame.locals import *
######### constants ##########
jumpvel=20
fallingspeed=0.5
running= True
blue= [129,183 ,253]
pink=[255,174,201]
textcolour= [255,255,255]
x=700//2
y=1000//2
score=0
#### fruits and naughty ######
thingylist= ['fruit1.bmp','fruit2.bmp','fruit3.bmp','fruit4.bmp','fruit5.bmp','fruit1.bmp','fruit2.bmp','fruit3.bmp','fruit4.bmp','fruit5.bmp','naughty1.bmp','naughty2.bmp','naughty3.bmp',]
all_things=[]
for i in range (12):
new_thing_image=pygame.image.load(thingylist[(random.randrange(0,12))])
new_thing_image.set_colorkey(pink)
new_thing_rect=new_thing_image.get_rect()
new_thing_rect.x=random.randrange(0,950)
new_thing_rect.y=-random.randrange(50,500)
all_things.append([new_thing_image,new_thing_rect])
################collision###############
def checkCollision (frog_rect,all_things,score):
collides_with=None
for i in range (len(all_things)):
thing_rect=all_things[i][1]
if (frog_rect.colliderect(thing_rect)):
score=score+100
return collides_with
######## initialising screen#########
pygame.init()
gamedisplay=pygame.display.set_mode((1000,600)) #making the screen
pygame.display.set_caption('frog')
clock=pygame.time.Clock()# frames per second
bg=pygame.image.load('actual clouds.bmp').convert()
############ initialising sprites##############
frog= pygame.image.load('actual frog.bmp')
frog.set_colorkey(blue)
frog_rect=frog.get_rect()
frog_rect.centerx=(x)
frog_rect.centery=(y)
####### score###########
pygame.font.init()
font= pygame.font.SysFont ('Dubai MS', 48)
##########drawing things#############
def drawThings (all_things):
for item in all_things:
new_thing_image, new_thing_rect= item
gamedisplay.blit(new_thing_image, (new_thing_rect.x, new_thing_rect.y))
#########update display function###########
def update(x,y,all_things,score):
gamedisplay.blit(bg,[0,0])
gamedisplay.blit(frog,(x,y))
for thing in range (len(all_things)):
new_thing_rect=all_things[i][1]
#thing_rect.y=thing_rect.y+fallingspeed
new_thing_rect.y+= fallingspeed
drawThings(all_things)
label=font.render("score "+ str(score) ,1,textcolour)
gamedisplay.blit(label,(750,10))
pygame.display.update()
pygame.time.delay(50)
#########main game loop ############
while running == True:
gamedisplay.blit(bg,[0,0])
gamedisplay.blit(frog,(x,y))
drawThings(all_things)
label=font.render("score "+ str(score) ,1,textcolour)
gamedisplay.blit(label,(750,10))
pygame.display.flip()
pygame.event.pump()
key=pygame.key.get_pressed()
########### escape ###########
if key [pygame.K_ESCAPE]:
sys.exit()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
########### controls ##############
if key[pygame.K_LEFT]:
x -=2
elif key[pygame.K_RIGHT]:
x +=2
elif key[pygame.K_SPACE]or key[pygame.K_UP]:
for i in range (5):
y -= jumpvel
update(x,y,all_things,score)
for i in range (5):
y += jumpvel
update(x,y,all_things,score)
######## limits ####################
if x < 10:
x = 10
elif (x > (900 - 2)):
x= 900-2
######### falling###########
for item in all_things:
new_thing_image, new_thing_rect= item
#new_thing_rect=all_things[i][1]
#thing_rect.y=thing_rect.y+fallingspeed
new_thing_rect.y+= fallingspeed
############collision detection##########
detect=checkCollision (frog_rect, all_things,score)
if (detect !=None):
score=score+100
update(x,y,all_things,score)
The 'things' are meant to fall to the bottom of the screen for the frog to catch but they are all seemingly getting stuck at the top. When i tested the code for collision detection even when the two images collided it had no effect on the score- meaning something is not working.
Falling images stop because of this
fallingspeed = 0.5
rect uses integer values to keep positon so it will rount 0.5 to integer - int(0.5) == 0.
When you have y = 0 and you add 0.5 - so you could expect y = 0.5 - it will round it to y = 0. In next loop you will add again 0.5 and it will round it to y = 0 again. This way it stops on y = 0
When you have ie y = -5 and you add 0.5 then you could expect -4.5 but it round to -4 (not -5) so it is moving.
Use
fallingspeed = 1
and it will not stop on y = 0
old_y = 0
new_y = int(old_y+0.5)
print(old_y, new_y)
# 0 0
old_y = -5
new_y = int(old_y+0.5)
print(old_y, new_y)
# -5 -4
Sometimes in more complex code it is important to keep position in float values and then you would have to use float variables instead of rect to keep positon and copy it to rect only when you have to draw or check collision
collisions
Inside checkCollision you set collides_with = None but you never change it to `True
def checkCollision (frog_rect,all_things, score):
collides_with = None
for i in range (len(all_things)):
thing_rect = all_things[i][1]
if (frog_rect.colliderect(thing_rect)):
score=score+100
collides_with = True
return collides_with
You could write it shorter
def checkCollision(frog_rect,all_things, score):
collides_with = None
for thing_image, thing_rect in all_things:
if frog_rect.colliderect(thing_rect):
score = score+100
collides_with = True
return collides_with
Now it should change score in line
detect = checkCollision(frog_rect, all_things, score)
if detect:
score = score+100
But if you want to change it inside checkCollision then you have to return value score. Variable score inside checkCollision is local variable and it doesn't change value in global variable score
def checkCollision(frog_rect,all_things, score):
collides_with = None
for thing_image, thing_rect in all_things:
if frog_rect.colliderect(thing_rect):
score = score+100
collides_with = True
return collides_with, score
and then you have to assing to global score
detect, score = checkCollision(frog_rect, all_things, score)
So, my snake makes a continuous movement, but if I press any key it goes back in time and lags back and forward. Here is a video: https://youtu.be/KCesu5bGiS8
My guess would be to update the key input faster, but when I do that everything updates faster, so the snake goes faster etc.
Code (as requested in text form) here:
import pyglet
import random
pyglet.resource.path = ["resources"]
pyglet.resource.reindex()
# sets the resource path
class Snake_Window(pyglet.window.Window):
a = 0
dtx = 160
dty = 200
# sets the basic direction and snake body x and y
def __init__(self):
super(Snake_Window, self).__init__(width=1280, height=720)
# sets resolution and inherits
self.key_handler = pyglet.window.key.KeyStateHandler()
self.push_handlers(self.key_handler)
# sets keys
self.set_caption("Wild Snake")
# gives it a name
self.background_image = pyglet.resource.image("background.png")
self.food_image = pyglet.resource.image("food.png")
self.snake_head_image = pyglet.resource.image("snake_head.png")
self.snake_body_image = pyglet.resource.image("snake_body.png")
# makes images usable
self.center_image(self.food_image)
self.center_image(self.snake_head_image)
self.center_image(self.snake_body_image)
# centers the images using center_image
self.snake_head = pyglet.sprite.Sprite(img=self.snake_head_image, x=200, y=200)
self.snake_head.scale = 0.1
self.snake_head.rotation = 270
# sets snake_head as a image on screen
self.snake_body = pyglet.sprite.Sprite(img=self.snake_body_image, x=self.dtx, y=self.dty)
self.snake_body.scale = 0.1
self.snake_body.rotation = 90
# sets snake_body as a image on screen
self.background = pyglet.sprite.Sprite(img=self.background_image, x=0, y=0)
# sets background as a image on screen
self.food = []
# sets food
pyglet.clock.schedule_interval(self.game_tick, 0.1)
def center_image(self, image):
# sets the center of the image to the actual center
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
def update_snake_head(self):
# makes the snake head go and sets the x and y for the body
if self.a == 0:
self.snake_head.x += 40
self.dtx = self.snake_head.x - 40
self.dty = self.snake_head.y
elif self.a == 1:
self.snake_head.x -= 40
self.dtx = self.snake_head.x + 40
self.dty = self.snake_head.y
elif self.a == 2:
self.snake_head.y += 40
self.dty = self.snake_head.y - 40
self.dtx = self.snake_head.x
elif self.a == 3:
self.snake_head.y -= 40
self.dty = self.snake_head.y + 40
self.dtx = self.snake_head.x
def update_snake_body(self, dtx, dty):
# makes the snakes body go
self.snake_body.x = dtx
self.snake_body.y = dty
def game_tick(self, dt):
# updates snakes head, snakes body, key presses and sets the background
self.update_snake_head()
self.update_snake_body(self.dtx, self.dty)
self.draw_elements()
self.key_press()
print(dt)
def draw_elements(self):
# draws everything in window
self.clear()
self.background.draw()
self.snake_head.draw()
self.snake_body.draw()
def key_press(self):
# sets direction of snake upon key press and rotates his head accordingly
if self.key_handler[pyglet.window.key.RIGHT]:
if self.a == 1:
pass
else:
self.a = 0
self.snake_head.rotation = 270
elif self.key_handler[pyglet.window.key.LEFT]:
if self.a == 0:
pass
else:
self.a = 1
self.snake_head.rotation = 90
elif self.key_handler[pyglet.window.key.UP]:
if self.a == 3:
pass
else:
self.a = 2
self.snake_head.rotation = 180
elif self.key_handler[pyglet.window.key.DOWN]:
if self.a == 2:
pass
else:
self.a = 3
self.snake_head.rotation = 0
game_window = Snake_Window()
pyglet.app.run()
Converting all my comments into a answer instead. It won't solve your problem completely. But due to lack of time, I'll leave something useful at least that almost solves it.
The reason for these are a couple. One of them is that you use a scheduler to render stuff instead of using the built-in on_draw event. Can't say for sure, but a good guess is that the graphical buffer gets updated/flipped automatically in on_draw while you're doing your drawing and stuff in a side-chained render function. So moving all the rendering stuff into on_draw makes sense.
Another issue is that you don't trigger on the actual key press, but instead you need to time the key press to each tick in the scheduler you got going - which you also have mushed into the rendering function. Essentially you're doing eventhandling+rendering+updating+IO in one cluster*** of a function, heh. Instead, you should rely on on_key_press for keyboard events.
Lastly, you're doing math operations all over the place - any of which might be half way done when you're doing the actual rendering. That's why you might get ghosting or odd artifacts (some things aren't completely done updating positions etc).
But here is a almost working example of a few steps taken to get closer to what you want. If no one else (including you) haven't solved this by a few days I'll go ahead and re-write most of your code and point you in a few good directions (batches being one of them).
import pyglet
import random
pyglet.resource.path = ["resources"]
pyglet.resource.reindex()
# sets the resource path
class Snake_Window(pyglet.window.Window):
a = 0
dtx = 160
dty = 200
# sets the basic direction and snake body x and y
def __init__(self):
super(Snake_Window, self).__init__(width=1280, height=720)
# sets resolution and inherits
self.key_handler = pyglet.window.key.KeyStateHandler()
self.push_handlers(self.key_handler)
# sets keys
self.set_caption("Wild Snake")
# gives it a name
self.background_image = pyglet.resource.image("background.png")
self.food_image = pyglet.resource.image("food.png")
self.snake_head_image = pyglet.resource.image("snake_head.png")
self.snake_body_image = pyglet.resource.image("snake_body.png")
# makes images usable
self.center_image(self.food_image)
self.center_image(self.snake_head_image)
self.center_image(self.snake_body_image)
# centers the images using center_image
self.snake_head = pyglet.sprite.Sprite(img=self.snake_head_image, x=200, y=200)
self.snake_head.rotation = 270
# sets snake_head as a image on screen
self.snake_body = pyglet.sprite.Sprite(img=self.snake_body_image, x=self.dtx, y=self.dty)
self.snake_body.scale = 0.1
self.snake_body.rotation = 90
# sets snake_body as a image on screen
self.background = pyglet.sprite.Sprite(img=self.background_image, x=0, y=0)
# sets background as a image on screen
self.food = []
# sets food
pyglet.clock.schedule_interval(self.game_tick, 0.1)
def on_draw(self):
self.draw_elements()
def center_image(self, image):
# sets the center of the image to the actual center
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
def update_snake_head(self):
# makes the snake head go and sets the x and y for the body
if self.a == 0:
self.snake_head.x += 40
self.dtx = self.snake_head.x - 40
self.dty = self.snake_head.y
elif self.a == 1:
self.snake_head.x -= 40
self.dtx = self.snake_head.x + 40
self.dty = self.snake_head.y
elif self.a == 2:
self.snake_head.y += 40
self.dty = self.snake_head.y - 40
self.dtx = self.snake_head.x
elif self.a == 3:
self.snake_head.y -= 40
self.dty = self.snake_head.y + 40
self.dtx = self.snake_head.x
def update_snake_body(self, dtx, dty):
# makes the snakes body go
self.snake_body.x = dtx
self.snake_body.y = dty
def game_tick(self, dt):
# updates snakes head, snakes body, key presses and sets the background
self.update_snake_head()
self.update_snake_body(self.dtx, self.dty)
def draw_elements(self):
# draws everything in window
self.clear()
self.background.draw()
print('Head:', self.snake_head.x, self.snake_head.y, {0:'Right', 1:'Left', 2: 'Up', 3:'Down'}[self.a])
self.snake_head.draw()
self.snake_body.draw()
self.flip()
def on_key_press(self, symbol, modifier):
# sets direction of snake upon key press and rotates his head accordingly
if symbol == pyglet.window.key.ESCAPE:
pyglet.app.exit()
if symbol == pyglet.window.key.SPACE:
print('Here')
if symbol == pyglet.window.key.RIGHT:
if self.a == 1:
pass
else:
self.a = 0
self.snake_head.rotation = 270
elif symbol == pyglet.window.key.LEFT:
if self.a == 0:
pass
else:
self.a = 1
self.snake_head.rotation = 90
elif symbol == pyglet.window.key.UP:
if self.a == 3:
pass
else:
self.a = 2
self.snake_head.rotation = 180
elif symbol == pyglet.window.key.DOWN:
if self.a == 2:
pass
else:
self.a = 3
self.snake_head.rotation = 0
game_window = Snake_Window()
pyglet.app.run()
I'll leave you not only with this ish-working code, but a good advice.
Stop asking so many questions, and start learn ways to debug why things are happening the way they do. You're shooting in the dark right now, asking questions hoping someone will solve the problems for you so you can focus on the fun stuff - which is the game development itself.
But what you'll have a lot of use for later in life - is finding ways to debug, probe, understand and pinpoint why things are or aren't happening the way you want.
Put some print("moo") here and there, print some values, add logging/debugging all over the place until you get wiser. It's not always efficient, but it got me to this point with your code.
I am trying to create the A Star algorithm and apply to the game Snake. The problem I'm having is that I never get anything drawn on the window because it never reaches that code. It gets stuck when trying to calculate the path in the method run(). Specifically, open set never goes to zero and it adds already existing nodes.
I've tried printing the various lists to the console, but all i've discovered is that "current" repeats tiles at random points, and "openSet and "closedSet" continue to get larger with these repeated tiles. I've also tested my Tile object, my contains method, and my giveNeighbors method and all of these seem to be working and give the expected results. Also as a reference, I am going of the pseudo code found here: https://en.wikipedia.org/wiki/A*_search_algorithm
import Tile
import pygame
class A_star:
def __init__(self):
self.closedSet = []
self.openSet = []
def run(self, start, goal):
self.closedSet = []
self.openSet = [start]
path = []
#THE LENGTH OF OPEN SET NEVER GOES TO ZERO!
while len(self.openSet) > 0:
#Set current to node in openSet with lowest fscore
current = self.openSet[0]
currindex = 0
for i, node in enumerate(self.openSet):
if node.fScore < current.fScore:
current = node
currindex = i
#romove from Open set and add to closed
self.openSet.pop(currindex)
self.closedSet.append(current)
#Reached the end
if current.tileEquals(goal):
print("Done")
while current != None:
path.append(current)
current = current.cameFrom
return path[::-1]
neighbors = self.giveNeighbors(current)
print("Current: " + str(current.posx) + ", " + str(current.posy))
print("Neighbors")
for i in neighbors:
print(str(i.posx) + ", " + str(i.posy))
for i in neighbors:
#if neighbor is already checked, then ignore it.
if not self.contains(self.closedSet, i):
#Distance between start adn neighbor. tenative gscore
tempGScore = current.gScore + 1
#if neighbor is not in openset. Discovered a new node!
if not self.contains(self.openSet, i):
self.openSet.append(i)
elif tempGScore < i.gScore:
i.gScore = tempGScore
i.cameFrom = current
i.fScore = i.gScore + self.heuristicCost(i, goal) #f = g + h
print("Open:")
for i in self.openSet:
print(str(i.posx) + ", " + str(i.posy))
print("Closed")
for i in self.closedSet:
print(str(i.posx) + ", " + str(i.posy))
#Calculates the estimated distance from a given tile to end
def heuristicCost(self, neighbor, goal):
#The snake never goes diagonal, therefore calculate manhatten distance
distance = abs(neighbor.posx - goal.posx) + abs(neighbor.posy - goal.posy)
return distance
def giveNeighbors(self, current):
neighbors = []
if current.posx > 0:
neighbors.append(Tile(current.posx - 10, current.posy))
if current.posx < 200:
neighbors.append(Tile(current.posx + 10, current.posy))
if current.posy > 0:
neighbors.append(Tile(current.posx, current.posy - 10))
if current.posy < 200:
neighbors.append(Tile(current.posx, current.posy + 10))
return neighbors
def contains(self, s, tile):
for i in s:
if i.tileEquals(tile):
return True
else:
return False
def testAStar():
pygame.init()
window = pygame.display.set_mode((200, 200))
pygame.display.set_caption("AStar Test")
window.fill((255,255,255))
clock = pygame.time.Clock()
start = Tile(0, 0)
end = Tile(190, 190)
astar = A_star()
path = astar.run(start, end)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.draw.rect(window, (0, 255, 0), (start.posx, start.posy, 10, 10))
pygame.draw.rect(window, (255, 0, 0), (end.posx, end.posy, 10, 10))
for i in path:
pygame.draw.rect(window, (0, 0, 255), (i.posx, i.posy, 10, 10))
print("drew stuff")
pygame.display.update()
window.fill((255,255,255))
clock.tick(10)
pygame.quit()
if __name__== "__main__":
testAStar()
class Tile:
def __init__(self, x, y):
self.posx = x
self.posy = y
self.fScore = 0
self.gScore = 0
self.cameFrom = None
def nextTileRight(self):
self.posx = self.posx + 10
def nextTileDown(self):
self.posy = self.posy + 10
def nextTileUp(self):
self.posy = self.posy - 10
def nextTileLeft(self):
self.posx = self.posx - 10
def tileEquals(self, t):
if self.posx == t.posx and self.posy == t.posy:
return True
else:
return False
The expected result should be a non-diagonal path drawn on the window from the start node to the end node. (Also, sorry if my indentation is off. I am still very new to this website)
I'm not sure why you are checking openSets == 0 , you are looking for currentNode = destination or all the neighbours are closed meaning everything has been checked and no path was found.
I was trying to code a game in pygame, but then when I tried to make a walking animation it only displayed one of the sprites.
def go_left(time):
ness_current = 1
global ness
global is_walking_left
ness_list = [ness_walking,ness_standing]
current_time = pygame.time.get_ticks()
go_left.walking_steps = 1
now = 0
cooldown = 1000
flag = 0
ness = ness_list[ness_current]
print current_time - game_loop.animation_timer
if (current_time - game_loop.animation_timer) > 200:
print 'Changing'
if ness_current == 0:
print 'Changing to sprite 1'
now = pygame.time.get_ticks()
ness_current = 1
current_time = now
elif ness_current == 1:
print 'Changing to sprite 0'
if (current_time - game_loop.animation_timer) > 200:
ness_current = 0
current_time = now
else:
'Changing to sprite 0 because of sprite reset'
ness_current = 0
current_time = now
def stop_it():
global ness
ness = pygame.image.load('nessthekid.png').convert()
ness.set_colorkey(WHITE)
car_list = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
player_list = pygame.sprite.Group()
When I try to use this it only displays one of the sprites not the other for the character. I want it to swich every 1 or 2 seconds to make it look like its walking. Help is appreciated.
First, I highly suggest using a class, to avoid using global variables.
Second, as you set the current time back to now (0), you will make it so current_time - game_loop.animation_timer will always be negative. This will keep the statement from running.
For now, I suggest completely removing the "if (current_time - game_loop.animation_timer) > 200:" from your code.
Here is an example to get you started (obviously you will have to alter it to make it work for you)
class Ness:
def __init__(self):
self.all_images = [ness_walking,ness_standing]
self.current = 0
self.image = self.all_images[self.current]
def walk_left(self):
# Every 200 clicks
if pygame.time.get_ticks() % 200 == 0:
if self.current == 0:
self.current = 1
else:
self.current = 0
self.image = self.all_images[self.current]
As suggested by #Red Twoon put the stuff separated in class is a very good practice. Another thing that you should do is no rely in get_ticks directly but instead use some kind of time independent movement/animation.
You can achieve this using delta times in your game loop.
Game loop.
while(True):
delta = #Get the delta time.
handle_events();
update(delta);
draw(delta);
#Animation stuff.
time_to_animate_frame = 200;
time_since_last_update = 0;
...
time_since_last_update += delta;
if(time_since_last_update > time_to_animate_frame):
time_since_last_update - time_to_animate_frame;
#Do the animation.....