An accurate tweening method.

camaleonyco

  • Posts: 204
I have read several complains about the tweening blocks and how inaccurate they are. They never reach their goals, and we have to fix their angle/coordinates every time we use a tween. I would like to share the method I use to avoid this. It needs just a couple of lines of code, but I understand that beginners could be scared from code blocks, so I will try to explain it as clearly as I can:

Stencyl uses a tweener class (caurina.transitions.Tweener) to manage all the tweening. This class allows you to do a lot of things, although there are no blocks for those things. One interesting parameter is "useFrames", a boolean value that makes the transition to happen in a certain amount of frames, instead of using seconds. When you turn this on, the actor always reaches its goal, because the tweener actually works with frames, not seconds.

As I said, you just need a couple of lines of code to turn this on:

Open your behavior, the one that contains the tween. On the event's panel add a new "Custom Import" event (Add Event/Advanced/Custom Import). Inside the "Import Statements" block, add a Code block from the palette (Flow/Advanced/Code). Write this in that block:

import caurina.transitions.*;

This will let you call the tweener directly form your behavior.

Here comes the tricky part... You have to replace the tweening block you are using (slide, spin, fade or grow) for a code block, and write down something like this:

Tweener.addTween(actor, {property:number, time:number, transition:"transition name", useFrames:true});

And that's it!... of course you have to write the parameters correctly.

actor:If you want to tween "self" just write actor, if you have and actor attribute you can use _YourActorAttribute (with and underscore before the name), or if you want to tween the last created actor write getLastCreatedActor().

property:
-To "slide", you can use x or y, and if you want to tween both you have use two addTweens (one for X and one for Y). The value should be a number (a positive or negative integer), and if you want to "slide by", it should be something like   x:actor.x-100  or  x:getLastCreatedActor().x+100  , etc... Note that this properties (x and y) refer to the actor's center.
-To "spin" use angle. The value should be in radians, something like angle:Util.toRadians(90) and to "spin by" you should add the current angle: angle:actor.angle+Util.toRadians(90)  .
-To "fade" use alpha. A number from 0 to 1, so if you want 50% alpha, that should be alpha:0.5  .
-To "grow" you should use scale, height or width, depending on your needs. Frankly I haven't used this property yet, but it should work. I guess that 100% scale should be 1.

time: This should be a positive integer, the number of frames for the tweening duration. Stencyl runs at 60 frames per second, the default value, so if you want to make a 0.5 seconds tweening it should be: time:30  .

transition: This defines the easing method, and it should be a quoted string, something like transition:"easeInBounce"  . To know what possible transition names you can use check this: http://hosted.zeh.com.br/tweener/docs/en-us/misc/transitions.html

The last parameter is useFrames, obviosly we will set it as "true".

Example: I have an actor attribute called myActor, containing an actor which center is on 200x,300y... I want to slide it to -20x,300y using a "normalIn/Out" easing. I just need this code line:

Tweener.addTween(_myActor, {x:-20, time: 30, transition:"easeInOutQuad", useFrames:true});

Sorry for my english and the wall of text. I hope someone finds this useful.


camaleonyco

  • Posts: 204
Glad to know it helped you.

For those who want to take it to the next level:

Let's say that I want to call an event once the tweening has ended...

In the same behavior create a new Custom Event (Add Event/Advanced/Custom Event). Then give it a name (TweenComplete) and build your event using the blocks you need. For testing purposes I will put there just a print block with the string "Tweening complete!!!". So, using the same example I used the last time, I will just tell the tweener that I want it to run the TweenComplete event once it has reached its goal. We just need to add to the code:

Tweener.addTween(_myActor, {x:-20, time: 30, transition:"easeInOutQuad", useFrames:true, onComplete:TweenComplete()});

So, I just added a new parameter: onComplete:TweenComplete() . Note that the event name has to have those parentheses at the end. Once the tween has ended, guess what happens?... "Tweening complete!!!".

bernard

  • Posts: 120
I have read several complains about the tweening blocks and how inaccurate they are. They never reach their goals, and we have to fix their angle/coordinates every time we use a tween. I would like to share the method I use to avoid this. It needs just a couple of lines of code, but I understand that beginners could be scared from code blocks, so I will try to explain it as clearly as I can:

Stencyl uses a tweener class (caurina.transitions.Tweener) to manage all the tweening. This class allows you to do a lot of things, although there are no blocks for those things. One interesting parameter is "useFrames", a boolean value that makes the transition to happen in a certain amount of frames, instead of using seconds. When you turn this on, the actor always reaches its goal, because the tweener actually works with frames, not seconds.

As I said, you just need a couple of lines of code to turn this on:

