Papervision3D + GoASAP = Go3D

Well, ok the name needs some work, but temporarily I’ll call it this since I’m using GoASAP under the hood of Go3D Tween classes. The idea is simple: Write a tween class specifically catering to a DisplayObject3D with x, y, z, rotationX, rotationY, rotationZ properties. What I ended up with was Tween3D (again, the name will likely change as this is a bit generic).

Now, after all my rants about untyped objects blah blah blah, I’ve really come to at least appreciate *why* people use this approach. It certainly is faster to type and get something up and running. But, I didn’t want to walk down that path with this one AND I wanted something reusable. I think there are situations where you can waste alot of time recreating these transient tweens within the body of your code that could easily be re-executed after being created one time. Tween3D does this.

It’s not a magic trick by any means, I’m simply initializing the “start” values when a Tween3D actually “starts”. It’s very simple and allows for reuse and instantiation.

I really wanted to take this on for a couple of reasons, but mainly to get into writing my own tween classes using Go. I’ve had the pleasure of being able to sit and talk to Moses at length about what Go is and the first thing I wanted to do ever since was try my hand at creating a strongly typed API. So far so good, but I think there’s room for improving the amount of code you have to write. It’s actually pretty compact and very easy to understand, but still, I’d like to see some even shorter ways of creating strongly typed tweens.

As you might expect, if you’ve used Fuse or Tweener in the past, a strongly typed tween might look like this:
[as]
var tween:Tween3D = new Tween3D(target:DisplayObject3D, duration:Number, easing:Function, delay:Number);
tween.x = 2000;
tween.y = 1000;
tween.start();
[/as]

As you can see, its alot like a Tweener call, except the properties aren’t in an untyped object, they set after instantiation. This setup is good for a few reasons:

1. Faster – always faster to set properties after instantiation than it is through the constructor
2. Strict typing – easy to understand what you can and can’t do with the Tween3D
3. reusable – you can create once, and reuse the tween over and over. This is nice in a situation where tweens are kept in a manager class that someone else maintains through out a project. Single point of management can be a very cool thing. This can also keep the CPU hit down when you need a tween.

Another thing about GoASAP is the Sequencing classes that come with it. Alot of times I’ve used Fuse for sequencing events, not necessarily animation tweens🙂 The sequencing is a very strong and abstract part of GoASAP. If you need it, great, hook it up. If you don’t, then it’s not there apart of the payload. Adding your own custom managers is a snap as well. if you need the OverlapManager that comes with Go, you can hook it in with one line of code. You can also create your own or just leave it out altogether.

I’ll be covering Go3D at the classes in Vancouver (this weekend) and Toronto in June, but you can get the source here and the Rolodex demo files here. Its definitely in early stages, but I think it’d be great to get some input if anyone’s interested. You’ll also need to sync or download GoASAP here. Tween3D is written specifically for use with Papervision3D’s DisplayObject3D, so if you’re gonna play, you obviously need to use it with a 3D scene and 3D objects😉

+++++++ [ shameless classes plug ]++++++++++
Vancouver: May 3rd & 4th

Toronto: June 7th & 8th

This demo below is something I converted from Tweener to using Go3D. The lines of code were more for sure, but thats because of what GoASAP isn’t assuming for me like other animation engines would have to. So, I had to add a Sequence and an OverlapManager to acheive the same effect. Ok so it’s more complexx, but it’s also a ton more flexible. if I didn’t like Moses’ OverlapManager, i could write my own. if I didn’t like his sequencer, I could write my own and plug it in. The point is, I can completely customize any portion of the Tween engine to my project, company or general tastes at any given level. Companies can actually dictate the API and have maximum control over *what* the tween/sequence engine is doing in their application. I think once people realize what GoASAP is, we’ll see alot of uses for it in many ways we’re not even able to comprehend yet.

