Forum Archive

Sprite animation linked with an action

jackattack

Hey people very new to all this but having a great time. So here’s the issue, I’ve been working on sprite animation, using the change texture feature and a sprite sheet. What I now want is for my sprite animation to happen when I run an action. So for example I press a move down button and my character transitions through a walking animation. I can’t figure out a way to get something like this to happen.

My main issue is that for my animation to work it needs to be in Update and all my actions are in touch began.

I’ve seen the game tutorial using the players position to update the spritesheet but I don’t know how this would work for different directions, like up, down, left, right.

Anyway I hope this question makes some sense, I’ve been working really hard to improve but just can’t seem to figure this one out, any help is really truly appreciated

jackattack

I was wondering is this could be done with Action.call()? I have never used this feature and can’t really get it working so if someone could give me an example of how to set up a custom action I might be able to adapt that to my needs

stephen

@jackattack

Hey buddy! I have a "full" example game for new developers to take a look at here on the forums. He game doesn't use sprite sheets but it should give you a good understanding of the animation system 😀

While you look this over I will prepare an example for your specific use and give some comment explanation of what's going on! I got 4 kiddos so just bare with me lol I'm not as fast as I used to be buddy!

Here Is the post for the example Game 😉

stephen

@jackattack

Also can you post the code you are using just in case it's just a small mishap? Make sure to put the code inside two ``` so that it is formatted properly

Checkout the *** COMPOSE *** on top right when your writing a post 😀

jackattack

Hey thanks so much for getting back to me, I’ll take a look at the examples, I’m a bit busy myself at the moment but I’ll try to get the code I’m using to you ASAP. Be warned it’s a hot mess cause I’m just practicing some ideas at the moment, and I’m also very bad at this 😂

mikael

