HTML5遊戲開發進階 3 :物理引擎基礎
Box2D最初是有Erin Catto用C++編寫的。很多流行的物理模擬類遊戲都使用了該引擎,包含《憤怒的小鳥》,該引擎已經被轉化為幾種不同語言的版本,包括Java,ActionScript,C#和JavaScript。JS版本又被稱為Box2dWeb。
http://code.google.com/p/box2dweb
3.1 Box2D基礎
Box2D使用一些基本物件來定義和模擬遊戲世界,其中最重要的有如下幾個:
- world:世界,Box2D的主物件,包含世界中的所有物體,對遊戲中的物理現象進行模擬
- body:剛性的物體,可能由一個或多個形狀組成,這些形狀通過載具新增到物體上。
- shape:二維形狀,如圓或多邊形這些Box2D中用到的基本形狀。
- fixture:載具,用來向物體上新增形狀以監測碰撞。載具還包括一些非形狀資料,如摩擦係數、碰撞係數和碰撞閥值。
- joint:接合點,用來以不同的方式將兩個物體連線在一起。比如,一個旋轉接合點使兩個物體共享一個點,它們可以自由地繞著該點旋轉。
在遊戲中使用Box2D時,首先要定義遊戲的world物件,然後使用載具新增物體和形狀。接著,逐個操作world物件中的body物件,讓Box2D確定它們的位置和狀態。最後,將所有body物件繪製出來。最複雜的計算(即確定物體的位置和狀態)由Box2D world物件完成。
引入Box2D:box2d.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Box2d Test</title>
<script src="Box2dWeb-2.1.a.3.min.js" type="text/javascript" charset="utf-8"></script>
<script src="box2d.js" type="text/javascript" charset="utf-8"></script>
</head>
<body onload="init();">
<canvas id="canvas" width="640" height="480" style="border:1px solid black;">Your browser does not support HTML5 Canvas</canvas>
</body>
</html>
box2d.js
//為了方便,將常用的物件定義為快捷變數
var b2Vec2 = Box2D.Common.Math.b2Vec2;
var b2BodyDef = Box2D.Dynamics.b2BodyDef;
var b2Body = Box2D.Dynamics.b2Body;
var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
var b2Fixture = Box2D.Dynamics.b2Fixture;
var b2World = Box2D.Dynamics.b2World;
var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;
var b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef;
定義World變數:重力加速度
//建立b2World物件
var world;
var scale = 30; //在canvas上的30畫素表示Box2d世界中的1米
function init() {
// 建立Box2D world物件,該物件將完成大部分物理計算
var gravity = new b2Vec2(0, 9.8); //重力加速度9.8m/s^2
var allowSleep = true; //允許靜止的物體進入休眠狀態,休眠物體不參與物理模擬計算
world = new b2World(gravity, allowSleep);
}
新增第一物體: 地面 createFloor()
在Box2D中建立任何物體都需經過以下步驟:
- 使用b2BodyDef宣告一個body的預定義物件。該物件包含了物體的細節,如物體的位置(x,y)和物體的型別(靜態和動態)。靜態物體不會受重力加速度和其他物體的碰撞影響。
- 使用b2FixtureDef宣告一個fixture的預定義物件。載具用來給物體指定形狀。該物件也包含了一些其他資訊,如指定形狀的密度、摩擦係數和彈性恢復係數。
- 設定載具指定的形狀。這裡使用Box2D中兩種型別的形狀:多邊形(b2polygonshape)和圓(b2circleshape)。
- 將物體的預定義物件傳入createBody()方法中,返回一個body物件。
- 將載具的預定義物件傳入createFixture()方法中,為物體指定形狀。
繪製世界:除錯繪圖模式 setupDebugDraw()
使用DrawDebugData()在給定的canvas上繪製出世界
動畫:步驟
- 告訴Box2D為很短時間進行一次模擬。使用world.Step()方法完成這一步
- 將所有的物體重新繪製在新的位置。使用world.DrawDebugData()或這些物體自身的繪製函式
- 使用world.ClearForces()方法清除作用在物體上的力。
//為了方便,將常用的物件定義為快捷變數
var b2Vec2 = Box2D.Common.Math.b2Vec2;
var b2BodyDef = Box2D.Dynamics.b2BodyDef;
var b2Body = Box2D.Dynamics.b2Body;
var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
var b2Fixture = Box2D.Dynamics.b2Fixture;
var b2World = Box2D.Dynamics.b2World;
var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;
var b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef;
//建立b2World物件
var world;
var scale = 30; //在canvas上的30畫素表示Box2d世界中的1米
function init() {
// 建立Box2D world物件,該物件將完成大部分物理計算
var gravity = new b2Vec2(0, 9.8); //重力加速度9.8m/s^2
var allowSleep = true; //允許靜止的物體進入休眠狀態,休眠物體不參與物理模擬計算
world = new b2World(gravity, allowSleep);
createFloor();
setupDebugDraw();
animate();
}
//建立地面
function createFloor() {
//body的預定義物件,包含建立body剛體需要用到的所有資料
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_staticBody; //靜態 地面不受重力或其他物體碰撞影響
bodyDef.position.x = 640/2/scale; // 位置 x=320px, y=450px
bodyDef.position.y = 450/scale;
// fixture用來向body新增shape以實現碰撞檢測
// fixture的預定義物件,用來建立fixture
var fixtureDef = new b2FixtureDef;
fixtureDef.density = 1.0; //形狀的密度、摩擦係數、彈性恢復係數等屬性
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
fixtureDef.shape = new b2PolygonShape; //多邊形
fixtureDef.shape.SetAsBox(320/scale, 10/scale); //640寬,20高
var body = world.CreateBody(bodyDef);
var fixture = body.CreateFixture(fixtureDef);
}
//設定除錯繪圖
var context;
function setupDebugDraw() {
context = document.getElementById("canvas").getContext('2d');
var debugDraw = new b2DebugDraw();
//使用canvas繪圖環境來繪製除錯畫面
debugDraw.SetSprite(context);
//設定繪圖比例
debugDraw.SetDrawScale(scale);
//填充的透明度為0.3
debugDraw.SetFillAlpha(0.3);
//線條的寬度為1
debugDraw.SetLineThickness(1.0);
//繪製所有的shape和joint
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
//設定調製繪圖模式
world.SetDebugDraw(debugDraw);
}
//設立Box2D動畫迴圈
var timeStep = 1/60;
//按照Box2D手冊建議的迭代數,速度是8,位置是3
var velocityIterations = 8;
var positionIterations = 3;
function animate() {
world.Step(timeStep, velocityIterations, positionIterations);
world.ClearForces();
world.DrawDebugData();
setTimeout(animate, timeStep);
}
3.2 更多的Box2D元素
Box2D允許向世界中新增不同種類的元素,包括:
- 簡單形狀的物體,如矩形、圓、多邊形。
- 複雜的由多個形狀組成的物體。
- 接合點,如連線多個物體的旋轉接合點
- 接觸監聽器,允許我們處理碰撞事件。
建立矩形物體:createRectangularBody()
建立圓形物體:createCircularBody()
建立多邊形:createSimplePolygonBody()
建立多種形狀的複雜物體:createComplexBody()
連線物體的接合點:接合點用來將物體與物體之間連線起來。包括滑輪、齒輪、杆、轉動關節和焊接點。
createRevoluteJoint()函式
3.3 追蹤碰撞與破壞
在調查一個物體是否被損壞前,需要將該物體與“生命值”或“健康值”聯絡起來。
建立具有自定義屬性的物體:createSpecialBody()
接觸監聽器:b2ContactListener物件,有四個事件
- BeginContact():兩個物體開始接觸時被呼叫
- EndContact():兩個物體結束接觸時被呼叫
- PostSolve():求解器完成後呼叫,進行碰撞檢測時很有用
- PreSolve():在求解器求解前呼叫
實現: listenForContact()函式
摧毀物體:
3.4 繪製角色
//為了方便,將常用的物件定義為快捷變數
var b2Vec2 = Box2D.Common.Math.b2Vec2;
var b2BodyDef = Box2D.Dynamics.b2BodyDef;
var b2Body = Box2D.Dynamics.b2Body;
var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
var b2Fixture = Box2D.Dynamics.b2Fixture;
var b2World = Box2D.Dynamics.b2World;
var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;
var b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef;
//建立b2World物件
var world;
var scale = 30; //在canvas上的30畫素表示Box2d世界中的1米
function init() {
// 建立Box2D world物件,該物件將完成大部分物理計算
var gravity = new b2Vec2(0, 9.8); //重力加速度9.8m/s^2
var allowSleep = true; //允許靜止的物體進入休眠狀態,休眠物體不參與物理模擬計算
world = new b2World(gravity, allowSleep);
createFloor();
//建立一些具有簡單形狀的物體
createRectangularBody();
createCircularBody();
createSimplePolygonBody();
createComplexBody();
createRevoluteJoint();
createSpecialBody();
listenForContact();
setupDebugDraw();
animate();
}
//建立地面
function createFloor() {
//body的預定義物件,包含建立body剛體需要用到的所有資料
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_staticBody; //靜態 地面不受重力或其他物體碰撞影響
bodyDef.position.x = 640/2/scale; // 位置 x=320px, y=450px
bodyDef.position.y = 450/scale;
// fixture用來向body新增shape以實現碰撞檢測
// fixture的預定義物件,用來建立fixture
var fixtureDef = new b2FixtureDef;
fixtureDef.density = 1.0; //形狀的密度、摩擦係數、彈性恢復係數等屬性
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
fixtureDef.shape = new b2PolygonShape; //多邊形
fixtureDef.shape.SetAsBox(320/scale, 10/scale); //640寬,20高
var body = world.CreateBody(bodyDef);
var fixture = body.CreateFixture(fixtureDef);
}
//設定除錯繪圖
var context;
function setupDebugDraw() {
context = document.getElementById("canvas").getContext('2d');
var debugDraw = new b2DebugDraw();
//使用canvas繪圖環境來繪製除錯畫面
debugDraw.SetSprite(context);
//設定繪圖比例
debugDraw.SetDrawScale(scale);
//填充的透明度為0.3
debugDraw.SetFillAlpha(0.3);
//線條的寬度為1
debugDraw.SetLineThickness(1.0);
//繪製所有的shape和joint
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
//設定調製繪圖模式
world.SetDebugDraw(debugDraw);
}
//設立Box2D動畫迴圈
var timeStep = 1/60;
//按照Box2D手冊建議的迭代數,速度是8,位置是3
var velocityIterations = 8;
var positionIterations = 3;
function animate() {
world.Step(timeStep, velocityIterations, positionIterations);
world.ClearForces();
world.DrawDebugData();
setTimeout(animate, timeStep);
//自定義繪製
if (specialBody) {
drawSpecialBody();
}
//摧毀耗盡生命值的物體
if (specialBody && specialBody.GetUserData().life <= 0){
//world.DestroyBody(specialBody);
//specialBody = undefined;
console.log("destroyed");
}
setTimeout(animate, timeStep);
}
//建立矩形物體
function createRectangularBody(){
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.x = 40/scale;
bodyDef.position.y = 100/scale;
var fixtureDef = new b2FixtureDef;
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.3;
fixtureDef.shape = new b2PolygonShape; //多邊形
fixtureDef.shape.SetAsBox(30/scale, 50/scale); //60寬,100高
var body = world.CreateBody(bodyDef);
var fixture = body.CreateFixture(fixtureDef);
}
//建立一個圓形
function createCircularBody(){
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.x = 130/scale;
bodyDef.position.y = 100/scale;
var fixtureDef = new b2FixtureDef;
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.7;
fixtureDef.shape = new b2CircleShape(30/scale); //
var body = world.CreateBody(bodyDef);
var fixture = body.CreateFixture(fixtureDef);
}
//建立多邊形物體
function createSimplePolygonBody(){
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.x = 230/scale;
bodyDef.position.y = 50/scale;
var fixtureDef = new b2FixtureDef;
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
fixtureDef.shape = new b2PolygonShape;
//按順時針方向建立一個b2Vec2頂點陣列
var points = [
new b2Vec2(0,0),
new b2Vec2(40/scale,50/scale),
new b2Vec2(50/scale,100/scale),
new b2Vec2(-50/scale,100/scale),
new b2Vec2(-40/scale,50/scale),
];
fixtureDef.shape.SetAsArray(points, points.length);
var body = world.CreateBody(bodyDef);
var fixture = body.CreateFixture(fixtureDef);
}
// 建立多種形狀的複雜物體,由兩個形狀組成的物體
function createComplexBody(){
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.x = 350/scale;
bodyDef.position.y = 50/scale;
var body = world.CreateBody(bodyDef);
//建立第一個載具併為物體新增圓形狀
var fixtureDef = new b2FixtureDef;
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.7;
fixtureDef.shape = new b2CircleShape(40/scale); //
body.CreateFixture(fixtureDef);
//建立第二個
fixtureDef.shape = new b2PolygonShape;
//按順時針方向建立一個b2Vec2頂點陣列
var points = [
new b2Vec2(0,0),
new b2Vec2(40/scale,50/scale),
new b2Vec2(50/scale,100/scale),
new b2Vec2(-50/scale,100/scale),
new b2Vec2(-40/scale,50/scale),
];
fixtureDef.shape.SetAsArray(points, points.length);
var fixture = body.CreateFixture(fixtureDef);
}
//建立轉動關節
function createRevoluteJoint(){
//定義第一個物體
var bodyDef1 = new b2BodyDef;
bodyDef1.type = b2Body.b2_dynamicBody;
bodyDef1.position.x = 480/scale;
bodyDef1.position.y = 50/scale;
var body1 = world.CreateBody(bodyDef1);
var fixtureDef1 = new b2FixtureDef;
fixtureDef1.density = 1.0;
fixtureDef1.friction = 0.5;
fixtureDef1.restitution = 0.5;
fixtureDef1.shape = new b2PolygonShape; //多邊形
fixtureDef1.shape.SetAsBox(50/scale, 10/scale); //60寬,100高
body1.CreateFixture(fixtureDef1);
//定義第二個物體
var bodyDef2 = new b2BodyDef;
bodyDef2.type = b2Body.b2_dynamicBody;
bodyDef2.position.x = 470/scale;
bodyDef2.position.y = 50/scale;
var body2 = world.CreateBody(bodyDef2);
var fixtureDef2 = new b2FixtureDef;
fixtureDef2.density = 1.0;
fixtureDef2.friction = 0.5;
fixtureDef2.restitution = 0.5;
fixtureDef2.shape = new b2PolygonShape; //多邊形
var points = [
new b2Vec2(0,0),
new b2Vec2(40/scale,50/scale),
new b2Vec2(50/scale,100/scale),
new b2Vec2(-50/scale,100/scale),
new b2Vec2(-40/scale,50/scale),
];
fixtureDef2.shape.SetAsArray(points, points.length);
body2.CreateFixture(fixtureDef2);
//建立接合點連線body1和body2
var jointDef = new b2RevoluteJointDef;
var jointCenter = new b2Vec2(470/scale, 50/scale);
jointDef.Initialize(body1, body2, jointCenter);
world.CreateJoint(jointDef);
}
//建立具有自定義屬性的物體
var specialBody;
function createSpecialBody(){
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.x = 450/scale;
bodyDef.position.y = 0/scale;
specialBody = world.CreateBody(bodyDef);
specialBody.SetUserData({name:"special", life:250});
var fixtureDef = new b2FixtureDef;
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.5;
fixtureDef.shape = new b2CircleShape(30/scale); //
var fixture = specialBody.CreateFixture(fixtureDef);
}
//實現接觸監聽器
function listenForContact(){
var listener = new Box2D.Dynamics.b2ContactListener;
//引數為接觸和衝擊力(法向和切向衝擊力)
listener.PostSolve = function(contact, impulse){
var body1 = contact.GetFixtureA().GetBody();
var body2 = contact.GetFixtureB().GetBody();
//如果接觸的兩個物體都具有生命值,則減少其生命值
if (body1 == specialBody || body2 == specialBody){
var impulseAlongNormal = impulse.normalImpulses[0];
//生命值減少
specialBody.GetUserData().life -= impulseAlongNormal;
console.log("impulse:", impulseAlongNormal,
"life:", specialBody.GetUserData().life);
}
};
world.SetContactListener(listener);
}
//繪製自己的角色
function drawSpecialBody(){
//獲取body的位置和角度
var position = specialBody.GetPosition();
var angle = specialBody.GetAngle();
//移動並旋轉物體
context.translate(position.x*scale, position.y*scale);
context.rotate(angle);
//繪製實心的圓面
context.fillStyle = "rgb(200, 150, 250);";
context.beginPath();
context.arc(0,0,30,0.2*Math.PI,false);
context.fill();
//繪製兩個矩形的眼睛
context.fillStyle = "rgb(255, 255, 255);";
context.fillRect(-15, -15, 10, 5);
context.fillRect(5, -15, 10, 5);
//繪製向上或向下的圓弧,根據生命值決定是否微笑
context.strokeStyle = "rgb(255, 255, 255);";
context.beginPath();
if (specialBody.GetUserData().life > 100){
context.arc(0,0,10,Math.PI,2*Math.PI,true);
} else {
context.arc(0,10,10,Math.PI,2*Math.PI,false);
}
context.stroke();
//移動並旋轉座標軸至最初的位置和角度
context.rotate(-angle);
context.translate(-position.x*scale, -position.y*scale);
}
相關文章
- 不懂物理的前端不是好的遊戲開發者(一)—— 物理引擎基礎前端遊戲開發
- JavaScript 遊戲開發:手把手實現碰撞物理引擎JavaScript遊戲開發
- 遊戲開發3D基礎知識遊戲開發3D
- 【h5遊戲開發】egret引擎p2物理引擎(2) - 小球碰撞地面搞笑的物理現象H5遊戲開發
- 不懂物理的前端不是好的遊戲開發者(二)—— 物理引擎的學習之路前端遊戲開發
- Three.js 進階之旅:物理效果-3D乒乓球小遊戲 ?JS3D遊戲
- 零基礎瞭解3D遊戲開發3D遊戲開發
- 聚焦遊戲開發速度與成本,穩定引擎基礎助力開發者快速整合應用遊戲開發
- 喵的Unity遊戲開發之路 - 推球:遊戲中的物理Unity遊戲開發
- HTML5遊戲開發(一):3分鐘建立一個hello worldHTML遊戲開發
- Python的基礎進階Python
- 獨立遊戲開發中的物理系統遊戲開發
- Oracle開發基礎-遊標Oracle
- 遊戲基礎知識——劇情的展開和推進手法遊戲
- 前端基礎之jQuery進階前端jQuery
- 【Go進階—基礎特性】反射Go反射
- 【Go進階—基礎特性】deferGo
- 【Go進階—基礎特性】介面Go
- Pandas進階貳 pandas基礎
- Shopee Games 遊戲引擎演進之路GAM遊戲引擎
- 《無限法則》開發經驗分享:射擊遊戲的物理引擎應用和移動模擬遊戲
- 白鷺引擎助力《迷你世界》研發團隊開發3D小遊戲版3D遊戲
- 使用Laya引擎開發微信小遊戲(上)遊戲
- 使用Laya引擎開發微信小遊戲(下)遊戲
- Git 遊戲攻略(上篇)- 基礎&高階&整理提交記錄Git遊戲
- HTML5遊戲開發過程中的二三事HTML遊戲開發
- 使用 .NET 進行遊戲開發遊戲開發
- HTML5 基礎HTML
- 前端開發入門到實戰:HTML5進階FileReader的使用前端HTML
- SpringCloud基礎教程(三)-Eureka進階SpringGCCloud
- 【Go進階—基礎特性】錯誤Go
- 遊戲趣史:遊戲引擎的發展史遊戲引擎
- 微信小遊戲開發(3)遊戲開發
- html5的遊戲引擎你瞭解多少?都有哪些比較好用的引擎呢?HTML遊戲引擎
- 3、Pico Robot 基礎開發課程
- Swift開發基礎08-高階函式Swift函式
- Facebook開發小遊戲引擎列表(下載連結)遊戲引擎
- HTML5遊戲開發(二):使用TypeScript編寫程式碼HTML遊戲開發TypeScript
- 遊戲與遊戲引擎遊戲引擎