issue #2771: fix some retain/release bugs

This commit is contained in:
boyu0 2013-10-28 11:08:41 +08:00
parent fdcf019665
commit d0d8694091
9 changed files with 280 additions and 98 deletions

View File

@ -749,6 +749,14 @@ void Node::detachChild(Node *child, int childIndex, bool doCleanup)
child->onExitTransitionDidStart();
child->onExit();
}
#ifdef CC_USE_PHYSICS
if (child->_physicsBody != nullptr)
{
child->_physicsBody->removeFromWorld();
}
#endif
// If you don't do cleanup, the child's actions will not get removed and the
// its scheduledSelectors_ dict will not get released!

View File

@ -62,6 +62,7 @@ namespace
PhysicsBody::PhysicsBody()
: _owner(nullptr)
, _shapes(nullptr)
, _world(nullptr)
, _info(nullptr)
, _dynamic(true)
@ -85,18 +86,23 @@ PhysicsBody::PhysicsBody()
PhysicsBody::~PhysicsBody()
{
CC_SAFE_DELETE(_info);
if (_world)
{
removeFromWorld();
}
removeAllShapes();
for (auto it = _joints.begin(); it != _joints.end(); ++it)
{
PhysicsJoint* joint = *it;
PhysicsBody* other = joint->getBodyA() == this ? joint->getBodyA() : joint->getBodyB();
PhysicsBody* other = joint->getBodyA() == this ? joint->getBodyB() : joint->getBodyA();
other->_joints.erase(std::find(other->_joints.begin(), other->_joints.end(), joint));
delete joint;
}
CC_SAFE_DELETE(_info);
}
PhysicsBody* PhysicsBody::create()
@ -112,6 +118,45 @@ PhysicsBody* PhysicsBody::create()
return nullptr;
}
PhysicsBody* PhysicsBody::create(float mass)
{
PhysicsBody* body = new PhysicsBody();
if (body)
{
body->_mass = mass;
body->_massDefault = false;
if (body->init())
{
body->autorelease();
return body;
}
}
CC_SAFE_DELETE(body);
return nullptr;
}
PhysicsBody* PhysicsBody::create(float mass, float moment)
{
PhysicsBody* body = new PhysicsBody();
if (body)
{
body->_mass = mass;
body->_massDefault = false;
body->_moment = moment;
body->_momentDefault = false;
if (body->init())
{
body->autorelease();
return body;
}
}
CC_SAFE_DELETE(body);
return nullptr;
}
PhysicsBody* PhysicsBody::createCircle(float radius, PhysicsMaterial material)
{
PhysicsBody* body = new PhysicsBody();
@ -223,6 +268,9 @@ bool PhysicsBody::init()
{
_info = new PhysicsBodyInfo();
CC_BREAK_IF(_info == nullptr);
_shapes = Array::create();
CC_BREAK_IF(_shapes == nullptr);
_shapes->retain();
_info->body = cpBodyNew(PhysicsHelper::float2cpfloat(_mass), PhysicsHelper::float2cpfloat(_moment));
_info->group = ++GROUP_INDEX;
@ -308,10 +356,9 @@ void PhysicsBody::addShape(PhysicsShape* shape)
if (shape == nullptr) return;
// add shape to body
if (std::find(_shapes.begin(), _shapes.end(), shape) == _shapes.end())
if (_shapes->getIndexOfObject(shape) == UINT_MAX)
{
shape->setBody(this);
_shapes.push_back(shape);
// calculate the area, mass, and desity
// area must update before mass, because the density changes depend on it.
@ -324,7 +371,7 @@ void PhysicsBody::addShape(PhysicsShape* shape)
_world->addShape(shape);
}
shape->retain();
_shapes->addObject(shape);
}
}
@ -519,8 +566,9 @@ void PhysicsBody::setMoment(float moment)
PhysicsShape* PhysicsBody::getShapeByTag(int tag)
{
for (auto shape : _shapes)
for (auto child : *_shapes)
{
PhysicsShape* shape = dynamic_cast<PhysicsShape*>(child);
if (shape->getTag() == tag)
{
return shape;
@ -532,8 +580,9 @@ PhysicsShape* PhysicsBody::getShapeByTag(int tag)
void PhysicsBody::removeShapeByTag(int tag)
{
for (auto shape : _shapes)
for (auto child : *_shapes)
{
PhysicsShape* shape = dynamic_cast<PhysicsShape*>(child);
if (shape->getTag() == tag)
{
removeShape(shape);
@ -544,9 +593,7 @@ void PhysicsBody::removeShapeByTag(int tag)
void PhysicsBody::removeShape(PhysicsShape* shape)
{
auto it = std::find(_shapes.begin(), _shapes.end(), shape);
if (it != _shapes.end())
if (_shapes->getIndexOfObject(shape) == UINT_MAX)
{
// deduce the area, mass and moment
// area must update before mass, because the density changes depend on it.
@ -559,25 +606,39 @@ void PhysicsBody::removeShape(PhysicsShape* shape)
{
_world->removeShape(shape);
}
_shapes.erase(it);
shape->setBody(nullptr);
shape->release();
_shapes->removeObject(shape);
}
}
void PhysicsBody::removeAllShapes()
{
for (auto shape : _shapes)
for (auto child : *_shapes)
{
PhysicsShape* shape = dynamic_cast<PhysicsShape*>(child);
// deduce the area, mass and moment
// area must update before mass, because the density changes depend on it.
_area -= shape->getArea();
addMass(-shape->getMass());
addMoment(-shape->getMoment());
if (_world)
{
_world->removeShape(shape);
}
delete shape;
shape->setBody(nullptr);
}
_shapes.clear();
_shapes->removeAllObjects();
}
void PhysicsBody::removeFromWorld()
{
if (_world)
{
_world->removeBody(this);
}
}
void PhysicsBody::setEnable(bool enable)

View File

@ -30,6 +30,7 @@
#include "CCObject.h"
#include "CCGeometry.h"
#include "CCArray.h"
#include "CCPhysicsShape.h"
@ -53,6 +54,8 @@ class PhysicsBody : public Object//, public Clonable
{
public:
static PhysicsBody* create();
static PhysicsBody* create(float mass);
static PhysicsBody* create(float mass, float moment);
/**
* @brief Create a body contains a circle shape.
*/
@ -119,11 +122,11 @@ public:
/*
* @brief get the body shapes.
*/
inline std::vector<PhysicsShape*>& getShapes() { return _shapes; }
inline Array* getShapes() { return _shapes; }
/*
* @brief get the first body shapes.
*/
inline PhysicsShape* getShape() { return _shapes.size() >= 1 ? _shapes.front() : nullptr; }
inline PhysicsShape* getShape() { return _shapes->count() >= 1 ? dynamic_cast<PhysicsShape*>(_shapes->getObjectAtIndex(0)) : nullptr; }
PhysicsShape* getShapeByTag(int tag);
/*
* @brief remove a shape from body
@ -135,6 +138,8 @@ public:
*/
void removeAllShapes();
void removeFromWorld();
/*
* @brief get the world body added to.
*/
@ -258,7 +263,7 @@ protected:
protected:
Sprite* _owner;
std::vector<PhysicsJoint*> _joints;
std::vector<PhysicsShape*> _shapes;
Array* _shapes;
PhysicsWorld* _world;
PhysicsBodyInfo* _info;
bool _dynamic;

View File

@ -61,9 +61,6 @@ PhysicsJoint::~PhysicsJoint()
setCollisionEnable(true);
CC_SAFE_DELETE(_info);
CC_SAFE_RELEASE(_bodyA);
CC_SAFE_RELEASE(_bodyB);
}
bool PhysicsJoint::init(cocos2d::PhysicsBody *a, cocos2d::PhysicsBody *b)
@ -75,14 +72,12 @@ bool PhysicsJoint::init(cocos2d::PhysicsBody *a, cocos2d::PhysicsBody *b)
if (a != nullptr)
{
_bodyA = a;
_bodyA->retain();
_bodyA->_joints.push_back(this);
}
if (b != nullptr)
{
_bodyB = b;
_bodyB->retain();
_bodyB->_joints.push_back(this);
}
@ -174,7 +169,6 @@ PhysicsJointFixed* PhysicsJointFixed::create(PhysicsBody* a, PhysicsBody* b, con
if (joint && joint->init(a, b, anchr))
{
joint->autorelease();
return joint;
}
@ -191,12 +185,12 @@ bool PhysicsJointFixed::init(PhysicsBody* a, PhysicsBody* b, const Point& anchr)
// add a pivot joint to fixed two body together
cpConstraint* joint = cpPivotJointNew(bodyInfo(a)->body, bodyInfo(b)->body,
PhysicsHelper::point2cpv(anchr));
CC_BREAK_IF(joint);
CC_BREAK_IF(joint == nullptr);
_info->add(joint);
// add a gear joint to make two body have the same rotation.
joint = cpGearJointNew(bodyInfo(a)->body, bodyInfo(b)->body, 0, 1);
CC_BREAK_IF(joint);
CC_BREAK_IF(joint == nullptr);
_info->add(joint);
setCollisionEnable(false);
@ -213,7 +207,6 @@ PhysicsJointPin* PhysicsJointPin::create(PhysicsBody* a, PhysicsBody* b, const P
if (joint && joint->init(a, b, anchr))
{
joint->autorelease();
return joint;
}
@ -230,7 +223,7 @@ bool PhysicsJointPin::init(PhysicsBody *a, PhysicsBody *b, const Point& anchr)
cpConstraint* joint = cpPivotJointNew(bodyInfo(a)->body, bodyInfo(b)->body,
PhysicsHelper::point2cpv(anchr));
CC_BREAK_IF(joint);
CC_BREAK_IF(joint == nullptr);
_info->add(joint);
@ -266,7 +259,7 @@ bool PhysicsJointSliding::init(PhysicsBody* a, PhysicsBody* b, const Point& groo
PhysicsHelper::point2cpv(grooveB),
PhysicsHelper::point2cpv(anchr));
CC_BREAK_IF(joint);
CC_BREAK_IF(joint == nullptr);
_info->add(joint);
@ -302,7 +295,7 @@ bool PhysicsJointLimit::init(PhysicsBody* a, PhysicsBody* b, const Point& anchr1
0,
PhysicsHelper::float2cpfloat(anchr1.getDistance(anchr2)));
CC_BREAK_IF(joint);
CC_BREAK_IF(joint == nullptr);
_info->add(joint);

View File

@ -40,7 +40,7 @@ class PhysicsBodyInfo;
/*
* @brief An PhysicsJoint object connects two physics bodies together.
*/
class PhysicsJoint : public Object
class PhysicsJoint
{
protected:
PhysicsJoint();

View File

@ -177,18 +177,27 @@ void PhysicsWorldCallback::nearestPointQueryFunc(cpShape *shape, cpFloat distanc
bool PhysicsWorld::init()
{
_info = new PhysicsWorldInfo();
do
{
_info = new PhysicsWorldInfo();
CC_BREAK_IF(_info == nullptr);
_bodys = Array::create();
CC_BREAK_IF(_bodys == nullptr);
_bodys->retain();
cpSpaceSetGravity(_info->space, PhysicsHelper::point2cpv(_gravity));
cpSpaceSetDefaultCollisionHandler(_info->space,
(cpCollisionBeginFunc)PhysicsWorldCallback::collisionBeginCallbackFunc,
(cpCollisionPreSolveFunc)PhysicsWorldCallback::collisionPreSolveCallbackFunc,
(cpCollisionPostSolveFunc)PhysicsWorldCallback::collisionPostSolveCallbackFunc,
(cpCollisionSeparateFunc)PhysicsWorldCallback::collisionSeparateCallbackFunc,
this);
return true;
} while (false);
cpSpaceSetGravity(_info->space, PhysicsHelper::point2cpv(_gravity));
cpSpaceSetDefaultCollisionHandler(_info->space,
(cpCollisionBeginFunc)PhysicsWorldCallback::collisionBeginCallbackFunc,
(cpCollisionPreSolveFunc)PhysicsWorldCallback::collisionPreSolveCallbackFunc,
(cpCollisionPostSolveFunc)PhysicsWorldCallback::collisionPostSolveCallbackFunc,
(cpCollisionSeparateFunc)PhysicsWorldCallback::collisionSeparateCallbackFunc,
this);
return true;
return false;
}
void PhysicsWorld::addJoint(PhysicsJoint* joint)
@ -197,8 +206,6 @@ void PhysicsWorld::addJoint(PhysicsJoint* joint)
if (it == _joints.end())
{
_joints.push_back(joint);
for (auto subjoint : joint->_info->joints)
{
if (!cpSpaceContainsConstraint(_info->space, subjoint))
@ -206,18 +213,44 @@ void PhysicsWorld::addJoint(PhysicsJoint* joint)
cpSpaceAddConstraint(_info->space, subjoint);
}
}
_joints.push_back(joint);
}
}
void PhysicsWorld::removeJoint(PhysicsJoint* joint)
{
auto it = std::find(_joints.begin(), _joints.end(), joint);
if (it != _joints.end())
{
for (auto subjoint : joint->_info->joints)
{
if (cpSpaceContainsConstraint(_info->space, subjoint))
{
cpSpaceRemoveConstraint(_info->space, subjoint);
}
}
_joints.remove(joint);
}
}
void PhysicsWorld::removeAllJoints()
{
for (auto joint : _joints)
{
for (auto subjoint : joint->_info->joints)
{
if (!cpSpaceContainsConstraint(_info->space, subjoint))
{
cpSpaceRemoveConstraint(_info->space, subjoint);
}
}
}
_joints.clear();
}
void PhysicsWorld::addShape(PhysicsShape* shape)
@ -243,8 +276,15 @@ void PhysicsWorld::addBody(PhysicsBody* body)
{
CCASSERT(body != nullptr, "the body can not be nullptr");
if (body->getWorld() != this && body->getWorld() != nullptr)
{
body->removeFromWorld();
}
if (body->isEnable())
{
body->_world = this;
//is gravity enable
if (!body->isGravityEnable())
{
@ -258,45 +298,40 @@ void PhysicsWorld::addBody(PhysicsBody* body)
}
// add shapes to space
for (auto shape : body->getShapes())
for (auto shape : *body->getShapes())
{
addShape(shape);
addShape(dynamic_cast<PhysicsShape*>(shape));
}
}
if (_bodys == nullptr)
{
_bodys = Array::create(body, NULL);
_bodys->retain();
}else
{
_bodys->addObject(body);
}
_bodys->addObject(body);
}
void PhysicsWorld::removeBody(PhysicsBody* body)
{
CCASSERT(body != nullptr, "the body can not be nullptr");
if (body->getWorld() == this)
if (body->getWorld() != this)
{
// reset the gravity
if (!body->isGravityEnable())
{
body->applyForce(-_gravity);
}
return;
}
// reset the gravity
if (!body->isGravityEnable())
{
body->applyForce(-_gravity);
}
// remove joints
for (auto joint : body->_joints)
{
removeJoint(joint);
}
// remove shaps
for (auto shape : body->getShapes())
for (auto shape : *body->getShapes())
{
for (auto cps : shape->_info->shapes)
{
if (cpSpaceContainsShape(_info->space, cps))
{
cpSpaceRemoveShape(_info->space, cps);
}
}
removeShape(dynamic_cast<PhysicsShape*>(shape));
}
// remove body
@ -305,10 +340,8 @@ void PhysicsWorld::removeBody(PhysicsBody* body)
cpSpaceRemoveBody(_info->space, body->_info->body);
}
if (_bodys != nullptr)
{
_bodys->removeObject(body);
}
body->_world = nullptr;
_bodys->removeObject(body);
}
void PhysicsWorld::removeBodyByTag(int tag)
@ -324,6 +357,43 @@ void PhysicsWorld::removeBodyByTag(int tag)
}
}
void PhysicsWorld::removeAllBodys()
{
for (Object* obj : *_bodys)
{
PhysicsBody* body = dynamic_cast<PhysicsBody*>(obj);
// reset the gravity
if (!body->isGravityEnable())
{
body->applyForce(-_gravity);
}
// remove joints
for (auto joint : body->_joints)
{
removeJoint(joint);
}
// remove shaps
for (auto shape : *body->getShapes())
{
removeShape(dynamic_cast<PhysicsShape*>(shape));
}
// remove body
if (cpSpaceContainsBody(_info->space, body->_info->body))
{
cpSpaceRemoveBody(_info->space, body->_info->body);
}
body->_world = nullptr;
}
_bodys->removeAllObjects();
CC_SAFE_RELEASE(_bodys);
}
void PhysicsWorld::removeShape(PhysicsShape* shape)
{
for (auto cps : shape->_info->shapes)
@ -366,11 +436,9 @@ void PhysicsWorld::debugDraw()
{
PhysicsBody* body = dynamic_cast<PhysicsBody*>(obj);
std::vector<PhysicsShape*> shapes = body->getShapes();
for (auto shape : shapes)
for (auto shape : *body->getShapes())
{
drawWithShape(_drawNode, shape);
drawWithShape(_drawNode, dynamic_cast<PhysicsShape*>(shape));
}
}
@ -449,6 +517,11 @@ int PhysicsWorld::collisionBeginCallback(PhysicsContact& contact)
// check the joint is collision enable or not
for (PhysicsJoint* joint : jointsA)
{
if (std::find(_joints.begin(), _joints.end(), joint) == _joints.end())
{
continue;
}
if (!joint->isCollisionEnable())
{
PhysicsBody* body = joint->getBodyA() == bodyA ? bodyB : bodyA;
@ -657,8 +730,9 @@ PhysicsWorld::PhysicsWorld()
PhysicsWorld::~PhysicsWorld()
{
removeAllBodys();
removeAllJoints();
CC_SAFE_DELETE(_info);
CC_SAFE_RELEASE(_bodys);
}
NS_CC_END

View File

@ -116,6 +116,7 @@ public:
virtual void removeBody(PhysicsBody* body);
virtual void removeBodyByTag(int tag);
virtual void removeAllBodys();
protected:
static PhysicsWorld* create();

View File

@ -92,7 +92,6 @@ PhysicsDemo::PhysicsDemo()
: _scene(nullptr)
, _ball(nullptr)
, _spriteTexture(nullptr)
, _mouse(nullptr)
{
}
@ -160,7 +159,7 @@ void PhysicsDemo::onEnter()
#endif
}
void PhysicsDemo::addGrossiniAtPosition(Point p, float scale/* = 1.0*/)
Sprite* PhysicsDemo::addGrossiniAtPosition(Point p, float scale/* = 1.0*/)
{
#ifdef CC_USE_PHYSICS
CCLOG("Add sprite %0.2f x %02.f",p.x,p.y);
@ -178,6 +177,8 @@ void PhysicsDemo::addGrossiniAtPosition(Point p, float scale/* = 1.0*/)
sp->setPhysicsBody(PhysicsBody::createBox(Size(48.0f * scale, 108.0f * scale)));
this->addChild(sp);
sp->setPosition(p);
return sp;
#endif
}
@ -367,28 +368,60 @@ Sprite* PhysicsDemo::makeTriangle(float x, float y, Size size, PhysicsMaterial m
return triangle;
}
void PhysicsDemo::onTouchesBegan(const std::vector<Touch*>& touches, Event* event)
bool PhysicsDemo::onTouchBegan(Touch* touch, Event* event)
{
for( auto &touch: touches)
auto location = touch->getLocation();
Array* arr = _scene->getPhysicsWorld()->getShapesAtPoint(location);
PhysicsShape* shape = nullptr;
for (Object* obj : *arr)
{
auto location = touch->getLocation();
Array* arr = _scene->getPhysicsWorld()->getShapesAtPoint(location);
shape = dynamic_cast<PhysicsShape*>(obj);
PhysicsShape* shape = nullptr;
for (Object* obj : *arr)
if (shape->getTag() == 1)
{
break;
}
}
if (shape != nullptr)
{
Node* mouse = Node::create();
mouse->setPhysicsBody(PhysicsBody::create(PHYSICS_INFINITY, PHYSICS_INFINITY));
mouse->getPhysicsBody()->setDynamic(false);
mouse->setPosition(location);
this->addChild(mouse);
PhysicsJoint* joint = PhysicsJointPin::create(mouse->getPhysicsBody(), shape->getBody(), location);
_scene->getPhysicsWorld()->addJoint(joint);
_mouses.insert(std::make_pair(touch->getID(), mouse));
return true;
}
return false;
}
void PhysicsDemo::onTouchMoved(Touch* touch, Event* event)
{
auto it = _mouses.find(touch->getID());
if (it != _mouses.end())
{
it->second->getPhysicsBody()->setVelocity((touch->getLocation() - it->second->getPosition()) * 60.0f);
it->second->setPosition(touch->getLocation());
}
}
void PhysicsDemo::onTouchesMoved(const std::vector<Touch*>& touches, Event* event)
void PhysicsDemo::onTouchEnded(Touch* touch, Event* event)
{
auto it = _mouses.find(touch->getID());
}
void PhysicsDemo::onTouchesEnded(const std::vector<Touch*>& touches, Event* event)
{
if (it != _mouses.end())
{
this->removeChild(it->second);
_mouses.erase(it);
}
}
@ -440,6 +473,9 @@ void PhysicsDemoPyramidStack::onEnter()
{
PhysicsDemo::onEnter();
setTouchEnabled(true);
setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
auto node = Node::create();
node->setPhysicsBody(PhysicsBody::createEdgeSegment(VisibleRect::leftBottom() + Point(0, 50), VisibleRect::rightBottom() + Point(0, 50)));
this->addChild(node);
@ -454,7 +490,9 @@ void PhysicsDemoPyramidStack::onEnter()
{
for(int j=0; j<=i; j++)
{
addGrossiniAtPosition(VisibleRect::bottom() + Point((i/2 - j) * 11, (14 - i) * 23 + 100), 0.2f);
auto sp = addGrossiniAtPosition(VisibleRect::bottom() + Point((i/2 - j) * 11, (14 - i) * 23 + 100), 0.2f);
sp->getPhysicsBody()->setTag(1);
}
}
}

View File

@ -5,6 +5,8 @@
#include "../testBasic.h"
#include "../BaseTest.h"
#include <map>
class PhysicsTestScene : public TestScene
{
@ -36,19 +38,19 @@ public:
void backCallback(Object* sender);
void toggleDebugCallback(Object* sender);
void addGrossiniAtPosition(Point p, float scale = 1.0);
Sprite* addGrossiniAtPosition(Point p, float scale = 1.0);
Sprite* makeBall(float x, float y, float radius, PhysicsMaterial material = PhysicsMaterial(1.0f, 1.0f, 1.0f));
Sprite* makeBox(float x, float y, Size size, PhysicsMaterial material = PhysicsMaterial(1.0f, 1.0f, 1.0f));
Sprite* makeTriangle(float x, float y, Size size, PhysicsMaterial material = PhysicsMaterial(1.0f, 1.0f, 1.0f));
void onTouchesBegan(const std::vector<Touch*>& touches, Event* event) override;
void onTouchesMoved(const std::vector<Touch*>& touches, Event* event) override;
void onTouchesEnded(const std::vector<Touch*>& touches, Event* event) override;
bool onTouchBegan(Touch* touch, Event* event) override;
void onTouchMoved(Touch* touch, Event* event) override;
void onTouchEnded(Touch* touch, Event* event) override;
protected:
Texture2D* _spriteTexture; // weak ref
SpriteBatchNode* _ball;
DrawNode* _mouse;
std::map<int, Node*> _mouses;
};
class PhysicsDemoClickAdd : public PhysicsDemo