Attract-Mode Support Forum
Attract-Mode Support => Scripting => Topic started by: liquid8d on May 17, 2015, 12:34:10 PM
-
I was previously working on a whole ExtendedObjects and Animate module. It got a bit overwhelming, and I really wanted the animation parts to be available to everyone, so I sort of rewrote from scratch a new "animate" module.
It's currently in my github for anyone that would like to test it out:
https://github.com/liquid8d/attract-extra/tree/master/modules/animate
There is also some sample layouts that show how to use different animations:
https://github.com/liquid8d/attract-extra/tree/master/layouts
There is still some buggy issues I need to work out, particularly centering objects properly (doesn't work with skew/pinch right now) and a bunch of misc. stuff, but hopefully this helps some people add some nice transition animations without having to write your own transition callbacks and handle everything.
An example of how easy this makes animations:
fe.load_module("animate/animate");
//property animation - can be used for any properties that has a numeric value
local logo = fe.add_image("default.png", 0, 100, 640, 360);
local alpha_cfg = {
when = Transition.ToNewSelection,
property = "alpha",
start = 0,
end = 255,
time = 1000
}
animation.add( PropertyAnimation( logo, alpha_cfg ) );
//sprite animation - use a spritesheet to animate specific frames of the sprite sheet
local joystick = fe.add_image("joystick-move.png", (fe.layout.width / 2) - 296, fe.layout.height - 128, 128, 128);
local sprite_cfg = {
when = When.Always,
width = 128,
frame = 0,
time = 3000,
order = [ 0, 3, 0, 3, 0, 4, 0, 4 ],
loop = true
}
animation.add( SpriteAnimation( joystick, sprite_cfg ) );
//particle animation - create particle effects
local particle_cfg = {
resources = [ "default.png" ],
ppm = 60,
x = 0,
y = 0,
width = fe.layout.width,
speed = [ 100, 250 ],
angle = [ 0, 180 ],
startScale = [ 1, 2 ],
gravity = 1,
fade = 10000,
lifespan = 10000
}
animation.add( ParticleAnimation( particle_cfg ) );
I should be able to work to improve this in the next couple weeks. I hope a few will test it out and provide any feedback. Once I can get some issues corrected, hopefully this module can be included in the next version of Attract Mode by default :)
-
Liquid-
Good to see you're still active. I've been using your module in my layouts since Raygun included it in the last build. See my recent grey theme on how I am utilizing your module. Anyway, this looks very promising.
-
Thanks! Glad to hear, I'll check it out. This module works a bit differently from the other, but it's not too drastic as far as animations go. Instead of it being a animate function on the object, you just use animation.add( Animation( obj, cfg ) ); - it's probable the two will conflict though, so if you get to try this one out, don't include both.
My plan is to separate the whole "ExtendedObjects" part, they could be used independently or together but I want to sort of get this stuff mostly finalized before I go diving back into ExtendedObjects stuff. It was too much too fast :)
Here's some videos of the sample layouts:
https://www.youtube.com/watch?v=aaZ_2aDxQZE
https://www.youtube.com/watch?v=6yE8S0PjPHw
https://www.youtube.com/watch?v=M2pc0crlrys
-
Looks good liquid8d. Thanks for the update, I'll be sure to check it out. I'm happy to add your work to Attract-Mode, just let me know when you think its ready!
-
Hi Liquid
First let me say this is a great module, and it should make things a lot easier!
I have discovered a bug with the Properties module:
When operating on an artwork (I haven't tried it out on an image yet), if you don't set the height and width when declaring the artwork
--ie: if you use ("marquee", 10, 5) rather than ("marquee", 10, 5, 150, 50) --
it fails.
In my experience the artwork just disappears from the screen (it may be ending up at coordinates far off the screen).
I prefer not defining height and width, as this sticks with the original artwork sizes rather than resizing it, but your module is so great, I'm happy to work with what I have!
-
Thanks, I'll check that out. I think I had that working properly in my original module, but might not have done it properly for those functions.
Glad you like it.. if you have thoughts, suggestions or other bugs you find just let me know!
-
Liquid-
I have another item that needs to be addressed and kind of expands on what playerzero highlighted. If an improper name definition is used in the fe.add_image() function using animation module it crashes AM. So, for example, if I use fe.add_image("mega.png", 0 ); and that image is not found it crashes the frontend.
Also, how to go about transitioning marquee art from offtop to top like with the old module? I tried to draw it off screen using start then end but I couldn't get the right results. Thanks for all your contributions...
-
Thanks, I'll check into non-existing filenames. Does AM usually just ignore it?
As for positions, yes - I know they were super handy, but since that was part of what I was doing for ExtendedObjects, it isn't included in the animate module currently for a few reasons. I will look into having set positions again for animate, but for now here's a list of the ones I had in there:
const OFFSCREEN = 20;
POSITIONS <- {
centerX = function() { return fe.layout.width / 2; },
centerY = function() { return fe.layout.height / 2; },
screenPercentX = function(p) { return (p.tofloat() / 100) * fe.layout.width; },
screenPercentY = function(p) { return (p.tofloat() / 100) * fe.layout.height; },
start = function(o) { return [ o.config.start_x, o.config.start_y ]; },
last = function(o) { if ("last_x" in o.config == false || "last_y" in o.config == false) return start(o); return [ o.config.last_x, o.config.last_y ]; },
current = function(o) { return [ o.getX(), o.getY() ]; },
center = function(o) { return [ centerX() - (o.width / 2), centerY() - (o.height / 2) ]; },
top = function(o) { return [ centerX() - (o.width / 2), 0 ]; },
left = function(o) { return [ 0, centerY() - (o.height / 2) ]; },
bottom = function(o) { return [ centerX() - (o.width / 2), fe.layout.height - o.height ]; },
right = function(o) { return [ fe.layout.width - o.width, centerY() - (o.height / 2) ]; },
topleft = function(o) { return [ 0, 0 ]; },
topright = function(o) { return [ fe.layout.width - o.width, 0 ]; },
bottomleft = function(o) { return [ 0, fe.layout.height - o.height ]; },
bottomright = function(o) { return [ fe.layout.width - o.width, fe.layout.height - o.height ]; },
midtop = function(o) { return [ centerX() - (o.width / 2), (centerY() / 2) - (o.height / 2) ] },
midbottom = function(o) { return [ centerX() - (o.width / 2), centerY() + (centerY() / 2) - (o.height / 2) ] },
midleft = function(o) { return [ (centerX() / 2) - (o.width / 2), centerY() - (o.height / 2) ] },
midright = function(o) { return [ centerX() + (centerX() / 2) - (o.width / 2), centerY() - (o.height / 2) ] },
midtopleft = function(o) { return [ (centerX() / 2) - (o.width / 2), (centerY() / 2) - (o.height / 2) ] },
midbottomleft = function(o) { return [ (centerX() / 2) - (o.width / 2), centerY() + (centerY() / 2) - (o.height / 2) ] },
midtopright = function(o) { return [ centerX() + (centerX() / 2) - (o.width / 2), (centerY() / 2) - (o.height / 2) ] },
midbottomright = function(o) { return [ centerX() + (centerX() / 2) - (o.width / 2), centerY() + (centerY() / 2) - (o.height / 2) ] },
offmidtopleftx = function(o) { return [ -o.width - OFFSCREEN, (centerY() / 2) - (o.height / 2) ] },
offmidtoplefty = function(o) { return [ (centerX() / 2) - (o.width / 2), -o.height - OFFSCREEN ] },
offmidbottomleftx = function(o) { return [ -o.width - OFFSCREEN, centerY() + (centerY() / 2) - (o.height / 2) ] },
offmidbottomlefty = function(o) { return [ (centerX() / 2) - (o.width / 2), fe.layout.height + OFFSCREEN ] },
offmidtoprightx = function(o) { return [ fe.layout.width + OFFSCREEN, (centerY() / 2) - (o.height / 2) ] },
offmidtoprighty = function(o) { return [ centerX() + (centerX() / 2) - (o.width / 2), -o.height - OFFSCREEN ] },
offmidbottomrightx = function(o) { return [ fe.layout.width + OFFSCREEN, centerY() + (centerY() / 2) - (o.height / 2) ] },
offmidbottomrighty = function(o) { return [ centerX() + (centerX() / 2) - (o.width / 2), fe.layout.height + OFFSCREEN ] },
offtop = function(o) { return [ centerX() - (o.width / 2), -o.height - OFFSCREEN ]; },
offbottom = function(o) { return [ centerX() - (o.width / 2), fe.layout.height + o.height + OFFSCREEN ]; },
offleft = function(o) { return [ - o.width - OFFSCREEN, centerY() - (o.height / 2) ]; },
offright = function(o) { return [ fe.layout.width + o.width + OFFSCREEN, centerY() - (o.height / 2) ]; },
offtopleftx = function(o) { return [ - o.width - OFFSCREEN, 0 ]; },
offtoplefty = function(o) { return [ 0, - o.height - OFFSCREEN ]; },
offtopleft = function(o) { return [ - o.width - OFFSCREEN, - o.height - OFFSCREEN ]; },
offtoprightx = function(o) { return [ fe.layout.width + OFFSCREEN, 0 ]; },
offtoprighty = function(o) { return [ 0, - o.height - OFFSCREEN ]; },
offtopright = function(o) { return [ fe.layout.width + OFFSCREEN, - o.height - OFFSCREEN ]; },
offbottomleftx = function(o) { return [ - o.width - OFFSCREEN, 0 ]; },
offbottomlefty = function(o) { return [ 0, fe.layout.height + OFFSCREEN ]; },
offbottomleft = function(o) { return [ - o.width - OFFSCREEN, fe.layout.height + OFFSCREEN ]; },
offbottomrightx = function(o) { return [ fe.layout.width + OFFSCREEN, 0 ]; },
offbottomrighty = function(o) { return [ 0, fe.layout.height + OFFSCREEN ]; },
offbottomright = function(o) { return [ fe.layout.width + OFFSCREEN, fe.layout.height + OFFSCREEN ]; }
}
Some of these position functions are trickier since they require the objects current position or the current layout width/height - or center of the current layout width/height :)
Because of that I had these functions where you just passed the object to the function to determine the coordinates. These are returning an array, so offtop would be offtop[0] (x) and offtop[1] (y), top would be top[0] (x) and top[1] (y)
You could technically include that in your layout if you use a lot of them, then for start end values, just pass it like:
local start = { x = POSITIONS['offtop'][0], y = POSITIONS['offtop'][1] }
local end = { x = POSITIONS['top'][0], y = POSITIONS['top'][1] }
If you just want to implement "offtop" and "top", you can set the start and end values like so:
local OFFSCREEN = 20;
local myObj = fe.add_text( 0, 0, 300, 30 );
local offtop = [ fe.layout.width + myObj .width + OFFSCREEN, (fe.layout.height / 2) - (o.height / 2) ];
local top = [ (fe.layout.width / 2) - (myObj.width / 2), 0 ];
local config = {
when = Transition.ToNewSelection,
property = "position",
start = { x = offtop[0], y = offtop[1] }
end = { x = top[0], y = top[1] }
}
animation.add( PropertyAnimation( myObj, config ) );
Hope that makes sense.. let me know if you have any issues.
-
Liquid-
Yes, AM will just report the error once the front-end exits gracefully. Thanks for the info on how to position the marquee.
-
I finally made a proper full on "layout" tutorial that demonstrates the features of the animate module... :)
https://www.youtube.com/watch?v=blvbkHXvcYg
Working on a few oh the bugs mentioned in the module and some cleanup of these demo layouts and they will be available to show you how to use everything properly.
-
Liquid8-
Fantastic job man, this is just what the doctor ordered. Hopefully, more members will start discovering how your awesome mod can enrich their layouts.
-
Liquid-
If an improper name definition is used in the fe.add_image() function using animation module it crashes AM. So, for example, if I use fe.add_image("mega.png", 0 ); and that image is not found it crashes the frontend.
I just tried this and didn't have any crashes. Can you confirm if you are using 1.5.2 / 1.5.3? Maybe something specific to what you were doing with that particular image crashed it. If you can give me an example layout or something, I can check it out.
-
When operating on an artwork (I haven't tried it out on an image yet), if you don't set the height and width when declaring the artwork
--ie: if you use ("marquee", 10, 5) rather than ("marquee", 10, 5, 150, 50) --
it fails.
I have a workaround for this and will be updating the animate module soon - but for now, you will not be able to use 'scale' and 'rotation' if the width and height are not set but any existing properties should work ok. Those need the width and height to work and that will be a little more complicated for me to implement using texture_width and texture_height :)
-
Liquid8-
I am running v.1.5.2, don't use a duel monitor. Anyway, I can reproduce the error every time by changing the name from barrow.png to something that doesn't exist. See attached .nut as well.
/sprite animation - use a spritesheet to animate specific frames of the sprite sheet
local pointer = fe.add_image("barrow.png", flx*0.46, fly*0.91, flw*0.1, flh*0.1);
pointer.rotation = 270;
local sprite_cfg = {
when = When.Always,
width = 100,
frame = 0,
time = 2000,
order = [ 2, 4, 2, 4, ],
loop = true
}
animation.add( SpriteAnimation( pointer, sprite_cfg ) );
-
Ahhh, ok - I forgot to try sprite.. I just found what's causing it - it applies fix_masked_image() to the sprite.. and the non-existent object makes that function crash apparently.
Should be able to figure something out and have that fixed in the next update :)
Here's an arrow I worked up for testing, I used:
order = [ 0, 1, 2, 1, 0, 3, 4, 3 ]
:)
-
OK, I pushed my new version of animate to github...
https://github.com/liquid8d/attract-extra/tree/master/modules
Just a note if you are using the current version - I MOVED the animate.nut to the main module directory - so delete the old animate/ folder - and you'll need to update your layouts to use fe.load_module("animate") instead of fe.load_module("animate/animate"); Annoying - but that's cleaner and it won't change anymore :)
Here's what's new:
Updated animate module to what is now AnimateVersion 1.1
animate.nut
- moved animate.nut to base module path
- moved animations to the animate folder - each have their own folder for resources
- now loads .nut files in the animate folder - to support additional animations
- put positions back in (not sure if they all work)
- fixed animation transitions When.OnPageUp and When.OnPageDown error
- moved evaluate function to the Animation base class so it can be used for other animations
PropertyAnimations
- fix for unscaled images/artwork ( note: some property attributes like scale, rotation and position won't work right without a provided width/height )
ParticleAnimations
- 'surface' can be provided to a ParticleAnimation config to say which surface to draw on (default is the standard fe surface = on top of objects drawn before it was created. this means you can now create a surface under your objects )
- moved particles debug emitter angle lines to center of emitter
SpriteAnimations
- fixed bug that crashed AM when using a SpriteAnimation and the image was missing
- added some presets for sprites ( SpriteAnimations.joystick_left, joystick_right, joystick_up, joystick_down, joystick_leftright, joystick_updown, button_red, button_white )
Plus my new sample_animate tutorial layout is available that has a walkthrough on what you can do - slightly updated from the video earlier :)
https://github.com/liquid8d/attract-extra/tree/master/layouts/sample_animate
-
Super! :) Well, that didn't take long. I'm not worthy... ;D
-
This is great liquid8d... I haven't had much free time lately but I'll definitely check it out when I get the chance.
-
I've been working with this module for my new layout and have to say, it is a work of beauty. It simplifies the whole animation process enorm.
I have a problem I was hoping you could assist with. I try to move the x and y coordinates of an image at the same time;however, the animation only occurs on the property that occurs latest. In the example below, only the "y" property will be executed.
Do you know how can I achieve this effect?
local myImage = fe.add_image("thefile.png",0,0,100,100);
add.animation(PropertyAnimation(myImage, { property ="x", start = 0, end = 15}));
add.animation(PropertyAnimation(myImage, { property ="y", start = 0, end = 15}));
SOLUTION: I found the solution in the source code. There is a "position" property I can use to give the x,y coordinates. Just wonderful.
-
got some work done on particle effects:
https://gfycat.com/LimpingAcrobaticAdeliepenguin
Just a few random effects together. Most of this could be done before, the big new thing here is you can have multiple emitters with a single particleanimation. The code looks like this:
local anim = ParticleAnimation()
.name("multiple_emitters")
.target(OBJECTS.surface)
.delay("3s")
.duration(-1)
.resources([ "resources/circle.png" ])
.startScale( [ 0.25, 0.1 ] )
.scale([0.25, 0.1])
.accel(1)
.lifespan("4s")
.x(50)
.y(20)
.width(1)
.height(150)
.ppm(60)
.colors( [ [ 255, 0, 0 ] ])
.next_emitter()
.resources([ "resources/square.png" ])
.startScale( [ 0.25, 0.1 ] )
.scale([0.25, 0.1])
.accel(1)
.lifespan("4s")
.x(50)
.y(190)
.width(1)
.height(150)
.ppm(60)
.colors( [ [ 255, 0, 255 ] ])
.next_emitter()
.resources([ "resources/circle.png" ])
.startScale( [ 0.25, 0.1 ] )
.scale([0.25, 0.1])
.accel(1)
.lifespan("4s")
.x(-20)
.y(0)
.width(1)
.height(fe.layout.height)
.ppm(300)
.limit(1000)
.colors( [ [ 200, 200, 200 ], [ 200, 0, 0 ], [ 200, 200, 0 ], [ 0, 200, 0 ] ])
.next_emitter()
//rain
.resources([ "resources/drop.png" ])
.startScale( [ 0.05, 0.05 ] )
.scale([0.05, 0.07])
.speed([ 200, 400 ])
.angle([90,90])
.accel(1)
.gravity(20)
.lifespan("4s")
.x(0)
.y(-20)
.width(fe.layout.width)
.height(1)
.burst(10)
.ppm(420)
.colors( [ [ 118, 135, 222 ], [ 77, 93, 174 ], [ 89, 115, 247 ] ])
.next_emitter()
//snow
.resources([ "resources/snow.png" ])
.startScale( [ 0.25, 0.1 ] )
.scale([0.25, 0.1])
.angle([90,90])
.gravity(-10)
.xOscillate([20,500])
.lifespan("4s")
.x(0)
.y(-20)
.width(fe.layout.width)
.height(1)
.ppm(240)
.colors( [ [ 245, 245, 245 ] ])
.next_emitter()
//scratches
.resources([ "resources/scratch.png" ])
.startScale( [ 1, 1 ] )
.movement(false)
.lifespan("10s")
.x(0)
.y(0)
.width(fe.layout.width)
.height(fe.layout.height)
.ppm(240)
.play();
I also added a few new attributes like burst and a colors array - still have more planned :) I will get a test version together of the whole animate module soon but I have some kinks out before anyone starts using this new version in their layouts. I also need to see what kind of performance improvements I can do, max particles is pretty limited on something low-end.
-
Looking forward to testing it! 8)
-
how can I assign a current object position as start position for the animation?
ive tried:
onUpdate = function ( anim ) { if ("last" in anim.config) { debugText.msg = anim.config.last.x } },
but .last does not exist, I need that for the situation when I play a new animation but the old one hasn't finished, so I need to know the current position of the object.
-
how can I assign a current object position as start position for the animation?
ive tried:
onUpdate = function ( anim ) { if ("last" in anim.config) { debugText.msg = anim.config.last.x } },
but .last does not exist, I need that for the situation when I play a new animation but the old one hasn't finished, so I need to know the current position of the object.
This might be a little tricky with the current animate module, but I think you should be able to access anim.object - which is your target object for the animation and you could get its current property values in the update function...
In the animate module I am working on, there is states - like "start" and "current" to make something like this easier.