Jump to content
GreenSock

Matt Spaeth

Creating a scrolling dynamic shape...

Recommended Posts

I am creating an application that has a dynamic scrolling shape. The shape is two sine curves sitting one above the other, mirrored on on the x-axis, and filled in between. The amplitude and frequency will change based on variables.

 

I am new to GreenSock and have been going through the tutorials. I understand that I will be making use of the BezierPlugin, but I am not sure what the process is for animating it. Does this involve simply changing the variables and redrawing to give the appearance of scrolling?

 

There will be sprites moving moving with the y-value of the curves. This y-value will also be used to modulate the volume of a sound.

 

In an email, Jake offered this advice: "it may be as simple as using a plain y tween and a Sine.easeInOut while animating the wave along the x axis."

 

I have been playing around with the Plugin Explorer and I am not quite connecting this advice to implementation.

 

Sorry for the noob questions. I am not looking for someone to do this for me, but to perhaps direct me towards the strategies/tutorials through which I can learn.

 

Best,

Matt

Link to comment
Share on other sites

Hi Matt,

 

I have a demo that may get you closer to your end goal, or at least visualize what is possible with the Bezier Plugin

 

http://snorkl.tv/dev/bezierDemo/

 

Notice when you drag the boxes around they act as anchor points for the bezier path.

The red dot then follows the path. In the same way that the user can alter the curve by dragging the boxes, you could be tweening the control points of a Bezier curve to make it look like is pulsing, waving or scrolling.

 

The BezierPlugin ONLY handles motion along a bezier curve, it does not draw the path. I have some code in there that monitors the positions of the control points and uses the AS3 Drawing API to render the curves.

 

Here is a zip of those files: http://snorkl.tv/dev.../bezierDemo.zip

NOTE: these files use v11 of our platform. The BezierPlugin has recently had an overhaul in v12 to support cubic and quadratic Bezier curves, some custom types (thru, thruBasic, soft) and a "curviness" property.

 

The parseBeziers() method has been removed in v12 too. In short the way I approached this in v11 isn't going to work in v12, but the same effect is still achievable.

 

In the BezierPlugin docs there is an example of how to convert a curve to a quadratic bezier that can be used with the Flash drawing API. From your description it doesn't appear that you actually need an object to be animating along the curve, it sounds like you just need to animate the position of the points that are used to draw the curve, I'm not even sure you need the BezierPlugin.

  • Like 1
Link to comment
Share on other sites

Thank you Carl. This is definitely some great help. I really like your site BTW. I was working through a lot of your tutorials a few days ago.

 

I have been playing around with the following code. Basically, it's drawing a curve from a designated start point.

 

The interval is a number that represents seconds between each peak. This is multiplied by a the multiplier variable to create pixel distance on the screen.

 

pointNum is the number of points to pass to the BezierPlugin.

 

bezierPoints is an array that holds the points to create the bezier.

 

The function createBezierPoints uses the currentStartPoint variable to calculate the points based on the above variables. My theory is that I can then use this variable to animate the the bezier so it scrolls to the left.

 

I am getting an Error #1009: Cannot access a property or method of a null object reference. at the g.moveTo(bx[0].a, by[0].a);

 

This code was working fine when I was manually passing values to the BezierPlugin. The traces I have used indicate that the bezierPoints arrays is populating.

 

I am wondering if this line of thinking is not the most efficient way?

 

Edit: I just realized that I didn't have my variables correct in the array. They are missing the "x:" and "y:" before the value.

 

import flash.display.Sprite;
import com.greensock.TweenMax;
import com.greensock.plugins.BezierPlugin;
import com.greensock.plugins.BezierThroughPlugin;
import flash.display.MovieClip;
import flash.events.Event;

var interval:uint = 4;
var pointNum:uint = 4;
var multiplier:uint = 20;
var bezierPoints:Array = new Array();
var currentStartPoint:Object = new Object();
currentStartPoint.x = 0;
currentStartPoint.y = stage.stageHeight / 2;
function createBezierPoints():void {
var cx:int = currentStartPoint.x;
var cy:int = currentStartPoint.y;
for (var i:int = 0; i < pointNum; i++) {
bezierPoints[i] = [ cx, cy ];
cx += interval * multiplier;
cy += 100;
}
}

createBezierPoints();
var bezier:Object = BezierPlugin.bezierThrough(bezierPoints, 1, true);
var bx:Array = bezier.x; // the "x" Beziers
var by:Array = bezier.y; // the "y" Beziers
var g:Graphics = this.graphics;
this.graphics.lineStyle(1, 0x0000);
g.moveTo(bx[0].a, by[0].a);
for (var i:int = 0; i < bx.length; i++) {
 g.curveTo(bx[i].b, by[i].b, bx[i].c, by[i].c);
}

