既然已经发现问题,我也发现Box2D for web漏掉了每一面:/
为了表明这一点,我制作了一个在静态多边形中移动的简单圆圈,这是一段时间后的结果。
请注意以下项目是如何泄漏的,因为我没有创建任何身体或以任何方式改变世界:
我有一个问题,因为我正在分析我的游戏,而垃圾收集器并没有'删除我的身体,联系人和其他东西。然后我看了他们从GC保留了什么,是Box2D本身。这可能导致2个选项:我做得不好或Box2D泄漏。我认为是我的原因。
究竟是什么保留了它?
您可以看到正文具有__destroyed属性。在使用world.DestroyBody(body)
删除之前手动设置当我摧毁一个身体时,我称之为世界上的步法。
正如你从box2d方法中看到的那样,它没有摆脱另一个变量,也没有将它改为另一个身体而我的身体不是GC。
知道我在这里缺少什么吗?
var gravity = new Box2D.Vec2(0, 0);
var doSleep = true;
var world = new Box2D.World(gravity, doSleep);
var step = false;
var fixtureDef = new Box2D.FixtureDef();
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
fixtureDef.shape = new Box2D.PolygonShape();
fixtureDef.shape.SetAsBox(1, 1);
var bodyDef = new Box2D.BodyDef;
bodyDef.type = Box2D.Body.b2_dynamicBody;
bodyDef.position.x = 0.4;
bodyDef.position.y = 0.4;
var bodies = []
var fix = [];
window.c = function(){
for(var i = 0; i < 100; i++){
var body = world.CreateBody(bodyDef);
body._id = i;
fix.push(body.CreateFixture(fixtureDef));
bodies.push(body);
}
if(step){world.Step(1/60, 3, 3); world.ClearForces();}
console.log('Created', bodies)
fixtureDef = null;
bodyDef = null;
}
window.d = function(){
_.each(bodies, function(body, i){
body.DestroyFixture(fix[i]);
world.DestroyBody(body);
fix[i] = null;
bodies[i] = null;
})
if(step){world.Step(1/60, 3, 3); world.ClearForces();}
bodies = null;
fix = null;
}
将步骤更改为true,再次出现内存泄漏问题。
文件中的代码:
var gravity = new Box2D.Vec2(0, 0);
var doSleep = true;
var world = new Box2D.World(gravity, doSleep);
var bodies = []
window.c = function(){
for(var i = 0; i < 100; i++){
var bodyDef = new Box2D.BodyDef();
bodyDef.type = 2;
var shape = new Box2D.PolygonShape();
shape.SetAsBox(1, 1);
var fixtureDef = new Box2D.FixtureDef();
fixtureDef.shape = shape;
var body = world.CreateBody(bodyDef);
body._id = i;
body.CreateFixture(fixtureDef);
bodies.push(body);
}
world.Step(0.3, 3, 3);
console.log('Created', bodies)
}
window.d = function(){
_.each(bodies, function(body, i){
world.DestroyBody(body);
bodies[i] = null;
})
world.Step(0.3, 3, 3);
bodies = null;
}
打开Google Chrome:
最后一步应该只有0个对象,因为它们已被销毁。而不是这个,你会发现这个:
现在你可以看到b2ContactEdge有很多引用。现在,如果删除world.Step部分代码,您将只看到2个对正文的引用。
如果删除此行
body.CreateFixture(fixtureDef);
或使身体静止不再泄漏。
我的游戏循环
...gameLoop = function(o){
// used a lot here
var world = o.world;
// calculate the new positions
var worldStepSeconds = o.worldStepMs / 1000;
// step world
world.Step(worldStepSeconds, o.velocityIterations, o.positionIterations)
// render debug
if(o.renderDebug){
world.DrawDebugData();
}
// always to not accumulate forces, maybe some bug occurs
world.ClearForces();
// tick all ticking entities
_.each(o.getTickEntitiesFn(), function(actor){
if(!actor) return;
actor.tick(o.worldStepMs, o.lastFrameMs);
})
// update PIXI entities
var body = world.GetBodyList();
var worldScale = world.SCALE;
var destroyBody = world.DestroyBody.bind(world);
while(body){
var actor = null;
var visualEntity = null;
var box2DEntity = o.getBox2DEntityByIdFn(body.GetUserData());
if(box2DEntity){
visualEntity = o.getVisualEntityByIdFn(box2DEntity.getVisualEntityId());
if(box2DEntity.isDestroying()){
// optimization
body.__destroyed = true;
world.DestroyBody(body);
box2DEntity.completeDestroy();
}
}
if(visualEntity){
if(visualEntity.isDestroying()){
visualEntity.completeDestroy();
}else{
var inverseY = true;
var bodyDetails = Utils.getScreenPositionAndRotationOfBody(world, body, inverseY);
visualEntity.updateSprite(bodyDetails.x, bodyDetails.y, bodyDetails.rotation);
}
}
// this delegates out functionality for each body processed
if(o.triggersFn.eachBody) o.triggersFn.eachBody(world, body, visualEntity);
body = body.GetNext();
}
// when a joint is created is then also created it's visual counterpart and then set to userData.
var joint = world.GetJointList();
while(joint){
var pixiGraphics = joint.GetUserData();
if(pixiGraphics){
// In order to draw a distance joint we need to know the start and end positions.
// The joint saves the global (yes) anchor positions for each body.
// After that we need to scale to our screen and invert y axis.
var anchorA = joint.GetAnchorA();
var anchorB = joint.GetAnchorB();
var screenPositionA = anchorA.Copy();
var screenPositionB = anchorB.Copy();
// scale
screenPositionA.Multiply(world.SCALE);
screenPositionB.Multiply(world.SCALE);
// invert y
screenPositionA.y = world.CANVAS_HEIGHT - screenPositionA.y
screenPositionB.y = world.CANVAS_HEIGHT - screenPositionB.y
// draw a black line
pixiGraphics.clear();
pixiGraphics.lineStyle(1, 0x000000, 0.7);
pixiGraphics.moveTo(screenPositionA.x, screenPositionA.y);
pixiGraphics.lineTo(screenPositionB.x, screenPositionB.y);
}
joint = joint.GetNext();
}
// render the PIXI scene
if(o.renderPixi){
o.renderer.render(o.stage)
}
// render next frame
requestAnimFrame(o.requestAnimFrameFn);
}
来自Box2d的代码:
b2ContactManager.prototype.Destroy = function (c) {
var fixtureA = c.GetFixtureA();
var fixtureB = c.GetFixtureB();
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
if (c.IsTouching()) {
this.m_contactListener.EndContact(c);
}
if (c.m_prev) {
c.m_prev.m_next = c.m_next;
}
if (c.m_next) {
c.m_next.m_prev = c.m_prev;
}
if (c == this.m_world.m_contactList) {
this.m_world.m_contactList = c.m_next;
}
if (c.m_nodeA.prev) {
c.m_nodeA.prev.next = c.m_nodeA.next;
}
if (c.m_nodeA.next) {
c.m_nodeA.next.prev = c.m_nodeA.prev;
}
if (c.m_nodeA == bodyA.m_contactList) {
bodyA.m_contactList = c.m_nodeA.next;
}
if (c.m_nodeB.prev) {
c.m_nodeB.prev.next = c.m_nodeB.next;
}
if (c.m_nodeB.next) {
c.m_nodeB.next.prev = c.m_nodeB.prev;
}
if (c.m_nodeB == bodyB.m_contactList) {
bodyB.m_contactList = c.m_nodeB.next;
}
this.m_contactFactory.Destroy(c);
--this.m_contactCount;
}
b2ContactFactory.prototype.Destroy = function (contact) {
if (contact.m_manifold.m_pointCount > 0) {
contact.m_fixtureA.m_body.SetAwake(true);
contact.m_fixtureB.m_body.SetAwake(true);
}
var type1 = parseInt(contact.m_fixtureA.GetType());
var type2 = parseInt(contact.m_fixtureB.GetType());
var reg = this.m_registers[type1][type2];
if (true) {
reg.poolCount++;
contact.m_next = reg.pool;
reg.pool = contact;
}
var destroyFcn = reg.destroyFcn;
destroyFcn(contact, this.m_allocator);
}
答案 0 :(得分:0)
我遇到了同样的问题,但我想我会从中找到答案。
而不是m_ *尝试功能,例如GetFixtureA()
而不是m_fixtureA
。
答案 1 :(得分:-2)
我想我已经找到了你的泄漏,未实现的(静态类)销毁函数:
b2Joint.Destroy = function (joint, allocator) {}
b2CircleContact.Destroy = function (contact, allocator) {}<
b2PolygonContact.Destroy = function (contact, allocator) {}
b2EdgeAndCircleContact.Destroy = function (contact, allocator) {}<
b2PolyAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndEdgeContact.Destroy = function (contact, allocator) {}
[UPDATE...]
b2DestructionListener.b2DestructionListener = function () {};
b2DestructionListener.prototype.SayGoodbyeJoint = function (joint) {}
b2DestructionListener.prototype.SayGoodbyeFixture = function (fixture) {}
b2Contact.prototype.Reset(fixtureA, fixtureB)
使用一个/两个fixture参数调用在fixture / s中传递的重置但是也传递了NO参数并且它'nulls'所有的b2Contact属性! (UNTESTED :)但我建议你设置你的YOURcontactListener类以处理所有的联系回调每次调用Reset(??)动态配置为逻辑需要每次调用(有超过你想象的每一个世界步骤)。
同时采取Colt McAnlis聪明的建议并战略性地预先分配游戏生命所需的所有内存(通过创建游戏和box2d对象池,现在你知道对象可以重置)所以垃圾收集器永远不会运行,直到你摧毁对象在您方便的时候游泳池....即关闭标签或您的设备需要充电! ; D [[更新]
//你可以定义和分配你自己的联系人监听器......来自......
YOUR.b2world.b2ContactManager.m_world.m_contactList = new YOURcontactlistener();<br>[edit]...if you dont it actually does have Box2D.Dynamics.b2ContactListener.b2_defaultListener.
worldPtep中的 // box2d通过以下方式调用YOURcontactlistener.update():
this.b2world.b2ContactManager.m_world.m_contactList.Update(this.m_contactListener)
// this.m_contactListener是YOURS || b2_defaultListener;
//实例化所有列出的泄漏对象,如下所示:
{b2Contact which instantiates {b2ContactEdge}
和{b2Manifold which instantiates {b2ManifoldPoint{which instantiates m_id.key == ContactID{which instantiates Features}}}}
以及{B2Vec2}在b2ContactResult中被实例化...我实际上找不到但是假设它必须在求解器中实例化。
//在....创建了一个Contacts.destroyFcn回调
b2ContactFactory.prototype.Destroy = function (contact) {...}
//然后Contacts.destroyFcn回调在....中私下注册。
b2ContactFactory.prototype.InitializeRegisters() {...}
...经由
...this.AddType = function (createFcn, destroyFcn, type1, type2) {...}
...但是......这些是私人注册的四个来自上面的未被授予的静态类功能...
b2PolygonContact.Destroy = function (contact, allocator) {}
b2EdgeAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndEdgeContact.Destroy = function (contact, allocator) {}
所以我还没有测试它,但看起来box2dweb只是给你Destroy回调/处理函数,你必须读取源代码才能找到你需要null的所有属性。 [编辑]结合b2Contact.prototype.Reset(fixtureA,fixtureB)
但无论哪种方式都非常有信心,上面的函数(可能是不完整的)都是回调/处理程序,并且可以用来为那些偶然发现这个问题的其他人取回性能。很确定Totti已经开始了(不要忘记在回调中处理你的'this'范围)。