Open your behavior, the one that contains the tween. On the event's panel add a new "Custom Import" event (Add Event/Advanced/Custom Import). Inside the "Import Statements" block, add a Code block from the palette (Flow/Advanced/Code). Write this in that block:

import caurina.transitions.*;

This will let you call the tweener directly form your behavior.

Here comes the tricky part... You have to replace the tweening block you are using (slide, spin, fade or grow) for a code block, and write down something like this:

Tweener.addTween(actor, {property:number, time:number, transition:"transition name", useFrames:true});

And that's it!... of course you have to write the parameters correctly.

actor:If you want to tween "self" just write actor, if you have and actor attribute you can use _YourActorAttribute (with and underscore before the name), or if you want to tween the last created actor write getLastCreatedActor().

property:
-To "slide", you can use x or y, and if you want to tween both you have use two addTweens (one for X and one for Y). The value should be a number (a positive or negative integer), and if you want to "slide by", it should be something like   x:actor.x-100  or  x:getLastCreatedActor().x+100  , etc... Note that this properties (x and y) refer to the actor's center.
-To "spin" use angle. The value should be in radians, something like angle:Util.toRadians(90) and to "spin by" you should add the current angle: angle:actor.angle+Util.toRadians(90)  .
-To "fade" use alpha. A number from 0 to 1, so if you want 50% alpha, that should be alpha:0.5  .
-To "grow" you should use scale, height or width, depending on your needs. Frankly I haven't used this property yet, but it should work. I guess that 100% scale should be 1.

time: This should be a positive integer, the number of frames for the tweening duration. Stencyl runs at 60 frames per second, the default value, so if you want to make a 0.5 seconds tweening it should be: time:30  .

transition: This defines the easing method, and it should be a quoted string, something like transition:"easeInBounce"  . To know what possible transition names you can use check this: http://hosted.zeh.com.br/tweener/docs/en-us/misc/transitions.html

The last parameter is useFrames, obviosly we will set it as "true".

Example: I have an actor attribute called myActor, containing an actor which center is on 200x,300y... I want to slide it to -20x,300y using a "normalIn/Out" easing. I just need this code line:

Tweener.addTween(_myActor, {x:-20, time: 30, transition:"easeInOutQuad", useFrames:true});

Sorry for my english and the wall of text. I hope someone finds this useful.

I tried to do this and kept getting the error shown in attached image1.jpg

A code dump of the behaviour is shown in image2.jpg

I have attached my project below - TweenerCodeSample1.stencyl

Can you please check this project and tell me what is wrong. I have the latest version of Stencyl

Thanks

Update: I checked the code and found I was adding "(" instead of
"{"

Now there is no error but nothing happens.

Please test the attached project and tell me where I am going wrong. Maybe attach a simple project to show this working would be a great help.

Thanks

« Last Edit: September 27, 2012, 07:41:37 pm by bernard »

Hectate

  • *
  • Posts: 4643
It wants a third parenthesis before the semicolon. The line is actually inside the parenthesis of the line above it.
:
:
Patience is a Virtue,
But Haste is my Life.
Proud member of the League of Idiotic Stencylers; doing things in Stencyl that probably shouldn't be done.

camaleonyco

  • Posts: 204
@bernard:

Try disabling physics for the actor you want to tween. I'm sure that will solve your problem. There should be a workaround if you want to use physics, but usually you won't want to do that, because that could bring a lot of issues. 

You may want to put that code in an actor behavior... Somehow when you use a tween inside an input event in a scene behavior, no matter if it's a block or custom code, it just works one time. The second time you click/release the actor the tween will not work.

kiwixoats

  • Posts: 104
How would I translate this block into the custom code? :

Quote
[for each (actor inside region) (box 1)]
--> [slide (actor inside region) to (x: x-center of box1) (y: y-center of box1 ) over (0.5) sec using (none)]

I tried doing this, but it didn't work:

Code: [Select]
Tweener.addTween(actorInRegion, {x:_box1.getXCenter(), time:30, transition:"linear", useFrames:true});
Tweener.addTween(actorInRegion, {y:_box1.getYCenter(), time:30, transition:"linear", useFrames:true});


camaleonyco

  • Posts: 204
As I explained in my previous post, you need to disable physics for the actor you want to tween... and once you have disabled physics, the actor can't be detected by a region. Regions can't detect lightweight actors. However, there are many ways to solve this, but each case is different.