Link to comment
Share on other sites

Sorry, that was a dumb question above. Filling the array with points worked much better!

Link to comment
Share on other sites

This is looking much better now. The for loop is creating the proper point values which is populating the bezierPoints array. However, I dont' understand why the resulting bezierPoints array is filled with the same value (x=320, y=300)?

 

import flash.display.Sprite;
import com.greensock.TweenMax;
import com.greensock.plugins.BezierPlugin;
import com.greensock.plugins.BezierThroughPlugin;
import flash.display.MovieClip;
import flash.events.Event;

var interval:uint = 4;
var pointNum:uint = 4;
var multiplier:uint = 20;
var bezierPoints:Array = new Array();
var currentStartPoint:Point = new Point();
currentStartPoint.x = 0;
currentStartPoint.y = 300;
function createBezierPoints(pointArray:Array):void {
var currentPoint:Point = currentStartPoint;
var peak:Boolean = true;
for (var i:int = 0; i < pointNum; i++) {
 pointArray[i] = currentPoint;
 trace(pointArray[i]);
 trace(peak);
 currentPoint.x += interval * multiplier;
 if (peak == true) {
  currentPoint.y = 100;
  peak = false;
 }
 else {
  currentPoint.y = 300;
  peak = true;
 }

}
}

createBezierPoints(bezierPoints);
trace(bezierPoints);
var bezier:Object = BezierPlugin.bezierThrough(bezierPoints, 1, true);
var bx:Array = bezier.x; // the "x" Beziers
var by:Array = bezier.y; // the "y" Beziers
var g:Graphics = this.graphics;
this.graphics.lineStyle(1, 0x0000);
g.moveTo(bx[0].a, by[0].a);
for (var i:int = 0; i < bx.length; i++) {
 g.curveTo(bx[i].b, by[i].b, bx[i].c, by[i].c);
}

Link to comment
Share on other sites

Hi Matt,

 

Try this code block on its own:

 

var interval:uint = 4;
var pointNum:uint = 4;
var multiplier:uint = 20;
var bezierPoints:Array = new Array();
var currentStartPoint:Point = new Point();
currentStartPoint.x = 0;
currentStartPoint.y = 300;
function createBezierPoints(pointArray:Array):void
{
var currentPoint:Point = currentStartPoint;
var peak:Boolean = true;
for (var i:int = 0; i < pointNum; i++)
{
trace(peak);
currentPoint.x += interval * multiplier;
if (peak == true)
{
currentPoint.y = 100;
peak = false;
}
else
{
currentPoint.y = 300;
peak = true;
}
//DO THIS
pointArray[i] = new Point(currentPoint.x, currentPoint.y);
trace(pointArray[i]);

}

}

createBezierPoints(bezierPoints);
trace(bezierPoints);

 

After the loop runs, bezierPoints contains:

(x=80, y=100),(x=160, y=300),(x=240, y=100),(x=320, y=300)

 

It seems that since bezierPoints = currentPoint was messing things up.

On some level each item in bezierPoints was pointing to the same object and your array got populated with a bunch of duplicates of the last value of currentPoint.

 

I think the code provided gives the result you need. I wish I had a more technical explanation.

Link to comment
Share on other sites

Wonderful! Thank you Carl. That works great. That makes sense to me the error had to do setting up all values of the array to point to the same reference. I really appreciate your help. My oo programming is a little rusty but I feel it coming back.

 

I was wanted to ask you why the curves did not look symmetrical. The curve on one side of a peak has a different radius than the other side. However, this changes when I increase the number of points.

 

I increased pointNum to 6 and increased multiplier to 30. Then, to my surprise, the curves in the middle of the tween were symmetrical but the ones on the ends were not. I assume this has to do with the ends of tween being points.

 

Is there any way I can make these curves symmetrical without extending the tween off the stage by an addition point on both sides?

Link to comment
Share on other sites

I am getting a lot closer. I have it moving and redrawing with the following code. I wouldn't call it animating though, but it's a step in the right direction. Would you offer some suggestions on how to use TweenMax to tween the points used to draw the curve?

 

To make it look continuous would you suggest repeatedly tweening the drawing to scroll off stage and then redrawing when it gets too far? Is there an easier solution?

 

Thanks for your help Carl.

 

