[javascript] Box2D JS初探(一個控制小球的遊戲例子)。學習用!
阿新 • • 發佈:2019-02-10
Box2D 開源物理引擎,第一次接觸,主要用途應該是做遊戲,不過也可以作些簡單的頁面特性,比如切割粉碎。拖動帶彈性係數。
從寫了個控制小球運動,可以方向鍵控制左右和彈跳,碰到障礙物就死掉的DEMO來試試。
演示地址: null
這裡注意的是,市面上有box2d JS 和 box2d Web2種js版本,有些小夥伴用老的box2d JS,那就不是這麼寫的了非常麻煩,而且box2d js不更新了,不建議學習使用。
<html> <head> <title>Box2dWeb example</title> </head> <body onload="init();"> <img id="tulip" style="display: none" src="33.jpg" alt="The Tulip" /> <canvas id="canvas" width="900" height="600"></canvas> </body> <script type="text/javascript" src="Box2dWeb-2.1.a.3.min.js"></script> <script type="text/javascript"> var world; function init() { var b2Vec2 = Box2D.Common.Math.b2Vec2 , b2BodyDef = Box2D.Dynamics.b2BodyDef , b2Body = Box2D.Dynamics.b2Body , b2FixtureDef = Box2D.Dynamics.b2FixtureDef , b2Fixture = Box2D.Dynamics.b2Fixture , b2World = Box2D.Dynamics.b2World , b2MassData = Box2D.Collision.Shapes.b2MassData , b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape , b2CircleShape = Box2D.Collision.Shapes.b2CircleShape , b2DebugDraw = Box2D.Dynamics.b2DebugDraw , b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef , b2WeldJointDef = Box2D.Dynamics.Joints.b2WeldJointDef ; world = new b2World( new b2Vec2(0,10) //gravity 重力的方向,力度 單位牛,10就是重力加速度G , true //allow sleep ); var fixDef = new b2FixtureDef; //剛體建立 fixDef.density = 1.0; //密度 fixDef.friction = 0.5; //摩擦力 加大了之後正方形被推動就比較不容易動 fixDef.restitution = 0.8; //彈性係數,1應該就是不衰減,大於1反而越來越快 var bodyDef = new b2BodyDef; //create ground bodyDef.type = b2Body.b2_staticBody; bodyDef.position.x = 1; //單位是米,一米是30畫素。 bodyDef.position.y = 19.8; //以下試圖建立四邊框(用剛體來做,遊戲四邊的場景邊框,不然會掉出去) fixDef.shape = new b2PolygonShape; fixDef.shape.SetAsBox(30, 0.1); //長寬應該(寬長,xy對應) world.CreateBody(bodyDef).CreateFixture(fixDef); fixDef.shape.SetAsBox(0.1, 20); bodyDef.position.Set(0.1, 0.1); world.CreateBody(bodyDef).CreateFixture(fixDef); fixDef.shape.SetAsBox(30, 0.1); bodyDef.position.Set(0.1, 0.1); world.CreateBody(bodyDef).CreateFixture(fixDef); fixDef.shape.SetAsBox(0.1, 20); bodyDef.position.Set(29.8, 0.1); world.CreateBody(bodyDef).CreateFixture(fixDef); //在場景中製作臺階 fixDef.shape.SetAsBox(3, 0.1); bodyDef.position.Set(10, 18.5); world.CreateBody(bodyDef).CreateFixture(fixDef); var mouseX, mouseY,selectBody, keyBoll,vX, vY,keyVelocity,Death = false,barrier; var canvasPosition = getElementPosition(document.getElementById("canvas")); var context = document.getElementById("canvas").getContext("2d"); var img=document.getElementById("tulip"); //在場景中的空中放置一個正方形 bodyDef.type = b2Body.b2_dynamicBody; fixDef.shape = new b2PolygonShape(); fixDef.shape.SetAsBox(1,1); //30px * 30px 的矩形 fixDef.restitution = 2; //改變彈性係數,如果撞到他會更快的反彈 bodyDef.position.Set(10,13); //矩形陷阱的放置位置 console.log(fixDef.shape.GetVertices() ); console.log(typeof fixDef.shape.GetVertices()[0] ); selectBody = world.CreateBody(bodyDef).CreateFixture(fixDef).GetBody(); //建個連結器。把矩形固定在場景中 var jointDef = new b2MouseJointDef(); jointDef.bodyA = world.GetGroundBody(); jointDef.bodyB = selectBody; //矩形和場景本體相連 jointDef.target.Set(10, 13); //連線位置 jointDef.collideConnected = true; //是否碰撞。要碰。不然就是根線,不能固定用 jointDef.maxForce = 3000.0; //最大力,如果設定過小,會被從固定位置撞開 world.CreateJoint(jointDef); //聯結器加入世界 // 建立釘子 fixDef.restitution = 0; fixDef.friction = 5000 fixDef.shape = new b2PolygonShape; var vArray = [new b2Vec2(0,-0.5),new b2Vec2(0.25,0.5),new b2Vec2(-0.25,0.5)] fixDef.shape.SetAsVector(vArray, 3); //建立一個三角形這裡要注意了,Vector字面是向量,其實穿進去的是三角形頂點座標List,3是頂點個數。而且要順時針方向,不然無法物理判定。 bodyDef.position.Set(15,19.5); barrier = world.CreateBody(bodyDef).CreateFixture(fixDef).GetBody(); //建立連線,這個WeldJoint看字面就知道,是剛體連線,上面那個連線會被力改變,會旋轉。這個就是焊死了。相當於固定 jointDef = new b2WeldJointDef(); jointDef.bodyA = world.GetGroundBody(); jointDef.bodyB = barrier; jointDef.localAnchorA = new b2Vec2(15, 19.3) jointDef.localAnchorB = barrier.GetLocalCenter(); jointDef.collideConnected = true; world.CreateJoint(jointDef); //建立主角--一個球 fixDef.restitution = 0.8; fixDef.shape = new b2CircleShape(1); bodyDef.position.Set(1,19.5); bodyDef.userData=img; //給球上圖片 keyBoll = world.CreateBody(bodyDef).CreateFixture(fixDef).GetBody(); //把這個球的body記錄下來。 /** 這裡是點選小球在滑鼠位置降落下來的 監聽。 document.addEventListener("mousedown", function(e) { mouseX = (e.layerX - canvasPosition.x) / 30; mouseY = (e.layerY - canvasPosition.y) / 30; }, true); document.addEventListener("mouseup", function(e) { fixDef.shape = new b2CircleShape(0.5); bodyDef.position.Set(mouseX,mouseY); selectBody = world.CreateBody(bodyDef).CreateFixture(fixDef).GetBody(); vX = (e.layerX - canvasPosition.x) / 30 - mouseX; vY = (e.layerY - canvasPosition.y) / 30 - mouseY; selectBody.ApplyImpulse( new b2Vec2(vX*5,vY*5), selectBody.GetLocalCenter()); }, true); **/ //監控鍵盤事件 document.addEventListener("keydown", function(e) { var theEvent = window.event || e; var code = theEvent.keyCode || theEvent.which; console.log(code); var keyVelocity = keyBoll.GetLinearVelocity(); //按上方向鍵: if(code==38){ //判定是不是y方向速度為0 ,我要球落地挺穩才讓他繼續下一次起跳,不然會越來越高。 if (keyVelocity.y == 0) { //給一個y方向20的向上加速度。算下10的重力。應該也就2秒就下落了。 keyBoll.ApplyImpulse(new b2Vec2(0,-20), keyBoll.GetWorldCenter()); } } //左按鍵 if(code==37){ keyBoll.SetAwake(true); //喚醒目標,並直接給向左10的速度,這裡和上不一樣,是直接替換速度,並不是加速度,所以Y方向的速度還需要加入。 keyBoll.SetLinearVelocity(new b2Vec2(-10,keyVelocity.y), keyBoll.GetWorldCenter()); } //右按鍵 if(code==39){ keyBoll.SetAwake(true); //同理 keyBoll.SetLinearVelocity(new b2Vec2(10,keyVelocity.y), keyBoll.GetWorldCenter()); } }, false); //監聽按鍵彈起,並給個減速(也可以直接熟讀至0)看個人喜好。 document.addEventListener("keyup", function(e) { var theEvent = window.event || e; var code = theEvent.keyCode || theEvent.which; console.log(code); if(code==37){ keyBoll.ApplyImpulse(new b2Vec2(10,0), keyBoll.GetWorldCenter()); } if(code==39){ keyBoll.ApplyImpulse(new b2Vec2(-10,0), keyBoll.GetWorldCenter()); } }, false); //setup debug draw var debugDraw = new b2DebugDraw(); debugDraw.SetSprite(document.getElementById("canvas").getContext("2d")); debugDraw.SetDrawScale(30.0); debugDraw.SetFillAlpha(0.3); debugDraw.SetLineThickness(2.0); debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit); world.SetDebugDraw(debugDraw); window.setInterval(update, 1000 / 60); //配置偵數 //官網的例子裡的一個canvans定位函式 function getElementPosition(element) { var elem=element, tagname="", x=0, y=0; while((typeof(elem) == "object") && (typeof(elem.tagName) != "undefined")) { y += elem.offsetTop; x += elem.offsetLeft; tagname = elem.tagName.toUpperCase(); if(tagname == "BODY") elem=0; if(typeof(elem) == "object") { if(typeof(elem.offsetParent) == "object") elem = elem.offsetParent; } } return {x: x, y: y}; } function update() { var a = 1 / 60; //如果2個球被被喚醒了,並且球和釘子在一定範圍內,則說明撞到釘子了。GameOver 並且把重新整理率置99999,表示不動了。 if (barrier.IsAwake() && keyBoll.IsAwake()) { if (barrier.GetPosition().x + 2 > keyBoll.GetPosition().x && barrier.GetPosition().x - 2 < keyBoll.GetPosition().x ) { alert("Game Over"); a = 999999; } console.log(barrier.GetPosition()); console.log(keyBoll.GetPosition()); } world.Step( a //frame-rate , 10 //velocity iterations , 10 //position iterations ); world.DrawDebugData(); //貼圖 context.save(); context.translate(keyBoll.GetPosition().x*30,keyBoll.GetPosition().y*30); context.rotate(keyBoll.GetAngle()); context.drawImage(keyBoll.GetUserData(),-keyBoll.GetUserData().width/2,-keyBoll.GetUserData().height/2); context.restore(); world.ClearForces(); }; }; </script> </html>