[Pygame] chop spritesheet up into usable individual sprites

[Pygame] chop spritesheet up into usable individual sprites

Postby metulburr » Tue Apr 16, 2013 6:24 pm

Ok Mekire i am going to milk you for as much information as i can, lol. I have gotten quite far into my pygame knowledge just from you. So thank you.

So i have an example spirtesheet of basic movements, of all 4 directions. Now normally i would go into GIMP and manuelly chop and crop each one into their own image, and name them accorndingly. And then would fit them into their dict. So how would you chop and crop them in pygame and put them in a dictionary for future access via key?

Also what would happen if you had different spritesheets with different number of pixels between character or even different sized characters on different spritesheets or worse, on the same spritesheet? You would have different functions to chop each one up, if you were basing it on pixel width right?
indianajones.png
indianajones.png (3.41 KiB) Viewed 4065 times


EDIT:
The closest i think i got to the correct way of doing this is:
Code: Select all
import pygame

pygame.init()
screen = pygame.display.set_mode((400,400))
clock = pygame.time.Clock()

fullpath = '/home/metulburr/Downloads/explosions.png'
image = pygame.image.load(fullpath).convert_alpha()
width, height = image.get_size()

print(width, height)

image_num_h = 6
image_num_w = 8

image_w = width / image_num_w
image_h = height / image_num_h

print(image_w)
print(image_h)

image_list = []

counter = 0
count = 0
#counter = (counter+1) % 7
for i in range(image_num_w):
   for j in range(image_num_h):
      image_list.append(image.subsurface(j*image_w,j*image_h,image_w,image_h))
      





   
   
   
   
   
def animate_explosion(screen, index):
   total_images = 48
   for i in range(total_images):
      screen.blit(image_list[index], (0,0))
      if i >= total_images:
         return False
      else:
         return True


explode = False
run = True
while run:
   screen.fill((0,0,0))
   counter = (counter+1) % 60
   for event in pygame.event.get():
      if event.type == pygame.QUIT:
         run = False
      if event.type == pygame.KEYDOWN:
         if event.key == pygame.K_SPACE:
            explode = True
   
   if explode:
      explode = animate_explosion(screen, counter)
   pygame.display.flip()
   clock.tick(60)


where explosions is the image from http://april-young.com/home/wp-content/ ... esheet.png,

for one, i tried to animate the explosion by pressing spacebar to initiate it. but i cant seem to figure out how ot make it stop when it reaches the end of the list. Also the speed is normal if you cahnge the clock ticks top somethign like 5, but what if the rest of your stuff is going at the speed of 60? I tried counter = (counter+1) % 20, to give 3 blits every sceond, but i guess that must not be right.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1472
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: chop spritesheet up into usable individual sprites in py

Postby metulburr » Tue Apr 16, 2013 9:04 pm

This code i kind of got to work, except it has some funky things going on. Well i am out of ideas, or i am just over thinking it. I feel like i code stupidily when i am frustrated.
Code: Select all
import pygame

pygame.init()
screen = pygame.display.set_mode((400,400))
clock = pygame.time.Clock()

fullpath = '/home/metulburr/Downloads/explosions.png'
image = pygame.image.load(fullpath).convert_alpha()
width, height = image.get_size()

print(width, height)

image_num_h = 6
image_num_w = 8

image_w = width / image_num_w
image_h = height / image_num_h

print(image_w)
print(image_h)

image_list = []

for i in range(image_num_w):
   for j in range(image_num_h):
      image_list.append(image.subsurface(j*image_w,j*image_h,image_w,image_h))
      

duration = 1000
image_blit_time = duration / len(image_list)
start = 100000000000000000
end = 100000000000000000
counter = 0

explode = False
timer = None


run = True
while run:
   screen.fill((0,0,0))


      
   for event in pygame.event.get():
      if event.type == pygame.QUIT:
         run = False
      if event.type == pygame.KEYDOWN:
         if event.key == pygame.K_SPACE:
            explode = True
            start = pygame.time.get_ticks()
            end = start + duration

   if start <= end:
      start += image_blit_time
      print(counter)
      screen.blit(image_list[counter], (0,0))
      if counter >= len(image_list):
         counter = 0
         explode = False
         start = 100000000000000000
         end = 100000000000000000
      else:
         counter += 1
   else:
      print('not displaying image')


         


   #counter += 1
   pygame.display.flip()
   clock.tick(60)
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1472
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: chop spritesheet up into usable individual sprites in py

Postby sheffieldlad » Tue Apr 16, 2013 9:42 pm

I'm trying to learn a bit about pygame and this is something I bookmarked the other day.

http://thepythongamebook.com/en:pygame:step008

