// 「大人の科学マガジン vol.27」の付録 // Japanino と光残像キット・P.O.V を使った魚釣りゲーム // -------------------------------------------------- // カニ漁 // -------------------------------------------------- // // ■遊び方 // // P.O.V のハンドルを回すとリ漁船の網が上がります。 // ハンドルを止めると網が下がります。 // 網を上下にうまくコントロールして、カニを漁船の中に引き上げると、 // P.O.V の LEDユニットにカニが表示されます。 // // ※ 画面をクリックでも P.O.V のハンドルを回すかわりになります。 // // ※ ゲームクリアはありません。 // // mail:usami@i-tach.com // // -------------------------------------------------- // // 参考:セットアップ方法の詳細に関しては、以下のページでの説明を // 参照してください。 // // 大人の科学マガジンVol.27 // http://otonanokagaku.net/magazine/vol27/ // // wonderfl x japanino // http://wonderfl.net/event/japanino/ // // 注意:光残像キットは、あまり激しく操作すると壊れてしまうことが // ありますので注意してください。 package { import flash.display.Graphics; import flash.display.MovieClip; import flash.display.Sprite; import flash.display.Loader; import flash.display.Bitmap; import flash.display.BitmapData; import flash.geom.Point; import flash.geom.Rectangle; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.utils.Timer; import flash.events.Event; import flash.events.TimerEvent; import flash.events.MouseEvent; import flash.net.URLRequest; import flash.system.LoaderContext; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; import Box2D.Dynamics.Joints.b2Joint; import Box2D.Dynamics.Joints.b2MouseJoint; import Box2D.Dynamics.Joints.b2MouseJointDef; import funnel.*; [SWF(frameRate="30")] public class JapaninoFishingExample extends Sprite { private var _japanino:Arduino; private var loader1:Loader; private var loader2:Loader; private var loadCnt:int = 0; public var m_world:b2World; public var m_iterations:int = 10; public var m_timeStep:Number = 1.0 / 30.0; private var body:b2Body; private var ami:b2Body; private var bodyDef:b2BodyDef; private var boxDef:b2PolygonDef; private var circleDef:b2CircleDef; private var mouseJointDef:b2MouseJointDef; private var mouseJoint:b2MouseJoint; private var xGCnt:int = 0; private var efCnt:int = 0; private var amiY:Number = 3; private var container:Sprite; private var cntBmpData:BitmapData; private var cntBmp:Bitmap; private var amiSp:Sprite private var amiPt1:Point; private var amiPt2:Point; private var line:Sprite; private var tx:TextField; private var fm:TextFormat; //----------------------------------------------------- //コンストラクタ public function JapaninoFishingExample() { //カニ画像 var url:String = "http://assets.wonderfl.net/images/related_images/d/d2/d202/d20265e99e54231799ebc88f7edbcfd7755ec5f4"; loader1 = new Loader(); loader1.load(new URLRequest(url), new LoaderContext(true)); loader1.contentLoaderInfo.addEventListener(Event.COMPLETE, loadCompleteHandler); //船画像 url = "http://assets.wonderfl.net/images/related_images/3/3d/3d1a/3d1a1353a9c41b0849060661537fb7debab44fa0"; loader2 = new Loader(); loader2.load(new URLRequest(url), new LoaderContext(true)); loader2.contentLoaderInfo.addEventListener(Event.COMPLETE, loadCompleteHandler); container = new Sprite(); addChild(container); var config:Configuration = Arduino.FIRMATA; _japanino = new Arduino(config); _japanino.addEventListener(FunnelEvent.READY, onJapaninoReady); } //----------------------------------------------------- //画像ロード完了 private function loadCompleteHandler(e:Event):void { loadCnt++ if (loadCnt == 2) { set_init(); } } //画像ロード完了 private function set_init():void{ // シミュレーションする座標の範囲を指定する var worldAABB:b2AABB = new b2AABB(); worldAABB.lowerBound.Set(-100.0, -100.0); worldAABB.upperBound.Set(100.0, 100.0); var gravity:b2Vec2 = new b2Vec2(0.0, 3.0);// 重力を定義する var doSleep:Boolean = false;//物体が動いていないときに処理削減するか m_world = new b2World(worldAABB, gravity, doSleep);// 世界のインスタンスを作成する //海 var umi:Sprite = new Sprite(); umi.graphics.beginFill(0x1F5CEB); umi.graphics.drawRect(0, 0, 465, 465); container.addChild(umi); //空 var sora:Sprite = new Sprite(); sora.graphics.beginFill(0x79D0FF); sora.graphics.drawRect(0, 0, 465, 115); container.addChild(sora); //雲 var kumo1:Sprite = getKumo(); var kumo2:Sprite = getKumo(); var kumo3:Sprite = getKumo(); kumo1.x = 50; kumo1.y = -20; kumo2.x = -40; kumo2.y = 50; kumo3.x = 360; kumo3.y = 35; container.addChild(kumo1); container.addChild(kumo2); container.addChild(kumo3); //壁 setWall(-1.3, 1, 0.3, 3, 0x584130); setWall(6.6, 1, 0.3, 3, 0x584130); setWall( 3, 4.5, 4, 0.6, 0x584130); //船壁 setWall(1.62, 0.75, 0.01, 0.4,0x333333); setWall(3.02, 0.75, 0.01, 0.4,0x333333); setWall( 2.32, 0.2, 0.6, 0.01, 0x333333); //アミ(糸) line = new Sprite(); container.addChild(line); //アミ(底) bodyDef.position.x = 2.32; bodyDef.position.y = 3.6; rW = 0.6; rH = 0.02; boxDef = new b2PolygonDef(); boxDef.SetAsBox(rW , rH*0.1); boxDef.density = 3000;//密度 boxDef.friction = 0.05;//摩擦 boxDef.restitution = 0.0001;//跳ね返り amiSp = new Sprite(); amiSp.graphics.beginFill(0x000000); amiSp.graphics.drawRect( -5, -5, 10, 10); amiPt1 = new Point(-5,0); amiPt2 = new Point(5,0); bodyDef.userData = amiSp; bodyDef.userData.width = 100 * 2 * rW; bodyDef.userData.height = 100 * 2 * rH; container.addChild(bodyDef.userData); ami = m_world.CreateBody(bodyDef);//物体を作る ami.CreateShape(boxDef);//形を物体に追加する ami.SetMassFromShapes();//重さを計算する //船 addChild(loader2) loader2.x = 112; // カニ for (var i:int = 1; i < 6; i++){ bodyDef = new b2BodyDef(); bodyDef.position.x = Math.random() * 3+1.2; bodyDef.position.y = Math.random() * 1.2+1.6; var rW:Number = 0.5; var rH:Number = 0.4; boxDef = new b2PolygonDef(); var sp:Sprite = getKani(); bodyDef.userData = sp; boxDef.SetAsBox(rW * 0.7, rH * 0.85); boxDef.density = 0.3;//密度 boxDef.friction = 0.005;//摩擦 boxDef.restitution = 0.0001;//跳ね返り bodyDef.userData.width = 100 * 2 * rW; bodyDef.userData.height = 100 * 2 * rH; body = m_world.CreateBody(bodyDef);//物体を作る body.CreateShape(boxDef);//形を物体に追加する body.SetMassFromShapes();//重さを計算する container.addChild(bodyDef.userData); } //テキスト fm = new TextFormat(); fm.size = 90; fm.bold = true; fm.color = 0xff0000; tx = new TextField(); tx.text = "大漁!!!"; tx.visible = false; tx.autoSize = TextFieldAutoSize.LEFT; tx.setTextFormat(fm); tx.x = 87; tx.y = 10; addChild(tx); //ドットフィルタ var dot:Sprite = new Sprite(); var len:int = 465 / 3; for (i = 0; i <= len; i++) { dot.graphics.beginFill(0x000000,0.2); dot.graphics.drawRect(i*3, 0, 1, 465); } len = 465 / 2; for (i = 0; i <= len; i++) { dot.graphics.beginFill(0x000000,0.2); dot.graphics.drawRect(0, i*2, 465, 1); } addChild(dot); mouseJointDef = new b2MouseJointDef();// マウスジョイントを定義する mouseJointDef.body1 = m_world.GetGroundBody();// 片方のbodyにはワールド全体を設定する mouseJointDef.body2 = ami;// もう片方には移動させたい物体を設定する mouseJointDef.target = ami.GetWorldCenter();// 車体の中心座標を設定する mouseJointDef.maxForce = 7000;// マウスで引っ張られるときの力 mouseJointDef.timeStep = 1 / 30;// シミュレーションの間隔 mouseJoint = b2MouseJoint(m_world.CreateJoint(mouseJointDef));//マウスジョイントを作る stage.addEventListener(MouseEvent.CLICK, clickHandler); cntBmpData = new BitmapData(465, 465); cntBmp = new Bitmap(cntBmpData); addEventListener(Event.ENTER_FRAME, Update, false, 0, true); } //画像をチェックして P.O.V に送る private function set_imgCheck():void { var pattern:Array = []; var rowStr:String; var checkN:int = 0; for (var i:int = 0; i < 15; i++) { rowStr = ""; for (var n:int = 6; n >= 0; n--) { var col:uint = cntBmpData.getPixel(i * 9 + 169, n * 10 + 30); var r:uint = ( col & 0xff0000 ) >> 16; var b:int = (r > 150)? 1 : 0; rowStr = rowStr + String(b); checkN += b; } pattern.push(parseInt(rowStr, 2)) } if (checkN > 1) {//船にカニが入った場合 tx.visible = true; }else {//船にカニが入ってない場合 tx.visible = false; } // パターンをカスタムのメッセージとしてJapaninoに転送する _japanino.sendSysexMessage(0x10, pattern); } //----------------------------------------------------- /** * ハンドラ */ // EnterFrame private function Update(e:Event):void{ m_world.Step(m_timeStep, m_iterations); // Go through body list and update sprite positions/rotations for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next) { if (bb.m_userData is Sprite){ bb.m_userData.x = bb.GetPosition().x * 100; bb.m_userData.y = bb.GetPosition().y * 100; bb.m_userData.rotation = bb.GetAngle() * (180 / Math.PI); } } //アミの糸描画 var gPt1:Point = amiSp.localToGlobal(amiPt1); var gPt2:Point = amiSp.localToGlobal(amiPt2); var clossY:Number = (gPt1.y + gPt2.y) * 0.5 - 90; var g:Graphics = line.graphics; g.clear(); g.lineStyle(2, 0x000000, 0.2); g.moveTo(gPt1.x, gPt1.y); g.lineTo(232, clossY); g.moveTo(gPt2.x, gPt2.y); g.lineTo(232, clossY); g.lineTo(232, 30); if (efCnt % 2 == 0) { cntBmpData.draw(container); } if (Math.random() < 0.5) xGCnt++; m_world.m_gravity.x = Math.cos(xGCnt*0.1)*2;//重力変更 if (amiY < 3.9) amiY += 0.02; mouseJoint.SetTarget(new b2Vec2(2.32, amiY)); // マウスジョイントのカーソル位置を更新 efCnt++ } // JapaninoReady private function onJapaninoReady(e:Event):void { // Japaninoからのイベントに対してイベントリスナをセット _japanino.addEventListener(FunnelEvent.FIRMATA_STRING, onMessage); // 光残像キットの全面消灯のパターン(7行×19桁) var pattern:Array = [ parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2), parseInt("0000000", 2),]; // パターンをカスタムのメッセージとしてJapaninoに転送して表示をクリアする _japanino.sendSysexMessage(0x10, pattern); } // Japaninoからメッセージを受信 private function onMessage(event:FunnelEvent):void { // メッセージが"!"であれば(=光残像キットのハンドルが回転した通知であれば) if (event.message == "!") { set_imgCheck(); if (amiY > 1.2) amiY -= 0.3; } } // Click private function clickHandler(e:MouseEvent):void { set_imgCheck(); if (amiY > 1.2) amiY -= 0.35; } //----------------------------------------------------- //雲を返す private function getKumo():Sprite { var _kumo:Sprite = new Sprite(); var xs:Array = [0, 31, 71, 21, 58]; var ys:Array = [10, 0, 4, 31, 26]; for (var i:int = 0; i < 5; i++) { _kumo.graphics.beginFill(0xffffff); _kumo.graphics.drawCircle(xs[i], ys[i], 25); } _kumo.scaleY = 0.85; return _kumo; } //壁を配置 private function setWall(_x:Number,_y:Number,_w:Number,_h:Number,col:uint):void { bodyDef = new b2BodyDef();//物体の定義を作る bodyDef.position.Set(_x, _y); boxDef = new b2PolygonDef(); boxDef.SetAsBox(_w, _h); boxDef.friction = 0.01;//摩擦 boxDef.density = 0;//密度 var sp:Sprite = new Sprite(); sp.graphics.beginFill(col); sp.graphics.drawRect( -5, -5, 10, 10); bodyDef.userData = sp; bodyDef.userData.width = 100 * 2 * _w; bodyDef.userData.height = 100 * 2 * _h; container.addChild(bodyDef.userData); body = m_world.CreateBody(bodyDef);//物体を作る body.CreateShape(boxDef);//形を物体に追加する body.SetMassFromShapes(); } //カニSpriteを返す private function getKani():Sprite { var myBitmapData:BitmapData = new BitmapData(loader1.width, loader1.height, true, 0x00000000); myBitmapData.draw(loader1); var bmp:Bitmap = new Bitmap(myBitmapData); var sp:Sprite = new Sprite(); bmp.x = -bmp.width * 0.5; bmp.y = -bmp.height * 0.5; sp.addChild(bmp); return sp; } //----------------------------------------------------- } } カニ漁