I'm so excited that I finally got to work on some experimental Flash at my professional employment. It is a nice change of pace to push an idea to the limits...and then some. In fact, I'm still not done working on the possibilities. This was only the starting point. Making text fall. Tie it into the Box2DFlash Physics Engine.
I want to give yet another shout out to Emanuele Feronato. If you are looking at this code, and are completely lost, he has EXCELLENT tutorials to get you started. Thats where I ended up taking my first steps in Box2DFlash.
Now in the guise of learning. I don't intend on going too indepth with the code...there is a lot going on. But hopefully, by breaking it down into phases, you can understand my building process. Something like this isn't accomplished in one chunk of code. It is build up by steps. At least the way I code.
There is one important element I need to mention.
EMBEDDING FONT: In the Library in the FLA, you need to embed the font via "New Font..." in the Library options. I embedded Verdana for this example. Because if you use a dynamic textfield to animate (especially rotations) the font could randomly just disappear on you. So, this fixes that problem.
So here is the game plan:
- Make a base Box2DFlash file
- add in falling boxes
- add in dynamic text
- make the text match the orientation of the boxes
- make the boxes match the size of the text
- display only the text
Simple, right? Let us begin.
And here is a zip with FLA and AS files to follow along.
The Base
So we start with just a build of Box2DFlash. I've just added a floor and a circle to bounce boxes off of. I'll add in my copy of Box2DFlash into the zip. There is one thing to remember when figuring out placement and measurements in Box2DFlash. The timing and physics is calculated not using the normal frame rate. It need to be calculated by some other way, time I believe (just not frame). By default it seems like the X and Y measurements are figured out by taking the value you want it to be in pixels and just dividing by 30. So if you want something 1024 pixels it'll be 34.133 in Box2DFlash.
package { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.events.Event; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class TextFalls extends Sprite { public var the_world:b2World; public var physScale:Number=30; public var final_body:b2Body; public var the_body:b2BodyDef; public var the_box:b2PolygonDef; public var the_circle:b2CircleDef; public var environment:b2AABB = new b2AABB(); public var gravity:b2Vec2=new b2Vec2(0.0,10.0); public function TextFalls() { environment.lowerBound.Set(-100.0, -100.0); environment.upperBound.Set(100.0, 100.0); the_world=new b2World(environment,gravity,true); buildWalls(); addEventListener(Event.ENTER_FRAME, on_enter_frame); } public function buildWalls() { trace("Building Walls"); //This Turns on The Ability to View Where the Physics Blockades are (best to get used to the spacing) var debug_draw:b2DebugDraw = new b2DebugDraw(); var debug_sprite:Sprite = new Sprite(); addChild(debug_sprite); debug_draw.m_sprite=debug_sprite; debug_draw.m_drawScale=30; debug_draw.m_fillAlpha=0.5; debug_draw.m_lineThickness=1; debug_draw.m_drawFlags=b2DebugDraw.e_shapeBit; the_world.SetDebugDraw(debug_draw); the_body = new b2BodyDef(); the_body.position.Set(1024 / 2 / physScale, 768 / physScale);//physScale is used to make calculating time based physics and not frame based. the_box = new b2PolygonDef(); the_box.SetAsBox(400 / physScale, 20 / physScale); the_box.friction=0.3; the_box.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); the_body = new b2BodyDef(); the_body.position.Set(1024 / 5 / physScale, 768 / 2 / physScale); the_circle = new b2CircleDef(); the_circle.radius=3.0; the_circle.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_circle); final_body.SetMassFromShapes(); } public function on_enter_frame(e:Event) { the_world.Step(1/30, 10); } } }
Adding Blocks
Now we need some physics items. Since text has a mostly rectangular shape. Lets start with boxes.
package { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class TextFalls extends Sprite { public var the_world:b2World; public var physScale:Number=30; public var final_body:b2Body; public var the_body:b2BodyDef; public var the_box:b2PolygonDef; public var the_circle:b2CircleDef; public var environment:b2AABB = new b2AABB(); public var gravity:b2Vec2=new b2Vec2(0.0,10.0); public var dropTextTime:Timer; public function TextFalls() { environment.lowerBound.Set(-100.0, -100.0); environment.upperBound.Set(100.0, 100.0); the_world=new b2World(environment,gravity,true); buildWalls(); addEventListener(Event.ENTER_FRAME, on_enter_frame); dropTextTime=new Timer(1000, 0); dropTextTime.addEventListener(TimerEvent.TIMER, newText); dropTextTime.start(); } public function buildWalls() { trace("Building Walls"); //This Turns on The Ability to View Where the Physics Blockades are (best to get used to the spacing) var debug_draw:b2DebugDraw = new b2DebugDraw(); var debug_sprite:Sprite = new Sprite(); addChild(debug_sprite); debug_draw.m_sprite=debug_sprite; debug_draw.m_drawScale=30; debug_draw.m_fillAlpha=0.5; debug_draw.m_lineThickness=1; debug_draw.m_drawFlags=b2DebugDraw.e_shapeBit; the_world.SetDebugDraw(debug_draw); the_body = new b2BodyDef(); the_body.position.Set(1024 / 2 / physScale, 768 / physScale);//physScale is used to make calculating time based physics and not frame based. the_box = new b2PolygonDef(); the_box.SetAsBox(400 / physScale, 20 / physScale); the_box.friction=0.3; the_box.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); the_body = new b2BodyDef(); the_body.position.Set(1024 / 5 / physScale, 768 / 2 / physScale); the_circle = new b2CircleDef(); the_circle.radius=3.0; the_circle.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_circle); final_body.SetMassFromShapes(); } public function newText(evt:TimerEvent):void{ var final_body:b2Body; var the_body:b2BodyDef; var the_box:b2PolygonDef; the_body = new b2BodyDef(); the_body.position.Set(Math.random()*6.6+10, -1); the_box = new b2PolygonDef(); the_box.SetAsBox(1, 1); the_box.friction=1; the_box.density=.9; the_box.restitution=.2; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); } public function on_enter_frame(evt:Event) { the_world.Step(1/30, 10); } } }
Keep Memory Clean
Now, I don't want the falling block to exist forever. So in case they do fall off of the floor, I want to remove the items from the Flash.
package { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class TextFalls extends Sprite { public var the_world:b2World; public var physScale:Number=30; public var final_body:b2Body; public var the_body:b2BodyDef; public var the_box:b2PolygonDef; public var the_circle:b2CircleDef; public var environment:b2AABB = new b2AABB(); public var gravity:b2Vec2=new b2Vec2(0.0,10.0); public var dropTextTime:Timer; public var blockTrackingArray:Array = new Array(); public function TextFalls() { environment.lowerBound.Set(-100.0, -100.0); environment.upperBound.Set(100.0, 100.0); the_world=new b2World(environment,gravity,true); buildWalls(); addEventListener(Event.ENTER_FRAME, on_enter_frame); dropTextTime=new Timer(1000, 0); dropTextTime.addEventListener(TimerEvent.TIMER, newText); dropTextTime.start(); } public function buildWalls() { trace("Building Walls"); //This Turns on The Ability to View Where the Physics Blockades are (best to get used to the spacing) var debug_draw:b2DebugDraw = new b2DebugDraw(); var debug_sprite:Sprite = new Sprite(); addChild(debug_sprite); debug_draw.m_sprite=debug_sprite; debug_draw.m_drawScale=30; debug_draw.m_fillAlpha=0.5; debug_draw.m_lineThickness=1; debug_draw.m_drawFlags=b2DebugDraw.e_shapeBit; the_world.SetDebugDraw(debug_draw); the_body = new b2BodyDef(); the_body.position.Set(1024 / 2 / physScale, 768 / physScale);//physScale is used to make calculating time based physics and not frame based. the_box = new b2PolygonDef(); the_box.SetAsBox(400 / physScale, 20 / physScale); the_box.friction=0.3; the_box.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); the_body = new b2BodyDef(); the_body.position.Set(1024 / 5 / physScale, 768 / 2 / physScale); the_circle = new b2CircleDef(); the_circle.radius=3.0; the_circle.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_circle); final_body.SetMassFromShapes(); } public function newText(evt:TimerEvent):void{ var final_body:b2Body; var the_body:b2BodyDef; var the_box:b2PolygonDef; the_body = new b2BodyDef(); the_body.position.Set(Math.random()*6.6+10, -1); the_box = new b2PolygonDef(); the_box.SetAsBox(1, 1); the_box.friction=1; the_box.density=.9; the_box.restitution=.2; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); blockTrackingArray.push(final_body); } public function on_enter_frame(evt:Event) { the_world.Step(1/30, 10); var detroyAt:Array = new Array(); var totalBlocks = blockTrackingArray.length; for(var b = 0; b < totalBlocks; ++b){ if(blockTrackingArray[b].GetPosition().y > 768 / physScale){ //Kills Block if it fall past the screen. the_world.DestroyBody(blockTrackingArray[b]); detroyAt.push(b); } } for(var intd = detroyAt.length-1; intd >= 0; --intd){ blockTrackingArray.splice(detroyAt[intd], 1); detroyAt.pop(); } } } }
Adding Text
Now its time to add some text. I'm building a long string of 'lorem ipsum' into an array. As the text is created, it just moves to the next word. This is where you can change the content being pulled into the page.
package { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class TextFalls extends Sprite { public var the_world:b2World; public var physScale:Number=30; public var final_body:b2Body; public var the_body:b2BodyDef; public var the_box:b2PolygonDef; public var the_circle:b2CircleDef; public var environment:b2AABB = new b2AABB(); public var gravity:b2Vec2=new b2Vec2(0.0,10.0); public var dropTextTime:Timer; public var blockTrackingArray:Array = new Array(); public var labelTxt:TextField = new TextField(); public var labelTrackingArray:Array = new Array(); public var wordsArray:Array = new Array(); public var slot=0; public var Text:String="Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum."; public function TextFalls() { environment.lowerBound.Set(-100.0, -100.0); environment.upperBound.Set(100.0, 100.0); the_world=new b2World(environment,gravity,true); buildWalls(); addEventListener(Event.ENTER_FRAME, on_enter_frame); dropTextTime=new Timer(1000,0); dropTextTime.addEventListener(TimerEvent.TIMER, newText); dropTextTime.start(); wordsArray=Text.split(" "); } public function buildWalls() { trace("Building Walls"); //This Turns on The Ability to View Where the Physics Blockades are (best to get used to the spacing) var debug_draw:b2DebugDraw = new b2DebugDraw(); var debug_sprite:Sprite = new Sprite(); addChild(debug_sprite); debug_draw.m_sprite=debug_sprite; debug_draw.m_drawScale=30; debug_draw.m_fillAlpha=0.5; debug_draw.m_lineThickness=1; debug_draw.m_drawFlags=b2DebugDraw.e_shapeBit; the_world.SetDebugDraw(debug_draw); the_body = new b2BodyDef(); the_body.position.Set(1024 / 2 / physScale, 768 / physScale);//physScale is used to make calculating time based physics and not frame based. the_box = new b2PolygonDef(); the_box.SetAsBox(400 / physScale, 20 / physScale); the_box.friction=0.3; the_box.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); the_body = new b2BodyDef(); the_body.position.Set(1024 / 5 / physScale, 768 / 2 / physScale); the_circle = new b2CircleDef(); the_circle.radius=3.0; the_circle.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_circle); final_body.SetMassFromShapes(); } private function NewTextLabel():void { labelTxt = new TextField(); labelTxt.autoSize=TextFieldAutoSize.LEFT; labelTxt.cacheAsBitmap=true; labelTxt.embedFonts=true; labelTxt.x=-100; var format:TextFormat = new TextFormat(); format.font="Verdana"; format.color=0xFFFFFF; if (wordsArray[slot].length==0) { ++slot; } if (slot>=wordsArray.length-1) { slot=0; } labelTxt.text=wordsArray[slot]; format.size=20; labelTxt.defaultTextFormat=format; labelTxt.text=wordsArray[slot]; ++slot; } public function newText(evt:TimerEvent):void { NewTextLabel(); addChild(labelTxt); labelTrackingArray.push(labelTxt); var final_body:b2Body; var the_body:b2BodyDef; var the_box:b2PolygonDef; the_body = new b2BodyDef(); the_body.position.Set(Math.random()*6.6+10, -1); the_box = new b2PolygonDef(); the_box.SetAsBox(1, 1); the_box.friction=1; the_box.density=.9; the_box.restitution=.2; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); blockTrackingArray.push(final_body); } public function on_enter_frame(evt:Event) { the_world.Step(1/30, 10); var detroyAt:Array = new Array(); var totalBlocks=blockTrackingArray.length; for (var b = 0; b < totalBlocks; ++b) { if (blockTrackingArray[b].GetPosition().y>768/physScale) {//Kills Block if it fall past the screen. the_world.DestroyBody(blockTrackingArray[b]); removeChild(labelTrackingArray[b]); detroyAt.push(b); } else { var bodyRotation:Number=blockTrackingArray[b].GetAngle(); labelTrackingArray[b].rotation = bodyRotation * (180/Math.PI) % 360; labelTrackingArray[b].x = (blockTrackingArray[b].GetPosition().x * physScale); labelTrackingArray[b].y = (blockTrackingArray[b].GetPosition().y * physScale); } } for (var d = detroyAt.length-1; d >= 0; --d) { blockTrackingArray.splice(detroyAt[d], 1); labelTrackingArray.splice(detroyAt[d], 1); detroyAt.pop(); } } } }
Scale Physics Boxes
Now the physics boxes get rescaled to match the width and height of the text.
package { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class TextFalls extends Sprite { public var the_world:b2World; public var physScale:Number=30; public var final_body:b2Body; public var the_body:b2BodyDef; public var the_box:b2PolygonDef; public var the_circle:b2CircleDef; public var environment:b2AABB = new b2AABB(); public var gravity:b2Vec2=new b2Vec2(0.0,10.0); public var dropTextTime:Timer; public var blockTrackingArray:Array = new Array(); public var labelTxt:TextField = new TextField(); public var labelTrackingArray:Array = new Array(); public var wordsArray:Array = new Array(); public var slot=0; public var Text:String="Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum."; public function TextFalls() { environment.lowerBound.Set(-100.0, -100.0); environment.upperBound.Set(100.0, 100.0); the_world=new b2World(environment,gravity,true); buildWalls(); addEventListener(Event.ENTER_FRAME, on_enter_frame); dropTextTime=new Timer(1000,0); dropTextTime.addEventListener(TimerEvent.TIMER, newText); dropTextTime.start(); wordsArray=Text.split(" "); } public function buildWalls() { trace("Building Walls"); //This Turns on The Ability to View Where the Physics Blockades are (best to get used to the spacing) var debug_draw:b2DebugDraw = new b2DebugDraw(); var debug_sprite:Sprite = new Sprite(); addChild(debug_sprite); debug_draw.m_sprite=debug_sprite; debug_draw.m_drawScale=30; debug_draw.m_fillAlpha=0.5; debug_draw.m_lineThickness=1; debug_draw.m_drawFlags=b2DebugDraw.e_shapeBit; the_world.SetDebugDraw(debug_draw); the_body = new b2BodyDef(); the_body.position.Set(1024 / 2 / physScale, 768 / physScale);//physScale is used to make calculating time based physics and not frame based. the_box = new b2PolygonDef(); the_box.SetAsBox(400 / physScale, 20 / physScale); the_box.friction=0.3; the_box.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); the_body = new b2BodyDef(); the_body.position.Set(1024 / 5 / physScale, 768 / 2 / physScale); the_circle = new b2CircleDef(); the_circle.radius=3.0; the_circle.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_circle); final_body.SetMassFromShapes(); } private function NewTextLabel():void { labelTxt = new TextField(); labelTxt.autoSize=TextFieldAutoSize.LEFT; labelTxt.cacheAsBitmap=true; labelTxt.embedFonts=true; labelTxt.x=-100; var format:TextFormat = new TextFormat(); format.font="Verdana"; format.color=0xFFFFFF; if (wordsArray[slot].length==0) { ++slot; } if (slot>=wordsArray.length-1) { slot=0; } labelTxt.text=wordsArray[slot]; format.size=20; labelTxt.defaultTextFormat=format; labelTxt.text=wordsArray[slot]; ++slot; } public function newText(evt:TimerEvent):void { NewTextLabel(); addChild(labelTxt); labelTrackingArray.push(labelTxt); var final_body:b2Body; var the_body:b2BodyDef; var the_box:b2PolygonDef; the_body = new b2BodyDef(); the_body.position.Set(Math.random()*6.6+10, -1); the_box = new b2PolygonDef(); the_box.SetAsBox(labelTxt.width/physScale/2, labelTxt.height/physScale/2); the_box.friction=1; the_box.density=.9; the_box.restitution=.2; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); blockTrackingArray.push(final_body); } public function on_enter_frame(evt:Event) { the_world.Step(1/30, 10); var detroyAt:Array = new Array(); var totalBlocks=blockTrackingArray.length; for (var b = 0; b < totalBlocks; ++b) { if (blockTrackingArray[b].GetPosition().y>768/physScale) {//Kills Block if it fall past the screen. the_world.DestroyBody(blockTrackingArray[b]); removeChild(labelTrackingArray[b]); detroyAt.push(b); } else { var bodyRotation:Number=blockTrackingArray[b].GetAngle(); labelTrackingArray[b].rotation = bodyRotation * (180/Math.PI) % 360; labelTrackingArray[b].x = (blockTrackingArray[b].GetPosition().x * physScale); labelTrackingArray[b].y = (blockTrackingArray[b].GetPosition().y * physScale); } } for (var d = detroyAt.length-1; d >= 0; --d) { blockTrackingArray.splice(detroyAt[d], 1); labelTrackingArray.splice(detroyAt[d], 1); detroyAt.pop(); } } } }
Re-Positioning Text
Time to move the text into the block area of the physics item.
package { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class TextFalls extends Sprite { public var the_world:b2World; public var physScale:Number=30; public var final_body:b2Body; public var the_body:b2BodyDef; public var the_box:b2PolygonDef; public var the_circle:b2CircleDef; public var environment:b2AABB = new b2AABB(); public var gravity:b2Vec2=new b2Vec2(0.0,10.0); public var dropTextTime:Timer; public var blockTrackingArray:Array = new Array(); public var labelTxt:TextField = new TextField(); public var labelTrackingArray:Array = new Array(); public var wordsArray:Array = new Array(); public var slot=0; public var Text:String="Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum."; public var spriteArray:Array = new Array(); public var holderSprite:Sprite = new Sprite(); public function TextFalls() { environment.lowerBound.Set(-100.0, -100.0); environment.upperBound.Set(100.0, 100.0); the_world=new b2World(environment,gravity,true); buildWalls(); addEventListener(Event.ENTER_FRAME, on_enter_frame); dropTextTime=new Timer(1000,0); dropTextTime.addEventListener(TimerEvent.TIMER, newText); dropTextTime.start(); wordsArray=Text.split(" "); } public function buildWalls() { trace("Building Walls"); //This Turns on The Ability to View Where the Physics Blockades are (best to get used to the spacing) var debug_draw:b2DebugDraw = new b2DebugDraw(); var debug_sprite:Sprite = new Sprite(); addChild(debug_sprite); debug_draw.m_sprite=debug_sprite; debug_draw.m_drawScale=30; debug_draw.m_fillAlpha=0.5; debug_draw.m_lineThickness=1; debug_draw.m_drawFlags=b2DebugDraw.e_shapeBit; the_world.SetDebugDraw(debug_draw); the_body = new b2BodyDef(); the_body.position.Set(1024 / 2 / physScale, 768 / physScale);//physScale is used to make calculating time based physics and not frame based. the_box = new b2PolygonDef(); the_box.SetAsBox(400 / physScale, 20 / physScale); the_box.friction=0.3; the_box.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); the_body = new b2BodyDef(); the_body.position.Set(1024 / 5 / physScale, 768 / 2 / physScale); the_circle = new b2CircleDef(); the_circle.radius=3.0; the_circle.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_circle); final_body.SetMassFromShapes(); } private function NewTextLabel():void { labelTxt = new TextField(); labelTxt.autoSize=TextFieldAutoSize.LEFT; labelTxt.cacheAsBitmap=true; labelTxt.embedFonts=true; var format:TextFormat = new TextFormat(); format.font="Verdana"; format.color=0xFFFFFF; if (wordsArray[slot].length==0) { ++slot; } if (slot>=wordsArray.length-1) { slot=0; } labelTxt.text=wordsArray[slot]; format.size=20; labelTxt.defaultTextFormat=format; labelTxt.text=wordsArray[slot]; ++slot; } public function newText(evt:TimerEvent):void { NewTextLabel(); holderSprite = new Sprite(); addChild(holderSprite); holderSprite.addChild(labelTxt); labelTxt.x=holderSprite.x-labelTxt.width/2; labelTxt.y=holderSprite.y-labelTxt.height/2; holderSprite.x = -1000; holderSprite.y = -1000; spriteArray.push(holderSprite); labelTrackingArray.push(labelTxt); var final_body:b2Body; var the_body:b2BodyDef; var the_box:b2PolygonDef; the_body = new b2BodyDef(); the_body.position.Set(Math.random()*6.6+10, -1); the_box = new b2PolygonDef(); the_box.SetAsBox(labelTxt.width/physScale/2, labelTxt.height/physScale/2); the_box.friction=1; the_box.density=.9; the_box.restitution=.2; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); blockTrackingArray.push(final_body); } public function on_enter_frame(evt:Event) { the_world.Step(1/30, 10); var detroyAt:Array = new Array(); var totalBlocks=blockTrackingArray.length; for (var b = 0; b < totalBlocks; ++b) { if (blockTrackingArray[b].GetPosition().y>768/physScale) {//Kills Block if it fall past the screen. spriteArray[b].removeChild(labelTrackingArray[b]); removeChild(spriteArray[b]); the_world.DestroyBody(blockTrackingArray[b]); detroyAt.push(b); } var bodyRotation:Number=blockTrackingArray[b].GetAngle(); spriteArray[b].rotation = bodyRotation * (180/Math.PI) % 360; spriteArray[b].x = (blockTrackingArray[b].GetPosition().x * physScale); spriteArray[b].y = (blockTrackingArray[b].GetPosition().y * physScale); } for (var d = detroyAt.length-1; d >= 0; --d) { blockTrackingArray.splice(detroyAt[d], 1); labelTrackingArray.splice(detroyAt[d], 1); spriteArray.splice(detroyAt[d], 1); detroyAt.pop(); } } } }
Final Touch
Finally, its time to take off the debug view of the physics items and just let the words fall!
package { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class TextFalls extends Sprite { public var the_world:b2World; public var physScale:Number=30; public var final_body:b2Body; public var the_body:b2BodyDef; public var the_box:b2PolygonDef; public var the_circle:b2CircleDef; public var environment:b2AABB = new b2AABB(); public var gravity:b2Vec2=new b2Vec2(0.0,10.0); public var dropTextTime:Timer; public var blockTrackingArray:Array = new Array(); public var labelTxt:TextField = new TextField(); public var labelTrackingArray:Array = new Array(); public var wordsArray:Array = new Array(); public var slot=0; public var Text:String="Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum."; public var spriteArray:Array = new Array(); public var holderSprite:Sprite = new Sprite(); public function TextFalls() { environment.lowerBound.Set(-100.0, -100.0); environment.upperBound.Set(100.0, 100.0); the_world=new b2World(environment,gravity,true); buildWalls(); addEventListener(Event.ENTER_FRAME, on_enter_frame); dropTextTime=new Timer(1000,0); dropTextTime.addEventListener(TimerEvent.TIMER, newText); dropTextTime.start(); wordsArray=Text.split(" "); } public function buildWalls() { trace("Building Walls"); //This Turns on The Ability to View Where the Physics Blockades are (best to get used to the spacing) /*var debug_draw:b2DebugDraw = new b2DebugDraw(); var debug_sprite:Sprite = new Sprite(); addChild(debug_sprite); debug_draw.m_sprite=debug_sprite; debug_draw.m_drawScale=30; debug_draw.m_fillAlpha=0.5; debug_draw.m_lineThickness=1; debug_draw.m_drawFlags=b2DebugDraw.e_shapeBit; the_world.SetDebugDraw(debug_draw);*/ the_body = new b2BodyDef(); the_body.position.Set(1024 / 2 / physScale, 768 / physScale);//physScale is used to make calculating time based physics and not frame based. the_box = new b2PolygonDef(); the_box.SetAsBox(400 / physScale, 20 / physScale); the_box.friction=0.3; the_box.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); the_body = new b2BodyDef(); the_body.position.Set(1024 / 5 / physScale, 768 / 2 / physScale); the_circle = new b2CircleDef(); the_circle.radius=3.0; the_circle.density=0; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_circle); final_body.SetMassFromShapes(); } private function NewTextLabel():void { labelTxt = new TextField(); labelTxt.autoSize=TextFieldAutoSize.LEFT; labelTxt.cacheAsBitmap=true; labelTxt.embedFonts=true; var format:TextFormat = new TextFormat(); format.font="Verdana"; format.color=0xFFFFFF; if (wordsArray[slot].length==0) { ++slot; } if (slot>=wordsArray.length-1) { slot=0; } labelTxt.text=wordsArray[slot]; format.size=20; labelTxt.defaultTextFormat=format; labelTxt.text=wordsArray[slot]; ++slot; } public function newText(evt:TimerEvent):void { NewTextLabel(); holderSprite = new Sprite(); addChild(holderSprite); holderSprite.addChild(labelTxt); labelTxt.x=holderSprite.x-labelTxt.width/2; labelTxt.y=holderSprite.y-labelTxt.height/2; holderSprite.x=-1000; holderSprite.y=-1000; spriteArray.push(holderSprite); labelTrackingArray.push(labelTxt); var final_body:b2Body; var the_body:b2BodyDef; var the_box:b2PolygonDef; the_body = new b2BodyDef(); the_body.position.Set(Math.random()*6.6+10, -1); the_box = new b2PolygonDef(); the_box.SetAsBox(labelTxt.width/physScale/2-.1, labelTxt.height/physScale/2-.2);/////////////////Tighten Edges the_box.friction=1; the_box.density=.9; the_box.restitution=.2; final_body=the_world.CreateBody(the_body); final_body.CreateShape(the_box); final_body.SetMassFromShapes(); blockTrackingArray.push(final_body); } public function on_enter_frame(evt:Event) { the_world.Step(1/30, 10); var detroyAt:Array = new Array(); var totalBlocks=blockTrackingArray.length; for (var b = 0; b < totalBlocks; ++b) { if (blockTrackingArray[b].GetPosition().y>768/physScale) {//Kills Block if it fall past the screen. spriteArray[b].removeChild(labelTrackingArray[b]); removeChild(spriteArray[b]); the_world.DestroyBody(blockTrackingArray[b]); detroyAt.push(b); } var bodyRotation:Number=blockTrackingArray[b].GetAngle(); spriteArray[b].rotation = bodyRotation * (180/Math.PI) % 360; spriteArray[b].x = (blockTrackingArray[b].GetPosition().x * physScale); spriteArray[b].y = (blockTrackingArray[b].GetPosition().y * physScale); } for (var d = detroyAt.length-1; d >= 0; --d) { blockTrackingArray.splice(detroyAt[d], 1); labelTrackingArray.splice(detroyAt[d], 1); spriteArray.splice(detroyAt[d], 1); detroyAt.pop(); } } } }
From here...I'm still not done thinking of possibilities. There is a whole world of things you can do. Where you go, is up to your imagination.
Happy Experimenting
(^_^)//
Tags: Actionscript 3.0, Box2DFlash, Flash, Tutorial
Hi,
Thx very much for this awesome tutorial !
Helped me very much in getting an effect I always wanted on a website.
Just one question. How could I limit the number of text blocks there are totally. Because if nothing falls below the viewport, its just getting more and more and flash player is getting slower and slower
bernhard
Since this is timer based, you could just change the timer settings. dropTextTime=new Timer(1000,0);
dropTextTime=new Timer(1000, 100); should make it only drop 100 words and then stop.
Thanks for the great tutorial, just a quick question. Is there a way to turn the Text:String into hyperlinks?
You can, sort of.
Line 106, is where you can add the
labelTxt.htmlText=”<a href=’http://www.jamesbull.ca/’ >”+wordsArray[slot]+”</a>”;
~ .text now becomes .htmlText
I tried adding the tag into the initial string, but the way I’m parsing it to create the little bits of text it doesn’t work properly.
If you were to change the string getting parsed into an array and included the tags in each item, it should work then.
Nice website btw James.
Thanks for the compliments on the site! It looks like I wont be adding the links in, since I am only creating a “demo” for a client before handing the project off to a real flash/as3 developer. But I would like to get a hover effect working. The working demo can be found here:
http://jamesbull.ca/projects/wax/orda/HelloWorld.html
But I am getting the following error:
ArgumentError: Error #1063: Argument count mismatch on MethodInfo-669(). Expected 2, got 1.
And finally the AS code snippet can be found here:
http://snipplr.com/view/23307/box2dflash-falling-text/
I have just started learning AS, and don’t quite understand its syntax, but I’ve been learning and am looking forward to understanding the solution to my dilemma!
I saw something like this effect as a game preloader.
Thanks for the tutorial.
unfortunately, Error 404 – Not Found…
looks promising, I wonder if you can assign the physics collider to movable sprites onstage that are controlled by a web camera/blog detect.
Oops. I got to be careful with my spring cleaning. It is back online now.
http://www.orda.ca/ Looks like this was successfully implimented in a real world project. (^_^) AWESOME! Looking good James.