Python Forum
[PyGame] I found a way to generate sprites without .blit. Is it effecient?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] I found a way to generate sprites without .blit. Is it effecient?
#11
Actually, my game doesn't lag anymore. As for the lag part, I found it was because of a list I was using to draw the snake's tail. Every time the snake moves (which is does every second about), it first adds its current coordinates to an ever growing list. This of course caused the list to grow rather quickly. I deduced this after I realized that the game would continue to slow down at the same rate regardless of whether or not the snake grew. I fixed this by adding in code to shorten the list to the current length of the snake.

Of course, as I said, I'm still having a problem with the thing taking up an insane amount of RAM. I mean, seriously here, this is a game that Atari could've run (its actually based on a snake game I once had that came with a port of several atari games, which included things like pitfall), and my gaming computer here has 1 gig of RAM! Why on earth would such a primitive 200-line program (whose length includes spaces and comments) take up so much RAM? I've heard that python programs are infamous for this, but this is just ridiculous. I was originally planning on putting this thing on my laptop, but I honestly doubt it could run it.

And if you want to see my code in its current form, here:

import random
import pygame
pygame.mixer.pre_init(44100,-16,2,2048)
pygame.mixer.init()
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=6
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
    if length>=((screenWidth/snakeSize)*(screenHeight/snakeSize))-1:
        winGame()
    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():
    bleep.play()
    pygame.time.delay(300)
    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]:
            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()

        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()


        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()

Also, Metalbur here was the one that suggested I switch to limiting the FPS rather than using the time.delay command to keep the game from running insanely fast. So yeah, that's the solution HE offered me. Also, I wasn't experiencing any graphical glitches (other than the food occasionally spawning on top of the snake until I turned its function into a do-while loop). Interestingly enough btw, I DID start to get graphical glitches in a version I made that plays itself (so that I could see my own victory screen). I upped the fps to keep it from taking forever, but I noticed that the thing doesn't render the snake right when I do that. Strangely enough btw, I have never shown that version on here or even mentioned it. So, how did Metalbur even know that by using code HE suggested to me that I would have glitches if I changed it, when I never revealed that I was having that problem? He even told me what I should set my fps at btw.

As for why I changed to limiting the fps rather than using time.delay, its because using time.delay created input lag. It also made the sound effects delayed, but that was fixed once I switched to limiting the fps (which, looking at tutorials online, is a common thing to do for snake games it appears).
Reply
#12
There's a lot going on in that code lol. If it works, that's cool, but I'd like to offer some notes for the next project:
1) try to stick to a single event loop. The snake game has 6. https://gameprogrammingpatterns.com/game-loop.html
2) I'd recommend looking into State Machines to help control what the game should be doing at any given point in time. A Menu-State, Game-State, PauseScreen-State, etc. That will help with having just one event loop, and will also help with keeping the call stack down (what I mean by that is, you don't currently use while loops for repeating events... if you PlayAgain 20 times, you've added 20 things to the callstack when really that should have been 0). I don't think this would actually help with performance, but it'd help make the code much cleaner to look at, which then makes it significantly easier to add features or fix bugs. https://gameprogrammingpatterns.com/state.html
3) Personally, I hate the fact that there's a quit() function available in python. The program ends just fine when it reaches the end of the file, and things are easier to understand without sudden exits like that. You could just as easily replace all the quit() calls with a return statement, which has the added benefit of syntax highlighting in editors, so it sticks out a bit more. Plus, it helps with debugging when you can unwind what's happening, instead of the program just killing itself lol.
4) I also personally hate global variables. But a lot of that might just clean itself up if you switch the control flow to use a state machine.
Reply
#13
I did try to put everything into one loop, but I haven't been able to get the thing to run (when I press enter at the start screen, it just freezes and I have no clue why). That's why the version I actually play uses a separate loop for each screen. For some reason, that's the only way I could find to do it.

Here's that version if anyone's interested:

import random
import pygame
pygame.init()

screenWidth=500
screenHeight=500

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

#snake's graphics
x=screenWidth/2
y=screenHeight/2
snakeSize=50
move="down"
length=0
path=[]
#food's graphics
posx=0
posy=0
#fps
fps=60
fpsClock=pygame.time.Clock()
#game states
win=True
state='start'
#sound effects
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():
    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()

frandxy()
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 state=='start':
        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()
            state='game'
    if state=='game':
        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
                state='end'
            if length>=((screenWidth/snakeSize)*(screenHeight/snakeSize))-1:
                state='end'

            mainWindow.fill((0,0,0))
            #draw snake
            fps=6
            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))
            fps=60
            if len(path)>length:
                path=path[:(length)]
    if state=='end':
        key=pygame.key.get_pressed()
        if key[pygame.K_ESCAPE]:
            closeGame()
        if key[pygame.K_RETURN]:
            bleep.play()
            state='playAgain'
        if win:
            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))
        if win==False:
            textTop=font.render('GAME OVER',0,(0,0,0))
            textBottom=font.render(('Your Length Was: '+str(length)),0,(0,0,0))

        mainWindow.fill((0,0,0))
        pygame.draw.rect(mainWindow,(0,255,0),(screenWidth/2-snakeSize*3,\
                                               screenHeight/2-snakeSize,\
                                               snakeSize*6,snakeSize*2))
        mainWindow.blit(textTop, (screenWidth/2-textTop.get_width()/2,screenHeight/2-snakeSize))
        mainWindow.blit(textBottom, (screenWidth/2-textBottom.get_width()/2,screenHeight/2))

    if state=='playAgain':
        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()
            win=True
            x=screenWidth/2
            y=screenHeight/2
            length=0
            move="down"
            path=[]
        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()
    fpsClock.tick(fps)