var interval:uint = 4;
var pointNum:uint = 6;
var multiplier:uint = 30;
var bezierPoints:Array = new Array();
var currentStartPoint:Point = new Point();
currentStartPoint.x = 0;
currentStartPoint.y = 300;
function createBezierPoints(pointArray:Array):void {
var currentPoint:Point = currentStartPoint;
var peak:Boolean = true;
for (var i:int = 0; i < pointNum; i++) {
 pointArray[i] = new Point (currentPoint.x, currentPoint.y);
 currentPoint.x += interval * multiplier;
 if (peak == true) {
  currentPoint.y = 100;
  peak = false;
 }
 else {
  currentPoint.y = 300;
  peak = true;
 }
}
}
function drawBezier():void {
var bezier:Object = BezierPlugin.bezierThrough(bezierPoints, 1, true);
var bx:Array = bezier.x; // the "x" Beziers
var by:Array = bezier.y; // the "y" Beziers
var g:Graphics = this.graphics;
this.graphics.lineStyle(1, 0x0000);
g.moveTo(bx[0].a, by[0].a);
for (var i:int = 0; i < bx.length; i++) {
  g.curveTo(bx[i].b, by[i].b, bx[i].c, by[i].c);
}
}
function moveBezier(pointArray:Array):void {
for (var i:int = 0; i < pointNum; i++) {
 pointArray[i].x -= 1;
}
}
function gameLoop(e:Event):void {
moveBezier(bezierPoints);
drawBezier();
}

createBezierPoints(bezierPoints);
addEventListener(Event.ENTER_FRAME, gameLoop);

Link to comment
Share on other sites

i messed around with it a little

 

 

 

import flash.display.Sprite;
import com.greensock.TweenMax;
import com.greensock.plugins.BezierPlugin;
import com.greensock.plugins.BezierThroughPlugin;
import com.greensock.easing.*;
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
var interval:uint = 4;
var pointNum:uint = 20;
var multiplier:uint = 30;
var bezierPoints:Array = new Array();
var currentStartPoint:Point = new Point();
currentStartPoint.x = 0;
currentStartPoint.y = 300;
function createBezierPoints(pointArray:Array):void {
var currentPoint:Point = currentStartPoint;
var peak:Boolean = true;
for (var i:int = 0; i < pointNum; i++) {
pointArray[i] = new Point(currentPoint.x,currentPoint.y);
currentPoint.x += interval * multiplier;
if (peak == true) {
currentPoint.y = 100;
peak = false;
}
else {
currentPoint.y = 300;
peak = true;
}
}
}
function drawBezier():void {
var bezier:Object = BezierPlugin.bezierThrough(bezierPoints,1,true);
var bx:Array = bezier.x;// the "x" Beziers
var by:Array = bezier.y;// the "y" Beziers
var g:Graphics = this.graphics;
g.clear();
this.graphics.lineStyle(1, 0x0000);
g.moveTo(bx[0].a, by[0].a);
for (var i:int = 0; i < bx.length; i++) {
g.curveTo(bx[i].b, by[i].b, bx[i].c, by[i].c);
}
}
function moveBezier(pointArray:Array):void {
for (var i:int = 0; i < pointNum; i++) {
pointArray[i].x -= 240;
}
}
function gameLoop(e:Event):void {
drawBezier();
}


createBezierPoints(bezierPoints);
addEventListener(Event.ENTER_FRAME, gameLoop);



//initially offset the position of all the points cause the first curve segment looks kind of funky
moveBezier(bezierPoints);

//use a loop to make a bunch of repeating tweens on the coordinates

for (var i:int = 0; i < pointNum; i++) {
//scroll left by repeating the same animation over and over
TweenMax.to(bezierPoints[i], 2, {x:"-240", repeat:-1, ease:Linear.easeNone});

//optional for y flux
if (i%2==0) {
trace(i + " even " + bezierPoints[i]);
TweenMax.to(bezierPoints[i], 1, {y:100, repeat:-1, yoyo:true});

}
else {
TweenMax.to(bezierPoints[i], 1, {y:300, repeat:-1, yoyo:true});

}
}

 

The majority of what I changed is really that last for loop that creates a bunch of tweens on the points that repeat. It gives the illusion that its an endless scroll. Feel free to take the y tweens out of course and play with the values.

 

Demo:

http://www.snorkl.tv...lsingCurve.html

 

BTW, really nice work so far. I appreciate that you solve half this stuff before I have time to get to it!

  • Like 1
Link to comment
Share on other sites

Carl, thank you, I really appreciate your awesome help.

 

I tweaked your code and I have something that is looking pretty good.

 

I tried to use the following code to add a background. The drawing method that you use for the bezier curve in the above code doesn't use the addChild() method to put the drawing on the stage, so where is it in the display list? Do I need to use a different method to draw if I want to have it layered?

 

var bg:Sprite = new Sprite();
bg.graphics.beginFill(0xFFCC66);
bg.graphics.drawRect(0,0,stage.stageWidth, stage.stageHeight);
bg.graphics.endFill();
bg.x = 0;
bg.y = 0;
addChildAt(bg,0);

 

