Spritesheet animation in cocos2d?

913 views
Skip to first unread message

Eamonn Rea

unread,
Oct 15, 2013, 1:38:12 PM10/15/13
to cocos-...@googlegroups.com
Hi! I'm developing a game, and in my game I would like to have some sprite sheet animation. It isn't uncommon for games to have sprite sheet animation. I looked into it, and I couldn't find anything on it. So, I decided to do a little DIY thing.

I checked out PyGlet a little more and found the pyglet.image.Animation, which took a list of pyglet.image.AnimationFrame objects, which took an image, which I used cocos.sprite.Sprite() for. So, after a lot of testing, I came up with this:

        playerLeft1 = cocos.sprite.Sprite("assets/img/playerLeft1.png")
        playerLeft2 = cocos.sprite.Sprite("assets/img/playerLeft2.png")
        playerLeft3 = cocos.sprite.Sprite("assets/img/playerLeft3.png")

        playerRight1 = cocos.sprite.Sprite("assets/img/playerRight1.png")
        playerRight2 = cocos.sprite.Sprite("assets/img/playerRight2.png")
        playerRight3 = cocos.sprite.Sprite("assets/img/playerRight3.png")

        playerLeftFrame1 = pyglet.image.AnimationFrame(playerLeft1, .11)
        playerLeftFrame2 = pyglet.image.AnimationFrame(playerLeft2, .11)
        playerLeftFrame3 = pyglet.image.AnimationFrame(playerLeft3, None)

        playerRightFrame1 = pyglet.image.AnimationFrame(playerRight1, .11)
        playerRightFrame2 = pyglet.image.AnimationFrame(playerRight2, .11)
        playerRightFrame3 = pyglet.image.AnimationFrame(playerRight3, None)

        playerLeftAnimationList = [playerLeftFrame1, playerLeftFrame2, playerLeftFrame3]
        playerRightAnimationList = [playerRightFrame1, playerRightFrame2, playerRightFrame3]

        playerLeftAnimation = pyglet.image.Animation(playerLeftAnimationList)
        playerRightAnimation = pyglet.image.Animation(playerRightAnimationList)

I was very proud of myself for being able to do this, after having only used Cocos2d for about 10-15 minutes. But sadly, when I went to run the program, I got the following error:

Traceback (most recent call last):
  File "/Users/eamonn/Desktop/Programming/Python/Game development/2D/Cocos/Mr. BallGuy/main.py", line 52, in <module>
    director.run(helloScene) 
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/cocos2d-0.5.5-py2.7.egg/cocos/director.py", line 371, in run
    self._set_scene( scene )
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/cocos2d-0.5.5-py2.7.egg/cocos/director.py", line 499, in _set_scene
    scene.on_enter()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/cocos2d-0.5.5-py2.7.egg/cocos/scene.py", line 110, in on_enter
    super(Scene, self).on_enter()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/cocos2d-0.5.5-py2.7.egg/cocos/cocosnode.py", line 519, in on_enter
    c.on_enter()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/cocos2d-0.5.5-py2.7.egg/cocos/layer/util_layers.py", line 89, in on_enter
    super(ColorLayer, self).on_enter()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/cocos2d-0.5.5-py2.7.egg/cocos/layer/base_layers.py", line 89, in on_enter
    super(Layer, self).on_enter()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/cocos2d-0.5.5-py2.7.egg/cocos/cocosnode.py", line 519, in on_enter
    c.on_enter()
AttributeError: 'Animation' object has no attribute 'on_enter'

I'm not entirely sure why I would be getting this error. I mean, from what I can tell it's looking through an Animation object and trying to find an attribute called on_enter. I haven't did too much on attributes in Python, but from what I can tell they're basically just userdata, right? It's just an empty value that you can give to an object. To set an attribute you do something like __setattr__ or something like that. I'm not new to Python, but I've been learning so much of it over the past few days! :D