[kml_flashembed movie=”http://www.rockonflash.com/papervision3d/Go3D/RolodexGODemo.swf” width=”550″ height=”500″/]

Code for this animation sequence:
[as]package {
import com.rockonflash.go3d.Tween3D;
import com.rockonflash.go3d.utils.Equations;

import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.utils.getDefinitionByName;

import org.goasap.GoEngine;
import org.goasap.events.GoEvent;
import org.goasap.managers.OverlapMonitor;
import org.goasap.utils.SequenceCA;
import org.goasap.utils.SequenceStepCA;
import org.goasap.utils.customadvance.OnDurationComplete;
import org.papervision3d.cameras.FreeCamera3D;
import org.papervision3d.materials.MovieMaterial;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.Viewport3D;

//import com.rockonflash.go3d.utils.Equations;

public class RolodexGODemo extends Sprite
{
public var target :Plane;
public var viewport :Viewport3D;
public var scene :Scene3D = new Scene3D();
public var camera :FreeCamera3D = new FreeCamera3D(11);
public var renderer :BasicRenderEngine = new BasicRenderEngine();

public var sequence :SequenceCA;
public var tween_0 :Tween3D;
public var tween_0b :Tween3D;
public var tween_1 :Tween3D;
public var tween_2 :Tween3D;

protected var doLoop :Boolean = true;

public function RolodexGODemo()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
init();
}

public function init():void
{
GoEngine.addManager(new OverlapMonitor());

viewport = new Viewport3D(0, 0, true, false);
addChild(viewport);

var cls:Class = getDefinitionByName(“rolodex”) as Class;
var rolodex:Sprite = new cls() as Sprite;

var mat:MovieMaterial = new MovieMaterial(rolodex, true, false);
mat.smooth = true;

target = new Plane(mat, 292, 168, 4, 4);
scene.addChild(target);

reset();
createTween();

finalizeInit();
}

protected function finalizeInit():void
{
stage.addEventListener(MouseEvent.CLICK, handleClick);
stage.addEventListener(Event.ENTER_FRAME, loop);

loop();
doLoop = false;
}

protected function loop(e:Event=null):void
{
if( !doLoop ) return; // only render when we have to
renderer.renderScene(scene, camera, viewport);
}

protected function handleClick(e:MouseEvent):void
{
doTween();
}

protected function createTween():void
{
sequence = new SequenceCA();
sequence.addEventListener(GoEvent.COMPLETE, handleSequenceComplete, false, 0, true);

tween_0 = new Tween3D(target, 1, Equations.easeOutCubic);
tween_0.x = 0;
tween_0.y = 50;
tween_0.rotationZ = 0;
sequence.addStep(tween_0);
sequence.lastStep.advance = new OnDurationComplete(.2); // advance early/overlap

tween_0b = new Tween3D(target, 1, Equations.easeOutCubic);
tween_0b.z = 200;
sequence.addStep(tween_0b, true); // 2nd param groups it with previous step. param is “addToLastStep”

tween_1 = new Tween3D(target, 1, Equations.easeOutCubic);
tween_1.x = -10;
tween_1.y = 85;
tween_1.rotationZ = 15;
sequence.addStep(tween_1);
sequence.lastStep.advance = new OnDurationComplete(.25); // advance early/overlap

tween_2 = new Tween3D(target, 1, Equations.easeOutBounce);
tween_2.rotationX = 0;
tween_2.rotationY = 0;
sequence.addStep(tween_2);
}

protected function handleSequenceComplete(e:GoEvent):void
{
doLoop = false;
}

protected function reset():void
{
target.x = (Math.random() * (stage.stageWidth*.5));
target.y = -350;
target.z = -1000;
target.rotationY = 30;
target.rotationX = 30;
target.rotationZ = Math.random() *-180;//-10;
loop();
}

protected function doTween():void
{
reset();
//createTween();
doLoop = true;
sequence.start();
}
}
}
[/as]

