SyntaxScrewer's Blog

August 5, 2010

Making an interactive chain in AS3 (without any physics)

Filed under: Flash — geekymaira @ 8:44 am

Heylo folks,

Many-a-times it happens that one makes an ill informed hurried analysis of a simple problem. This is followed by rapid questions on IRC / discussion forums etc, until someone
someone more sensible comes along and makes you realize what a dumb ass you’ve been. 😛

Well something similar happened to me the other day. The task at hand was to create an interactive chain sortta an element for a  project. I started thinking about Physics / The Bone Tool
Linked Lists and what not. I knew I was thinking too much for a simple problem but I couldn’t strike the solution. Then finally, I logged onto IRC and asked around.

Here was my question ,

The answer was – YES!
However, that’s not the one I chose.  The stuff I couldn’t figure out was the “Links” between the nodes.
How would they rise / stretch when the node rises on rollover.  Do I rotate them? Do I scale them ? What should I do ???

The solution was elementary – Anchor the links to the nodes and redraw the links every frame.
That way when a node’s position changes, the new link is drawn from the previous node to  the new position, thus giving
a “stretching” effect when  you use a tween to update the  nodes position.
To give it a bouncy effect I used TweenLite by Greensock. That’s what changes the colors too.

Here’s what it looks like :  http://dazzlecode.com/downloads/NodeOver.swf

Here’s the code :

package
{
 import com.greensock.TweenLite;
 import com.greensock.easing.Bounce;
 import com.greensock.plugins.*;

 import flash.display.MovieClip;
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.MouseEvent;
 import flash.geom.Point;

 TweenPlugin.activate([TintPlugin]);

 /**
 * @author smaira
 */
 [SWF(backgroundColor="#FFFFFF", frameRate="31", width="550", height="400")]

 public class NodeOver extends MovieClip
 {
 private var lastX : Number = 10;
 private var numberOfLinks : Number = 7;
 //Helper function handler. This is just a class I created for common functions I use in projects. Ignore it.
 private var hf : Funcs = Funcs.getInstance();
 //Array to hold the nodes
 private var nodeArray : Array = new Array();
 //Array to hold the links
 private var linkArray : Array = new Array();

 public function NodeOver()
 {
 createNodes();
 createLinks();
 addHeader();
 addEventListener(Event.ENTER_FRAME, onEnterFrameListener);
 }

 //Just adds a graphic on the top of the movie
 private function addHeader() : void
 {
 var hd : Header = new Header();
 hd.x = (stage.stageWidth - hd.width) / 2;
 hd.y = 20;
 addChild(hd);
 }

 //Recreate the links every frame - Clear them first.
 private function onEnterFrameListener(event : Event) : void
 {
 clearLinks();
 createLinks();
 }

 private function clearLinks() : void
 {
 //Removing the child from the stage
 for each (var link in linkArray)
 {
 removeChild(getChildByName(link.name));
 link = null;
 }
 //Clearing the link array
 linkArray.splice(0, linkArray.length);
 }

 private function createLinks() : void
 {
 //Each link is created between the previous and the next node.
 for (var i : uint = 0;i < nodeArray.length - 1;i++)
 {
 var linkSprite : Sprite = new Sprite();
 linkSprite.graphics.lineStyle(2, 0x0000FF, 1, false, "NONE");
 linkSprite.graphics.moveTo(nodeArray[i].node.x + nodeArray[i].node.width / 2, nodeArray[i].node.y + nodeArray[i].node.height / 2);
 linkSprite.graphics.lineTo(nodeArray[i + 1].node.x + nodeArray[i + 1].node.width / 2, nodeArray[i + 1].node.y + nodeArray[i + 1].node.height / 2);
 addChild(linkSprite);
 //Setting the depth of the link , sending it behind the node
 setChildIndex(linkSprite, getChildIndex(nodeArray[i].node) + 1);
 //Setting the mouse to false
 linkSprite.mouseEnabled = false;
 //Setting the name of the link
 linkSprite.name = "link" + i;
 linkArray.push(linkSprite);
 }
 }

 private function createNodes() : void
 {
 // 7 is the number of nodes I created. You can change this to any number.
 for (var i : uint = 0;i < numberOfLinks;i++)
 {
 var node : MovieClip = new Node();
 var nodeObj : Object = new Object();
 //Positioning the new node relative to the last one.
 node.x += lastX + 60;
 node.y = hf.randomRange(270, 300);
 lastX = node.x;
 addChild(node);
 //Just setting the alpha to a lower value for the first and last node
 if (i == 0 || i == numberOfLinks - 1)
 {
 node.alpha = 0.3;
 }

 //Add the mouse over event to the node
 node.addEventListener(MouseEvent.MOUSE_OVER, onNodeMouseOver);
 node.buttonMode = true;

 //Creating the node object that goes into the nodeArray.
 nodeObj.node = node;
 nodeObj.initPos = new Point(node.x, node.y);
 nodeObj.dataMc = setNodeData(node); //TODO: Instead of giving each node a seperate nodeData mc just have 1 mc for all the nodes and just store the image/text in this object.
 nodeObj.initColor = 0x0000CC;
 nodeObj.rollOverFlag = false;
 nodeArray.push(nodeObj);
 }
 }

 //This just creates and returns a movieclip holding the data for a node.
 private function setNodeData(target : MovieClip) : MovieClip
 {
 //Pull out the node data
 var nodeData : NodeData = new NodeData(); //NodeData is the linkage id of an asset in the library.
 nodeData.nodeText.text = "I am just a test, nothing more, nothing less. Test Test Test Test TestTest TestTest TestTest TestTest Test";
 nodeData.alpha = 0;
 return MovieClip(nodeData);
 }

 private function onNodeMouseOver(event : MouseEvent) : void
 {
 //Resets the positions of all other NODES except the active one to their initial position.
 resetNodePositions(MovieClip(event.target));
 event.target.rollOverFlag = true;
 TweenLite.to(event.target, 1, {y:event.target.y - 100, tint:0x00FF00, ease:Bounce.easeOut, onComplete:setFlagFalse, onCompleteParams:[event.target]});
 }

 private function setFlagFalse(target : MovieClip) : void
 {
 target.setRollOverFlag = false;

 //Popout the NodeData movie clip

 //First find the object in the nodeArray which contains the current node (target)
 var nodeObject : Object;
 //TODO: There has to be a function to do such a search. Find out how.
 for (var i : uint = 0;i < nodeArray.length;i++)
 {
 if (nodeArray[i].node == target)
 {
 nodeObject = nodeArray[i];
 break;
 }
 }

 //Set the node position relative to the target.
 nodeObject.dataMc.x = target.x;
 nodeObject.dataMc.y = target.y - nodeObject.dataMc.height / 2;

 //Add the dataMc to the stage
 //TODO: Should I add this here or on top when I'm creating it ?
 addChild(nodeObject.dataMc);

 TweenLite.to(nodeObject.dataMc, 1, {x:nodeObject.node.x + 50, alpha:1});
 }

 //Reset the positions of all the nodes to their initPositions except the passed one
 private function resetNodePositions(excludedNode : MovieClip) : void
 {
 for each (var nodeObj:Object in nodeArray)
 {
 if (nodeObj.node != excludedNode)
 {
 TweenLite.to(nodeObj.node, 1, {y:nodeObj.initPos.y, tint:nodeObj.initColor, ease:Bounce.easeOut});
 TweenLite.to(nodeObj.dataMc, 0.5, {alpha:0});
 }
 }
 }
 }
}

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.