I have some other static objects that I want to draw on stage as well. I created a function to do this, but it has to redraw every time because of the g.clear(); I assume that it is not necessary to redraw these every time. Is that correct? How do you recommend drawing these elements so they don't get erased on the g.clear()?

 

Also, was wondering how I could put the for loop which does the tweening into a function? I tried putting it in the gameLoop() function but it doesn't work there. The bezier scrolls to the left but doesn't refresh to the new position. Not sure I totally understand how it works.

Link to comment
Share on other sites

It's probably easier to answer the above questions if I just put the code here of where I'm at. I put in a bunch of traces. The trace shows that this just goes through the tween loop once and then enters the gameLoop. Why does that work? Is it because of the repeat?

 

var interval:uint = 4;
var pointNum:uint = 12;
var multiplier:uint = 30;
var bezierPoints:Array = new Array();
var currentStartPoint:Point = new Point();
currentStartPoint.x = 0;
currentStartPoint.y = 300;
function createBezierPoints(pointArray:Array):void {
trace("createBezierPoints");
var currentPoint:Point = currentStartPoint;
var peak:Boolean = true;
for (var i:int = 0; i < pointNum; i++) {
 pointArray[i] = new Point (currentPoint.x, currentPoint.y);
 currentPoint.x += interval * multiplier;
 if (peak == true) {
  currentPoint.y = 100;
  peak = false;
 }
 else {
  currentPoint.y = 300;
  peak = true;
 }
}
}
function drawBezier():void {
trace("drawBezier");
var bezier:Object = BezierPlugin.bezierThrough(bezierPoints, 1, true);
var bx:Array = bezier.x; // the "x" Beziers
var by:Array = bezier.y; // the "y" Beziers
var g:Graphics = this.graphics;
g.clear();
this.graphics.lineStyle(1, 0x0000);
g.moveTo(bx[0].a, by[0].a);
for (var i:int = 0; i < bx.length; i++) {
  g.curveTo(bx[i].b, by[i].b, bx[i].c, by[i].c);
}

}
function drawLine():void {
trace("drawLine");
var l:Graphics = this.graphics;
this.graphics.lineStyle(1, 0x0000);
l.moveTo(75, 0);
l.lineTo(75, 500);
l.moveTo(0, 325);
l.lineTo(550, 325);
l.moveTo(0, 75);
l.lineTo(550, 75);

}
function moveSprite():void {

}
function drawBackground():void {
var bg:Sprite = new Sprite();
bg.graphics.beginFill(0x66CCFF);
bg.graphics.drawRect(0,0,stage.stageWidth, stage.stageHeight);
bg.graphics.endFill();
bg.x = 0;
bg.y = 0;
addChildAt(bg,0);
}

function moveBezier(pointArray:Array):void {
trace("moveBezier");
for (var i:int = 0; i < pointNum; i++) {
 pointArray[i].x -= 240;
}
}
function init():void {
trace("init");
createBezierPoints(bezierPoints);
addEventListener(Event.ENTER_FRAME, gameLoop);
//initially offset the position of all the points cause the first curve segment looks kind of funky
moveBezier(bezierPoints);
}
function gameLoop(e:Event):void {
trace("gameLoop");
drawBezier();
drawLine();
}

init();
//use a loop to make a bunch of repeating tweens on the coordinates
for (var i:int = 0; i < pointNum; i++) {
//scroll left by repeating the same animation over and over
trace("tween");
TweenMax.to(bezierPoints[i], 16, {x:"-240", repeat:-1, ease:Linear.easeNone});
}

Link to comment
Share on other sites

Hi Matt,

 

Just create a new Sprite() for each element that you want to draw graphics into, just like you are doing with bg.

 

The drawLine() method is using this.graphics which would just place drawings on the stage.

It would probably be neater to have the curve be drawn into its own Sprite that you can layer how you please with other elements.

 

And yes the TweenMaxes that moves the x values around have a repeat that's why they don't need to be part of the loop. They just get created once and loop forever.

 

it you want to create these tweens in a function just do this:

 

 

function init():void {
trace("init");
createBezierPoints(bezierPoints);
addEventListener(Event.ENTER_FRAME, gameLoop);
//initially offset the position of all the points cause the first curve segment looks kind of funky
moveBezier(bezierPoints);
startXTweens();
}



function startXTweens() {
for (var i:int = 0; i < pointNum; i++) {
//scroll left by repeating the same animation over and over
trace("tween");
TweenMax.to(bezierPoints[i], 16, {x:"-240", repeat:-1, ease:Linear.easeNone});
}
}


function gameLoop(e:Event):void {
trace("gameLoop");
drawBezier();
drawLine();
}

init();

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×