package { /* 黒猫の尻尾です */ import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.GradientType; import flash.display.Graphics; import flash.display.Sprite; import flash.display.StageQuality; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.filters.BevelFilter; import flash.filters.BlurFilter; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; public class CatTail extends Sprite { public function CatTail() { addEventListener(Event.ADDED_TO_STAGE, init); // flexBuilderとの互換性。 Wonderfl.capture_delay(1); } public static const STAGE_W:uint = 465; public static const STAGE_H:uint = 465; private static const _PI:Number = Math.PI; private static const _PI2:Number = 2.0 * _PI; private static const _WALL_LEFT:Number = 0; private static const _WALL_RIGHT:Number = 465; private static const _GROUND_LINE:Number = 400; private static const _DERIVATION:int = 4; private static const _GRAVITY:Number = 0.4 / _DERIVATION; private static const _ROTATION_RATE:Number = 0.05 / _DERIVATION; // 自身バネ(根元) private static const _VERTICAL_RATE:Number = 0.5 / _DERIVATION; // ターゲットバネ(さきっぽ) private static const _MOUSE_PULL_RATE:Number = 0.1 / _DERIVATION; private static const _FRICTION:Number = 1 - 0.1 / _DERIVATION; private static const _MOUSE_ROTATE_FRICTION:Number = 1 - 0.8 / _DERIVATION; private static const _MOUSE_MOVE_FRICTION:Number = 1 - 0.5 / _DERIVATION; private static const _COLOR:uint = 0x333344; private static const _BEVEL_H_COLOR:uint = 0x404050; private static const _BEVEL_S_COLOR:uint = 0x000000; private var _boneList:Array = []; private var _dragId:int = -1; private var _boneLayer:Sprite; private var _displayLayer:Sprite; private var _shadowLayer:Bitmap; private var _shadow:BitmapData = new BitmapData(STAGE_W, STAGE_H, true, 0x00000000); private var _clear:BitmapData = new BitmapData(STAGE_W, STAGE_H, false, 0x888888); private var _cover:BitmapData = new BitmapData(STAGE_W, STAGE_H, true, 0x00000000); private var _bevel:BevelFilter = new BevelFilter(20, 100, _BEVEL_H_COLOR, 1, _BEVEL_S_COLOR, 1, 33, 33, 1, 1); private var _topLock:Boolean = true; private static const _LOCK_Y:int = 50; private function init(e:Event):void { // ここから開始 removeEventListener(Event.ADDED_TO_STAGE, init); // SWF設定 stage.frameRate = 30; stage.quality = StageQuality.MEDIUM; var bg:Sprite = new Sprite(); // wonderflではキャプチャに背景色が反映されないので、背景色Spriteで覆う。 bg.graphics.beginFill(0xffffff, 1); bg.graphics.drawRect(0, 0, STAGE_W, STAGE_H); addChild(bg); // 色々準備 var coverGradient:Sprite = new Sprite(); var matrix:Matrix = new Matrix() matrix.createGradientBox(STAGE_W, STAGE_H, Math.PI / 2, STAGE_W / 2, STAGE_H / 2) coverGradient.graphics.beginGradientFill(GradientType.LINEAR, [0xffffff, 0xffffff], [1, 0], [0, 230], matrix); coverGradient.graphics.drawRect(0, 0, STAGE_W, STAGE_H); _cover.draw(coverGradient); _shadowLayer = new Bitmap(_shadow); _displayLayer = new Sprite(); _boneLayer = new Sprite(); addChild(_shadowLayer); addChild(_displayLayer); addChild(_boneLayer); _boneLayer.alpha = 0; // マウスを取ってもらうので、完全には消さない _displayLayer.filters = [_bevel]; // 骨組みを用意する。 var i:int=0; addBone(5, 5); addBone(12, 30); addBone(12, 30); addBone(12, 30); addBone(12, 30); addBone(12, 30); addBone(12, 30); addBone(12, 30); addBone(12, 30); addBone(12, 30); addBone(2, 30); // ドラッグ解除 stage.addEventListener(MouseEvent.MOUSE_UP, bornMouseUpEvent()); // フレームの処理を登録 addEventListener(Event.ENTER_FRAME, frame); // キーの処理を登録 stage.addEventListener(KeyboardEvent.KEY_DOWN, key); } private var _id:int = 0; private var _lastX:Number = 50; private function addBone(radius:Number, connectLength:Number):void{ var bone:BoneCircle = new BoneCircle(radius, connectLength); bone.y = _LOCK_Y; bone.x = _lastX; _boneList.push(bone); _boneLayer.addChild(bone); // マウスイベント bone.addEventListener(MouseEvent.MOUSE_DOWN, bornMouseDownEvent(_id++)); _lastX += connectLength; } // フレーム挙動 private function frame(event:Event):void{ for (var i:int=0; i<_DERIVATION; i++){ rotate(); foce(); move(); } draw(); drawShadow(); debugDraw(); } // ボーンの向きを決定する private function rotate():void{ var l:int = _boneList.length; for (var i:int=0; i < l; i++){ var baceBone:BoneCircle = _boneList[i]; var targetBone:BoneCircle = _boneList[i+1]; if (i + 1 < l){ calcConnectRForce(baceBone, targetBone, 0); calcConnectRForce(targetBone, baceBone, _PI); } if (i == _dragId) baceBone.vr *= _MOUSE_ROTATE_FRICTION; baceBone.vr *= _FRICTION; // 摩擦 } } // 接続されたパーツの回転方向を計算する private function calcConnectRForce(baceBone:BoneCircle, targetBone:BoneCircle, connectAngle:Number):void{ var angle:Number = Math.atan2(targetBone.y - baceBone.y, targetBone.x - baceBone.x); baceBone.vr += ajustRadian(angle - (connectAngle + baceBone.radian)) * _ROTATION_RATE; } // 力関係、加速度を整理する。 private function foce():void{ var i:int; var l:int = _boneList.length; var bone:BoneCircle; for (i=0; i < l; i++){ bone = _boneList[i]; bone.vy += _GRAVITY; if (_dragId == i){ // マウスで引っ張る var vPoint:Point = pullForce(bone.x, bone.y, mouseX, mouseY, _MOUSE_PULL_RATE); bone.vx += vPoint.x; bone.vy += vPoint.y; } if (i + 1 < l){ calcConnectFoce(bone, _boneList[i+1], 0); calcConnectFoce(_boneList[i+1], bone, _PI); } // 摩擦 bone = _boneList[i]; bone.vx *= _FRICTION; bone.vy *= _FRICTION; if (i == _dragId){ bone.vx *= _MOUSE_MOVE_FRICTION; bone.vy *= _MOUSE_MOVE_FRICTION; } } } // 接続された2パーツの力を計算する private function calcConnectFoce(baceBone:BoneCircle, targetBone:BoneCircle, connectAngle:Number):void{ var toAngle:Number = ajustRadian(connectAngle + baceBone.radian); var toX:Number = baceBone.x + Math.cos(toAngle) * baceBone.connectLength; var toY:Number = baceBone.y + Math.sin(toAngle) * baceBone.connectLength; var vx:Number = (targetBone.x - toX) * _VERTICAL_RATE; var vy:Number = (targetBone.y - toY) * _VERTICAL_RATE; baceBone.vx += vx; baceBone.vy += vy; targetBone.vx -= vx; targetBone.vy -= vy; } // ポイントx1 y1を、ポイントx2 y2へ、係数rateだけ移動させる場合の、XYの力を返す private function pullForce(x1:Number, y1:Number, x2:Number, y2:Number, rate:Number):Point{ var point:Point = new Point(); var distance:Number = calcDistance(x1, y1, x2, y2); var angle:Number = Math.atan2(y2 - y1, x2 - x1); point.x = Math.cos(angle) * distance * rate; point.y = Math.sin(angle) * distance * rate; return point; } // ポイントx1 y1から、ポイントx2 y2までの距離 private function calcDistance(x1:Number, y1:Number, x2:Number, y2:Number):Number{ return Math.sqrt(Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2)); } // radian角度を、-π~πの範囲に修正する private function ajustRadian(radian:Number):Number{ return radian - _PI2 * Math.floor( 0.5 + radian / _PI2); } // 移動を反映。 private function move():void{ for (var i:int=0; i < _boneList.length; i++){ var bone:BoneCircle = _boneList[i]; bone.x += bone.vx; bone.y += bone.vy; bone.radian += bone.vr; // 壁チェック if (0 < bone.vy && _GROUND_LINE - bone.radius < bone.y){ bone.y = _GROUND_LINE - bone.radius; bone.vy = 0; } if (bone.vx < 0 && bone.x < _WALL_LEFT + bone.radius){ bone.x = _WALL_LEFT + bone.radius; bone.vx = 0; }else if (0 < bone.vx && _WALL_RIGHT - bone.radius < bone.x){ bone.x = _WALL_RIGHT - bone.radius; bone.vx = 0; } // 回転方向を反映 bone.rotation = bone.radian * 180 / Math.PI; } if (_topLock){ bone = _boneList[_boneList.length - 1]; bone.vy = 0; bone.y = _LOCK_Y; } } // 表示。 private function draw():void{ var pointListX:Array = []; var pointListY:Array = []; var i:int; var l:int = _boneList.length; for (i=0; i < l; i++){ var bone:BoneCircle = _boneList[i]; var cosR:Number = Math.cos(bone.radian + _PI/2)*bone.radius; var sinR:Number = Math.sin(bone.radian + _PI/2)*bone.radius; pointListX.push(bone.x + cosR); pointListY.push(bone.y + sinR); pointListX.unshift(bone.x - cosR); pointListY.unshift(bone.y - sinR); } var g:Graphics = _displayLayer.graphics; g.clear(); g.beginFill(_COLOR, 1); l = pointListX.length - 1; i = 0; var baceX:Number = (pointListX[i] + pointListX[i+1])/2; var baceY:Number = (pointListY[i] + pointListY[i+1])/2; g.moveTo(baceX, baceY); for (i=1; i < l; i++){ g.curveTo(pointListX[i], pointListY[i], (pointListX[i] + pointListX[i+1])/2, (pointListY[i] + pointListY[i+1])/2); } g.lineTo(baceX, baceY); } private var _rect:Rectangle = new Rectangle(0, 0, STAGE_W, STAGE_H); private var _point:Point = new Point(); private var _shadowMatrix:Matrix = new Matrix(1, 0, 0, 0.5, 0, STAGE_H/2-30); private var _shadowColor:ColorTransform = new ColorTransform(0,0,0,1, 0,0,0,0); private var _shadowBlur:BlurFilter = new BlurFilter(30, 30, 1); private function drawShadow():void{ _shadow.lock(); _shadow.copyPixels(_clear, _rect, _point); _shadow.draw(_displayLayer, _shadowMatrix, _shadowColor); _shadow.applyFilter(_shadow, _rect, _point, _shadowBlur); _shadow.draw(_cover); _shadow.unlock(); } // デバッグ表示。 private function debugDraw():void{ } // マウスイベント private function bornMouseDownEvent(i:int):Function{ return function (event:Event):void{ startBornDrag(i);}; } private function bornMouseUpEvent():Function{ return function (event:Event):void{ endBornDrag();}; } // ドラッグ private function startBornDrag(bornNum:int):void{ _dragId = bornNum; } private function endBornDrag():void{ _dragId = -1; } private function key(event:KeyboardEvent):void{ if (event.keyCode == 76){ // L trace("changeLock"); _topLock = !_topLock; } if (event.keyCode == 68){ // D trace("changeDisplay"); _boneLayer.alpha = _boneLayer.alpha ? 0 : 1; } } } } import flash.display.Sprite; class BoneCircle extends Sprite { public var radius:Number; // 半径 public var connectLength:Number; // 次パーツとのつながりの長さ public var radian:Number = 0; // 一時的な回転量。 public var vx:Number = 0; public var vy:Number = 0; public var vr:Number = 0; public function BoneCircle(radius:Number, connectLength:Number) { this.radius = radius; this.connectLength = connectLength; graphics.beginFill(0xff0000, 0.02); graphics.lineStyle(0, 0, 0); graphics.drawCircle(0, 0, radius + 20); graphics.beginFill(0x0000ff, 0.3); graphics.lineStyle(1, 0x000088, 1); graphics.drawCircle(0, 0, radius); graphics.moveTo(-radius + 10, 0); graphics.lineTo(radius + 10, 0); } } 黒猫の尻尾