id:flashrodさんを参考に物理シミュレーション
(参考:flashrod)
Box2Dってバネ制約ないんすね。びっくりした。
http://blog.technohippy.net/Breast.swf
揺れます(不自然・・・)。
ソースは以下。
ところどころ自分でも何やってるかよく分かってません。
Spring.as
package { import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; import Box2D.Dynamics.*; import Box2D.Dynamics.Joints.*; import flash.display.Sprite; import flash.events.TimerEvent; import flash.geom.Point; import flash.utils.Timer; import Spring; public class Breast extends Sprite { private static var SWELLING_RATE:Number = 3; public var world:b2World; private var m_physScale:Number = 1.0; private var bodies:Array = new Array(); private var springs:Array = new Array(); public function Breast() { world = createWorld(); var points:Array = new Array(); var center:Point = new Point(100, 100); var radius:int = 80; // breast for (var i:int = 0; i < 7; i++) { var theta:Number = (i / 6) * Math.PI var point:Point = new Point(radius * Math.sin(theta), 200 - radius * Math.cos(theta)); point.y += point.x / SWELLING_RATE; var bodyDef:b2BodyDef = createBodyDef(point, [0, 6].indexOf(i) != -1); var body:b2Body = world.CreateBody(bodyDef); bodies.push(body); points.push(point); } // middle anchor bodyDef = createBodyDef(new Point( bodies[0].GetCenterPosition().x, (bodies[0].GetCenterPosition().y + bodies[bodies.length-1].GetCenterPosition().y) / 2 ), true); bodies.push(world.CreateBody(bodyDef)); points.push(point); // wall var wallBox:b2BoxDef = new b2BoxDef(); wallBox.extents.Set(10/m_physScale, 680/m_physScale); var wallBody:b2BodyDef = new b2BodyDef(); wallBody.position.Set(-10/m_physScale, (360+95)/m_physScale); wallBody.AddShape(wallBox); world.CreateBody(wallBody); for (i = 0; i < bodies.length; i++) { for (var j:int = i; j < bodies.length; j++) { if (i == j) continue; var spring:Spring = new Spring(bodies[i], bodies[j], 10, 0); springs.push(spring); } } startSimulation(); } private function createWorld():b2World { var aabb:b2AABB = new b2AABB(); aabb.minVertex.Set(-1000.0, -1000.0); aabb.maxVertex.Set(1000.0, 1000.0); var gravity:b2Vec2 = new b2Vec2(0.0, 300.0); var doSleep:Boolean = true; return new b2World(aabb, gravity, doSleep); } private function createBodyDef(point:Point, fixed:Boolean):b2BodyDef { var boxDef:b2BoxDef = new b2BoxDef(); boxDef.extents.Set(1, 1); if (!fixed) boxDef.density = 0.1; var bodyDef:b2BodyDef = new b2BodyDef(); bodyDef.position.Set(point.x, point.y); bodyDef.AddShape(boxDef); return bodyDef; } private function startSimulation():void { var step:Number = 1.0 / 60; var iterations:int = 100; var timer:Timer = new Timer(50); timer.addEventListener(TimerEvent.TIMER, function(event:TimerEvent):void { graphics.clear(); for (var i:int = 0; i < springs.length; i++) { springs[i].addSpringForce(); } world.Step(step, iterations); graphics.lineStyle(1,0xffffff,1); var firstPoint:b2Vec2 = bodies[0].GetCenterPosition(); graphics.moveTo(firstPoint.x, 0); graphics.lineTo(firstPoint.x, firstPoint.y); for (var k:int = 1; k < bodies.length - 2; k++) { var controlPoint:b2Vec2 = bodies[k].GetCenterPosition(); var anchorPoint:b2Vec2 = controlPoint.Copy(); anchorPoint.Add(bodies[k+1].GetCenterPosition()); anchorPoint.Multiply(0.5); graphics.curveTo(controlPoint.x, controlPoint.y, anchorPoint.x, anchorPoint.y); } var lastPoint:b2Vec2 = bodies[bodies.length-2].GetCenterPosition(); graphics.lineTo(lastPoint.x, lastPoint.y); graphics.lineTo(lastPoint.x, 1000); }); timer.start(); } } }
Spring.as
package { import Box2D.Common.Math.b2Vec2; import Box2D.Dynamics.b2Body; public class Spring { private var bA:b2Body; private var bB:b2Body; private var k:Number; private var friction:Number; private var desiredDistance:Number; public function Spring(bA:b2Body, bB:b2Body, k:Number, friction:Number) { var v:b2Vec2 = bA.m_position.Copy(); v.Subtract(bB.m_position); this.desiredDistance = v.Length(); this.bA = bA; this.bB = bB; this.k = k; this.friction = friction; } public function addSpringForce():void { var vecAtoB:b2Vec2 = bB.m_position.Copy(); vecAtoB.Subtract(bA.m_position); var diff:Number = vecAtoB.Length() - desiredDistance; var normAtoB:b2Vec2 = vecAtoB.Copy(); normAtoB.Multiply(1.0 / vecAtoB.Length()) var normBtoA:b2Vec2 = vecAtoB.Copy(); normAtoB.Multiply(k * diff); normBtoA.Multiply(-1 * k * diff); bB.ApplyForce(normBtoA, bB.m_position); bA.ApplyForce(normAtoB, bA.m_position); bA.WakeUp(); bB.WakeUp(); } } }