omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Sprite animation from sprite sheet at 8 FPS?

    Pythonista
    sprite spritesheet game scene module animation
    4
    16
    7502
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Vile
      Vile last edited by

      I have a png file containing 4 frames of a sprite animation sequence, aligned horizontally.

      By checking the forum I found out how to display each frame separately from the same image, by using subtexture(rect), so this isn’t the problem here.

      I want to run a loop during the game screen, which iterates through each frame in the list of image textures taken from the sprite sheet, every 0.125 seconds, so that the sprite is animated at 8 frames per second. This should not prevent other parts of the program from running at the default 60 FPS.

      I do not want to use any functions which pause the entire program, especially not the sleep function from the time module! This is because the sleep function pauses my program and makes everything stop working every few seconds, which is annoying and pointless.

      My sprite’s position is constantly at the centre of the screen, so animation by number of pixels moved (like in the Tutorial Part 3 file in the Game Tutorial folder) will not help.

      Here is my code so far:

      from scene import *
      
      sprite_sheet = [
      Texture('IMG_0227.png').subtexture(Rect(0,0,0.25,1)),
      Texture('IMG_0227.png').subtexture(Rect(0.25,0,0.25,1)),
      Texture('IMG_0227.png').subtexture(Rect(0.5,0,0.25,1)),
      Texture('IMG_0227.png').subtexture(Rect(0.75,0,.25,1)),
      ]
      
      class MyScene (Scene):
      	def setup(self):
      		self.screen="game"
      		self.background_color = 'black'
      		self.sprite = SpriteNode(sprite_sheet[0],
      		scale = 1,
      		position = self.size / 2,
      		parent = self)
      		self.add_child(self.sprite)
      		n=0
      		for sprite in sprite_sheet: #i want this to loop infinitely but only during the game screen. i don't want to use while because it crashes the app, probably because the images keep loading and appearing infinitely?
      			self.sprite.texture = sprite_sheet[n] #my attempt at iterating through the list of sprite frames.
      			n=n+1
      			'''wait 0.125 seconds to make sprite change image at 8 frames per second, to make an animation'''
      run(MyScene())
      

      I would appreciate any help, since this is a key part of making a game with working animations.

      1 Reply Last reply Reply Quote 0
      • pulbrich
        pulbrich last edited by

        Move the sprite changing code to the update method of your scene; and do the change only when enough time passed since the last change.

        Scene.update()
        Performs any scene-specific updates that need to occur before scene actions are evaluated.

        Vile 1 Reply Last reply Reply Quote 0
        • JonB
          JonB last edited by

          Take a look at the Game Tutorial in the examples folder... I think the second or third installment, where it shows how to animate walking. As stated above, it should be called from update -- or, you could create an Action.sequence consisting of ab Action.wait, and an Action.call, which calls a method you create that sets the texture to the next one. (add a texture_idx and texture_list as attributes of your SpriteNode, so you can easily cycle through them). Then wrap the sequence in an Action.repeat, set to repeat forever.

          Or, you could simplify even more, and have a single Action.call (wrapped in a repeat), where you include the duration parameter. The duration would be the time it takes to execute the entire sequence -- within the call, you'd check if progress*len(textures) is different than your current index, and if so, you'd set the texture_idx to that value, and then set the texture . That eliminates the wait, and might allow you to use different timing nodes (though maybe not)

          Vile 2 Replies Last reply Reply Quote 0
          • Vile
            Vile @pulbrich last edited by

            @pulbrich, could you give an example of how to check that the correct amount of time has passed?

            1 Reply Last reply Reply Quote 0
            • Vile
              Vile @JonB last edited by

              @JonB, I’ve already looked at the game tutorial, where it showed how to animate a walking sequence. However, that method only works when the sprite is changing its position in pixels on the screen, so it will not work for my sprite. This is because my sprite’s position is constantly at the centre of the screen.

              The effect I’m trying to achieve is basically a GIF with a customisable frame speed, which is permanently in exactly the same position at the centre of the screen.

              1 Reply Last reply Reply Quote 0
              • Vile
                Vile @JonB last edited by

                @JonB, I’ve also never used Action.wait before, so I’d appreciate an example of this, thanks.

                Remember, I’m still a noob here lol

                1 Reply Last reply Reply Quote 0
                • pulbrich
                  pulbrich last edited by

                  @Vile :

                  import time #this module is needed to get the system time
                  

                  In your set-up code:

                  self.last_update = time.process_time()
                  

                  In your update method:

                  current_time = time.process_time()
                  if current_time - self.last_update > 0.125:
                  	self.last_update = current_time
                  	modify_the_sprite
                  
                  Vile 1 Reply Last reply Reply Quote 1
                  • Vile
                    Vile @pulbrich last edited by

                    @pulbrich

                    I tried your method, but it seems that anything I put into the update function doesn’t get used...

                    Here’s the updated code:

                    from scene import *
                    import time
                    
                    sprite_sheet = [
                    Texture('IMG_0227.png').subtexture(Rect(0,0,0.25,1)),
                    Texture('IMG_0227.png').subtexture(Rect(0.25,0,0.25,1)),
                    Texture('IMG_0227.png').subtexture(Rect(0.5,0,0.25,1)),
                    Texture('IMG_0227.png').subtexture(Rect(0.75,0,.25,1)),
                    ]
                    
                    class MyScene (Scene):
                    	def setup(self):
                    		self.screen="game"
                    		self.background_color = 'black'
                    		self.last_update = time.process_time()
                    		self.sprite = SpriteNode(sprite_sheet[0],
                    		scale = 1,
                    		position = self.size / 2,
                    		parent = self)
                    		self.add_child(self.sprite)
                    		n=0
                    		
                    		def update(self):
                    			current_time = time.process_time()
                    			if current_time - self.last_update > 0.125:
                    				self.last_update = current_time
                    				#modify_the_sprite
                    				self.sprite.texture=sprite_sheet[n]
                    				n=n+1
                    run(MyScene())
                    
                    1 Reply Last reply Reply Quote 0
                    • Vile
                      Vile last edited by

                      Wait it actually works, I just had the wrong indentation on the update function XD

                      Here’s the code:

                      from scene import *
                      import time
                      
                      sprite_sheet = [
                      Texture('IMG_0227.png').subtexture(Rect(0,0,0.25,1)),
                      Texture('IMG_0227.png').subtexture(Rect(0.25,0,0.25,1)),
                      Texture('IMG_0227.png').subtexture(Rect(0.5,0,0.25,1)),
                      Texture('IMG_0227.png').subtexture(Rect(0.75,0,.25,1)),
                      ]
                      
                      class MyScene (Scene):
                      	def setup(self):
                      		self.screen="game"
                      		self.background_color = 'black'
                      		self.last_update = time.process_time()
                      		self.sprite = SpriteNode(sprite_sheet[0],
                      		scale = 1,
                      		position = self.size / 2,
                      		parent = self)
                      		self.add_child(self.sprite)
                      		self.n=0
                      		
                      	def update(self):
                      		#self.sprite.texture=sprite_sheet[1]
                      		current_time = time.process_time()
                      		if current_time - self.last_update > 0.05:
                      			self.last_update = current_time
                      			#modify_the_sprite
                      			self.sprite.texture=sprite_sheet[self.n]
                      			self.n=self.n+1
                      			if self.n > 3:
                      				self.n=0
                      run(MyScene())
                      

                      Thanks a lot for your help!

                      1 Reply Last reply Reply Quote 0
                      • JonB
                        JonB last edited by

                        dont use time.process_time. instead use scene.t -- scene includes t which is the time since the start of the scene, and dt, which is the time since last update.

                        Vile 1 Reply Last reply Reply Quote 1
                        • Vile
                          Vile @JonB last edited by

                          @JonB, I tried using scene.t and scene.dt, but I can’t get it working...

                          1 Reply Last reply Reply Quote 0
                          • pulbrich
                            pulbrich last edited by pulbrich

                            Instead of using the time function that I suggested, put Scene.t (mind the capital S)

                            CORRECTION: it is self.t (instance attribute), thanks @JonB

                            self.dt is not directly suitable for your case; it gives you the time lapsed since the last call to update().

                            1 Reply Last reply Reply Quote 0
                            • JonB
                              JonB last edited by

                              I think it should be self.t.
                              it is an instance attribute, not a class attrib, if memory serves -- the time is incremented in the running scene only.

                              1 Reply Last reply Reply Quote 1
                              • cvp
                                cvp last edited by

                                Following @JonB advice, this works

                                .
                                .
                                .
                                        self.last_t = self.t
                                        
                                    def update(self):
                                        #self.sprite.texture=sprite_sheet[1]
                                        if (self.t-self.last_t) > 0.05:
                                            #modify_the_sprite
                                            self.last_t = self.t
                                .
                                .
                                .
                                
                                1 Reply Last reply Reply Quote 1
                                • Vile
                                  Vile last edited by

                                  @JonB and @cvp, thanks once again! It seems to work in the way I expected now.

                                  It seems that scene.t uses a different time frame from time.process_time, because with the same number value, the animations appear to go at different speeds.

                                  cvp 1 Reply Last reply Reply Quote 0
                                  • cvp
                                    cvp @Vile last edited by

                                    @Vile thank only @jonb 😀

                                    1 Reply Last reply Reply Quote 0
                                    • First post
                                      Last post
                                    Powered by NodeBB Forums | Contributors