What I do is to kill the solid actor and create a lightweight actor in the same place, and the tween it. That works for me. If you want to keep the physics for the actor, you could
-Use the "slide" block (I don't know why you decide to not use it)
-Use a "push" block.
-Kill the physics actor, create a lightweight actor, and then create the physics actor again when the tween is over.
-Create an invisible lightweight actor from your physics actor's behavior and set it to follow the lightweight one:
Code: [Select]
   when created
       create blankActor at (x/y: x/y of self)
       set myLightweight to [Last Created Actor]
   always
       set x/y to [x/y of myLightweight] for self
... and then you can tween myLightweight whenever you need to.

Those are some possible solutions, although I'm sure there are better ones.

You can't tween or change an actor's coordinates unless it's a lightweight one. For example if you add a code block in "always" with "actor.x += 1;" you would expect the actor to move to the right at a constant speed... but Stencyl doesn't let you to handle some of the actor's properties to avoid some issues. That's why it uses a "setX()" method. However if you use the same code with a lightweight actor, it will move to the right as you expected.

kiwixoats

  • Posts: 104
As I explained in my previous post, you need to disable physics for the actor you want to tween... and once you have disabled physics, the actor can't be detected by a region. Regions can't detect lightweight actors. However, there are many ways to solve this, but each case is different.

What I do is to kill the solid actor and create a lightweight actor in the same place, and the tween it. That works for me. If you want to keep the physics for the actor, you could
-Use the "slide" block (I don't know why you decide to not use it)
-Use a "push" block.
-Kill the physics actor, create a lightweight actor, and then create the physics actor again when the tween is over.
-Create an invisible lightweight actor from your physics actor's behavior and set it to follow the lightweight one:
Code: [Select]
   when created
       create blankActor at (x/y: x/y of self)
       set myLightweight to [Last Created Actor]
   always
       set x/y to [x/y of myLightweight] for self
... and then you can tween myLightweight whenever you need to.

Those are some possible solutions, although I'm sure there are better ones.

You can't tween or change an actor's coordinates unless it's a lightweight one. For example if you add a code block in "always" with "actor.x += 1;" you would expect the actor to move to the right at a constant speed... but Stencyl doesn't let you to handle some of the actor's properties to avoid some issues. That's why it uses a "setX()" method. However if you use the same code with a lightweight actor, it will move to the right as you expected.

Oooh ok thanks!

The reason why I'm not using the "slide" block is because its not tweeting correctly. I set it to tween to the x-center and y-center of the region (I'm trying to get the actor to tween to the center of the region), but it's going to the bottom right corner instead! Is that an error on my part or is it the "slide" block that's doing that?

« Last Edit: October 21, 2012, 06:10:59 am by kiwixoats »

camaleonyco

  • Posts: 204
The reason why I'm not using the "slide" block is because its not tweeting correctly. I set it to tween to the x-center and y-center of the region (I'm trying to get the actor to tween to the center of the region), but it's going to the bottom right corner instead! Is that an error on my part or is it the "slide" block that's doing that?

That's weird, it should work... I made a test and the slide block works fine for me... The actor goes to the region center. Perhaps your region is not in the right place.

kiwixoats

  • Posts: 104
The reason why I'm not using the "slide" block is because its not tweeting correctly. I set it to tween to the x-center and y-center of the region (I'm trying to get the actor to tween to the center of the region), but it's going to the bottom right corner instead! Is that an error on my part or is it the "slide" block that's doing that?

That's weird, it should work... I made a test and the slide block works fine for me... The actor goes to the region center. Perhaps your region is not in the right place.

I don't know why it's not working. You can take a look if you would like (see attachment).

If you import my attached "game", what you do is when you test it in flash, you click on the letter "R" and a letter "E" spawns which you must drag onto the region.

camaleonyco

  • Posts: 204
I don't know why it's not working. You can take a look if you would like (see attachment).

If you import my attached "game", what you do is when you test it in flash, you click on the letter "R" and a letter "E" spawns which you must drag onto the region.

Check the attached file. I think that's what you were looking for.

kiwixoats

  • Posts: 104
I don't know why it's not working. You can take a look if you would like (see attachment).

If you import my attached "game", what you do is when you test it in flash, you click on the letter "R" and a letter "E" spawns which you must drag onto the region.

Check the attached file. I think that's what you were looking for.

Wow, thank you! Oh no wonder, it was moving the actor based on its top left origin right?? While I was automatically assuming it had a center origin.

aedevelopers

  • Posts: 8
Consider a 2D game and that I want to slide my player in the direction(8-way movement) he is heading. It is possible to do so?

camaleonyco

  • Posts: 204
Consider a 2D game and that I want to slide my player in the direction(8-way movement) he is heading. It is possible to do so?

Yes, of course... You don't even need to use this method, a regular slide block would work. Remember: useFrames parameter is useful if you want to be sure that the actor reaches its goal at a tweening. If that's not your case, you should use a regular tweening block.