[Pygame] auto animation

[Pygame] auto animation

Postby metulburr » Tue May 14, 2013 10:24 pm

OK. I definately have animation upon an action each time, but i think i am somewhat confused on the process of auto animation. Being: hitting a key press and iterate through images at a specified time delay.

So my little project for figuring this out was to chop the sprite sheet, splice the sheet to use the wooden door only. Then upon spacebar press, animate through those 4 images to open, then spacebar again pressed to animate the door to close.

animate section of code:
Code: Select all
    def next_frame(self):
        print('frame {} of {} frames'.format(self.frame_count, len(self.frames)-1))
        if self.frame_count < len(self.frames)-1:
            self.frame_count += 1
        else:
            self.frame_count = 0
           
    def animate(self):
        self.ani_start_time = pygame.time.get_ticks()
        diff = pygame.time.get_ticks() - self.timer
        if diff >= self.delay:
            self.next_frame()
            self.timer = pygame.time.get_ticks()


full code:
Code: Select all
import pygame
import itertools


def url_image(url):
    try:
        from urllib2 import urlopen
        from cStringIO import StringIO as inout
    except ImportError:
        from urllib.request import urlopen
        from io import BytesIO as inout
    myurl = urlopen(url)
    return inout(myurl.read())
   
class Door:
    def __init__(self):
        self.x = 0
        self.y = 0
        url = 'http://i1297.photobucket.com/albums/ag23/metulburr/SC-Door-Arch01_zps1bd57433.png'
        self.spritesheet = pygame.image.load(url_image(url)).convert()
        self.rect = self.spritesheet.get_rect()
       
        self.frames = []
        self.frame_inds = itertools.product(range(4),range(4))
        self.cell_w = self.rect.width / 4
        self.cell_h = self.rect.height / 4
        self.frame_count = 0
       
        self.ani_start_time = 0
        self.timer = 0
        self.delay = 500
       
        self.set_image()
       
    def set_image(self):
        for cell in self.frame_inds:
            loc = ((self.cell_w * cell[0], self.cell_h * cell[1]), (self.cell_w, self.cell_h))
            self.frames.append(self.spritesheet.subsurface(loc))
           
        #assign the wooden door only
        self.frames = self.frames[12:]
           
    def next_frame(self):
        print('frame {} of {} frames'.format(self.frame_count, len(self.frames)-1))
        if self.frame_count < len(self.frames)-1:
            self.frame_count += 1
        else:
            self.frame_count = 0
           
    def animate(self):
        self.ani_start_time = pygame.time.get_ticks()
        diff = pygame.time.get_ticks() - self.timer
        if diff >= self.delay:
            self.next_frame()
            self.timer = pygame.time.get_ticks()
       

       
    def update(self, screen):
        self.image = self.frames[self.frame_count]
        screen.blit(self.image, (0,0))

class Control:
    def __init__(self):
        pygame.init()
        self.screensize = (800,600)
        self.screen = pygame.display.set_mode(self.screensize)
        self.clock = pygame.time.Clock()
        self.state = True
        self.door = Door()
       
        self.mainloop()
       
    def mainloop(self):
        while self.state:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.state = False
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        self.door.animate()
                        #self.door.next_frame()
           
            self.screen.fill((0,0,0))
            self.update()
            pygame.display.update()
           
    def update(self):
        self.door.update(self.screen)
           
app = Control()


I dont really get the concept to say, this is the start time of the whole animation, then keep looping through the mainloop but iterate through these images with some delay, not upon each iteration of the loop
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1384
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: [Pygame] auto animation

Postby Mekire » Wed May 15, 2013 12:10 am

I added a flag for whether the door was open and got rid of the start timer. Frame is incremented when opening and decremented when closing.

(spacebar to trigger)
Code: Select all
import pygame
import itertools,sys,os

def url_image(url):
    try:
        from urllib2 import urlopen
        from cStringIO import StringIO as inout
    except ImportError:
        from urllib.request import urlopen
        from io import BytesIO as inout
    myurl = urlopen(url)
    return inout(myurl.read())

