This week is really challenging : my second year school project has started this week. It’s a team project with a graphic designer, a designer, a project manager and me as a developer. Our concept sounds very promising!
No matter, this short introduction explained that I’m really busy, so this experimentation is not perfect due to a lack of time…
Anyway, click here to see a sound spectrum made with Box2D and the Citrus Engine using BitmapData.
To create this effect, I used Box2D ApplyImpulse method on my Bars. I also used one Rope Joint by bar to link them all to an horizontal platform and try to not make them bounces everywhere. Uncomment the line box2D.visible = true; to see box2d objects with joints (don’t forget to comment stage bitmap addChilded).
There is a bug, sometimes objects pass between two bars and move them. I tried to change some object params like density, friction, restitution… but can’t find a good result.
Here is a part of the code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | package { import Box2DAS.Common.V2; import graphics.BarEqualizerArt; import graphics.ElementCircleArt; import graphics.ElementSquareArt; import objects.BarEqualizer; import objects.Element; import utils.Const; import com.citrusengine.core.CitrusEngine; import com.citrusengine.core.State; import com.citrusengine.objects.platformer.Platform; import com.citrusengine.physics.Box2D; import flash.display.Bitmap; import flash.display.BitmapData; import flash.media.SoundMixer; import flash.utils.ByteArray; import flash.utils.setTimeout; /** * @author Aymeric */ public class SoundSpectrumState extends State { private var _ce:CitrusEngine; private var _vectBars:Vector.<BarEqualizer>; private var _ba:ByteArray; public function SoundSpectrumState() { super(); _ce = CitrusEngine.getInstance(); _vectBars = new Vector.<BarEqualizer>(); _ba = new ByteArray(); } override public function initialize():void { super.initialize(); Const.bmpDStage = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0); Const.bmpDStage.draw(stage); var bmpStage:Bitmap = new Bitmap(Const.bmpDStage); //addChild(bmpStage); var box2D:Box2D = new Box2D("Box2D"); box2D.visible = true; add(box2D); var borderLeft:Platform = new Platform("borderLeft", {y:stage.stageHeight >> 1, height:stage.stageHeight}); add(borderLeft); var borderRight:Platform = new Platform("borderRight", {x:stage.stageWidth, y:stage.stageHeight >> 1, height:stage.stageHeight}); add(borderRight); var borderTop:Platform = new Platform("borderTop", {x:stage.stageWidth >> 1, width:stage.stageWidth}); add(borderTop); var borderBottom:Platform = new Platform("borderBottom", {x:stage.stageWidth >> 1, y:350, width:stage.stageWidth}); add(borderBottom); var barEqualizer:BarEqualizer; for (var i:uint = 0; i < Const.NBR_BAR; ++i) { barEqualizer = new BarEqualizer("barEqualizer" + i, {x:22.5 + Const.BarEqualizerWidth * i, y:stage.stageHeight, width:Const.BarEqualizerWidth, height:Const.BarEqualizerHeight, view:BarEqualizerArt}); add(barEqualizer); _vectBars.push(barEqualizer); barEqualizer.initJoint(borderBottom, 22.5 + Const.BarEqualizerWidth * i); } _ce.sound.playSound("music"); setTimeout(_addElements, 2500); } private function _addElements():void { var element:Element; for (var j:uint = 0; j < 12; ++j) { element = new Element("Element" + j, {x: 40 + 40 * j, y:50, radius:Math.random() > 0.5 ? Const.ELEMENT_RADIUS : 0}); element.view = element.radius == Const.ELEMENT_RADIUS ? ElementCircleArt : ElementSquareArt; add(element); } } override public function update(timeDelta:Number):void { super.update(timeDelta); SoundMixer.computeSpectrum(_ba, true); for (var i:uint = 0; i < Const.NBR_BAR; ++i) { _vectBars[i].body.ApplyImpulse(new V2(0, -_ba.readFloat() * Const.STRENGTH), _vectBars[i].body.GetWorldCenter()); } } override public function destroy():void { super.destroy(); } } } |
The Box2D Bar class :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package objects{ import Box2DAS.Common.V2; import Box2DAS.Dynamics.Joints.b2RopeJoint; import Box2DAS.Dynamics.Joints.b2RopeJointDef; import com.citrusengine.objects.PhysicsObject; import com.citrusengine.objects.platformer.Platform; /** * @author Aymeric */ public class BarEqualizer extends PhysicsObject { public function BarEqualizer(name:String, params:Object = null) { super(name, params); } override public function destroy():void { super.destroy(); } override public function update(timeDelta:Number):void { super.update(timeDelta); } override protected function createBody():void { super.createBody(); _body.SetFixedRotation(true); } public function initJoint(platform:Platform, posX:uint):void { var jointDefPlatform:b2RopeJointDef = new b2RopeJointDef(); jointDefPlatform.Initialize(body, platform.body, body.GetPosition(), new V2(posX / _box2D.scale, platform.body.GetPosition().y)); var joint:b2RopeJoint = b2RopeJoint(_box2D.world.CreateJoint(jointDefPlatform)); joint.m_maxLength = 3; } } } |
And Bar graphics art :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package graphics { import utils.Const; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.events.Event; import flash.geom.Point; import flash.geom.Rectangle; /** * @author Aymeric */ public class BarEqualizerArt extends AArt { public function BarEqualizerArt() { super(); _art.graphics.clear(); _art.graphics.beginFill(Math.random() * 0xFFFFFF); _art.graphics.drawRect(0, 0, Const.BarEqualizerWidth, Const.BarEqualizerHeight); _art.graphics.endFill(); _bmpD = new BitmapData(Const.BarEqualizerWidth, Const.BarEqualizerHeight); _bmpD.draw(_art); } override protected function _ef(evt:Event):void { var point:Point = new Point((this as DisplayObject).localToGlobal(new Point()).x - Const.BarEqualizerWidth * 0.5, (this as DisplayObject).localToGlobal(new Point()).y - Const.BarEqualizerHeight * 0.5); Const.bmpDStage.applyFilter(_bmpD, _bmpD.rect, point, _setBitmapFilter()); Const.bmpDStage.fillRect(new Rectangle(point.x, 0, Const.bmpDStage.width, Const.bmpDStage.height), 0); Const.bmpDStage.copyPixels(_bmpD, _bmpD.rect, point, null, null, true); } } } |
Objects drop (after music start) don’t use the bitmap data to render graphics but a simple Sprite. This is not optimized!
To make it properly use only one bitmap data (no sprite) and one enter frame in the stat class. Then call a render method on each object (bars and other objects). Finally think to use : bitmapData.lock() and bitmapData.unlock() (and make your bitmap data update between them) for a better control.
If I have some time to focus on it, I will make some improvements… but it’s not my priority. You should have enough information to make a cool Sound Spectrum with box2d 😉
Oh and the music is an Abba cover song, made by Therion!