It's a bit above my level in terms of me being able to follow the code and understand it but maybe it will point you in the right direction?

-Paul.
Python 2.7
Windows XP
sheffieldlad
 
Posts: 37
Joined: Sat Feb 09, 2013 3:03 pm
Location: UK

Re: chop spritesheet up into usable individual sprites in py

Postby metulburr » Tue Apr 16, 2013 10:28 pm

i did end up mimicing that code and does do exactly that. Thanks by the way.
Code: Select all
import pygame

pygame.init()

class Explosion:
   def __init__(self):
      self.playtime = 0
      self.cycletime = 0
      self.interval = .10 #time lapse between images
      self.newnr = 0
      self.fullpath = '/home/metulburr/Downloads/explosions.png'
      self.image = pygame.image.load(self.fullpath)
      self.width, self.height = self.image.get_size()

      self.image_num_h = 6
      self.image_num_w = 8

      self.image_w = self.width / self.image_num_w
      self.image_h = self.height / self.image_num_h

      self.image_list = []
      self.is_exploding = False
      
      for i in range(self.image_num_w):
         for j in range(self.image_num_h):
            self.image_list.append(self.image.subsurface(
               j*self.image_w, j*self.image_h, self.image_w, self.image_h))

   def animate(self, screen, milli):
      seconds = milli / 1000.0 # seconds passed since last frame (float)
      self.playtime += seconds
      self.cycletime += seconds
      if self.cycletime > self.interval:
         self.cycletime = 0
         self.newnr += 1
      index = int(self.newnr % 6)
      if self.is_exploding:
         screen.blit(self.image_list[index], (0,0))


bomb = Explosion()
screen = pygame.display.set_mode((400,400))
clock = pygame.time.Clock()


run = True
while run:
   screen.fill((0,0,0))
   keys = pygame.key.get_pressed()
   
   for event in pygame.event.get():
      if event.type == pygame.QUIT:
         run = False
      elif event.type == pygame.KEYDOWN:
         if event.key == pygame.K_SPACE:
            bomb.is_exploding = True
         
   milliseconds = clock.tick(60)

   bomb.animate(screen, milliseconds)

   pygame.display.flip()





Now i was trying to implement the act of 1 animation upon key press of spacebar per keypress. which i might just be too tired right now to code or something cuase i find the simplest of task quite hard lol
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1472
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: chop spritesheet up into usable individual sprites in py

Postby metulburr » Tue Apr 16, 2013 11:22 pm

ok regarding executing the animation when a keypress:. i think i finally got something near it, but sometimes it does not execute when key pressed down and no animation is currently happening. NMot sure what i did?

Code: Select all
import pygame

pygame.init()

class Explosion:
   def __init__(self):
      self.playtime = 0
      self.cycletime = 0
      self.interval = .10 #time lapse between images
      self.newnr = 0
      self.fullpath = '/home/metulburr/Downloads/explosions.png'
      self.image = pygame.image.load(self.fullpath)
      self.width, self.height = self.image.get_size()

      self.image_num_h = 6
      self.image_num_w = 8

      self.image_w = self.width / self.image_num_w
      self.image_h = self.height / self.image_num_h

      self.image_list = []
      self.is_exploding = False
      
      for i in range(self.image_num_w):
         for j in range(self.image_num_h):
            self.image_list.append(self.image.subsurface(
               j*self.image_w, j*self.image_h, self.image_w, self.image_h))

   def animate(self, screen, milli):
      seconds = milli / 1000.0 # seconds passed since last frame (float)
      self.playtime += seconds
      self.cycletime += seconds
      if self.cycletime > self.interval:
         self.cycletime = 0
         self.newnr += 1
      index = int(self.newnr % 6)
      if self.is_exploding:
         screen.blit(self.image_list[index], (0,0))
      


bomb = Explosion()
screen = pygame.display.set_mode((400,400))
clock = pygame.time.Clock()

counter = 0
run = True
while run:
   counter = (counter+1)%48
   if (counter+1)%48 == 0:
      bomb.is_exploding = False
      bomb.playtime = 0
      bomb.cycletime = 0
      bomb.newnr = 0
   screen.fill((0,0,0))
   keys = pygame.key.get_pressed()
   
   for event in pygame.event.get():
      if event.type == pygame.QUIT:
         run = False
      elif event.type == pygame.KEYDOWN:
         if event.key == pygame.K_SPACE:
            bomb.is_exploding = True
         
   milliseconds = clock.tick(60)

   bomb.animate(screen, milliseconds)

   pygame.display.flip()



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

Re: chop spritesheet up into usable individual sprites in py

Postby Mekire » Wed Apr 17, 2013 12:03 am