Thanks for reading! Any help is appreciated!

Paul Pittlerson

unread,
Oct 15, 2013, 2:55:33 PM10/15/13
to cocos-...@googlegroups.com

While it's hard to tell exactly what you are doing wrong without seeing the rest of your code, there are a few things to point out right away.

The idea of a sprite sheet is to have all your sprites in one single image, and divide that image up into discrete sprites inside the program. For example you could have something like this:

http://s17.postimg.org/4mwxfrdob/running.png

..where all the images of player is combined in sequence. To access them as an animation you have some options, but I'm quite sure you generally do not want to hard code the animations frame by frame as you are doing. Try something like this:

#!/usr/bin/env python

import cocos
import pyglet

# load the example running.png as a pyglet image
spritesheet = pyglet.image.load('running.png')

# use ImageGrid to divide your sprite sheet into smaller regions
grid = pyglet.image.ImageGrid(spritesheet, 2, 2, item_width=48, item_height=48)

# convert to TextureGrid for memory efficiency
textures = pyglet.image.TextureGrid(grid)

# access the grid images as you would items in a list
# this way you get a sequence for your animation
PlayerRight = textures[:2]
PlayerLeft = textures[2:4]

# create pyglet animation objects from the sequences
MovingRight = pyglet.image.Animation.from_image_sequence(PlayerRight, 0.2, loop=True)
MovingLeft = pyglet.image.Animation.from_image_sequence(PlayerLeft, 0.2, loop=True)

class Main(cocos.layer.Layer):
    def __init__(self):
        super(Main, self).__init__()
     
        # finally, create a cocos sprite which you can add/remove from layers
        player_move_right = cocos.sprite.Sprite(MovingRight)
        player_move_right.position = 100, 100
       
        self.add(player_move_right, name='player')

if __name__ == '__main__':

    width, height = 200, 200
    cocos.director.director.init(width, height, do_not_scale=True,vsync=False)
    cocos.director.director.run(cocos.scene.Scene(Main()))

Eamonn Rea

unread,
Oct 15, 2013, 3:06:09 PM10/15/13
to cocos-...@googlegroups.com
This was my original plan, but I didn't think it was possible! Thanks! :-)

Nitneroc

unread,
Oct 15, 2013, 4:49:42 PM10/15/13
to cocos-...@googlegroups.com
I'm pretty sure the problem with your original solution is that you were using pyglet Animation class as a cocos node. You should this use this Animation as the image for your Sprite ie : player = Sprite(playerLeftAnimation) and you can change the animation by changing player.image.

Eamonn Rea

