Auto generated branches
By Andreas, 28 April, 2010
I want to create a nice 3D-effect with branches growing, flowing and rotating around each other. Just to make sure that my idea is working I decided just to create a proof of concept in 2D. With a few tweaks with the parameters it turned out to be some pretty nice organic trees/bushes.
I’ll go through the code and explain what it does.
I divided the example into 3 classes. Main.as (just puts the trees on the stage), Tree.as (keeps track of all the branches, updates and destroys them and draws them to the canvas) and Branch.as (holds the information of one single branch).
I believe its easiest if we just start with the smallest component, the branch.
package { import com.greensock.TweenLite; import se.xcom.math.Degrees; public class Branch { public var rotZ:Number; public var rotZSpeed:Number = 0; public var x:Number; public var y:Number; public var thickness:Number; public var speed:Number = 3; public var rotationSpeedSpan:Number; public var rotationSpeedFragility:Number = 0.2; public function Branch(x:Number,y:Number,rotZ:Number,thickness:Number,rotationSpeedSpan:Number) { this.x = x; this.y = y; this.rotZ = rotZ; this.thickness = thickness; this.rotationSpeedSpan = rotationSpeedSpan; setRotationSpeed(); } public function update() { x += Degrees.dSin(rotZ)*speed; y += Degrees.dCos(rotZ)*speed; rotZ += rotZSpeed; thickness-= 0.04; } public function destroy() { // kill the endless loop TweenLite.killTweensOf(this); } private function setRotationSpeed() { // To make sure that the rotation of the branch moves smoothly I decide not to tween the actual rotation but the speed of the rotation. // This is an endless loop, calling itself when the tween has ended. var newSpeed:Number = Math.random()*rotationSpeedSpan-rotationSpeedSpan*.5; var delay:Number = Math.random()*rotationSpeedFragility+rotationSpeedFragility*.5; TweenLite.to(this,delay,{rotZSpeed:newSpeed,onComplete:setRotationSpeed}); } } }
As you can see, the branch is not a display object. It just carries the info about the branch so Tree.as can draw it on it’s graphics canvas. The update() function makes sure that the branch shrinks in thickness, rotates and grows in the current direction it is given. The rotation is is changed through the Tween in setRotationSpeed(). Really nothing special (oh and if you are wondering about the Degrees class, it’s just converting radians into degrees as I hate radians.) Let’s move on to the Tree.
package { import com.greensock.TweenLite; import flash.display.Sprite; import flash.events.Event; public class Tree extends Sprite { private var color:Number private var aBranches:Array = []; public function Tree(color:Number) { this.color = color; reset(); super(); } public function start(delay:int) { // Just starts drawing the tree. Implanted only so the trees doesnt start drawing at the same time. TweenLite.delayedCall(delay,function(){addEventListener(Event.ENTER_FRAME,onTick)}); } private function onTick(e:Event) { // Goes through every branch in the tree. for each(var br:Branch in aBranches) { // Updates the branch and draws a new line between the old position and the new one. with (this.graphics) { lineStyle(br.thickness,color,1); moveTo(br.x,br.y); br.update(); lineTo(br.x,br.y); } // Randomly decides wether to create a new branch origin from the current branch. if (Math.random() < 0.1-(br.thickness*0.01)) { var th:int = (br.thickness>0.7)?(br.thickness-1):br.thickness; var tbr:Branch = new Branch(br.x,br.y,br.rotZ,th,br.rotationSpeedSpan+14); aBranches.push(tbr); } } // Goes through every branch in the tree (again!) just to destroy the branches that are too small. for (var i:int= aBranches.length-1;i > -1;i--) { var tbr:Branch = aBranches[i] if (tbr.thickness < 0.4) { tbr.destroy(); aBranches.splice(i,1) // Checks if there are no "alive" branches left. if (aBranches.length == 0) { TweenLite.to(this,2,{alpha:0,onComplete:reset}); } } } } private function reset() { this.graphics.clear(); alpha = 1; var tbr:Branch = new Branch(0,0,180,8,5) aBranches.push(tbr); } } }
As you can see, start() sets an Event.ENTER_FRAME listener so it automatically runs onTick() every frame. Most of the comments in the code tells you whats happening inside. It takes all the branches it currently has, updates them, draws them, sometimes spawns new branches and finally removes them if their thickness is, well not thick enough.
Only the Main.as class remains. I guess it need’s no comments.
package { import flash.display.Sprite; public class Main extends Sprite { private var aTrees:Array; private var aColors:Array=[0xffbb88,0xaaffaa,0xaabbff]; private var aBranches:Array public function Main() { for (var i:int = 0;i<3;i++) { var tree:Tree = new Tree(aColors[i]); tree.x = 250*i+200; tree.y = 370; tree.start(i*3.5); addChild(tree); } } } }
There you go, three tiny trees growing on your screen. There are lots of parameters that could be lifted out and tweaked with giving you more branches, faster drawing, stranger rotation of the branches and so on. But I leave it up to you to lift them out and customize the classes.