Python Forum
[PyGame] sound effect delay and program laggy
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] sound effect delay and program laggy
#1
I've been having fun making my games not silent, but I've had a small issue in that the sound effect is always slightly delayed, no matter what I do.

I've tried to use the time.delay command, but that doesn't seem to do anything to fix the issue. I'm using the .tick command to limit the framerate, but that doesn't seem to have any effect on the sound delay. I even get the sound delay on my start menu, even though it doesn't have a framerate limit.

Also, in general, I've noticed my programs tend to be really laggy. I'm running them on a high-end gaming pc, but my computer acts like its struggling to handle it. And keep in mind, this is a clone of snake, so its not exactly the most demanding on a pc. Though strangely enough, running it appears to take up a third of my RAM! And this is a pc that can run a heavily modded Skyrim at full graphics, and yet it has a hard time rendering a snake clone? I WAS thinking of putting the program on my laptop, but I've been afraid to do that since its so demanding on my main pc (I once fried a laptop running games that it couldn't handle).
#2
If i recall correctly, the sound delay was fixed via running this command before pygame.init(). I think i ran into this issue before and that solved it.
pygame.mixer.pre_init(44100, -16, 1, 512)
(Sep-26-2019, 09:36 PM)xBlackHeartx Wrote: Also, in general, I've noticed my programs tend to be really laggy.
We would have to see your full code. Most of the time people have some sort of bottleneck in your code that is dragging your program down. You can easily create a bottleneck that would drag a beefy computer to a halt in any language, not just python.

Common bottlenecks in code are:

--Do not load resources within the main game loop.
--Always load your images using .convert() or .convert_alpha()
--Reading and writing to disc is the slowest part. Either minimize this, or enhance the parts that are truly required.
--Use existing, well known 3rd party libraries instead of making your own reproductions of them. These libraries are updated and have been tweaked for maximum speed already, so why try to recreate it? One case might be NumPy usage.

If you can create a working example of the lag issue within minimized code it would be much easier to spot. But if not, we would need to pick through the code to find the bottleneck.
Recommended Tutorials:
#3
Its a 240 line program. But okay, here's the code. Keep in mind that its a snake program, and relies on three sound files. No idea how you can run this without the sound files, but I guess you could just create your own replacements if you're looking to run this.

import random
import pygame
pygame.init()

screenWidth=500
screenHeight=500

mainWindow=pygame.display.set_mode((screenWidth,screenHeight))
pygame.display.set_caption("PySnake")

x=screenWidth/2
y=screenHeight/2
snakeSize=50
move="down"
length=0
path=[]
fps=7
fpsClock=pygame.time.Clock()
win=True
loseGameSound=pygame.mixer.Sound('pysnake_lose_game.wav')
eatSound=pygame.mixer.Sound('pysnake_eat.wav')
bleep=pygame.mixer.Sound('pysnake_bleep.wav')

font=pygame.font.SysFont('arialblack',20)



def frandxy():
    global posx
    global posy
    posx=snakeSize*(random.randint(0,screenWidth/snakeSize-1))
    posy=snakeSize*(random.randint(0,screenWidth/snakeSize-1))
    if (posx,posy) in path or (posx,posy)==(x,y):
            frandxy()

def closeGame():
    pygame.quit()
    quit()

def playAgain():
    global x
    global y
    global length
    global move
    global path
    while True:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                closeGame()

        key=pygame.key.get_pressed()
        if key[pygame.K_ESCAPE]:
            bleep.play()
            closeGame()
        if key[pygame.K_y]:
            bleep.play()
            x=screenWidth/2
            y=screenHeight/2
            length=0
            move="down"
            path=[]
            game()
        if key[pygame.K_n]:
            closeGame()
        if win==False:
            mainWindow.fill((0,0,0))
        if win==True:
            mainWindow.fill((0,255,0))
        pygame.draw.rect(mainWindow,(0,255,0),(screenWidth/2-snakeSize*3,\
                                               screenHeight/2-snakeSize,\
                                               snakeSize*6,snakeSize*2))
        font=pygame.font.SysFont('arialblack',20)
        textTop=font.render('Play Again?',0,(0,0,0))
        textBottom=font.render(('(y)es/(n)o'),0,(0,0,0))

        mainWindow.blit(textTop, (screenWidth/2-textTop.get_width()/2,screenHeight/2-snakeSize))
        mainWindow.blit(textBottom, (screenWidth/2-textBottom.get_width()/2,screenHeight/2))
        pygame.display.update()