And yeah, having a quit() function may be kinda silly, but it feels a lot more elegant than just letting the game crash (which is what mine used to do when you lost).
Reply
#14
Line 96 onward. The game code is indented under the if key[pygame.K_e]: line, so nothing would happen unless you were closing the game.

But also, each of those states is still doing it's own event pumping. In a perfect world, the line for event in pygame.event.get(): would only exist in one place in your code.
Reply
#15
A typo? Well, that explains a lot...

Either way, this thread has gone way off topic. I want to know if its a good idea to be drawing sprites the way I am. Of course, as I stated, I don't really have much choice for the time being, so I was more asking if I should keep doing this in the long run. Of course, there's also the RAM issue, which I guess we're still talking about coming to think of it.

As for this 'game states' thing, I have no clue how to do them any other way. Obviously, how to make games isn't a part of a two-year degree. Honestly, my training didn't teach me anymore than making cheap websites, cheap office programs, and command-line programs. I did ask my professor about game making; all he told us was that is something you learn when you're getting your bachelor's. Though he will be teaching a pygame course next semester(he even gave me a preview of the textbook he was planning on using, sadly it doesn't cover game screens), but I may or may not be able to take it.

And by 'next semester', I actually meant next summer. This site really needs to let you edit your own posts so you can fix typos. Otherwise, its easy to accidentally make misleading posts.
Reply
#16
(Dec-06-2019, 03:27 AM)xBlackHeartx Wrote: And by 'next semester', I actually meant next summer. This site really needs to let you edit your own posts so you can fix typos. Otherwise, its easy to accidentally make misleading posts.
Everyone has 48 hours to edit a post for proofreading. However because you have a history of trolling and removing post content in your warnings you are in a limited user group that restricts your access even further.

(Dec-06-2019, 10:22 AM)metulburr Wrote: As for this 'game states' thing, I have no clue how to do them any other way.
Im pretty sure i told you how to do states with classes, but it might of been someone else. Anyway, there is a tutorial on this forum for that specific purpose for creating game states. And it uses only one for event in pygame.event.get(): in the entire program as described by nilamo.
Recommended Tutorials:
Reply
#17
(Dec-06-2019, 10:31 AM)metulburr Wrote:
(Dec-06-2019, 03:27 AM)xBlackHeartx Wrote: And by 'next semester', I actually meant next summer. This site really needs to let you edit your own posts so you can fix typos. Otherwise, its easy to accidentally make misleading posts.
Everyone has 48 hours to edit a post for proofreading. However because you have a history of trolling and removing post content in your warnings you are in a limited user group that restricts your access even further.

(Dec-06-2019, 10:22 AM)metulburr Wrote: As for this 'game states' thing, I have no clue how to do them any other way.
Im pretty sure i told you how to do states with classes, but it might of been someone else. Anyway, there is a tutorial on this forum for that specific purpose for creating game states. And it uses only one for event in pygame.event.get(): in the entire program as described by nilamo.

So, I now have limited rights on this forum just because you don't like my programming style, and I don't tolerate bs. Yeah, and how do you think your behavior makes the python language look?

Think I'll just go and make games in Java instead. Seeing the kind of treatment I get on this board, and the fact that no one's really addressed my original question.
Reply
#18
As for my self-playing snake program, I just decided to lower the fps to try and fix the rendering issue. Still had the issue. Turns out, there's something wrong with my algorithm that has nothing to do with the framerate. So it would appear MetalBur is also into playing devious mind games with people. Yeah, don't use this board. Who knows what else he and whoever else runs this forum are doing.
Reply
#19
Quote:Think I'll just go and make games in Java instead.

Promise? From what I can tell the mods here have the patients of saints.
Reply
#20
(Dec-07-2019, 12:03 AM)xBlackHeartx Wrote: So, I now have limited rights on this forum just because you don't like my programming style, and I don't tolerate bs. Yeah, and how do you think your behavior makes the python language look?
Your warnings are from 3 different team members on this forum for different situations. So there is no one member singling you out. None of said warnings were for anything remotely of dislike of programming styles. You can find our rules that you violated on our help forums here and here. I would suggest an attitude change is the best approach to remove your restrictions. You are currently walking a thin line already from your past and one warning will lock your account completely. I had every right to ban your account after trolling but i knew that you were just mad so i let it slide and gave you a warning instead to try to give you a chance to come back with a cool head.
Recommended Tutorials:
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  pygame, sprites, and rects menator01 12 1,862 Dec-07-2023, 02:37 AM
Last Post: Benixon
  [PyGame] Sprites just randomly appear and dissapear in my pygame.sprite.GoupeSingle trueShadoWrr 2 1,979 Feb-13-2023, 09:34 AM
Last Post: Vadanane
  [PyGame] pygame blit() method syafiq14 1 4,897 Oct-30-2020, 04:46 PM
Last Post: Russ_CW
  index error when using lists to blit rotated images codefun45 7 3,544 Sep-03-2020, 11:11 PM
Last Post: codefun45
  moving image with blit rwahdan 2 3,020 Jul-10-2019, 06:24 PM
Last Post: nilamo
  [PyGame] Having 4 players(Sprites) all being able to jump ElijahCastle 5 3,999 May-07-2019, 05:04 PM
Last Post: SheeppOSU
  Sprites and Actor error ajlconsulting 6 9,315 Jan-30-2019, 12:50 AM
Last Post: metulburr
  draw not showing all sprites ethanstrominger 0 2,575 Jan-25-2019, 10:10 PM
Last Post: ethanstrominger
  error with survace.blit joemcsk1 3 4,456 Aug-06-2018, 12:23 AM
Last Post: metulburr
  [PyGame] move randomly sprites reutB 4 8,226 Mar-29-2017, 01:12 PM
Last Post: metulburr

Forum Jump:

User Panel Messages

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