Here is my 4-direction movement animation example redone with your Indiana Jones sprite sheet.

Code: Select all
import os, sys
import pygame as pg
import itertools

#This global constant serves as a very useful convenience for me.
DIRECTDICT = {pg.K_LEFT  : (-1, 0),
              pg.K_RIGHT : ( 1, 0),
              pg.K_UP    : ( 0,-1),
              pg.K_DOWN  : ( 0, 1)}

class Player:
    def __init__(self,rect,speed,direction=pg.K_RIGHT):
        self.rect = pg.Rect(rect)
        self.speed = speed
        self.direction = direction
        self.oldy = None #the characters previous direction every frame
        self.walk = [] #held arrow keys, in the order they were pressed
        self.redraw = False #force redraw if needed

        self.spritesheet = pg.image.load("indianajones.png").convert_alpha()
        self.image = None
        self.frame_inds = itertools.product(range(4),range(4)) #loc of frames on sheet
        self.frame  = 0
        self.frames = [] #all frames off the sprite sheet
        self.timer = 0.0 #timer for animation
        self.fps   = 7.0 #fps of animation
        self.walkframes = [] #walkframes for given direction
        self.get_images() #rip images from the sprite sheet

    def get_images(self):
        """Get the desired images from the sprite sheet."""
        for cell in self.frame_inds:
            loc = ((self.rect.width*cell[0],self.rect.height*cell[1]),self.rect.size)
            self.frames.append(self.spritesheet.subsurface(loc))
        self.adjust_images()

    def adjust_images(self):
        """update the sprites walkframes as the sprites direction changes"""
        if self.direction != self.oldy:
            if self.direction == pg.K_LEFT:
                self.walkframes = [self.frames[i*4+1] for i in range(4)]
            elif self.direction == pg.K_RIGHT:
                self.walkframes = [self.frames[i*4+2] for i in range(4)]
            elif self.direction == pg.K_DOWN:
                self.walkframes = [self.frames[i*4] for i in range(4)]
            elif self.direction == pg.K_UP:
                self.walkframes = [self.frames[i*4+3] for i in range(4)]
            self.oldy = self.direction
            self.redraw = True
        self.make_image()

    def make_image(self):
        """update the sprites animation as needed"""
        if self.redraw or pg.time.get_ticks()-self.timer > 1000/self.fps:
            if self.walk:
                self.frame = (self.frame+1) % len(self.walkframes)
                self.image = self.walkframes[self.frame]
            self.timer = pg.time.get_ticks()
        if not self.image:
            self.image = self.walkframes[self.frame]
        self.redraw = False

    def update(self,Surf):
        """Updates our player appropriately every frame."""
        self.adjust_images()
        if self.walk:
            self.rect.x += self.speed*DIRECTDICT[self.walk[-1]][0]
            self.rect.y += self.speed*DIRECTDICT[self.walk[-1]][1]
        Surf.blit(self.image,self.rect)

############################################
def quit_game():
    """Call this anytime the program needs to close cleanly."""
    pg.quit();sys.exit()

def game(Player):
    """Our event loop goes here."""
    for event in pg.event.get():
        if event.type == pg.QUIT:
            quit_game() #lets us exit cleanly
        elif event.type == pg.KEYDOWN: #all key press events here.
            if event.key in DIRECTDICT:
                Player.walk.append(event.key)
                Player.direction = Player.walk[-1]
            elif event.key == pg.K_ESCAPE:
                quit_game() #Quit with escape key. Real games should give some warning.
        elif event.type == pg.KEYUP: #all key-up events here
            if event.key in DIRECTDICT:
                Player.walk.remove(event.key)
                if Player.walk:
                    Player.direction = Player.walk[-1]

def main(Player,Surf):
    """The main function calls the draw functions in the order they are required.
    Then updates the entire screen."""
    game(Player) #run the event loop every frame
    Surf.fill(0) #redraw background before player
    Player.update(Surf) #update the player
    pg.display.update() #now update the screen

#####
if __name__ == "__main__":
    os.environ['SDL_VIDEO_CENTERED'] = '1' #Center window.
    Screen = pg.display.set_mode((500,500))
    pg.init()
    Myclock = pg.time.Clock() #This clock will let us restrict fps.
    Myplayer = Player((250,250,32,48),3)  #Our Player instance
    while 1:
        main(Myplayer,Screen) #run main in an infinite loop
        Myclock.tick(60) #limit program FPS

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

Re: chop spritesheet up into usable individual sprites in py

Postby Mekire » Wed Apr 17, 2013 12:34 am

Your explosion on a loop:
Code: Select all
import os, sys
import pygame as pg
import itertools