def loseGame():
    loseGameSound.play()
    while True:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                closeGame()

        key=pygame.key.get_pressed()
        if key[pygame.K_ESCAPE]:
            closeGame()
        if key[pygame.K_RETURN]:
            bleep.play()
            playAgain()
        
        mainWindow.fill((0,0,0))
        pygame.draw.rect(mainWindow,(0,255,0),(screenWidth/2-snakeSize*3,\
                                               screenHeight/2-snakeSize,\
                                               snakeSize*6,snakeSize*2))
        font=pygame.font.SysFont('arialblack',20)
        textTop=font.render('GAME OVER',0,(0,0,0))
        textBottom=font.render(('Your Length Was: '+str(length)),0,(0,0,0))

        mainWindow.blit(textTop, (screenWidth/2-textTop.get_width()/2,screenHeight/2-snakeSize))
        mainWindow.blit(textBottom, (screenWidth/2-textBottom.get_width()/2,screenHeight/2))
        pygame.display.update()

def winGame():
    while True:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                closeGame()
        key=pygame.key.get_pressed()
        if key[pygame.K_ESCAPE]:
            closeGame()
        if key[pygame.K_RETURN]:
            bleep.play()
            playAgain()
        
        mainWindow.fill((0,255,0))
        textTop=font.render('Your Snake Filled the Screen!',0,(0,0,0))
        textBottom=font.render(('Congratulations!'),0,(0,0,0))

        mainWindow.blit(textTop, (screenWidth/2-textTop.get_width()/2,screenHeight/2-snakeSize))
        mainWindow.blit(textBottom, (screenWidth/2-textBottom.get_width()/2,screenHeight/2))
        pygame.display.update()
        
def game():
    global x
    global y
    global move
    global length
    global path
    global fpsClock
    global win
    while True:
        
        
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                closeGame()

        key=pygame.key.get_pressed()
        if key[pygame.K_UP] and move != "down":
            move="up"
        if key[pygame.K_w] and move !="down":
            move="up"
        if key[pygame.K_DOWN] and move != "up":
            move="down"
        if key[pygame.K_s] and move != "up":
            move="down"
        if key[pygame.K_RIGHT] and move != "left":
            move="right"
        if key[pygame.K_d] and move != "left":
            move="right"
        if key[pygame.K_LEFT] and move != "right":
            move="left"
        if key[pygame.K_a] and move != "right":
            move="left"
        if key[pygame.K_ESCAPE]:
            closeGame()
        if key[pygame.K_e]:
            closeGame()

        path.insert(0,(x,y))
    #move snake
        if move=="down" and y<=screenHeight-snakeSize:
            y+=snakeSize
        if move=="down" and y>=screenHeight:
            y=0
        if move=="up" and y>=0:
            y-=snakeSize
        if move=="up" and y<0:
            y=screenHeight-snakeSize
        if move=="right" and x<=screenWidth-snakeSize:
            x+=snakeSize
        if move=="right" and x>=screenWidth:
            x=0
        if move=="left" and x>=0:
            x-=snakeSize
        if move=="left" and x<0:
            x=screenWidth-snakeSize

        if x==posx and y==posy:
            length+=1
            eatSound.play()
            frandxy()
        if (x,y) in path:
            win=False
            loseGame()
        if length>=(screenWidth/snakeSize)*(screenHeight/snakeSize):
            winGame()

        mainWindow.fill((0,0,0))
        #draw snake
        if length>0:
            for i in range(length):
                pygame.draw.rect(mainWindow, (0,255,0),(path[i][0],path[i][1], snakeSize,snakeSize))
        pygame.draw.rect(mainWindow, (0,255,0),(x, y, snakeSize, snakeSize))
        #draw food
        pygame.draw.rect(mainWindow, (255,0,0),(posx,posy, snakeSize, snakeSize))
        if len(path)>length:
            path=path[:(length)]
        pygame.display.update()
        fpsClock.tick(fps)

def start():

    while True:

        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                closeGame()

        key=pygame.key.get_pressed()
        if key[pygame.K_ESCAPE]:
            closeGame()
        if key[pygame.K_e]:
            closeGame()
    
        titleFont=pygame.font.SysFont('arialblack',50)
        title=titleFont.render('PySnake',0,(0,255,0))
        enterPrompt=font.render('Press Enter to Start',0,(0,255,0))

        mainWindow.blit(title,(screenWidth/2-title.get_width()/2,screenHeight/2-snakeSize*2))
        mainWindow.blit(enterPrompt,(screenWidth/2-enterPrompt.get_width()/2,screenHeight/2))

        for event in pygame.event.get():
                if event.type==pygame.QUIT:
                    closeGame()

        key=pygame.key.get_pressed()
        if key[pygame.K_RETURN]:
            bleep.play()
            game()
        if key[pygame.K_ESCAPE]:
            closeGame()

        pygame.display.update()

