Just kicking back with some good old fashioned Flash experimentation.
[kml_flashembed publishmethod=”static” fversion=”10.0.0″ movie=”http://www.bit-101.com/blog/wp-content/uploads/2009/08/Blobs.swf” width=”600″ height=”600″ targetclass=”flashmovie”]
[/kml_flashembed]
Here’s the code:
[as]package
{
import com.bit101.components.CheckBox;
import com.bit101.components.Component;
import com.bit101.components.HUISlider;
import com.bit101.components.Knob;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
[SWF(width=600, height=600, backgroundColor=0xdddddd, frameRate=31)]
public class Blobs extends Sprite
{
private var firm:Number;
private var firmSlider:HUISlider;
private var gravity:Number;
private var gravitySlider:HUISlider;
private var radius:Number;
private var radiusSlider:HUISlider;
private var rotate:Number;
private var rotationAmount:Number;
private var rotateKnob:Knob;
private var points:Array;
private var numPoints:int;
private var renderLinesCB:CheckBox;
private var renderLines:Boolean;
private var renderPointsCB:CheckBox;
private var renderPoints:Boolean;
private var renderOutlineSegments:Boolean;
private var renderOutlineSegmentsCB:CheckBox;
private var renderOutlineCurves:Boolean;
private var renderOutlineCurvesCB:CheckBox;
public function Blobs()
{
Component.initStage(stage);
points = new Array();
numPoints = 20;
firm = 0.05;
firmSlider = new HUISlider(this, 10, 10, “Firmness”, onFirmChange);
firmSlider.width = 580;
firmSlider.setSliderParams(0, .5, firm);
firmSlider.labelPrecision = 3;
gravity = 1.5;
gravitySlider = new HUISlider(this, 10, 22, “Gravity”, onGravityChange);
gravitySlider.width = 580;
gravitySlider.setSliderParams(0, 5, gravity);
gravitySlider.labelPrecision = 2;
radius = 100;
radiusSlider = new HUISlider(this, 10, 34, “Radius”, onRadiusChange);
radiusSlider.width = 580;
radiusSlider.setSliderParams(1, 250, radius);
radiusSlider.labelPrecision = 0;
renderPoints = true
renderPointsCB = new CheckBox(this, 10, 55, “Render Points”, onRenderPoints);
renderPointsCB.selected = true;
renderLines = true;
renderLinesCB = new CheckBox(this, 10, 70, “Render Lines”, onRenderLines);
renderLinesCB.selected = true;
renderOutlineSegments = true;
renderOutlineSegmentsCB = new CheckBox(this, 10, 85, “Render Outline Segments”, onRenderOutlineSegments);
renderOutlineSegmentsCB.selected = true;
renderOutlineCurves = false;
renderOutlineCurvesCB = new CheckBox(this, 10, 100, “Render Outline Curves”, onRenderOutlineCurves);
renderOutlineCurvesCB.selected = false;
rotate = 0;
rotationAmount = 0;
rotateKnob = new Knob(this, 250, 60, “Rotate”, onRotateChange);
rotateKnob.minimum = -.1;
rotateKnob.maximum = .1;
rotateKnob.value = 0;
rotateKnob.labelPrecision = 3;
for(var i:int = 0; i < numPoints; i++)
{
var angle:Number = Math.PI * 2 / numPoints * i;
points.push(new Dot(400 + Math.cos(angle) * 100, 400 + Math.sin(angle) * 100));
}
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function onFirmChange(event:Event):void
{
firm = firmSlider.value;
}
private function onGravityChange(event:Event):void
{
gravity = gravitySlider.value;
}
private function onRadiusChange(event:Event):void
{
radius = radiusSlider.value;
}
private function onRotateChange(event:Event):void
{
rotationAmount = rotateKnob.value;
}
private function onRenderPoints(event:Event):void
{
renderPoints = renderPointsCB.selected;
}
private function onRenderLines(event:Event):void
{
renderLines = renderLinesCB.selected;
}
private function onRenderOutlineSegments(event:Event):void
{
renderOutlineSegments = renderOutlineSegmentsCB.selected;
}
private function onRenderOutlineCurves(event:Event):void
{
renderOutlineCurves = renderOutlineCurvesCB.selected;
}
private function enterFrameHandler(event:Event):void
{
graphics.clear();
var cx:Number = 0;
var cy:Number = 0;
for(var i:int = 0; i < numPoints; i++)
{
var point:Dot = points[i];
point.update(gravity);
cx += point.x;
cy += point.y;
if(renderPoints)
{
graphics.beginFill(0);
graphics.drawCircle(point.x, point.y, 2);
graphics.endFill();
}
}
cx /= numPoints;
cy /= numPoints;
if(renderPoints)
{
graphics.beginFill(0xff0000);
graphics.drawCircle(cx, cy, 5);
graphics.endFill();
}
for(i = 0; i < numPoints; i++)
{
var angle:Number = Math.PI * 2 / numPoints * i + rotate;
var tx:Number = cx + Math.cos(angle) * radius;
var ty:Number = cy + Math.sin(angle) * radius;
point = points[i];
point.vx += (tx - point.x) * firm;
point.vy += (ty - point.y) * firm;
if(renderLines)
{
graphics.lineStyle(0, 0, .25);
graphics.moveTo(cx, cy);
graphics.lineTo(point.x, point.y);
}
if(renderOutlineSegments && i > 0)
{
graphics.lineStyle(0, 0x0000ff, 0.5);
graphics.moveTo(points[i – 1].x, points[i – 1].y);
graphics.lineTo(point.x, point.y);
}
}
if(renderOutlineSegments)
{
graphics.lineStyle(0, 0x0000ff, 0.25);
graphics.moveTo(points[i – 1].x, points[i – 1].y);
graphics.lineTo(points[0].x, points[0].y);
}
if(renderOutlineCurves)
{
renderCurves();
}
rotate += rotationAmount;
}
private function renderCurves():void
{
var mids:Array = new Array();
for(var i:int = 0; i < points.length - 1; i++)
{
mids[i] = new Point((points[i].x + points[i + 1].x) / 2, (points[i].y + points[i + 1].y) / 2);
}
mids[i] = new Point((points[i].x + points[0].x) / 2, (points[i].y + points[0].y) / 2);
graphics.lineStyle(0, 0xff0000, .5);
graphics.moveTo(mids[0].x, mids[0].y);
for(i = 1; i < points.length; i++)
{
graphics.curveTo(points[i].x, points[i].y, mids[i].x, mids[i].y);
}
graphics.curveTo(points[0].x, points[0].y, mids[0].x, mids[0].y);
}
}
}[/as]
And you'll need this too:
[as]package
{
public class Dot
{
public var x:Number;
public var y:Number;
public var vx:Number;
public var vy:Number;
public function Dot(x:Number, y:Number)
{
this.x = x;
this.y = y;
vx = Math.random() * 10 - 5;
vy = Math.random() * 10 - 5;
}
public function update(gravity:Number):void
{
x += vx;
y += vy;
vy += gravity;
if(x < 0)
{
x = 0;
vx = 0;
vy = 0;
}
if(x > 600)
{
x = 600;
vx = 0;
vy = 0;
}
if(y > 600)
{
y = 600;
vy = 0;
vx = 0;
}
vx *= .99;
vy *= .99;
}
}
}[/as]
And, if you want it to run as-is, you’ll need my minimal comps.
It’s good to have you back in AS land again! It was a hard and tough time for us all but now that you’re back things are getting back to positive again. ^_-
hi,
glad to have you back to AS indeed ^^
I think you can make your blob more robust by adding a second row of balls & sticks as described in this article : http://cowboyprogramming.com/2007/01/05/blob-physics/
Pretty cool !
Now I remember why I subscribed to this blog in the first place.
I kept reading though, don’t get me wrong 😉
nicoptere, thanks for the link. interesting that I started out on a similar path.
Cool.. and while trying to get it to walk up the walls, I managed to get a circle floating on the bottom right.. 😉
Wow, that is really cool.
I don’t like the knob. It doesn’t respond quite like I expect.
Very nice. I played with it for quite a while 🙂
love it – really interesting how satisfying physics toys are to play with