class Bomb:
    def __init__(self,rect):
        self.rect = pg.Rect(rect)
        self.spritesheet = pg.image.load("explosion.png").convert_alpha()
        self.image = None
        self.frame_inds = list(itertools.product(range(6),range(8)))
        self.frame  = 0
        self.frames = [] #all frames off the sprite sheet
        self.timer = 0.0 #timer for animation
        self.fps   = 30.0 #fps of animation
        self.get_images() #rip images from the sprite sheet
    def get_images(self):
        """Get the desired images from the sprite sheet."""
        for cell in self.frame_inds:
            loc = ((self.rect.width*cell[1],self.rect.height*cell[0]),self.rect.size)
            self.frames.append(self.spritesheet.subsurface(loc))
        self.make_image()
    def make_image(self):
        if pg.time.get_ticks()-self.timer > 1000/self.fps:
            self.frame = (self.frame+1) % len(self.frames)
            self.image = self.frames[self.frame]
            self.timer = pg.time.get_ticks()
        if not self.image:
            self.image = self.frames[self.frame]
    def update(self,Surf):
        self.make_image()
        Surf.blit(self.image,self.rect)

############################################
def game(Player):
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit();sys.exit()

def main(Sprite,Surf):
    game(Sprite)
    Surf.fill(0)
    Sprite.update(Surf)
    pg.display.update()

#####
if __name__ == "__main__":
    os.environ['SDL_VIDEO_CENTERED'] = '1' #Center window.
    Screen = pg.display.set_mode((256,256))
    pg.init()
    Myclock = pg.time.Clock()
    MyBomb = Bomb((0,0,256,256))
    while 1:
        main(MyBomb,Screen) #run main in an infinite loop
        Myclock.tick(60) #limit program FPS

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

Re: chop spritesheet up into usable individual sprites in py

Postby Mekire » Wed Apr 17, 2013 8:26 am

Bombs created every time you hit space-bar (simultaneous bombs OK):
Code: Select all
import os, sys
import pygame as pg
import itertools

class Bomb:
    def __init__(self,rect):
        self.rect = pg.Rect(rect)
        self.image = None
        self.spritesheet = BOMB_IMG
        self.frame_inds = list(itertools.product(range(6),range(8)))
        self.frame  = 0
        self.frames = [] #all frames off the sprite sheet
        self.timer = 0.0 #timer for animation
        self.fps   = 30.0 #fps of animation
        self.get_images() #rip images from the sprite sheet
        self.done = False
    def get_images(self):
        """Get the desired images from the sprite sheet."""
        for cell in self.frame_inds:
            loc = ((self.rect.width*cell[1],self.rect.height*cell[0]),self.rect.size)
            self.frames.append(self.spritesheet.subsurface(loc))
        self.make_image()
    def make_image(self):
        if pg.time.get_ticks()-self.timer > 1000/self.fps:
            try:
                self.frame += 1
                self.image = self.frames[self.frame]
                self.timer = pg.time.get_ticks()
            except IndexError:
                self.done = True
        if not self.image:
            self.image = self.frames[self.frame]
    def update(self,Surf):
        self.make_image()
        Surf.blit(self.image,self.rect)

############################################
class Control:
    def __init__(self):
        os.environ['SDL_VIDEO_CENTERED'] = '1' #Center window.
        self.screen = pg.display.set_mode((256,256))
        pg.init()
        self.Clock = pg.time.Clock()
        self.done = False
        self.Bombs = []
    def event_loop(self):
        keys = pg.key.get_pressed()
        for event in pg.event.get():
            if event.type == pg.QUIT or keys[pg.K_ESCAPE]:
                self.done = True
            elif event.type == pg.KEYDOWN:
                if event.key == pg.K_SPACE:
                    self.Bombs.append(Bomb((0,0,256,256)))
    def update(self):
        while not self.done:
            self.event_loop()
            self.screen.fill(0)
            for Bom in self.Bombs[:]:
                Bom.update(self.screen)
                if Bom.done:
                    self.Bombs.remove(Bom)
            pg.display.update()
            self.Clock.tick(60)

#####
if __name__ == "__main__":
    RunIt = Control()
    BOMB_IMG = pg.image.load("explosion.png").convert_alpha()
    RunIt.update()
    pg.quit();sys.exit()

-Mek

Edit: Fixed an error related to changing the size of a list while iterating over it. Animation glitches should be gone now.
Last edited by Mekire on Wed Apr 17, 2013 10:19 am, edited 2 times in total.
User avatar
Mekire
 
Posts: 988
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: chop spritesheet up into usable individual sprites in py

Postby metulburr » Wed Apr 17, 2013 8:31 am

wow dude, you just wip up a code snippet just like that. I bow my hat to you good sir.

