Last active
March 26, 2018 05:21
-
-
Save abernier/3411189 to your computer and use it in GitHub Desktop.
Box2D
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html, body {height:100%;} | |
html {display:table; width:100%;} | |
body {display:table-cell; text-align:center; vertical-align:bottom;} | |
* {-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none; -webkit-user-drag:none;-moz-user-drag:none;-ms-user-drag:none;-o-user-drag:none;user-drag:none;} | |
.ball, | |
.ball b, | |
.crate, | |
.ground {box-sizing:border-box;} | |
.ball {display:inline-block; width:3em; height:3em; border-radius:100%; border:.4em solid; color:#dc3132; position:relative;} | |
.ball b {display:block; width:100%; border:.2em solid; border-radius:.2em;} | |
.ball {padding:.4em 0;} | |
.ball > div {width:100%; height:100%; position:relative;} | |
.ball b {position:absolute; left:100%; top:50%;margin-top:-.2em; margin-left:.45em;} | |
.ball b:first-child, | |
.ball b:last-child {margin-left:.3em;} | |
.ball b:first-child {top:0;} | |
.ball b:last-child {top:auto;margin-top:auto;bottom:0;margin-bottom:-.2em;} | |
.crate {display:inline-block; width:3em; height:3em; border:.4em solid #30aebf; border-radius:.15em;} | |
.ground {display:inline-block; width:12em; border:.2em solid; color:#8cc924; border-radius:.15em;} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset=utf-8> | |
<title>box2d</title> | |
<link rel="stylesheet" href="index.css"> | |
</head> | |
<body> | |
<div class="ball"> | |
<div> | |
<b></b> | |
<b></b> | |
<b></b> | |
</div> | |
</div><br> | |
<div class="crate"></div><br> | |
<div class="crate"></div><div class="crate"></div><br> | |
<div class="ground"></div> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/box2d.min.js"></script> | |
<script src="https://rawgithub.com/paulirish/1579671/raw/rAF.js"></script> | |
<script src="https://rawgithub.com/abernier/3225993/raw/loop.js"></script> | |
<script src="https://code.jquery.com/jquery-latest.js"></script> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
// Flatten Box2d (ugly but handy!) | |
(function b2(o) { | |
for (k in o) { | |
if (o.hasOwnProperty(k)) { | |
if ($.isPlainObject(o[k])) { | |
b2(o[k]); | |
} else if (/^b2/.test(k)) { | |
window[k] = o[k]; | |
} | |
} | |
} | |
}(Box2D)); | |
var world = new b2World( | |
new b2Vec2(0, 9.81), // gravity | |
true // allow sleep | |
); | |
var SCALE = 30; | |
// | |
// Ground | |
// | |
(function ($ground) { | |
// Fixture | |
var fixDef = new b2FixtureDef; | |
fixDef.density = 1; | |
fixDef.friction = 0.5; | |
fixDef.restitution = 0.2; | |
// Shape | |
fixDef.shape = new b2PolygonShape; | |
fixDef.shape.SetAsBox( | |
$ground.outerWidth() / 2 / SCALE, //half width | |
$ground.outerHeight() / 2 / SCALE //half height | |
); | |
// Body | |
var bodyDef = new b2BodyDef; | |
bodyDef.type = b2Body.b2_staticBody; | |
bodyDef.position.x = ($ground.offset().left + $ground.outerWidth() / 2) / SCALE; | |
bodyDef.position.y = ($ground.offset().top + $ground.outerHeight() / 2) / SCALE; | |
var body = world.CreateBody(bodyDef); | |
body.CreateFixture(fixDef); | |
$ground.data('body', body); | |
}($('.ground'))); | |
// | |
// Crates | |
// | |
$('.crate').each(function (i, el) { | |
var $crate = $(el); | |
// Fixture | |
var fixDef = new b2FixtureDef; | |
fixDef.density = 1; | |
fixDef.friction = 0.5; | |
fixDef.restitution = 0.2; | |
// Shape | |
fixDef.shape = new b2PolygonShape; | |
fixDef.shape.SetAsBox( | |
$crate.outerWidth() / 2 / SCALE, //half width | |
$crate.outerHeight() / 2 / SCALE //half height | |
); | |
// Body | |
var bodyDef = new b2BodyDef; | |
bodyDef.type = b2Body.b2_dynamicBody; | |
bodyDef.position.x = ($crate.offset().left + $crate.outerWidth() / 2) / SCALE; | |
bodyDef.position.y = ($crate.offset().top + $crate.outerHeight() / 2) / SCALE; | |
var body = world.CreateBody(bodyDef); | |
body.CreateFixture(fixDef); | |
$crate.data('body', body); | |
}); | |
// | |
// Ball | |
// | |
(function ($ball) { | |
// Fixture | |
var fixDef = new b2FixtureDef; | |
fixDef.density = 1; | |
fixDef.friction = 0.5; | |
fixDef.restitution = 0.2; | |
// Shape | |
fixDef.shape = new b2CircleShape($ball.outerWidth() / 2 / SCALE); | |
// Body | |
var bodyDef = new b2BodyDef; | |
bodyDef.type = b2Body.b2_dynamicBody; | |
bodyDef.position.x = ($ball.offset().left + $ball.outerWidth() / 2) / SCALE; | |
bodyDef.position.y = ($ball.offset().top + $ball.outerHeight() / 2) / SCALE; | |
var body = world.CreateBody(bodyDef); | |
body.CreateFixture(fixDef); | |
$ball.data('body', body); | |
}($('.ball'))); | |
// | |
// MouseJoint | |
// | |
var mouse = new b2Vec2(); | |
$(window).mousemove(function (e) { | |
mouse.Set(e.pageX / SCALE, e.pageY / SCALE); | |
}); | |
window.mouse = mouse; | |
(function (mouse) { | |
var mouseJointDef = new b2MouseJointDef(); | |
mouseJointDef.target = mouse; | |
mouseJointDef.bodyA = world.GetGroundBody(); | |
mouseJointDef.collideConnected = true; | |
var mouseJoint; | |
$('*').on({ | |
mousedown: function (e) { | |
var body = $(this).data('body'); | |
if (!body) { | |
return; | |
} | |
mouseJointDef.bodyB = body; | |
mouseJointDef.maxForce = 3000 * body.GetMass(); | |
mouseJoint = world.CreateJoint(mouseJointDef); | |
mouseJoint.SetTarget(mouse); | |
function mouseup(e) { | |
world.DestroyJoint(mouseJoint); | |
} | |
$(window).one('mouseup', mouseup); | |
} | |
}); | |
}(mouse)); | |
// | |
// Loops | |
// | |
(function () { | |
var dt = 30; | |
new Loop(function () { | |
world.Step( | |
1/dt, //frame-rate | |
10, //velocity iterations | |
10 //position iterations | |
); | |
world.ClearForces(); | |
}, 1000/dt).start(); | |
}()); | |
(function () { | |
var $entities = $('.ball, .crate'); | |
// cache some initial coordinates informations | |
$entities.each(function (i, el) { | |
var $el = $(el); | |
$el.data('origPos', { | |
left: $el.offset().left, | |
top: $el.offset().top, | |
width: $el.outerWidth(), | |
height: $el.outerHeight() | |
}); | |
}); | |
$ball = $('.ball'); | |
$tails = $('b', $ball); | |
new Loop(function (t, t0) { | |
if (!t0) { | |
return; | |
} | |
var dt = t - t0; | |
if (dt <= 0) { | |
return; | |
} | |
var i = $entities.length | |
while (i--) {(function () { | |
var entity = $entities[i]; | |
var $entity = $(entity); | |
var body = $entity.data('body'); | |
var pos = body.GetPosition(); | |
var ang = body.GetAngle() * 180 / Math.PI; | |
var origPos = $entity.data('origPos') | |
$entity.css('transform', 'translate3d(' + ~~(pos.x*SCALE - origPos.left - origPos.width / 2) + 'px, ' + ~~(pos.y*SCALE - origPos.top - origPos.height / 2) + 'px, 0) rotate3d(0,0,1,' + ~~ang + 'deg)'); | |
}());} | |
function angleVV(v1, v2) { | |
var n1 = v1.Length(); | |
var n2 = v2.Length(); | |
return Math.atan2(v1.y/n1, v1.x/n1) - Math.atan2(v2.y/n2, v2.x/n2); | |
} | |
var vel = $ball.data('body').GetLinearVelocityFromLocalPoint(new b2Vec2(0,0)); | |
$tails | |
.parent().css('transform', 'rotate3d(0,0,1,' + ~~((-$ball.data('body').GetAngle() + angleVV(vel, new b2Vec2(0, 1)) - Math.PI / 2) * 180 / Math.PI) + 'deg)').end() | |
.css({ | |
width: vel.Length() * 10 + '%', | |
opacity: vel.Length() / 10 | |
}); | |
}).start(); | |
}()); | |
}(jQuery, Box2D)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
http://bl.ocks.org/3411189