class Door:
    def __init__(self):
        self.open = False

        self.x = 0
        self.y = 0
        url = 'http://i1297.photobucket.com/albums/ag23/metulburr/SC-Door-Arch01_zps1bd57433.png'
        self.spritesheet = pygame.image.load(url_image(url)).convert()
        self.rect = self.spritesheet.get_rect()

        self.frames = []
        self.frame_inds = itertools.product(range(4),range(4))
        self.cell_w = self.rect.width / 4
        self.cell_h = self.rect.height / 4
        self.frame_count = 0

        self.start_anim = False
        self.timer = 0.0
        self.delay = 250

        self.set_image()

    def set_image(self):
        for cell in self.frame_inds:
            loc = ((self.cell_w * cell[0], self.cell_h * cell[1]), (self.cell_w, self.cell_h))
            self.frames.append(self.spritesheet.subsurface(loc))

        #assign the wooden door only
        self.frames = self.frames[12:]

    def next_frame(self):
        print('frame {} of {} frames'.format(self.frame_count+1, len(self.frames)))
        if not self.open:
            if self.frame_count < len(self.frames)-1:
                self.frame_count += 1
            else:
                self.open = True
                self.start_anim = False
        else:
            if self.frame_count > 0:
                self.frame_count -= 1
            else:
                self.open = False
                self.start_anim = False

    def animate(self):
        if self.start_anim:
            diff = pygame.time.get_ticks() - self.timer
            if diff >= self.delay:
                self.next_frame()
                self.timer = pygame.time.get_ticks()

    def update(self, screen):
        self.animate()
        self.image = self.frames[self.frame_count]
        screen.blit(self.image, (0,0))

class Control:
    def __init__(self):
        pygame.init()
        self.screensize = (800,600)
        self.screen = pygame.display.set_mode(self.screensize)
        self.Clock = pygame.time.Clock()
        self.state = True
        self.door = Door()

        self.mainloop()

    def mainloop(self):
        while self.state:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.state = False
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        if not self.door.start_anim:
                            self.door.start_anim = True

            self.update()


    def update(self):
        self.screen.fill((0,0,0))
        self.door.update(self.screen)
        pygame.display.update()
        self.Clock.tick(60)

app = Control()
pygame.quit();sys.exit()

-Mek
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: [Pygame] auto animation

Postby metulburr » Thu May 16, 2013 1:56 am

Code: Select all
    def next_frame(self):
        print('frame {} of {} frames'.format(self.frame_count+1, len(self.frames)))
        if not self.open:
            if self.frame_count < len(self.frames)-1:
                self.frame_count += 1
            else:
                self.open = True
                self.start_anim = False
        else:
            if self.frame_count > 0:
                self.frame_count -= 1
            else:
                self.open = False
                self.start_anim = False

    def animate(self):
        if self.start_anim:
            diff = pygame.time.get_ticks() - self.timer
            if diff >= self.delay:
                self.next_frame()
                self.timer = pygame.time.get_ticks()

    def update(self, screen):
        self.animate()

one thing that i dont understand about this code is when i look at it, it makes me think the way it is coded, it should only get executed the first time. To pinpoint what i mean, animate() gets run every time. if you hit spacebar ( IE self.start_anim), then it gets the difference (Ie diff), and if that difference is greater or equal to than self.delay... then increment/decrement the frames. This makes sense to me as run the first time, but the next time ran ( the next time looping), i dont see where it would execute self.next.frame as it also resets self.timer too that previous new time, making the new difference less and not executing the if condition:
Code: Select all
if diff >= self.delay:

but yet it does execute that if condition, otherwise it would only iterate one frame every time.

I dont know but yet it works, and i dont know why
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1384
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: [Pygame] auto animation

Postby Mekire » Thu May 16, 2013 2:39 am

The timer only updates if the inequality is true. If the diff is not greater than the delay the timer doesn`t change.
The current frame gets drawn no matter what. Once the diff is greater than delay then the frame count is incremented/decremented thus changing the current frame.
When the frame is changed we update the timer so it won`t change again till the diff is again greater than the delay. Throw some print statements around to see what is happening.

-Mek
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: [Pygame] auto animation

Postby metulburr » Thu May 16, 2013 5:07 am

wow ok, i redid it again, and it took me like an hour to figure out that i forgot the clock.tick() and i was not reassiging the timer because of a typo, lol.

OK i think i got the animation process. I also made a function to just reuse extract_frames(), just because i got sick of rewriting the code to extract the frames of a given sprite sheet. Which i might now just put as a Control() method from now on. I removed itertools and used a nested for loop, because i normally dont like importing if i dont have to, but does itertools.product() return the result faster than a nested for loop, because then it might suit well based on a somehwat large game loading a ton of images, would make sense.

I am going as slow as a turtle figuring this all out, lol, but whatever at least i am getting somewhere.


Code: Select all
import pygame
#import itertools

def url_image(url):
    try:
        from urllib2 import urlopen
        from cStringIO import StringIO as inout
    except ImportError:
        from urllib.request import urlopen
        from io import BytesIO as inout
    myurl = urlopen(url)
    return inout(myurl.read())
   