Again i would post back after i review your code. Thanks Mekire.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1472
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: chop spritesheet up into usable individual sprites in py

Postby metulburr » Wed Apr 17, 2013 7:59 pm

what would be the difference in using a spritesheet versus the same images chopped up via some other software( like GIMP) and set in a directory? I see games use either or.Like sort of hardcoding each individual image in a dict. Its not like you would have to make your own game dynamically grab images at whim, there is only X number of images on each game.

Maybe just frustrated to hell with this.


What i think i am most incorrectly doing is assuming i am using the terminal for output. So for example, if i think i need to iterate through a list of images to display them, a simple for loop. However being the fact that if you blit through a list of images with a for loop you wont see anything but a blur. So the normal way to do this is execute upon x number of loops, however in games you dont want to do this? you base it off time instead? Which i am not good with. I just now first time ever used pygame.time.get_ticks() as i first seen it from your exmaple code Mekire. Other than that i just clock.tick(). So i guess time is considerably more important than i thought it would be. ...Oh am i still typing? I thought i was thinking out loud.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1472
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: chop spritesheet up into usable individual sprites in py

Postby Mekire » Thu Apr 18, 2013 1:48 am

Yes, doing things with for loops and while loops is a bit confusing because we are only getting the next frame every run through the entire game loop. It takes a different way of thinking. I have even experimented with using yield generators so I can get away with using for loops and while loops as we would expect (I wouldn't suggest it though). But yes for basic timing of an animation (or anything really) it will always look something like:
Code: Select all
if pg.time.get_ticks()-self.timer > 1000/self.fps:
    #do stuff
    self.timer = pg.time.get_ticks() #Don't forget to update your timer if the code executes.

pygame.time.Clock.tick() on the other hand is only used to control the frame rate of the entire game; not individual events or animations.

As for individual images vs sheets; I strongly recommend you use sheets. I used to do exactly what you are suggesting. It really does cause unnecessary program bloat and increased loading times. Cutting sprites off a sheet really is just writing a single function which takes as its argument the locations on the sheet. If you write an extremely general version that also takes the size of the desired frame set it should be even more versatile. Another benefit is that you only need to convert or set a color key for the given sheet once; all subsurfaces will retain these properties.

I wouldn't give up on anything just yet; It just takes a different way of approaching certain problems which you will gradually adjust to.

-Mek

Additional: There is another method you could investigate called pygame.time.set_timer(eventid,milliseconds). When you call this, it tells pygame to automatically add an event of type eventid to the event queue every milliseconds . This means that you can tell something to trigger everytime you find this event on your event queue.

For example one way of making a block fall over time in a Tetris game is by putting:
Code: Select all
pg.time.set_timer(pg.USEREVENT,50)
somewhere at the beginning of your code, and then placing something like this in your main event loop:
Code: Select all
for event in pg.event.get():
    if event.type == pg.USEREVENT:
        self.Current.gravity()
where Current is the current block and gravity is a function telling it how to change its location. Every 50 milliseconds this event is automatically placed on the event queue and this piece of code executes.
User avatar
Mekire
 
Posts: 988
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: chop spritesheet up into usable individual sprites in py

Postby metulburr » Thu Apr 18, 2013 9:03 pm

OK. I think i just have to make my own little exercises to work with each element of pygame. I dont really like many peoples examples of pygame as they either write poor python code, or they think you dont know what a class is or how to use it, or they imply that you might know something when you dont.

i have a question regarding attaching a soudn object to the animation of the explosion of your example.
Code: Select all
import os, sys
import pygame as pg
import itertools

pg.mixer.init()

class Bomb:
   def __init__(self,rect):

      self.sound_path = "/home/metulburr/Downloads/Explosion04.wav"
      self.sounder = pg.mixer.Sound(self.sound_path)
      
      self.rect = pg.Rect(rect)
      self.image = None
      self.spritesheet = BOMB_IMG
      self.frame_inds = list(itertools.product(range(6),range(8)))
      self.frame  = 0
      self.frames = [] #all frames off the sprite sheet
      self.timer = 0.0 #timer for animation
      self.fps   = 30.0 #fps of animation
      self.get_images() #rip images from the sprite sheet
      self.done = False
   def get_images(self):
      """Get the desired images from the sprite sheet."""
      for cell in self.frame_inds:
         loc = ((self.rect.width*cell[1],self.rect.height*cell[0]),self.rect.size)
         self.frames.append(self.spritesheet.subsurface(loc))
         self.sounder.play()
      self.make_image()
   def make_image(self):
      if pg.time.get_ticks()-self.timer > 1000/self.fps:
         if self.frame == 15:
            self.sounder.fadeout(1000)
         try:
            self.frame = (self.frame+1)
            self.image = self.frames[self.frame]
            self.timer = pg.time.get_ticks()
         except IndexError:
            self.done = True
            
      if not self.image:
         self.image = self.frames[self.frame]
   def update(self,Surf):
      self.make_image()
      Surf.blit(self.image,self.rect)

############################################
class Control:
   def __init__(self):
      #os.environ['SDL_VIDEO_CENTERED'] = '1' #Center window.
      self.screen = pg.display.set_mode((256,256))
      pg.init()
      pg.mixer.init()
      self.Clock = pg.time.Clock()
      self.done = False
      self.Bombs = []
   def event_loop(self):
      keys = pg.key.get_pressed()
      for event in pg.event.get():
         if event.type == pg.QUIT or keys[pg.K_ESCAPE]:
            self.done = True
         elif event.type == pg.KEYDOWN:
            if event.key == pg.K_SPACE:
               self.Bombs.append(Bomb((0,0,256,256)))
   def update(self):
      while not self.done:
         self.event_loop()
         self.screen.fill(0)
         for Bom in self.Bombs:
            Bom.update(self.screen)
            if Bom.done:
               self.Bombs.remove(Bom)
               
         pg.display.update()
         self.Clock.tick(60)

#####
if __name__ == "__main__":
   RunIt = Control()
   BOMB_IMG = pg.image.load("/home/metulburr/Downloads/explosions.png").convert_alpha()
   RunIt.update()
   pg.quit();sys.exit()


It works but it acts like one channel of sound is alotted. If more Bomb objects are in the list, it ignores multiple sounds.

EIDT: . oh nevermind. I had the self.sound.play() indented inside the for loop, which i didnt mean to do. I did read the mixer had 8 channels ot utilize. Does that mean if more than 8 bomb animations were to go off, that it would not account for 8+ bomb sound? Plus not to mention you would probably have other music/sounds going off too at the same time

By the way. I appreciate your help. It unraveled a lot of the mysteries for me regarding game programming.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1472
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: chop spritesheet up into usable individual sprites in py

Postby Mekire » Fri Apr 19, 2013 5:13 am

Honestly I'm not sure about the channels. For this example I would personally probably put the play function call in the __init__. I also have never experimented with fadeout. One thing though; if you are consistently creating instances of an object (and not say, creating one, once at program start) I wouldn't put any loading in the __init__. This is why I took the explosion image loading out of our init. If you put it in the init, every time we create a new bomb it reloads the image (give it a try; it will freeze up every time you press spacebar).

The ideal situation would be a cache type setup for sounds and images. Any time you want an image call a function that will, if already loaded, provide it for you; and if not, will load it. I again don't often resort to this in programs I make. I generally load all images at program start and place them in a dictionary.

I will tell you however, that in my experience, sound loading in pygame can be a bit twitchy. Quite often you will have to play around with the settings in pygame.mixer.pre_init to get your sounds playing properly.

-Mek

Edit: Note that in your last sample you are still using the version I wrote with a glitch. I iterate over self.Bombs and change the size of the list during iteration. I have since changed it to iterate over self.Bombs[:] instead.
User avatar
Mekire
 
Posts: 988
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: [Pygame] chop spritesheet up into usable individual spri

Postby metulburr » Mon May 13, 2013 10:21 pm

i am not sure why i cannot figure this out. I just dont really understand what is going on here. I tried reimplementing your method into this, and i really just end up making a mess

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.set_image()
       
    def set_image(self):
        for cell in self.frame_inds:
            print(cell)
            print(self.cell_w * cell[0])
            #loc = ((self.rect.width * cell[0], self.rect.height * cell[1]), self.rect.size)
            #self.frames.append(self.spritesheet.subsurface(loc))

       
       
    def update(self, screen):
        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
                   
            self.update()
            pygame.display.update()
           
    def update(self):
        self.door.update(self.screen)
           
app = Control()


so the itertools.product(range(4),range(4)) gives you multiplier for hte cell? But i cant reimplement the width as you did.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1472
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: [Pygame] chop spritesheet up into usable individual spri

Postby Mekire » Mon May 13, 2013 10:56 pm

This:
Code: Select all
loc = ((self.rect.width * cell[0], self.rect.height * cell[1]), self.rect.size)
needs to use the size, width, and height of the cells, not of the whole sheet. I'll write it up later if you don't get it figured before then :p.

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

Re: [Pygame] chop spritesheet up into usable individual spri

Postby DrakeMagi » Tue May 14, 2013 3:39 am

This is how i handle it. I might like framework to much !
Code: Select all
import pygame
import PygameUI.Handler as Handler
from PygameUI.PygameString import PygameString
from PygameUI.Button import Button
pygame.init()

Images = {}

def LoadImages():
   image = pygame.image.load('../Images/Explosion1spritesheet.png')
   image = image.convert_alpha()
   
   Images['Bomb'] = []
   width, height = image.get_size()
   width = width / 8
   height = height / 6
   for row in xrange(6):
      for col in xrange(8):
         rect = pygame.Rect(col * width, row * height, width, height)
         Images['Bomb'].append(image.subsurface(rect))
         
   image = pygame.image.load('../Images/indianajones.png')
   image = image.convert_alpha()
   
   movement = ['Down','Left','Right','Up']   
   Images['Jones'] = {}
   for move in movement:
      Images['Jones'][move] = []
      
   width, height = image.get_size()
   width = width / 4
   height = height / 4
   
   for row in xrange(4):
      for col in xrange(4):
         rect = pygame.Rect(col * width, row * height, width, height)
         Images['Jones'][movement[row]].append(image.subsurface(rect))
   
class Player(object):
   def __init__(self, x, y):
      self.position = (x,y)
      self.image = Images['Jones']
      self.frame = 0
      self.direction = 0 # down
      self.tick = None
      self.movement = ['Down','Left','Right','Up']   
      
   def Draw(self, surface):
      surface.blit(self.image[self.movement[self.direction]][self.frame], self.position)
   
   def _update(self):
      self.frame += 1
      if self.frame > 3:
         self.frame = 0   
            
   def Update(self, tick):
      if self.tick is None:
         self.tick = tick + 100
      elif tick > self.tick:
         self.tick += 100
            
         if screen.GetKeyPress(pygame.K_DOWN):
            if self.direction != 0:
               self.frame = 0
               self.direction = 0
            else:
               self._update()
         elif screen.GetKeyPress(pygame.K_LEFT):
            if self.direction != 1:
               self.frame = 0
               self.direction = 1
            else:
               self._update()
         elif screen.GetKeyPress(pygame.K_RIGHT):
            if self.direction != 2:
               self.frame = 0
               self.direction = 2
            else:
               self._update()
         elif screen.GetKeyPress(pygame.K_UP):
            if self.direction != 3:
               self.frame = 0
               self.direction = 3
            else:
               self._update()

class Bomb(object):
   def __init__(self, x, y):
      self.image = Images['Bomb']
      self.position = (x,y)
      self.frame = 0
      self.tick = None
      self.expire = False
      
   def Draw(self, surface):
      if not self.expire:
         surface.blit(self.image[self.frame], self.position)
         
   def Update(self, tick):
      if self.tick is None:
         self.tick = tick + 60
      elif tick > self.tick:
         self.tick += 60
         self.frame += 1
         if self.frame >= len(self.image):
            self.expire = True

class Main(Handler.Page):
   def __init__(self):
      Handler.Page.__init__(self)
      
      self.Widgets['player'] = Button( PygameString('Test Player', 0, 0, size=24),
                               pygame.Rect(125, 100, 150, 30),
                               self.Push, 'Player', (0,0,120) )
                                                       
      self.Widgets['bomb'] = Button( PygameString('Explosion', 0, 0, size=24),
                              pygame.Rect(125, 140, 150, 30),
                              self.Push, 'Explosion', (0,0,120) )
      
   def Push(self, pydata):
      screen.SetPage(pydata)
                              
   def Draw(self, surface):
      surface.fill( (0,0,0) )
      
      
class PlayerPage(Handler.Page):
   def __init__(self):
      Handler.Page.__init__(self)
      self.player = Player(100,100)
      
      self.Widgets['Back'] = Button( PygameString("Back", 0, 0, size=20),
                              pygame.Rect( 290, 260, 100, 30),
                              self.Push, 'Main', (0,0,120) )
                              
   def Push(self, pydata):
      screen.SetPage(pydata)
      
   def Draw(self, surface):
      surface.fill( (0,0,0) )
      self.player.Draw(surface)
      
   def Update(self, tick):
      self.player.Update(tick)

class Explosion(Handler.Page):
   def __init__(self):
      Handler.Page.__init__(self)
      
      self.Bombs = []
      
      self.Widgets['Back'] = Button( PygameString("Back", 0, 0, size=20),
                              pygame.Rect( 290, 260, 100, 30),
                              self.Push, 'Main', (0,0,120) )
                              
   def Push(self, pydata):
      screen.SetPage(pydata)
      
   def Draw(self, surface):
      surface.fill( (0,0,0) )
      
      for bomb in self.Bombs:
         bomb.Draw(surface)
         
   def Update(self, tick):
      for bomb in self.Bombs:
         bomb.Update(tick)
         
      self.Bombs = [b for b in self.Bombs if not b.expire]
         
   def Event(self, event):
      if event.type == pygame.KEYDOWN:
         if event.key == pygame.K_SPACE:
            self.Bombs.append( Bomb(20,20) )
   
if __name__ == '__main__':
   Handler.CenterWindow()
   screen = Handler.Handler("Testing", 400, 300)
   LoadImages()
   
   screen.Pages['Main'] = Main()
   screen.Pages['Explosion'] = Explosion()
   screen.Pages['Player'] = PlayerPage()
   screen.SetPage('Main')
   
   screen.Loop()
Attachments
PygameUI.tar.gz
(1.59 KiB) Downloaded 115 times
Linux: won't find windows here.
Linux: the choice of a GNU generation.
https://github.com/DrakeMagi
DrakeMagi
 
Posts: 112
Joined: Sun May 12, 2013 8:36 pm

Re: [Pygame] chop spritesheet up into usable individual spri

Postby Mekire » Tue May 14, 2013 4:32 am

Assuming you wrote that framework, the work you are going to for key-presses is pointless. You don't need to create a dict to keep track of the keys that are held. pygame.key.get_pressed() returns a tuple in which the index corresponds to the constant of the keys.

IE:
Code: Select all
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
    #do stuff
You are just making more work for yourself and creating something that ignores the whole purpose of the key-constants actually being index values.

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

Re: [Pygame] chop spritesheet up into usable individual spri

Postby DrakeMagi » Tue May 14, 2013 3:14 pm

After looking at source code for pygame.key.get_pressed(). It looks like a lot of cpu actions.
it create a new tuple every time its called.
Linux: won't find windows here.
Linux: the choice of a GNU generation.
https://github.com/DrakeMagi
DrakeMagi
 
Posts: 112
Joined: Sun May 12, 2013 8:36 pm

Re: [Pygame] chop spritesheet up into usable individual spri

Postby metulburr » Tue May 14, 2013 7:09 pm

ok i finally figure out the chop image, set to frame. Currently i am only interested in the wooden door 4 frames of closing/opening. I just wanted to make sure i could chop the spritesheet up.

1) I was expecting the frames to be from left to right, from top to bottom. However it appears to be from top to bottom, from right to left. Which is OK, i just thought i was doing it the other way. I thought this method would of gone from left to right, from top to bottom:
Code: Select all
loc = ((self.cell_w * cell[0], self.cell_h * cell[1]), (self.cell_w, self.cell_h))