frandxy()
start()
#4
Damn.... i just wrote a whole post here and accidentally deleted it all before posting. I dont really want to spend another 20 minutes rewriting that again...sorry.

quick rundown:
A) There are a few bottlenecks. In each while loop you have render font that is static text unnecessarily.
B) You should avoid nested while loops to implement game states as it causes spaghetti code and camouflage bugs. Its more sane to create a state machine to organize your code.
C) Are you intending on it moving on a block by block basis or per pixel? Just saw that you shrunk FPS drastically
D) You can use github for sharing programs with resources.
E) Always set your FPS to 60. Do not reduce that to reduce speed. If you need to reduce its speed movement then you need to use time. The first example does this but you dont want every second, probably every millisecond, and "do something" is move. In your case you are going to up your FPS to 60 and slow the snake down to whatever you want. The longer delay, the slower it goes. Aside form that you can also do delta time movement.

You would also make it a lot easier on yourself and others if you used classes. For example the snake should be a class, with all its drawing in it, logic in it, data in it, collisions in it, etc.
Recommended Tutorials:
#5
I did originally use time.delay to slow down the game, but that caused input lag, which was fixed when I changed the code to instead reduce the framerate. And I needed to set the framerate that low just to keep the program from running insanely fast. I am running this on a high-end gaming pc, as I've already stated. Not having any kind of limit just made the program way too fast to play. Honestly, even that low of a framerate is a bit fast for me.

As for my code, I've been figuring out pygame on my own. I do have a degree in programming (complete with a certificate in python), but my college doesn't offer a class in pygame. Well, it didn't when I attended anyway. My old professor gave me a pdf file that he was planning on using for a game course he's developing. I've actually been looking at it to figure out how to optimize my program. That's where I got the framerate code from btw.

Sadly, I've never seen how someone else handles multiple screens; I just came up with something on my own. It does work so I never saw a point in figuring out another way to do it. And no, I'm not that experienced when it comes to making classes. When I was taking my python courses, honestly, I never really did get an understanding of them. Even now, to me they're just something you use to create custom modules.

And my professor told me once that python may honestly not be an ideal language for me. He suggested that I should use C++, c#, or java. I have certificates in c++14 and java, but I've never seen this c#. The C language also looks interesting to me, but it appears to be obsolete.

Come to think of it, I COULD condense all those while loops into one by using if-then statements. Have a variable that tracks which screen should be active (title, main, loss, win, playAgain), and then just use if-statements to have the program draw different screens depending on the state. I don't see how I could remove all the loops though. The frandxy (which sets the x,y coordinates of the 'food') has to have a do-while loop to work. Before I did that, I had a bug where the game would sometimes spawn in the food on top of the snake, which became increasingly common as the snake got longer.
#6
(Sep-27-2019, 12:08 AM)xBlackHeartx Wrote: I did originally use time.delay to slow down the game, but that caused input lag, which was fixed when I changed the code to instead reduce the framerate. And I needed to set the framerate that low just to keep the program from running insanely fast. I am running this on a high-end gaming pc, as I've already stated. Not having any kind of limit just made the program way too fast to play. Honestly, even that low of a framerate is a bit fast for me.
60 FPS on my machine is the same as 60 FPS on your machine regardless of what each specs are. That is the point to limiting it, so that yours is limited to 60, and not 200X times faster being a beefy PC. The point of putting in 60 is to limit it up to 60 FPS so that the specs of each computer does not change the rate, but are consistent between all computers. The only PC's that would have trouble are older computers that cannot keep up your game. By inserting 7 FPS you are restricting your game to a really low frame rate. You can always reduce speed, but cannot ever gain back those framerates that never exist.

(Sep-27-2019, 12:08 AM)xBlackHeartx Wrote: I did originally use time.delay to slow down the game, but that caused input lag,
I repeat: You dont change the speed by changing your framerate as i said earlier. You slow down your moving objects by delaying them. If there is a problem after that, the answer is not to revert the framerate back to a lower number. You put a band-aid on it by reducing the framerate when you should of fixed the real problem. What input lag? You would have to define this more to properly diagnose it. Show your code as you had it when you had this problem with the framerate at 60. When i change your code to 60 FPS, the snake just needs to be slowed down. This can be simply done by comparing ticks every frame.
        if pygame.time.get_ticks()-timer > delay:
            timer = pygame.time.get_ticks()
            snake.move()