def extract_frames(sheet, row_image_amt, col_image_amt):
    '''extract frames from sheet based on number of images of row and col'''
    frame_inds = []
    for row in range(row_image_amt):
        for col in range(col_image_amt):
            frame_inds.append((row,col))
   
    rect = sheet.get_rect()
    cell_w = rect.width / row_image_amt
    cell_h = rect.height / col_image_amt
   
    frames = []
    for cell in frame_inds:
        loc = ((cell_w * cell[0], cell_h * cell[1]), (cell_w, cell_h))
        frames.append(sheet.subsurface(loc))
    return frames

class Waver:
    def __init__(self):
        self.pos = (0,0)
        url = 'http://i1297.photobucket.com/albums/ag23/metulburr/waver2_zpsb38acbe8.png'
        self.spritesheet = pygame.image.load(url_image(url)).convert()
        self.spritesheet.set_colorkey((255,0,255))
        self.spritesheet_rect = self.spritesheet.get_rect()
        self.framecount = 0
        self.ani_in_progress = False
        self.ani_delay = 100
        self.ani_timer = 0

        self.frames = extract_frames(self.spritesheet, 10, 1)
       
    def animate(self):
        if self.ani_in_progress:
            diff = pygame.time.get_ticks() - self.ani_timer
            print(diff)
            print(self.ani_delay)
            if diff >= self.ani_delay:
                self.ani_timer = pygame.time.get_ticks()
                if self.framecount < len(self.frames)-1:
                    self.framecount += 1
                else:
                    self.framecount = 0
                    self.ani_in_progress = False
               
       
    def update(self, screen):
        self.animate()
        self.image = self.frames[self.framecount]
        screen.blit(self.image, self.pos)

class Control:
    def __init__(self):
        pygame.init()
        self.screensize = (800,600)
        self.screen = pygame.display.set_mode(self.screensize)
        self.clock = pygame.time.Clock()
        self.gamestate = True
        self.man = Waver()
       
        self.mainloop()
       
    def mainloop(self):
        while self.gamestate:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.gamestate = False
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        self.man.ani_in_progress = True
                   
            self.update()
           
    def update(self):
        self.screen.fill((200,200,200))
        self.man.update(self.screen)
        pygame.display.update()
        self.clock.tick(60)



   

app = Control()
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1384
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: [Pygame] auto animation

Postby Mekire » Thu May 16, 2013 5:26 am

In this case I wouldn't worry about the performance of a nested loop versus itertools.product. The time it takes to do either of these should be negligible compared to the actual subsurface assignment. I would concern yourself much more with only passing the coordinates you need to your chop-into subsurface function. Previously I have had such badly arranged sprite sheets that I just input the coordinates needed manually. Even this is fine, if a bit tedious.

-Mek
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: [Pygame] auto animation

Postby metulburr » Thu May 16, 2013 12:18 pm

yeah i have seen some crazy arranged spritesheets, different sized images, variously placed around, i was thinking the only method of extracting them is manually inserting the coords.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1384
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: [Pygame] auto animation

Postby Mekire » Thu May 16, 2013 12:27 pm

My biggest sprite project (the Cabbage game) although currently slated for recoding from the ground-up, demonstrated to me how hard it was to make a generalized sprite sheet. Some of my monsters needed to have images for all four directions, some for only two, and yet others only needed one. Some had death sequences that required numerous frames, while others just went poof. Some needed frames for special attacks while others simply hurt you upon contact. As nice as it would have been to have a single format on the sprite sheet this didn't end up being practical so I ended up using a lot of manually put in coordinates.

As long as you have a generalized function for getting the sprites off a sheet then who cares. As soon as you start having different sized sprites on a single sheet you may need to find a more generalized way than our current frame_coordinate method. Or you could make individual sheets always have a constant size. I personally favor the latter.

-Mek
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: [Pygame] auto animation

Postby metulburr » Thu May 16, 2013 12:54 pm

i havent made any images, nor know how to, so i have not even made a spritesheet yet. I have so far only used other peoples images and spritesheets. But it would seem plausible to organize the spritesheet accordingly, instead of looking like you dropped a bag of marbles and thats where the image gets placed on the sheet, lol.

but i have been using one person spritesheets that are organized, so i guess forgot about the people that just throw it on
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1384
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: [Pygame] auto animation

Postby Mekire » Thu May 16, 2013 1:39 pm

You should give animating this a go. Produces extremely convincing 3d.

http://content.makeyourflashgame.com/sp ... d-3-96.png

-Mek
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: [Pygame] auto animation

Postby metulburr » Fri May 17, 2013 1:23 am

wow cool. It does look close to 3d, but you can tell the axis is a little off. Well to me it looks a little off. Yeah once you get the hang of it, its quite simple, especially using the same code and modifying here and there.

