Using the same files in the demos folder with some changes
Example:
Click on the canvas (blue box) to add bubbles
bubbles:0
img source:
html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
<script>
var bubbleimage=new Image();
bubbleimage.src="image.png";
Object.extend = $.extend;
</script>
<script src='lib/box2d.js'></script>
<script src='js/box2d/dynamics/b2World.js'></script>
<script src='bubbles.js'></script>
<link href="style/box2d.css" rel="stylesheet" type="text/css" />
</head>
<body>
<canvas id="canvas" width='500px' height='600px' style="top:0px; left:0px;"></canvas>
<span class="countc">bubbles: <span id="count">0</span></span>
</body>
</html>
bubbles.js (some functions from demos folder in one file)
function createWorld() {
var worldAABB = new b2AABB();
worldAABB.minVertex.Set(-1000, -1000);
worldAABB.maxVertex.Set(1000, 1000);
var gravity = new b2Vec2(0, 300);
var doSleep = true;
var world = new b2World(worldAABB, gravity, doSleep);
createGround(world);
createBox(world, -9, 125, 10, canvasHeight,true);
createBox(world, canvasWidth+9, 125, 10, canvasHeight,true);
return world;
}
function createGround(world) {
var groundSd = new b2BoxDef();
groundSd.extents.Set(1000, 100);
groundSd.restitution = 1;
var groundBd = new b2BodyDef();
groundBd.AddShape(groundSd);
groundBd.position.Set(0, canvasHeight+99);
groundBd.userData="rgba(0,0,0,0)";// alpha 0 = no border
return world.CreateBody(groundBd)
}
function createBox(world, x, y, width, height, fixed) {
if (typeof(fixed) == 'undefined') fixed = true;
var boxSd = new b2BoxDef();
if (!fixed) boxSd.density = 1.0;
boxSd.extents.Set(width, height);
var boxBd = new b2BodyDef();
boxBd.AddShape(boxSd);
boxBd.position.Set(x,y);
boxBd.userData="rgba(0,0,0,0)";
return world.CreateBody(boxBd);
}
var bubbles = {};
function drawWorld(world, context) {
for (var b = world.m_bodyList; b; b = b.m_next) {
for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
drawShape(s, context,b.m_userData,b.m_rotation);
}
}
}
var PI2=Math.PI * 2;
function drawShape(shape, context,bodyuserData,bodyrotation) {
if(bodyuserData){
context.strokeStyle =bodyuserData;
}
context.beginPath();
context.lineWidth =1;
switch (shape.m_type) {
case b2Shape.e_circleShape:
{
var circle = shape;
var pos = circle.m_position;
var r = circle.m_radius;
var d=r*2;
context.strokeStyle = bodyuserData;
context.save();
context.translate(pos.x, pos.y);
context.arc(0, 0, r, 0,PI2 , false);
context.rotate(bodyrotation);
context.clip();
context.drawImage(bubbleimage,-r,-r,d,d);
context.restore();
}
break;
case b2Shape.e_polyShape:
{
var poly = shape;
var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
context.moveTo(tV.x, tV.y);
for (var i = 0; i < poly.m_vertexCount; i++) {
var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
context.lineTo(v.x, v.y);
}
context.lineTo(tV.x, tV.y);
}
break;
}
context.stroke();
}
bubbles.createBall = function(world, x, y, rad, fixed,color) {
var ballSd = new b2CircleDef();
if (!fixed) ballSd.density = 2.0;
ballSd.radius = rad || 10;
ballSd.restitution = .7;
var ballBd = new b2BodyDef();
ballBd.AddShape(ballSd);
ballBd.position.Set(x,y);
ballBd.userData=color;
return world.CreateBody(ballBd);
};
bubbles.createPoly = function(world, x, y, points, fixed) {
var polySd = new b2PolyDef();
if (!fixed) polySd.density = 100.0;
polySd.vertexCount = points.length;
for (var i = 0; i < points.length; i++) {
polySd.vertices[i].Set(points[i][0], points[i][1]);
}
var polyBd = new b2BodyDef();
polyBd.AddShape(polySd);
polyBd.position.Set(x,y);
return world.CreateBody(polyBd)
};
var initId = 0;
var world;
var ctx;
var canvasWidth;
var canvasHeight;
var canvasTop;
var canvasLeft;
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
var fps=1/60;
function step(){
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
world.Step(fps, 1);
drawWorld(world, ctx);
requestAnimFrame(step);
}
$(document).ready(function(){
ctx = $('#canvas')[0].getContext('2d');
var canvasElm = $('#canvas');
canvasWidth = parseInt($(canvasElm).width());
canvasHeight = parseInt($(canvasElm).height());
canvasTop = parseInt($(canvasElm).offset().top);
canvasLeft = parseInt($(canvasElm).offset().left);
world = createWorld();
$("#canvas").on('click', function(e) {
if(world.m_bodyCount-3>50){ // bodyCount - 3 (1 floor and 2 walls)
world.DestroyBody(world.m_bodyarray[4]); //destroy first bubble if there are more than 50
}
bubbles.createBall(world, e.pageX - canvasLeft, e.pageY - canvasTop,Math.floor((Math.random()*40)+20),false,"rgba(0,0,0,0)");
$("#count").html(world.m_bodyCount-4); //bubble count
});
step();
});
js/box2d/dynamics/b2World.js (world.m_bodyarray array containing all bodys on world)
/*
* Copyright (c) 2006-2007 Erin Catto http:
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked, and must not be
* misrepresented the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
var b2World = function(worldAABB, gravity, doSleep){
// initialize instance variables for references
this.step = new b2TimeStep();
this.m_contactManager = new b2ContactManager();
//
this.m_listener = null;
this.m_filter = b2CollisionFilter.b2_defaultFilter;
this.m_bodyList = null;
this.m_contactList = null;
this.m_jointList = null;
this.m_bodyCount = 0;
this.m_contactCount = 0;
this.m_jointCount = 0;
this.m_bodyarray= new Array();
this.m_bodyDestroyList = null;
this.m_allowSleep = doSleep;
this.m_gravity = gravity;
this.m_contactManager.m_world = this;
this.m_broadPhase = new b2BroadPhase(worldAABB, this.m_contactManager);
var bd = new b2BodyDef();
this.m_groundBody = this.CreateBody(bd);
};
b2World.prototype = {
// Set a callback to notify you when a joint is implicitly destroyed
// when an attached body is destroyed.
SetListener: function(listener){
this.m_listener = listener;
},
// Register a collision filter to provide specific control over collision.
// Otherwise the default filter is used (b2CollisionFilter).
SetFilter: function(filter){
this.m_filter = filter;
},
// Create and destroy rigid bodies. Destruction is deferred until the
// the next call to this.Step. This is done so that bodies may be destroyed
// while you iterate through the contact list.
CreateBody: function(def){
var b = new b2Body(def, this);
b.m_prev = null;
b.m_next = this.m_bodyList;
if (this.m_bodyList)
{
this.m_bodyList.m_prev = b;
}
this.m_bodyList = b;
++this.m_bodyCount;
this.m_bodyarray.push(b);
return b;
},
// Body destruction is deferred to make contact processing more robust.
DestroyBody: function(b)
{
if (b.m_flags & b2Body.e_destroyFlag)
{
return;
}
// Remove from normal body list.
if (b.m_prev)
{
b.m_prev.m_next = b.m_next;
}
if (b.m_next)
{
b.m_next.m_prev = b.m_prev;
}
if (b == this.m_bodyList)
{
this.m_bodyList = b.m_next;
}
b.m_flags |= b2Body.e_destroyFlag;
//b2Settings.b2Assert(this.m_bodyCount > 0);
--this.m_bodyCount;
//b->~b2Body();
//b.Destroy();
// Add to the deferred destruction list.
b.m_prev = null;
b.m_next = this.m_bodyDestroyList;
this.m_bodyarray.shift();
this.m_bodyDestroyList = b;
},
CleanBodyList: function()
{
this.m_contactManager.m_destroyImmediate = true;
var b = this.m_bodyDestroyList;
while (b)
{
//b2Settings.b2Assert((b.m_flags & b2Body.e_destroyFlag) != 0);
// Preserve the next pointer.
var b0 = b;
b = b.m_next;
// Delete the attached joints
var jn = b0.m_jointList;
while (jn)
{
var jn0 = jn;
jn = jn.next;
if (this.m_listener)
{
this.m_listener.NotifyJointDestroyed(jn0.joint);
}
this.DestroyJoint(jn0.joint);
}
b0.Destroy();
}
// Reset the list.
this.m_bodyDestroyList = null;
this.m_contactManager.m_destroyImmediate = false;
},
CreateJoint: function(def){
var j = b2Joint.Create(def);
// Connect to the world list.
j.m_prev = null;
j.m_next = this.m_jointList;
if (this.m_jointList)
{
this.m_jointList.m_prev = j;
}
this.m_jointList = j;
++this.m_jointCount;
// Connect to the bodies
j.m_node1.joint = j;
j.m_node1.other = j.m_body2;
j.m_node1.prev = null;
j.m_node1.next = j.m_body1.m_jointList;
if (j.m_body1.m_jointList) j.m_body1.m_jointList.prev = j.m_node1;
j.m_body1.m_jointList = j.m_node1;
j.m_node2.joint = j;
j.m_node2.other = j.m_body1;
j.m_node2.prev = null;
j.m_node2.next = j.m_body2.m_jointList;
if (j.m_body2.m_jointList) j.m_body2.m_jointList.prev = j.m_node2;
j.m_body2.m_jointList = j.m_node2;
// If the joint prevents collisions, then reset collision filtering.
if (def.collideConnected == false)
{
// Reset the proxies on the body with the minimum number of shapes.
var b = def.body1.m_shapeCount < def.body2.m_shapeCount ? def.body1 : def.body2;
for (var s = b.m_shapeList; s; s = s.m_next)
{
s.ResetProxy(this.m_broadPhase);
}
}
return j;
},
DestroyJoint: function(j)
{
var collideConnected = j.m_collideConnected;
// Remove from the world.
if (j.m_prev)
{
j.m_prev.m_next = j.m_next;
}
if (j.m_next)
{
j.m_next.m_prev = j.m_prev;
}
if (j == this.m_jointList)
{
this.m_jointList = j.m_next;
}
// Disconnect from island graph.
var body1 = j.m_body1;
var body2 = j.m_body2;
// Wake up touching bodies.
body1.WakeUp();
body2.WakeUp();
// Remove from body 1
if (j.m_node1.prev)
{
j.m_node1.prev.next = j.m_node1.next;
}
if (j.m_node1.next)
{
j.m_node1.next.prev = j.m_node1.prev;
}
if (j.m_node1 == body1.m_jointList)
{
body1.m_jointList = j.m_node1.next;
}
j.m_node1.prev = null;
j.m_node1.next = null;
// Remove from body 2
if (j.m_node2.prev)
{
j.m_node2.prev.next = j.m_node2.next;
}
if (j.m_node2.next)
{
j.m_node2.next.prev = j.m_node2.prev;
}
if (j.m_node2 == body2.m_jointList)
{
body2.m_jointList = j.m_node2.next;
}
j.m_node2.prev = null;
j.m_node2.next = null;
b2Joint.Destroy(j, this.m_blockAllocator);
//b2Settings.b2Assert(this.m_jointCount > 0);
--this.m_jointCount;
// If the joint prevents collisions, then reset collision filtering.
if (collideConnected == false)
{
// Reset the proxies on the body with the minimum number of shapes.
var b = body1.m_shapeCount < body2.m_shapeCount ? body1 : body2;
for (var s = b.m_shapeList; s; s = s.m_next)
{
s.ResetProxy(this.m_broadPhase);
}
}
},
// The world provides a single ground body with no collision shapes. You
// can use this to simplify the creation of joints.
GetGroundBody: function(){
return this.m_groundBody;
},
step: new b2TimeStep(),
// this.Step
Step: function(dt, iterations){
var b;
var other;
this.step.dt = dt;
this.step.iterations = iterations;
if (dt > 0.0)
{
this.step.inv_dt = 1.0 / dt;
}
else
{
this.step.inv_dt = 0.0;
}
this.m_positionIterationCount = 0;
// Handle deferred contact destruction.
this.m_contactManager.CleanContactList();
// Handle deferred body destruction.
this.CleanBodyList();
// Update contacts.
this.m_contactManager.Collide();
// Size the island for the worst case.
var island = new b2Island(this.m_bodyCount, this.m_contactCount, this.m_jointCount, this.m_stackAllocator);
// Clear all the island flags.
for (b = this.m_bodyList; b != null; b = b.m_next)
{
b.m_flags &= ~b2Body.e_islandFlag;
}
for (var c = this.m_contactList; c != null; c = c.m_next)
{
c.m_flags &= ~b2Contact.e_islandFlag;
}
for (var j = this.m_jointList; j != null; j = j.m_next)
{
j.m_islandFlag = false;
}
// Build and simulate all awake islands.
var stackSize = this.m_bodyCount;
//var stack = (b2Body**)this.m_stackAllocator.Allocate(stackSize * sizeof(b2Body*));
var stack = new Array(this.m_bodyCount);
for (var k = this.m_bodyCount; k--;)
stack[k] = null;
for (var seed = this.m_bodyList; seed != null; seed = seed.m_next)
{
if (seed.m_flags & (b2Body.e_staticFlag | b2Body.e_islandFlag | b2Body.e_sleepFlag | b2Body.e_frozenFlag))
{
continue;
}
// Reset island and stack.
island.Clear();
var stackCount = 0;
stack[stackCount++] = seed;
seed.m_flags |= b2Body.e_islandFlag;;
// Perform a depth first search (DFS) on the constraint graph.
while (stackCount > 0)
{
// Grab the next body off the stack and add it to the island.
b = stack[--stackCount];
island.AddBody(b);
// Make sure the body is awake.
b.m_flags &= ~b2Body.e_sleepFlag;
// To keep islands, we don't
// propagate islands across static bodies.
if (b.m_flags & b2Body.e_staticFlag)
{
continue;
}
// Search all contacts connected to this body.
for (var cn = b.m_contactList; cn != null; cn = cn.next)
{
if (cn.contact.m_flags & b2Contact.e_islandFlag)
{
continue;
}
island.AddContact(cn.contact);
cn.contact.m_flags |= b2Contact.e_islandFlag;
other = cn.other;
if (other.m_flags & b2Body.e_islandFlag)
{
continue;
}
//b2Settings.b2Assert(stackCount < stackSize);
stack[stackCount++] = other;
other.m_flags |= b2Body.e_islandFlag;
}
// Search all joints connect to this body.
for (var jn = b.m_jointList; jn != null; jn = jn.next)
{
if (jn.joint.m_islandFlag == true)
{
continue;
}
island.AddJoint(jn.joint);
jn.joint.m_islandFlag = true;
other = jn.other;
if (other.m_flags & b2Body.e_islandFlag)
{
continue;
}
//b2Settings.b2Assert(stackCount < stackSize);
stack[stackCount++] = other;
other.m_flags |= b2Body.e_islandFlag;
}
}
island.Solve(this.step, this.m_gravity);
this.m_positionIterationCount = b2Math.b2Max(this.m_positionIterationCount, b2Island.m_positionIterationCount);
if (this.m_allowSleep)
{
island.UpdateSleep(dt);
}
// Post solve cleanup.
for (var i = island.m_bodyCount; i--;)
{
// Allow static bodies to participate in other islands.
b = island.m_bodies[i];
if (b.m_flags & b2Body.e_staticFlag)
{
b.m_flags &= ~b2Body.e_islandFlag;
}
// Handle newly frozen bodies.
if (b.IsFrozen() && this.m_listener)
{
var response = this.m_listener.NotifyBoundaryViolated(b);
if (response == b2WorldListener.b2_destroyBody)
{
this.DestroyBody(b);
b = null;
island.m_bodies[i] = null;
}
}
}
}
this.m_broadPhase.Commit();
//this.m_stackAllocator.Free(stack);
},
// this.Query the world for all shapes that potentially overlap the
// provided AABB. You provide a shape pointer buffer of specified
// size. The number of shapes found is returned.
Query: function(aabb, shapes, maxCount){
//void** results = (void**)this.m_stackAllocator.Allocate(maxCount * sizeof(void*));
var results = new Array();
var count = this.m_broadPhase.QueryAABB(aabb, results, maxCount);
for (var i = count; i--;)
{
shapes[i] = results[i];
}
//this.m_stackAllocator.Free(results);
return count;
},
// You can use these to iterate over all the bodies, joints, and contacts.
GetBodyList: function(){
return this.m_bodyList;
},
GetJointList: function(){
return this.m_jointList;
},
GetContactList: function(){
return this.m_contactList;
},
//--------------- Internals Below -------------------
m_stackAllocator: null,
m_broadPhase: null,
m_contactManager: new b2ContactManager(),
m_bodyList: null,
m_contactList: null,
m_jointList: null,
m_bodyCount: 0,
m_contactCount: 0,
m_jointCount: 0,
// These bodies will be destroyed at the next time this.step.
m_bodyDestroyList: null,
m_gravity: null,
m_allowSleep: null,
m_groundBody: null,
m_listener: null,
m_filter: null,
m_positionIterationCount: 0
};
b2World.s_enablePositionCorrection = 1;
b2World.s_enableWarmStarting = 1;
"world.DestroyBody(world.m_bodyarray[4]);" destroy the first bubble m_bodyarray[0] to m_bodyarray[3] walls of world
style/box2d.css (user-select: none) prevent double click select
#canvas {
top:0px;
left:0px;
background:rgba(0,100,255,.5);border2px solid #000000;-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
cursor:pointer;
}
.countc{
position: relative;
top: -580px;
left: -500px;
}
0 Comments
Publicar un comentario