@jackattack, one idea:

  • In your touch handler, set a variable with up/down/left/right based on the main direction of movement, then use an Action to move the SpriteNode
  • In update, select the right set of textures based on the up/down/left/right variable, then the actual image based on ”coordinate modulo number of images”. You can scale dividing the coordinate value first. So, for example, if moving up and you want the image to change every 10 points, you can set the sprite texture with up_images[node.position.y // 10 % len(up_images)].
jackattack

from scene import *
import sound
import random
import math
A = Action

sprite_sheet=[Texture('scene_practice/new_sprites/IMG_3039.JPG').subtexture(Rect(0,0.5,0.33,0.25)),Texture('scene_practice/new_sprites/IMG_3039.JPG').subtexture(Rect(0.33,0.5,0.33,0.25)),Texture('scene_practice/new_sprites/IMG_3039.JPG').subtexture(Rect(0.66,0.5,0.33,0.25))]

class MyScene (Scene):
    def setup(self):
         self.screen="game"
         self.background_color = 'black'

         self.Sprite = SpriteNode(sprite_sheet[0],scale = 4, position = (self.size.x/2,self.size.y/1.1), parent = self)
         self.add_child(self.Sprite)
         self.n = -1

         self.button = SpriteNode('shp:Circle',scale=2,position=(self.size.x/1.5,self.size.y/3))
         self.add_child(self.button)


    def did_change_size(self):
        pass

    def update(self):

        step1 = int(self.Sprite.position.y / 15) % 3
        if step1 != self.n:
            self.Sprite.texture = sprite_sheet[step1]

    def touch_began(self, touch):
        moveaction = Action.move_by(0,-46)

        if touch.location in self.button.bbox:
            self.Sprite.run_action(moveaction)

if __name__ == '__main__':
    run(MyScene(), show_fps=False)

Okay so here is a basic version of what I have been doing.

So in my Update method I’m using the y coordinate of my sprite to return an integer 0 or 1 or 2, then I’m using this as a reference To set the texture from my spritesheet, basically just what was being done in the game tutorial.

The thing I’m stuck with is how I can expand this idea to another direction, so for instance if I wanted to go up instead of down I would need to use an entirely different sprite sheet but I don’t know how to do that

What I would really like to do if possible is tie the whole animation process to the button press so I could animate a more diverse range of things not just relying on position? Does that make sense?

I’m so grateful for you guys taking time to read my stuff and help, gives me real motivation to keep learning, so thanks again everyone

stephen

@jackattack
Any chance we can get the sprite sheet your using?

jackattack

Sure can do, what format can I post it on here? I basically just cropped one from online to make it easier to work with

stephen

@jackattack said:

Sure can do, what format can I post it on here?

You will have to upload somewhere then hyperlink it. I personally use Imgur

@jackattack said:

I basically just cropped one from online to make it easier to work with

no problem its just to help replicate the same execution you have 😎

jackattack

my sprite sheet
Just using one row of this in my code for a basic walk forward animation

stephen

@jackattack

here is my script example. sorry it took a while lol. its not very clean but i think it should be easy to follow. let me know if you have any questions. you should benable to use thenSprite class to create other objects and as you will see this approach doesnt use the update method at all or depend on position.


from scene import *
from time import sleep
import sound
import random
import math

A = Action

Character_Male='IMG_3039.jpg'

def get_texture(sheet, x, y, w, h):
    '''
    for cleaner Code
    '''
    return Texture(sheet).subtexture(Rect(x, y, w, h))


class Direction:
    '''
    simple Constants
    '''
    NORTH="north"
    SOUTH="south"
    EAST="east"
    WEST="west"

class Sprite(SpriteNode):
    '''
    Use this class for game objects
    '''
    def __init__(self,sprite_sheet, *args, **kwargs):
        self.sprite_sheet=sprite_sheet
        self.animations=dict(
            {"idle":get_texture(self.sprite_sheet, 0.00, 0.75, 0.33, 0.25)})
        SpriteNode.__init__(self, texture=self.animations["idle"], *args, **kwargs)

        self.movement_speed=50
        self.active_direction=Direction.SOUTH
        self.Texture_Group(
            "move-north", 
            [
                get_texture(self.sprite_sheet, 0.00, 0.75, 0.33, 0.25),
                get_texture(self.sprite_sheet, 0.33, 0.75, 0.33, 0.25),   
                get_texture(self.sprite_sheet, 0.66, 0.75, 0.33, 0.25)
            ])
        self.Texture_Group(
            "move-south", 
            [
                get_texture(self.sprite_sheet, 0.00, 0.50, 0.33, 0.25),
                get_texture(self.sprite_sheet, 0.33, 0.50, 0.33, 0.25),
                get_texture(self.sprite_sheet, 0.66, 0.50, 0.33, 0.25)
            ])
        self.Texture_Group(
            "move-east", 
            [
                get_texture(self.sprite_sheet, 0.00, 0.25, 0.33, 0.25),
                get_texture(self.sprite_sheet, 0.33, 0.25, 0.33, 0.25),
                get_texture(self.sprite_sheet, 0.66, 0.25, 0.33, 0.25)
            ])
        self.Texture_Group(
            "move-west", 
            [
                get_texture(self.sprite_sheet, 0.00, 0.00, 0.33, 0.25),
                get_texture(self.sprite_sheet, 0.33, 0.00, 0.33, 0.25),
                get_texture(self.sprite_sheet, 0.66, 0.00, 0.33, 0.25)
            ])


    # decorator alows the use of time.sleep() without blocking     
    @ui.in_background
    def Run_Animation(self, tag):
        for x in self.animations[f"{tag}-{self.active_direction}"]:
            self.texture = x
            sleep(0.2)
        self.texture=self.animations[f"{tag}-{self.active_direction}"][0]

    def Texture_Group(self, tag, texture_list):
        self.animations[tag]=texture_list

    def Change_Direction(self, direction):
        self.active_direction=direction
        if direction == Direction.SOUTH:
            self.velocity=(0.0, -self.movement_speed)
        elif direction == Direction.NORTH:
            self.velocity=(0.0, self.movement_speed)
        elif direction == Direction.EAST:
            self.velocity=(-self.movement_speed, 0.0)
        elif direction == Direction.WEST:
            self.velocity=(self.movement_speed, 0.0)


class Button(SpriteNode):
    '''
    Simple Button Object
    '''
    def __init__(self, text, meta, action=None, *args, **kwargs):
        SpriteNode.__init__(self, Texture('pzl:Gray7'), *args, **kwargs)
        self.texture=Texture('pzl:Gray7')
        self.action=action
        self.text=LabelNode(text, anchor_point=(0.5, 0.5), color="yellow", parent=self)

        self.meta=meta

    # Not used in this example but can be lol
    def Pressed(self):
        self.action(self)

class MyScene (Scene):
    def setup(self):
        self.screen="game"
        self.background_color='black'
        self.buttons=[]

        self.player=Sprite(Character_Male, position=self.size/2, scale=4, parent=self)

        self.n = -1


        self.up_button=Button(
            "↑",
            Direction.NORTH,
            position=Point(get_screen_size()[0]/4*3, 270),
            scale=2,
            parent=self)
        self.buttons.append(self.up_button)

        self.down_button=Button(
            "↓",
            Direction.SOUTH,
            position=Point(get_screen_size()[0]/4*3, 130),
            scale=2,
            parent=self)
        self.buttons.append(self.down_button)

        self.right_button=Button(
            "→",
            Direction.WEST,
            position=Point(get_screen_size()[0]/4*3+70, 200),
            scale=2,
            parent=self)
        self.buttons.append(self.right_button)

        self.left_button=Button(
            "←",
            Direction.EAST,
            position=Point(get_screen_size()[0]/4*3-70, 200),
            scale=2,
            parent=self)
        self.buttons.append(self.left_button)

    def touch_began(self, touch):
        for btn in self.buttons:
            if touch.location in btn.bbox:
                self.player.Change_Direction(btn.meta)
                self.player.run_action(
                    Action.move_by(
                        self.player.velocity[0],
                        self.player.velocity[1]))
                self.player.Run_Animation("move")

    def touch_ended(self, touch):
        pass


if __name__ == '__main__':
    run(MyScene(), show_fps=True)
jackattack

Wow this works perfectly thank you so much, I’m going to take some time to study it and try to learn how to recreate it with some other effects. Thanks for taking the time out 😌

jackattack

@stephen

I’ve learnt so much reading your code, love the way you’ve done so many things, spent ages going through it all. Could you give me any insight into the order you went about writing it?

jackattack

Also the only bit I didn’t really understand was when you set up the button class you used init, but then you did spritenode.init what is this for?

stephen

@jackattack said:

Also the only bit I didn’t really understand was when you set up the button class you used init, but then you did spritenode.init what is this for?

when you subclass an object that has __init__ paramiters you must either call the Parent class __init__ as i did here or you can use super(). if you do not call the parent's __init__ then the new object will not inherit the dirived.


@jackattack said:

I’ve learnt so much reading your code, love the way you’ve done so many things, spent ages going through it all. Could you give me any insight into the order you went about writing it?

I usually start with utility classes. in my Example game this would be stuff like Screen() and EventManager(), then visual testing. at this point i woud start my ButtonNode() and Animations().. now i create my Player(), GUI() and o on. the order on the script itself doesnt matter with Object Orientated Programing (OOP) just remember the Interpreter exec the scripts top down. so in order to Dirive from from Class A it must exist before Class B.


@jackattack said:

Wow this works perfectly thank you so much, I’m going to take some time to study it and try to learn how to recreate it with some other effects. Thanks for taking the time out 😌

Not a problem at all! 🥂💯 i enjoy helping others if you have any equestions please dont hesitate!;