I gotta say a huge thanks to Moses for putting up with my questions and debugging😉 It was FUN non the less!

  1. Very, very interesting. This is the first time I’ve heard GoASAP. It seems robust. I’ve got to take a look at this soon. By the way, nice animation!

  2. Generally we find the most simple way to do tween and have complete control is to tween a value from 0 to 1 or to reverse it 1 to 0. It’s then extremely easy to stop and revers a tween at any point.

    You then just listen to the change event and update an many properties of as many objects that you want.

  3. Just to add to that, if we had a sequence such as your example (4 tweens one after the other), we would just tween from 0-4 and 4-0 to reverse.

    In the MOTION_CHANGE we would just check the value and change the correct properties.

    We’d code all the change properties as constants at the top of the class to tweak at any point.

    • j
    • May 2nd, 2008

    “1. Faster – always faster to set properties after instantiation than it is through the constructor”

    i’m curious about this as it is the first time I have ever heard this. Do you have any specfic references or differences?

    thanks

  4. That’s a pretty interesting approach, if just for readability. While I really like working with TweenLite (and Tweener), sending a generic object definitely has its downsides. I always have to open up the class to see what I can send; Flex hints don’t help with generic objects.

    Have you seen a significant speed increase from setting properties after instantiation? I would imagine that the major bottlenecks would be elsewhere when using PV3D and any tweening engine.

    • nicklas
    • May 2nd, 2008

    Well, a tween with some properties based on go. Could be the WidthTween of Go with some more properties. In my eyes nothing special, there are much better Go projects out there and the most of them have a bigger functionality. sorry

  5. @nicklas: no worries man, it’s not meant to be some break through at all, really just wanted to get people to take a look at go and possibly see in it what I saw. This isn’t meant to be another tween api, it was just mean to say “see, this was pretty easy, and it’s as light weight or as insane as you need it to be.

  6. @Tink: so how do you handle a situation where you have a large team of developers or teams separated by continents that might be working with the same objects and properties via the tween engine? Do you guys have an OverlapManager you use to catch these issues? Or, how do you handle the demo above where I tell the second step in the sequence to take over the easeOutCubic .2 before the end of step1? See, in that scenario, I didn’t want the first step to finish, I wanted the next step to take over at a certain point so that we didn’t see it slow down, then bam, ease again for the last little bit of movement for the rotations and bouncing. Do you have something for that as well? AT that point, you’ve literally written your own tween engine I suppose, yes?

  7. @Zach: That’s always been my point – strong typing allows tools like Flexbuilder and FDT to provide auto-completion as you type. You’ll always know what’s possible with the tween your creating along with knowing all of the possible event dispatches. The learning curve is 100x’s less. I personally love how fast you can create a tween with 1 line of code with Tweener and fuse, but in reality, you’re just moving the property changes out of the constructor/method call – and it certainly is readable. I think Tweener’s code is readable too, but in all honesty, I can keep track of what’s going on by setting the properties like this much more clearly.

  8. Dude. I get a compile error in Flex as an AS project … Error #1065: Variable rolodex is not defined. Any ideas?

  9. Tink – Yeah man! That’s *exactly* how Go works, the whole idea is to add absolutely nothing to your code that you don’t use, it is small and simple. The tween base class LinearGo always runs its position property from 0-1 (and can cycle back to 0 as many times as you want, or repeat forwards as many times as you want). And it runs really fast – Check out the benchmarks.🙂

    You can do what you’re doing now, subscribe to an update event on a LinearGo instance to build tweens in your code. But when you come up with some functionality you feel like keeping and reusing, you can very quickly and easily package that up as a LinearGo subclass in just a few steps. That class stays cool, in that it doesn’t pre-define any targets or properties for you, so you can design it to work however you want it to – like John’s version with 3D properties here. Then, there’s Sequence and Group utils thrown in for good measure.

    Actually another little point too: Go is not “against” Fuse or Tweener “object syntax” at all – it’s very easy to create tween classes or parsers that use that syntax, in fact, with Go! Make up an XML syntax or whatever you want – it’s just a modular OO base system that you can do whatever with.🙂

    PS Thanks John!

  10. @lee: I put the FLA in there with the sample and it has a library asset with linkage ID of rolodex – that’s what it’s looking for. You can just change it out to use a generic ColorMaterial or something

  11. And just to add to what Moses said: this is not a knock on Tweener by any means – I love Tweener. I would love to see them take their API and put it on top of GO to be honest. That would be great to see that type of adoption of GO and it’s the best of both worlds – an API that people love using a very strong / lightweight GO engine under the hood.

  12. Your FLA has no file extension so got lost on the PC. Thanks for this though. Really useful.

  13. @lee: sorry about that man, Mac didn’t give it an extension ;( I’ll add that

    • Nick
    • May 22nd, 2008

    Lovely, the lack of strong typing is a bugger when developing with FDT. Thank you.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s