unread,
Oct 16, 2013, 3:43:35 PM10/16/13
to cocos-...@googlegroups.com
Well now I have a problem! I have both my images (one sheet for the player moving left and one for right (I'd like to have it this way)), but both of them draw... and only draw. The animation doesn't play. Is there a way I can make it so an image wont draw in Cocos unless I set it to, and also to stop/start an animation when I want too? If you want *all* my code:

import cocos, pyglet

windowWidth = 800
windowHeight = 600

title = "Mr. BallGuy"

class Player(cocos.cocosnode.CocosNode):
    def __init__(self):
        self.x = 100
        self.y = 100
        self.w = 32
        self.h = 32

        self.playerSheetLeft = pyglet.image.load("assets/img/playerLeft.png")
        self.playerSheetRight = pyglet.image.load("assets/img/playerRight.png")

        self.playerLeftGrid = pyglet.image.ImageGrid(self.playerSheetLeft, 1, 1, item_width=32, item_height=32)
        self.playerRightGrid = pyglet.image.ImageGrid(self.playerSheetRight, 1, 1, item_width=32, item_height=32)

        self.playerLeftTexture = pyglet.image.TextureGrid(self.playerLeftGrid)
        self.playerRightTexture = pyglet.image.TextureGrid(self.playerRightGrid)

        self.playerLeftAnim = pyglet.image.Animation.from_image_sequence(self.playerLeftTexture, .32, loop=True)
        self.playerRightAnim = pyglet.image.Animation.from_image_sequence(self.playerRightTexture, .32, loop=True)

player = Player()

class Playing(cocos.layer.Layer): 
    def __init__(self): 
        super(Playing, self).__init__() 

        player.playerLeft = cocos.sprite.Sprite(player.playerLeftAnim)
        player.playerRight = cocos.sprite.Sprite(player.playerRightAnim)

        player.playerLeft.position = player.x, player.y

        self.add(player.playerLeft, name="playerLeft")
        self.add(player.playerRight, name="playerRight")

if __name__ == '__main__':
    cocos.director.director.init(windowWidth, windowHeight, resizable=False, vsync=False, caption=title) 

    playing = Playing() 

    playingScene = cocos.scene.Scene(playing) 

    cocos.director.director.run(playingScene) 

Mikko Finell

unread,
Oct 16, 2013, 4:36:07 PM10/16/13
to cocos-...@googlegroups.com
On 16.10.2013 22:43, Eamonn Rea wrote:
> Well now I have a problem! I have both my images (one sheet for the
> player moving left and one for right (I'd like to have it this way)),
> but both of them draw... and only draw. The animation doesn't play. Is
> there a way I can make it so an image wont draw in Cocos unless I set
> it to, and also to stop/start an animation when I want too? If you
> want *all* my code:
>
Your animation does not play because it's only 1 image.

Take a look at this:
> self.playerLeftGrid =
> pyglet.image.ImageGrid(self.playerSheetLeft, 1, 1, item_width=32,
> item_height=32)
> self.playerRightGrid =
> pyglet.image.ImageGrid(self.playerSheetRight, 1, 1, item_width=32,
> item_height=32)
>

The 3 first arguments of ImageGrid are "image", "rows" and "columns".
You have passed 1, 1 for rows and columns, meaning that the grid only
contains one region, instead of a series of regions.

When you create the animations:
> self.playerLeftAnim =
> pyglet.image.Animation.from_image_sequence(self.playerLeftTexture,
> .32, loop=True)
> self.playerRightAnim =
> pyglet.image.Animation.from_image_sequence(self.playerRightTexture,
> .32, loop=True)

self.playerRightTexture is just a single image, the animation appears to
not be playing because it has no other frames to iterate over.

Eamonn Rea

unread,
Oct 18, 2013, 1:36:33 PM10/18/13
to cocos-...@googlegroups.com
Thanks a bunch :-D I' m new to Cocos and PyGlet, and I'm getting used to the transition between them and PyGame.

I just have 1 more problem: How do I stop certain images from drawing? I can't find anything anywhere. I mean, I want my left sprite sheet to draw when facing left and my right one to draw when I'm facing left. I know how to do the facing thing, but not how to stop drawing one of them. Is there a self.remove()?

claudio canepa

unread,
Oct 18, 2013, 2:12:49 PM10/18/13
to cocos2d discuss
On Fri, Oct 18, 2013 at 2:36 PM, Eamonn Rea <eamo...@gmail.com> wrote:
Thanks a bunch :-D I' m new to Cocos and PyGlet, and I'm getting used to the transition between them and PyGame.

I just have 1 more problem: How do I stop certain images from drawing? I can't find anything anywhere. I mean, I want my left sprite sheet to draw when facing left and my right one to draw when I'm facing left. I know how to do the facing thing, but not how to stop drawing one of them. Is there a self.remove()?
  -- 

You can hide a node by doing
    node.visible = False

Anyway, having a sprite for playerleft and other for player right don't feels good; the usual thing is to have one sprite and some methods to deal with animations, like
    def start_animation(self, anim_name):
         ...

    def update_animation(self, dt):

Those methods will care to put the appropiate image in the sprite.


Reply all
Reply to author
Forward
0 new messages