/** * Dobuemon's Ectoplasm * * インバースキネマティクスの習作。 * * ドブえもんによるエクトプラズム曲芸。 * こんな風に自分のエクトプラズムを自在に操って曲芸してみたい。 * ボールをドラッグできますのでドブえもんのエクトプラズムと思う存分戯れてみてください! * * この本のIKの項の説明を参考にしています。 * http://www.amazon.co.jp/ActionScript-3-0-アニメーション-Keith-Peters/dp/4862460496 * * @author Masayuki Daijima (ARCHETYP Inc.) * http://www.daijima.jp/ * http://twitter.com/daijimachine */ package { import com.bit101.components.ProgressBar; import flash.system.Security; import flash.display.Loader; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.events.ProgressEvent; import flash.filters.GlowFilter; import flash.geom.Point; import flash.net.URLRequest; import org.libspark.betweenas3.BetweenAS3; import org.libspark.betweenas3.easing.Quad; import org.libspark.betweenas3.tweens.ITween; [SWF(backgroundColor = "#000000", frameRate = "50", width = "465", height = "465")] public class Document extends Sprite { private const IMG_URL:String = "http://www.daijima.jp/materials/wonderfl/dobuemon/images/dobuemon.png"; private const D2R:Number = 180 / Math.PI; private var _loadPercent:Number = 0; private var _dobuemon:Loader; private var _ectoplasmContainer:Sprite; private var _segments:Array; private var _numSegments:uint = 10; private var _ball:Ball; private var _isDrag:Boolean = false; private var _gravity:Number = 0.5; private var _bounce:Number = -0.9; public function Document() { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; Security.loadPolicyFile("http://www.daijima.jp/crossdomain.xml"); loadDobuemon(); } private function loadDobuemon():void { _dobuemon = new Loader(); var req:URLRequest = new URLRequest(IMG_URL); _dobuemon.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress); _dobuemon.contentLoaderInfo.addEventListener(Event.COMPLETE, setWorld); _dobuemon.load(req); setProgressBar(); } private function loadProgress(event:ProgressEvent):void { _loadPercent = event.target.bytesLoaded / event.target.bytesTotal; } private function setProgressBar():void { var p_bar:ProgressBar = new ProgressBar(this, 182, 232); addEventListener(Event.ENTER_FRAME, function(event:Event):void { p_bar.value += (_loadPercent - p_bar.value) * .5; if (p_bar.value > .99) { p_bar.value = 1; event.target.removeEventListener(event.type, arguments.callee); var _tw:ITween = BetweenAS3.serial( BetweenAS3.to(p_bar, { alpha:0 }, .3, Quad.easeOut), BetweenAS3.removeFromParent(p_bar) ); _tw.play(); } }); } private function setWorld(event:Event):void { _dobuemon.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, loadProgress); _dobuemon.contentLoaderInfo.removeEventListener(Event.COMPLETE, setWorld); _segments = []; setEctoplasm(); setBall(); setDobuemon(); addEventListener(Event.ENTER_FRAME, update); } private function setEctoplasm():void { var segX:int = 315; var sh:Number = stage.stageHeight; _ectoplasmContainer = new Sprite(); addChild(_ectoplasmContainer); for(var i:uint = 0; i < _numSegments; i++){ var h:Number = Math.max(30, 100 - i * 10); var segment:EctoplasmSegment = new EctoplasmSegment(38, h); _ectoplasmContainer.addChild(segment); _segments.push(segment); } segment.x = segX; segment.y = sh - 40; segment.rotation = 90; segment = _segments[_numSegments - 2]; segment.x = segX; segment.y = sh - 50; segment.rotation = 90; segment = _segments[_numSegments - 3]; segment.x = segX; segment.y = sh - 60; segment.rotation = 90; var glow:GlowFilter = new GlowFilter(); glow.color = 0x000000; glow.inner = true; glow.strength = 1; glow.blurX = 30; glow.blurY = 30; glow.alpha = 1; _ectoplasmContainer.filters = [glow]; } private function setBall():void { _ball = new Ball(40); _ball.vx = 7; addChild(_ball); _ball.buttonMode = true; _ball.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); _ball.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); } private function setDobuemon():void { addChild(_dobuemon); _dobuemon.y = 344; } private function mouseUpHandler(event:MouseEvent):void { _isDrag = false; _ball.stopDrag(); this.stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); } private function mouseDownHandler(event:MouseEvent):void { _isDrag = true; _ball.startDrag(); this.stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); } private function update(event:Event):void { if(!_isDrag) moveBall(); var target:Point = reach(_segments[0], _ball.x, _ball.y); for(var i:uint = 1; i < _numSegments-2; i++){ var segment:EctoplasmSegment = _segments[i]; target = reach(segment, target.x, target.y); } for(i = _numSegments - 3; i > 0; i--){ var segmentA:EctoplasmSegment = _segments[i]; var segmentB:EctoplasmSegment = _segments[i - 1]; position(segmentB, segmentA); } if(!_isDrag) checkHit(); } private function reach(segment:EctoplasmSegment, xpos:Number, ypos:Number):Point { var dx:Number = xpos - segment.x; var dy:Number = ypos - segment.y; var angle:Number = Math.atan2(dy, dx); segment.rotation = angle * D2R; var w:Number = segment.getPoint().x - segment.x; var h:Number = segment.getPoint().y - segment.y; var tx:Number = xpos - w; var ty:Number = ypos - h; return new Point(tx, ty); } private function position(segmentA:EctoplasmSegment, segmentB:EctoplasmSegment):void { segmentA.x = segmentB.getPoint().x; segmentA.y = segmentB.getPoint().y; } private function moveBall():void { var sw:Number = stage.stageWidth; var sh:Number = stage.stageHeight; _ball.vy += _gravity; _ball.x += _ball.vx; _ball.y += _ball.vy; if (_ball.x + _ball.radius > sw) { _ball.x = sw - _ball.radius; _ball.vx *= _bounce; }else if (_ball.x - _ball.radius < 0) { _ball.x = _ball.radius; _ball.vx *= _bounce; } if (_ball.y + _ball.radius > sh) { _ball.y = sh - _ball.radius; _ball.vy *= _bounce; }else if (_ball.y - _ball.radius < 0) { _ball.y = _ball.radius; _ball.vy *= _bounce; } } public function checkHit():void { var segment:EctoplasmSegment = _segments[0]; var dx:Number = segment.getPoint().x - _ball.x; var dy:Number = segment.getPoint().y - _ball.y; var dist:Number = Math.sqrt(dx * dx + dy * dy); if(dist < _ball.radius){ _ball.vx += Math.random() * 2 - 1; _ball.vy -= 1; } } } } import flash.display.Sprite; import flash.filters.GlowFilter; import flash.geom.Point; class EctoplasmSegment extends Sprite { private var _color:uint; private var _segmentWidth:Number; private var _segmentHeight:Number; private const R2D:Number = Math.PI / 180; public function EctoplasmSegment(segmentWidth:Number, segmentHeight:Number, color:uint = 0xffffff) { this._segmentWidth = segmentWidth; this._segmentHeight = segmentHeight; this._color = color; init(); } public function init():void { graphics.beginFill(_color); graphics.drawRoundRect( -_segmentHeight >> 1, -_segmentHeight >> 1, _segmentWidth + _segmentHeight, _segmentHeight, _segmentHeight, _segmentHeight); graphics.endFill(); } public function getPoint():Point { var angle:Number = rotation * R2D; var xPos:Number = x + Math.cos(angle) * _segmentWidth; var yPos:Number = y + Math.sin(angle) * _segmentWidth; return new Point(xPos, yPos); } } class Ball extends Sprite { public var radius:Number; public var vx:Number = 0; public var vy:Number = 0; private var _color:uint; public function Ball(radius:Number = 40, color:uint = 0xff0000) { this.radius = radius; this._color = color; init(); setFilter(); } private function setFilter():void { var glow:GlowFilter = new GlowFilter(); glow.color = 0x000000; glow.inner = true; glow.strength = .5; glow.blurX = 30; glow.blurY = 30; glow.alpha = 1; filters = [glow]; } public function init():void { graphics.beginFill(_color); graphics.drawCircle(0, 0, radius); graphics.endFill(); } } Dobuemon's Ectoplasm