Fast Frame Swap

2

Comments

  • LumpAppsLumpApps Member Posts: 2,880
    edited January 2014
    Cool Socks! I made something like this before. Here it the framerate depends on the speed the character has.

  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @slowcut
    @JSproject
    @Socks if you [dump] the floor functions, double check it on the device.

    I have made the experience, that my iOS devices will calculate more accurate than the GS Viewer.
    @Socks Great find and very well described (as per usual). Also, I second what @slowcut wrote.
    Cheers for the input :)>-

    Just checked this on an iPad 1 and iPad 3, dumping the 'floor' function works just fine, it behaves on the device exactly as it does in the viewer, it doesn't seem to need the 'floor' function.
  • SocksSocks London, UK.Member Posts: 12,822
    Cool Socks! I made something like this before. Here it the framerate depends on the speed the character has.

    Nice ! :)>-

    It really extends the usefulness of animated loops to be able to speed them up and slow them down dynamically.
  • CodeMonkeyCodeMonkey Head Chef, Member, PRO Posts: 1,803


    I also addressed this in my original post . . .

    If your sequence isn't just numerals, but instead has an actual name (so basically an image sequence going Filename_0, Filename_1, Filename_2, Filename_3, Filename_4, Filename_5 . . . etc) then simply add it in like this:


    I was just adding some bonus logic tips to yours from what I did with Battle Legend Infinity, Too Fat To Fly and some other games. It comes in handy if you want to use the same actor with different skins. You don't need to add code with the hard coded name, and you don't need need to write out all the frames of animation in the table.

    Defining which frame the animation starts on is simple enough using an offset:
    (floor(( self.Time *30)+19)%25)

    This runs at 30 fps, it starts on frame 19 and the sequence/loop is 25 frames long.
    My thoughts of using any Time as the variable as an issue was not that you don't know where to start your animation because you want to start at frame 19 instead of frame 1, but that Time is mostly something you can't totally control and the animation is affected when you start it.
    The method you present is great for spawned items if you use self.Time and want to start the animation right away. (Explosions, etc)
    Though if you create a Real attribute to store the current game.Time(or self.Time) to subtract from the game.Time(or self.Time), then you can control the starting point of Time to 0, relative to when you started the animation.
    For example, if your character(actor) is running through the game, then gets hit by an ice ball, you could animate him to get encased in ice. But if you don't know when that will happen, just using self.Time or game.Time will give you undesired results.

    Also, please use mod(x,y) instead of %. % doesn't work everywhere and got replaced with mod, which does. Thanks.
  • BBEnkBBEnk Member Posts: 1,764
    edited January 2014
    @CodeMonkey

    I see you say use Mod(x,y) a lot but I don't really understand how to use it can you rewrite the code @Socks is using with it..I'm better when I can see a actual example of working code.

    (floor(( self.Time *30)+19)%25)

    add Mod(x,y) version here.. ;)
  • SocksSocks London, UK.Member Posts: 12,822
    I was just adding some bonus logic tips to yours from what I did with Battle Legend Infinity, Too Fat To Fly and some other games.
    Cheers for the input, it's all useful information !

    If you have the time (I know you are busy) could you post an example of what you mean by ". . . counter attribute with a Timer behavior with 1/FPS for the every timer . . . " ?
    For example, if your character(actor) is running through the game, then gets hit by an ice ball . . . .
    [runs off and copyrights IceBallsDeathRun™]
    . . . you could animate him to get encased in ice. But if you don't know when that will happen, just using self.Time or game.Time will give you undesired results.
    All these issues are trivially easy to address, in your example here you'd simply trigger the change on collision ? Unless I'm misunderstanding what you are saying ?
    Also, please use mod(x,y) instead of %. % doesn't work everywhere and got replaced with mod, which does. Thanks.
    Cheers, yep, I must get into the habit of mod(x,y) rather than %, old habits die hard.
  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @BBEnk
    @CodeMonkey

    I see you say use Mod(x,y) a lot but I don't really understand how to use it . . . .
    With mod(x,y), the 'x' is the value that is being cycled and y is the length of the cycle.

    So this . . .

    self.Time *30%25

    would become this . . .

    mod(game.time*30,25)


  • slowcutslowcut Member, PRO Posts: 164
    @BBEnk
    @CodeMonkey

    (floor(( self.Time *30)+19)%25)

    add Mod(x,y) version here.. ;)
    floor(mod(self.Time*30+19),25)

  • JSprojectJSproject Member Posts: 730
    edited January 2014
    floor(mod(((self.Time*30)+19),25))

    with reservation for being tired ;)
  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @slowcut
    floor(mod(self.Time*30+19),25)

    You have your brackets in the wrong place !

    It should be - floor(mod(self.Time*30+19,25))

    [/pedantry] :)


    Also as we can loose 'floor', we can also loose the brackets around 'mod', so it only need be:

    mod(self.Time*30+19,25)


  • slowcutslowcut Member, PRO Posts: 164
    @socks you are absolutely right, my mistake
  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @socks you are absolutely right, my mistake
    It's too late, you've already gone on my "someone got something very slightly wrong" list . . . come the day of the great revolution . . . .

    ;)
  • BBEnkBBEnk Member Posts: 1,764
    took me a minute but this is what I got to work..

    floor(mod(( self.Time *30),145)+1).."-Untitled.png" "145 frames and no Zero".
  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @BBEnk
    took me a minute but this is what I got to work..

    floor(mod(( self.Time *30),145)+1).."-Untitled.png"

    "145 frames and no Zero".

    Yep, that should work just fine :)>-

    You can, if you want, simply it further . . .

    mod((self.Time*30),145)+1.."-Untitled.png"

    And - if it's a loop that doesn't need to start on a particular frame - simplify it even further by taking frame 145 and renaming it frame 0 . . . .

    mod((self.Time*30),145).."-Untitled.png"




  • BBEnkBBEnk Member Posts: 1,764
    @Socks

    I tried it without floor and it want run, only works when I have floor in the math.
  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @BBEnk
    @Socks

    I tried it without floor and it want run, only works when I have floor in the math.
    I'm going to make a guess that this is because you have the filename after the numerical sequence . . .

    So you have

    1-Untitled.png
    2-Untitled.png
    3-Untitled.png
    4-Untitled.png
    5-Untitled.png
    6-Untitled.png
    7-Untitled.png
    8-Untitled.png
    9-Untitled.png

    . . . etc

    So unless you use floor, then self.time will be returning values like this:

    1.8983-Untitled.png
    2.2827-Untitled.png
    3.4539-Untitled.png

    . . . and GameSalad is unable to (or simply doesn't) remove the decimal places when they are in the middle of a name, it only cuts them off the end . .

    Like I say this is all a guess . . . . going to test it now . . .
  • CodeMonkeyCodeMonkey Head Chef, Member, PRO Posts: 1,803
    edited January 2014
    I'll do a long version but quickly. It does use a few self attributes, but it makes it pretty dynamic.

    Attributes:
    self.Counter integer attribute that does the frame value.
    self.CurrentAnimation integer that references which animation to do
    self.doAnimation integer that stores the value of the animation that was called
    self.animationDirection integer that determines if it is a forward or reverse animation [[1,-1]]
    self.MaxFrames integer that stores max number of frames in the animation
    self.FPS integer that stores number of frames per second
    self.baseImage text attribute that stores the base name for the animation


    When self.CurrentAnimation = self.doAnimation [[This is to start the new animation and reinitialize the Timer]]
    - Change Attribute: self.FPS = [FPS for self.CurrentAnimation from a table]
    - Change Attribute: self.baseImage = [base image name for self.CurrentAnimation from a table]
    - Change Attribute: self.MaxFrames = [max frames for self.CurrentAnimation from a table]
    - Change Attribute: self.animationDirection = [direction for self.CurrentAnimation from a table]
    - Change Attribute: self.Counter = 1 [[Reset the Counter]]
    -Timer(Every 1/self.FPS)
    --Change Attribute: self.Image = self.baseImage..self.Counter
    --Change Attribute: self.Counter = mod(self.Counter+self.MaxFrames+self.animationDirection,self.MaxFrames)

    Otherwise [[Update self.CurrentAnimation to start the newly called animation]]
    -Change Attribute: self.CurrentAnimation = self.doAnimation



    I think that is it.
  • SocksSocks London, UK.Member Posts: 12,822
    @BBEnk

    Yep, it seems to be because you have your numeral before the file name . . .

    So

    Untitled-1
    Untitled-2
    Untitled-3
    Untitled-4

    . . . etc, doesn't need 'floor', whereas . . .

    1-Untitled
    2-Untitled
    3-Untitled
    4-Untitled

    . . . etc, does need floor.



  • SocksSocks London, UK.Member Posts: 12,822
    @CodeMonkey

    Thanks for posting that, I'll plough through it later, it looks to be a pretty comprehensive way of wresting control over image sequences !

    Great stuff :)>-
  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @CodeMonkey
    . . . .

    Timer(Every 1/self.FPS)
    --Change Attribute: self.Image = self.baseImage..self.Counter
    --Change Attribute: self.Counter = mod(self.Counter+self.MaxFrames+self.animationDirection,self.MaxFrames)

    . . . .
    One problem with this from what I can see (I may of course be wrong), is a problem I've discussed with @tatiang before, and that seems to be a limitation with the timer, this is something I've found when trying out some of the different looping methods posted over the past year or so, like HyperLoops™ and FastForLoop™ and SuperDeathSpiralLoop™ . . . (ok, I may have made that last one up) . . is that I simply cannot get the timer to go past 1/30th of second - so the Timer(Every 1/self.FPS) part of your method tops out at 1/30 - values higher than 1/30 (e.g. 1/40, 1/50, 1/60 etc) will play back at 1/30th of second, values below 1/30 will honour the values entered.

    Even every 0 seconds tops out at 1/30th of a second.

    This is the reason I ended up playing around with game.time / self.time as an animation tool - when using these otherwise excellent methods (otherwise excellent meaning when not dealing with image sequences) is the 30fps limit with the timer behaviour, and if it did play faster than 30fps is was only when using duplicate rules, in which case the frame rate jumped straight to 60fps, so they tended to go from 'a little too slow' to 'way too fast', so no real control.

    I know this all might be fairly esoteric stuff given that most people won't really need their animations to run faster than 30fps, so in the real world the timer's limitation is not really a big issue, but I need better control - and the area between 35-40fps is exactly where I need to be for what I am doing, so for me locking the animation to a clock (self.time / game.time / device.clock) seems to be the better option, albeit less comprehensive than the method you posted.

    I've attached a demo file where I've stripped the timer/every method down to its basics, relieved it of all its chores, attributes and as much code as possible, so it can run as unhindered as possible . . . I've also added in an X position change for each cycle of the timer - so you can better see the differences between different timer based frame rates (just watching the image sequence whizz in a static box by doesn't tell you too much).

    There are 6 actors.

    From the top.

    Timer every 1/60
    Timer every 1/50
    Timer every 1/40
    Timer every 1/30
    Timer every 1/20
    Timer every 1/10

    Notice that the top 4 (everything from 1/30th upwards / faster) moves at the same speed.

    Link: https://www.mediafire.com/?9rh36pooes41569

    This is something the game.time method doesn't suffer from, it's not limited by the timer's 1/30th ceiling.
  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @BBEnk
    @CodeMonkey
    @slowcut
    @JSproject
    @LumpApps
    @JSproject
    @JGary321
    @quantumsheep
    @wpaten
    @RThurman
    @Braydon_SFX
    @GSAnimator.com
    @darrelf
    @kinzua
    @jamie_c
    @izam
    @Hopscotch
    @gyroscope

    . . . . basically anyone who has followed this thread . . ! :)


    A quick correction, ignore my idea of dumping the 'floor' function !

    If you remove the 'floor' function the animation still works, it still plays through the frames ok, but on the target device (I've tested this on an iPad 1, and iPad 3 and an iPhone 5) the animation will occasionally glitch and stutter, it's ok for most of the time, but every few seconds you'll see a little glitch and even the occasional very brief pause . . .

    . . . but even worse, without the 'floor' function the project's frame rate is reduced to 30fps - 30.04fps was the highest I could get it to go, and this was with a completely striped-out project with just a single 21 frame animation on a single actor - with no other rules.

    Put the 'floor' function back in and the project's frame rate on the target device jumped straight back to 60fps - and the glitches completely disappear.



    tl;dr - you need the floor function !
  • Braydon_SFXBraydon_SFX Member, Sous Chef, PRO, Bowlboy Sidekick Posts: 9,271
    Ah! Thanks for that bit, @Socks! Cheers!
  • LumpAppsLumpApps Member Posts: 2,880
    Thanks!
  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    ... whoops, wrong thread !
  • BBEnkBBEnk Member Posts: 1,764
    edited January 2014
    @BBEnk

    Yep, it seems to be because you have your numeral before the file name . . .

    So

    Untitled-1
    Untitled-2
    Untitled-3
    Untitled-4

    . . . etc, doesn't need 'floor', whereas . . .

    1-Untitled
    2-Untitled
    3-Untitled
    4-Untitled

    . . . etc, does need floor.




    Yes, I've noticed in the past that Gamesalad can be sensitive about how you number your images. Which is why I numbered them this way because of the way I was playing with animation in Gamesalad similar to your way but using interpolate to flip the frames anyway it's in your left spare code thread.
  • sawkasteesawkastee Member Posts: 184
    @Socks

    Using this method how to I get it to stop looping my animation?

    Constrain Image
    "Filename_"..floor( game.Time *60)%21

  • SocksSocks London, UK.Member Posts: 12,822
    edited January 2014
    @Socks

    Using this method how to I get it to stop looping my animation?

    Constrain Image
    "Filename_"..floor( game.Time *60)%21

    The '%21' tells it to loop.
  • sawkasteesawkastee Member Posts: 184
    @Socks

    I removed the '%21' but then it displays a white box when the animation ends.
  • SocksSocks London, UK.Member Posts: 12,822
    @Socks

    I removed the '%21' but then it displays a white box when the animation ends.
    Yes, that's right the frames have all finished playing.
  • sawkasteesawkastee Member Posts: 184
    @Socks

    How would I get it to display my last image after all frames have finished playing instead of a just a blank white box?
Sign In or Register to comment.