I modified extract_frames() to go right to left then top to bottom, or to go top to bottom then right to left, depending on whichever spritesheet i end up getting

Code: Select all

#### test


import pygame
#import itertools

def url_image(url):
    try:
        from urllib2 import urlopen
        from cStringIO import StringIO as inout
    except ImportError:
        from urllib.request import urlopen
        from io import BytesIO as inout
    myurl = urlopen(url)
    return inout(myurl.read())
   
def extract_frames(sheet, row_image_amt, col_image_amt, left_right_first=True):
    '''extract frames from sheet based on number of images of row and col
    left_right_first if True: left to right, top to bottom, where if False: top to bottom, left to right
    '''
    frame_inds = []
    if left_right_first:
        for row in range(col_image_amt):
            for col in range(row_image_amt):
                frame_inds.append((col, row))
    else:
        for row in range(row_image_amt):
            for col in range(col_image_amt):
                frame_inds.append((row, col))
   
    rect = sheet.get_rect()
    cell_w = rect.width / row_image_amt
    cell_h = rect.height / col_image_amt
   
    frames = []
    for cell in frame_inds:
        loc = ((cell_w * cell[0], cell_h * cell[1]), (cell_w, cell_h))
        frames.append(sheet.subsurface(loc))
    return frames

class Waver:
    def __init__(self):
        self.pos = (0,0)
        url = 'http://content.makeyourflashgame.com/spacecoredefender/asteroid-3-96.png'
        #url = 'http://i1297.photobucket.com/albums/ag23/metulburr/asteroid-3-96_colorkey_set_zps4ca96aa7.png'
        self.spritesheet = pygame.image.load(url_image(url)).convert_alpha()
        #self.spritesheet.set_colorkey((255,0,255))
        self.spritesheet_rect = self.spritesheet.get_rect()
        self.framecount = 0
        self.ani_in_progress = False
        self.ani_delay = 10
        self.ani_timer = 0

        self.frames = extract_frames(self.spritesheet, 21, 7)
        self.frames = self.frames[:-4] #remove empty frames
       
    def animate(self):
        if self.ani_in_progress:
            diff = pygame.time.get_ticks() - self.ani_timer
           
            if diff >= self.ani_delay:
                self.ani_timer = pygame.time.get_ticks()
                if self.framecount < len(self.frames)-1:
                    self.framecount += 1
                else:
                    self.framecount = 0
                    self.ani_in_progress = False
               
       
    def update(self, screen):
        self.animate()
        self.image = self.frames[self.framecount]
        screen.blit(self.image, self.pos)

class Control:
    def __init__(self):
        pygame.init()
        self.screensize = (800,600)
        self.screen = pygame.display.set_mode(self.screensize)
        self.clock = pygame.time.Clock()
        self.gamestate = True
        self.man = Waver()
       
        self.mainloop()
       
    def mainloop(self):
        while self.gamestate:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.gamestate = False
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        self.man.ani_in_progress = True
                   
            self.update()
           
    def update(self):
        self.screen.fill((200,200,200))
        self.man.update(self.screen)
        pygame.display.update()
        self.clock.tick(60)



   

app = Control()


There is however what appears to be quite a long waiting period to load though in that code. Also i tried changing the color on that spritesheet to ff00ff to set the colorkey to (255,0,255), but when i set the colorkey (code is commented out) i see what you mean when you get some pixels that apparently were not that color. So i just left the alpha layer on the image and used convert_alpha()
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1384
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: [Pygame] auto animation

Postby Mekire » Fri May 17, 2013 3:34 am

Well, that image is huge so doing it via urllib isn't really practical, but yeah. If you do it from a local file should work fine.

I do believe the creator of that sprite originally made it in blender, so technically it is a set of 3d frames put on a sprite sheet. Looks great in an asteroids game (I just hate not using custom art which really sucks as I'm a terrible artist).

-Mek
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: [Pygame] auto animation

Postby metulburr » Fri May 17, 2013 4:27 am

lol, yeah the last time i tried to draw something for an animation for pygame, it was basically a stick figure animation walk. Ever since then, i just used other peoples images, and probably plan on doing so later for large games, just because i cant draw for crap.

I didnt even think of that. Run a blender script, and take screenshots of it circling, or whatever. Throw them on a spriteshhet for me to use in a game.

But yeah that image rotating looks awesome compared to some other asteroid games, just because of the 3d look to it

but i guess i would have to stick with sprtiesheets of 3d images for now, as 3d programming is still way out there for me. Havent even made a true game yet in 2d pygame.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1384
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY


Return to Game Development

Who is online

Users browsing this forum: mr ham and 1 guest