2) Lets assume you have a sprite sheet that you only use half of the sriptes. Would it be cheaper to cut them out and not the load the uneeded sprites, or just load the whole spritesheet and grab jsut the ones you want. It would seem to me that cutting thme out and loading only the ones you are using would be the best, just asking?

3) I am trying to think which would be better. To load the spritesheet, and set each image as a frame as in index number ( which to me justs seems like it would confuse me, or then after assigning a subsurface to the spritesheet, to then set the frame to a dictionary where the key is some name to describe what frame it is. For some reason i would think that i would come back to it, and be like" um what image is that index again" kind if thing a lot. But then it would appear i would have to do each one indeividually, as to name the keys accordingly, which would seem liek a lot of work.

well anyways ok i think i conjured up something that works, at least for index ints
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.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))
           
    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 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.next_frame()
           
            self.screen.fill((0,0,0))
            self.update()
            pygame.display.update()
           
    def update(self):
        self.door.update(self.screen)
           
app = Control()


i mean i could jsut add:
Code: Select all
self.frames = self.frames[12:]

in set_image(), but that jsut seem like a waste and extra loading time
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1472
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: [Pygame] chop spritesheet up into usable individual spri

Postby Mekire » Wed May 15, 2013 1:52 am

1) Yeah, I personally put my sprites on sheets wrapping left to right. It is interesting to note though that because of the way itertools.product works, it is actually easier when using it, to arrange in columns.

2)There is no point in chopping the whole sheet into subsurfaces if you don't need to. We have been using itertools.product to conveniently generate lists of coordinates but that is all it does. If you don't want everything on the sheet then just have self.frame_inds (in this case) be the coordinates of the frames you do want. (you of course shouldn't load a giant image if you are going to only be using 10% of the images on it but this is more an issue of how you arranged your sheet to begin with.)

3)Depends entirely on your sheet and what you are doing. I think categories of animation in dictionaries is good. For instance keys could be ["idle"],["walking"],["running"] etc. Then if you also had different directions the values of those keys would be dicts based on direction. The frames themselves would still be in a list; not individually named. ie frames["idle"]["left"] would give you the list of frames to use while the player was in the idle state facing left.

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


Return to Game Development

Who is online

Users browsing this forum: No registered users and 2 guests

cron