Without a snake class and using nested while loops for game states.... your code is hard to read.

How did you get a certificate for C++ and Java without learning classes? Both use OOP. Classes are very powerful and would improve your code dramatically. It would remove all globals from your code, reduce redundancy of your code down to probably 100-150 lines, make it easier to read and maintain, and make it more adaptable to add more things. For example if i want to modify your snake's logic (like slow it down because of raising the FPS) i have to look through your entire code. It should be in a Snake class as data. You also are not using pygame rects for position coordinates as well as collision (which would reduce code even further).

This is an example of snake with classes. It is very readable being in classes.
Recommended Tutorials:
#7
Honestly, I haven't been saving older versions of my program (if I did, I would probably have over 100 different versions due to the innumerable tweaks I've made). I do however have ONE older version from before I added in the start menu. The only reason I have this is I started saving it outside the default folder, so that I could actually run it from outside the idle (I hadn't figured out how to set my AppData folder to not hidden at the time).

Here's that code:

import random
import pygame
pygame.init()

screenWidth=500
screenHeight=500

mainWindow=pygame.display.set_mode((screenWidth,screenHeight))
pygame.display.set_caption("Snake")

x=250
y=250
snakeSize=50
move="down"
length=0
path=[]
run=True

def frandxy():
    global posx
    global posy
    posx=snakeSize*(random.randint(0,screenWidth/snakeSize-1))
    posy=snakeSize*(random.randint(0,screenWidth/snakeSize-1))
    if (posx,posy) in path or x==posx and y==posy:
            frandxy()

def closeGame():
    pygame.quit()
    quit()

def loseGame():
    while run:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                closeGame()
        
        mainWindow.fill((0,0,0))
        pygame.draw.rect(mainWindow,(0,255,0),(screenWidth/2-snakeSize*3,\
                                               screenHeight/2-snakeSize,\
                                               snakeSize*6,snakeSize*2))
        font=pygame.font.SysFont('arialblack',20)
        textTop=font.render('GAME OVER',0,(0,0,0))
        textBottom=font.render(('Your Length Was: '+str(length)),0,(0,0,0))

        mainWindow.blit(textTop, (screenWidth/2-textTop.get_width()/2,screenHeight/2-snakeSize))
        mainWindow.blit(textBottom, (screenWidth/2-textBottom.get_width()/2,screenHeight/2))
        pygame.display.update()
        
def game():
    global run
    global x
    global y
    global move
    global length
    global path
    while run:
        
        pygame.time.delay(200)
        
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                closeGame()

        key=pygame.key.get_pressed()
        if key[pygame.K_UP] and move != "down":
            move="up"
        if key[pygame.K_DOWN] and move != "UP":
            move="down"
        if key[pygame.K_RIGHT] and move != "left":
            move="right"
        if key[pygame.K_LEFT] and move != "right":
            move="left"

        path.insert(0,(x,y))
    #move snake
        if move=="down" and y<=screenHeight-snakeSize:
            y+=snakeSize
        if move=="down" and y>=screenHeight:
            y=0
        if move=="up" and y>=0:
            y-=snakeSize
        if move=="up" and y<0:
            y=screenHeight-snakeSize
        if move=="right" and x<=screenWidth-snakeSize:
            x+=snakeSize
        if move=="right" and x>=screenWidth:
            x=0
        if move=="left" and x>=0:
            x-=snakeSize
        if move=="left" and x<0:
            x=screenWidth-snakeSize

        if x==posx and y==posy:
            length+=1
            frandxy()
        if (x,y) in path:
            loseGame()
            print("You lose. Your length was", length)
            pygame.quit()
            break
        if length>=100:
            print("You won! The snake filled up the whole screen!")
            pygame.quit()
            break

        mainWindow.fill((0,0,0))
        #draw snake
        if length>0:
            for i in range(length):
                pygame.draw.rect(mainWindow, (0,255,0),(path[i][0],path[i][1], snakeSize,snakeSize))
        pygame.draw.rect(mainWindow, (0,255,0),(x, y, snakeSize, snakeSize))
        #draw food
        pygame.draw.rect(mainWindow, (255,0,0),(posx,posy, snakeSize, snakeSize))
        if len(path)>length:
            path=path[:(length)]
        pygame.display.update()
frandxy()
game()
And yes, this is really sluggish. The reason for the input lag was it was only checking for key presses every fifth of a second, which meant that my button presses would fail to register if I didn't hold down the button for long enough. Even reducing the delay didn't really fix this. Replacing that line of code with an fps limiter completely removed the input lag problem.

And yes, I did learn about classes in my c++ and java classes, I just couldn't really wrap my head around them. One thing I was taught though, is it was pointless to put the code for classes in programs that actually use them; you're supposed to put them in a separate module and then import that into the a program you want to use them in. I never saw the need to do make highly a module with highly specialized classes that would only be used in one game. I've only thought about making classes for things I plan to use in more than one program, such as making buttons, labels, code so you can actually close the application, you get the idea.
#8
(Sep-27-2019, 04:01 AM)xBlackHeartx Wrote: Honestly, I haven't been saving older versions of my program
That is another benefit to using github repos as it does save every edit made. I now always backup my work there. Anytime i am done i push the changes to the repo. I have reverted code to an older version before as sometimes you get too far off track you have to abandon some work. And its always better to start over NOT at the beginning again.

I am getting too tired to think at the moment. So i will have to take a look at the code later.
Recommended Tutorials:
#9
And as I said, I got that method of limiting the fps from a coursebook my professor gave me to review. I literally just did what the program in this book does. The rest of my program is largely identical by coincidence (minus the addition of an actual start screen, and a screen that shows you your score). And no, my instructor didn't write the book. The book is called 'Making Games with Python and Pygame' by Al Sweigart.
#10
(Sep-27-2019, 04:01 AM)xBlackHeartx Wrote: One thing I was taught though, is it was pointless to put the code for classes in programs that actually use them; you're supposed to put them in a separate module and then import that into the a program you want to use them in. I never saw the need to do make highly a module with highly specialized classes that would only be used in one game.
If you use a class over and over again in one game it makes sense to make it a class. In Python, it is perfectly acceptable to put any number of classes together in a single file. Sometimes doing different things, sometimes just because they are subclasses of the super class, etc. However usually they are in a module together because the two classes are related. For example your snake game. I would make a Body class. Because you have potentially an unlimited number of body parts being added to the snake until the person fails. These body parts are all similar and thus require a class.

In my tutorials i make a Control class that is somewhat pointless in being a class. There will never be two instances of it. I only use that to group together common functions without passing variables back and forth (such as what state the game is in, etc.). But if i make Pong game with two paddles, i sure as hell am going to make a Paddle class. I will also make a Ball class. Different languages have different mindsets. In python if you have to repeat yourself 2 or 3 times you need a function. If you have to write 2 or 3 objects, you need a class. Sometimes you make a class for one object anticipating a possibility of more.

(Sep-27-2019, 04:01 AM)xBlackHeartx Wrote: it was pointless to put the code for classes in programs that actually use them; you're supposed to put them in a separate module and then import that into the a program you want to use them in.
Often to illustrate the point...it is shown classes among the code and not imported. Usually they are imported into the file. But it makes it more confusing separating them in tutorials or examples. It makes it a lot easier for users to copy one code section and paste and run, instead of explaining a bunch of files that import to each other.

(Sep-27-2019, 04:09 AM)xBlackHeartx Wrote: And as I said, I got that method of limiting the fps from a coursebook my professor gave me to review. I literally just did what the program in this book does. The rest of my program is largely identical by coincidence (minus the addition of an actual start screen, and a screen that shows you your score). And no, my instructor didn't write the book. The book is called 'Making Games with Python and Pygame' by Al Sweigart.
Everyone knows that book. It can get your feet wet in the process. Numerous pygamers complain that there is no such book out there that instructs proper pygame in OOP. That is why mekire made a ton of repos for instructions regarding pygame the proper way. That is why i wrote a game tutorial series using OOP. Mekire and I back in 2014 should of wrote a book on writing pygame in OOP, because it would of been the only one still out there now.
Recommended Tutorials:


Possibly Related Threads…
Thread Author Replies Views Last Post
  Laggy Game Movement game_slayer_99 12 4,217 Oct-05-2022, 11:34 AM
Last Post: metulburr
Music [PyGame] Chopper wash effect efficiency questions. michael1789 9 4,347 Jan-19-2021, 07:12 PM
Last Post: michael1789
  Appropriately delay this PyGame code kleynah22 2 4,356 Nov-09-2017, 02:00 PM
Last Post: Windspar
  [PyGame] My program is very laggy GamePlanet 6 6,984 Aug-15-2017, 03:00 PM
Last Post: nilamo

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020