From 97082cb1692592840e14a2fb9a5e3e7b7071ecc1 Mon Sep 17 00:00:00 2001 From: Wenhai Lin Date: Fri, 10 Apr 2015 15:39:39 +0800 Subject: [PATCH] Physics: Fixed position of physics body is wrong when the position of parent node changes --- cocos/2d/CCNode.cpp | 68 ++++++++++++++----- cocos/2d/CCNode.h | 6 +- cocos/2d/CCScene.cpp | 2 + .../Classes/PhysicsTest/PhysicsTest.cpp | 38 ++++++----- .../Classes/PhysicsTest/PhysicsTest.h | 3 + 5 files changed, 82 insertions(+), 35 deletions(-) diff --git a/cocos/2d/CCNode.cpp b/cocos/2d/CCNode.cpp index 8866adc041..ac19c48c8c 100644 --- a/cocos/2d/CCNode.cpp +++ b/cocos/2d/CCNode.cpp @@ -127,6 +127,8 @@ Node::Node(void) , _physicsRotation(0.0f) , _physicsTransformDirty(true) , _updateTransformFromPhysics(true) +, _physicsWorld(nullptr) +, _physicsBodyAssociatedWith(0) #endif , _displayedOpacity(255) , _realOpacity(255) @@ -334,8 +336,9 @@ void Node::setRotation(float rotation) _rotationZ_X = _rotationZ_Y = rotation; _transformUpdated = _transformDirty = _inverseDirty = true; #if CC_USE_PHYSICS - if (_physicsBody && _physicsBody->getWorld()) { - _physicsBody->getWorld()->_updateBodyTransform = true; + if (_physicsWorld && _physicsBodyAssociatedWith > 0) + { + _physicsWorld->_updateBodyTransform = true; } #endif @@ -475,8 +478,9 @@ void Node::setScale(float scale) _scaleX = _scaleY = _scaleZ = scale; _transformUpdated = _transformDirty = _inverseDirty = true; #if CC_USE_PHYSICS - if (_physicsBody && _physicsBody->getWorld()) { - _physicsBody->getWorld()->_updateBodyTransform = true; + if (_physicsWorld && _physicsBodyAssociatedWith > 0) + { + _physicsWorld->_updateBodyTransform = true; } #endif } @@ -497,8 +501,9 @@ void Node::setScale(float scaleX,float scaleY) _scaleY = scaleY; _transformUpdated = _transformDirty = _inverseDirty = true; #if CC_USE_PHYSICS - if (_physicsBody && _physicsBody->getWorld()) { - _physicsBody->getWorld()->_updateBodyTransform = true; + if (_physicsWorld && _physicsBodyAssociatedWith > 0) + { + _physicsWorld->_updateBodyTransform = true; } #endif } @@ -512,8 +517,9 @@ void Node::setScaleX(float scaleX) _scaleX = scaleX; _transformUpdated = _transformDirty = _inverseDirty = true; #if CC_USE_PHYSICS - if (_physicsBody && _physicsBody->getWorld()) { - _physicsBody->getWorld()->_updateBodyTransform = true; + if (_physicsWorld && _physicsBodyAssociatedWith > 0) + { + _physicsWorld->_updateBodyTransform = true; } #endif } @@ -556,8 +562,9 @@ void Node::setScaleY(float scaleY) _scaleY = scaleY; _transformUpdated = _transformDirty = _inverseDirty = true; #if CC_USE_PHYSICS - if (_physicsBody && _physicsBody->getWorld()) { - _physicsBody->getWorld()->_updateBodyTransform = true; + if (_physicsWorld && _physicsBodyAssociatedWith > 0) + { + _physicsWorld->_updateBodyTransform = true; } #endif } @@ -592,8 +599,9 @@ void Node::setPosition(float x, float y) _transformUpdated = _transformDirty = _inverseDirty = true; _usingNormalizedPosition = false; #if CC_USE_PHYSICS - if (_physicsBody && _physicsBody->getWorld()) { - _physicsBody->getWorld()->_updateBodyTransform = true; + if (_physicsWorld && _physicsBodyAssociatedWith > 0) + { + _physicsWorld->_updateBodyTransform = true; } #endif } @@ -661,8 +669,9 @@ void Node::setNormalizedPosition(const Vec2& position) _normalizedPositionDirty = true; _transformUpdated = _transformDirty = _inverseDirty = true; #if CC_USE_PHYSICS - if (_physicsBody && _physicsBody->getWorld()) { - _physicsBody->getWorld()->_updateBodyTransform = true; + if (_physicsWorld && _physicsBodyAssociatedWith > 0) + { + _physicsWorld->_updateBodyTransform = true; } #endif } @@ -1050,8 +1059,17 @@ void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::stri child->setOrderOfArrival(s_globalOrderOfArrival++); #if CC_USE_PHYSICS + _physicsBodyAssociatedWith += child->_physicsBodyAssociatedWith; + auto parentNode = this; + while (parentNode->_parent) + { + parentNode = parentNode->_parent; + parentNode->_physicsBodyAssociatedWith += child->_physicsBodyAssociatedWith; + } + + auto scene = dynamic_cast(parentNode); + // Recursive add children with which have physics body. - auto scene = this->getScene(); if (scene && scene->getPhysicsWorld()) { scene->addChildToPhysicsWorld(child); @@ -2029,6 +2047,14 @@ void Node::setPhysicsBody(PhysicsBody* body) _physicsBody->_node = nullptr; _physicsBody->release(); _physicsBody = nullptr; + + _physicsBodyAssociatedWith--; + auto parentNode = this; + while (parentNode->_parent) + { + parentNode = parentNode->_parent; + parentNode->_physicsBodyAssociatedWith--; + } } if (body) @@ -2053,7 +2079,15 @@ void Node::setPhysicsBody(PhysicsBody* body) _physicsScaleStartX = _scaleX; _physicsScaleStartY = _scaleY; - auto scene = getScene(); + _physicsBodyAssociatedWith++; + auto parentNode = this; + while (parentNode->_parent) + { + parentNode = parentNode->_parent; + parentNode->_physicsBodyAssociatedWith++; + } + + auto scene = dynamic_cast(parentNode); if (scene && scene->getPhysicsWorld()) { _physicsTransformDirty = true; @@ -2095,6 +2129,7 @@ void Node::updateTransformFromPhysics(const Mat4& parentTransform, uint32_t pare { auto& newPosition = _physicsBody->getPosition(); auto& recordedPosition = _physicsBody->_recordedPosition; + auto updateBodyTransform = _physicsWorld->_updateBodyTransform; if (parentFlags || recordedPosition.x != newPosition.x || recordedPosition.y != newPosition.y) { recordedPosition = newPosition; @@ -2105,6 +2140,7 @@ void Node::updateTransformFromPhysics(const Mat4& parentTransform, uint32_t pare } _physicsRotation = _physicsBody->getRotation(); setRotation(_physicsRotation - _parent->_physicsRotation); + _physicsWorld->_updateBodyTransform = updateBodyTransform; } #endif //CC_USE_PHYSICS diff --git a/cocos/2d/CCNode.h b/cocos/2d/CCNode.h index 21105f8b4c..0318570ea1 100644 --- a/cocos/2d/CCNode.h +++ b/cocos/2d/CCNode.h @@ -54,6 +54,7 @@ class GLProgram; class GLProgramState; #if CC_USE_PHYSICS class PhysicsBody; +class PhysicsWorld; #endif /** @@ -1838,6 +1839,9 @@ protected: float _physicsRotation; bool _physicsTransformDirty; bool _updateTransformFromPhysics; + + PhysicsWorld* _physicsWorld; /** The PhysicsWorld associated with the node.*/ + int _physicsBodyAssociatedWith; /** The count of PhysicsBody associated with the node and children.*/ #endif // opacity controls @@ -1862,7 +1866,7 @@ private: CC_DISALLOW_COPY_AND_ASSIGN(Node); #if CC_USE_PHYSICS - friend class Layer; + friend class Scene; #endif //CC_USTPS }; diff --git a/cocos/2d/CCScene.cpp b/cocos/2d/CCScene.cpp index 6e522f81c7..b6e36ce5ff 100644 --- a/cocos/2d/CCScene.cpp +++ b/cocos/2d/CCScene.cpp @@ -227,6 +227,8 @@ void Scene::addChildToPhysicsWorld(Node* child) std::function addToPhysicsWorldFunc = nullptr; addToPhysicsWorldFunc = [this, &addToPhysicsWorldFunc](Node* node) -> void { + node->_physicsWorld = _physicsWorld; + if (node->getPhysicsBody()) { _physicsWorld->addBody(node->getPhysicsBody()); diff --git a/tests/cpp-tests/Classes/PhysicsTest/PhysicsTest.cpp b/tests/cpp-tests/Classes/PhysicsTest/PhysicsTest.cpp index 7d4f9ae27e..924708cd4e 100644 --- a/tests/cpp-tests/Classes/PhysicsTest/PhysicsTest.cpp +++ b/tests/cpp-tests/Classes/PhysicsTest/PhysicsTest.cpp @@ -1716,8 +1716,7 @@ std::string PhysicsFixedUpdate::subtitle() const bool PhysicsTransformTest::onTouchBegan(Touch *touch, Event *event) { - Node* child = this->getChildByTag(1); - child->setPosition(this->convertTouchToNodeSpace(touch)); + _parentSprite->setPosition(_rootLayer->convertTouchToNodeSpace(touch)); return false; } @@ -1731,46 +1730,49 @@ void PhysicsTransformTest::onEnter() touchListener->onTouchBegan = CC_CALLBACK_2(PhysicsTransformTest::onTouchBegan, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this); + _rootLayer = Layer::create(); + addChild(_rootLayer); + auto wall = Node::create(); wall->setPhysicsBody(PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size, PhysicsMaterial(0.1f, 1.0f, 0.0f))); wall->setPosition(VisibleRect::center()); - addChild(wall); + _rootLayer->addChild(wall); //parent test - auto parent = Sprite::create("Images/YellowSquare.png"); - parent->setPosition(200, 100); - parent->setScale(0.25); - parent->setPhysicsBody(PhysicsBody::createBox(parent->getContentSize()*parent->getScale(), PhysicsMaterial(0.1f, 1.0f, 0.0f))); - parent->getPhysicsBody()->setTag(DRAG_BODYS_TAG); - parent->setTag(1); - addChild(parent); + _parentSprite = Sprite::create("Images/YellowSquare.png"); + _parentSprite->setPosition(200, 100); + _parentSprite->setScale(0.25); + _parentSprite->setPhysicsBody(PhysicsBody::createBox(_parentSprite->getContentSize()*_parentSprite->getScale(), PhysicsMaterial(0.1f, 1.0f, 0.0f))); + _parentSprite->getPhysicsBody()->setTag(DRAG_BODYS_TAG); + _parentSprite->setTag(1); + _rootLayer->addChild(_parentSprite); auto leftBall = Sprite::create("Images/ball.png"); leftBall->setPosition(-30, 0); leftBall->cocos2d::Node::setScale(2); leftBall->setPhysicsBody(PhysicsBody::createCircle(leftBall->getContentSize().width, PhysicsMaterial(0.1f, 1.0f, 0.0f))); leftBall->getPhysicsBody()->setTag(DRAG_BODYS_TAG); - parent->addChild(leftBall); + _parentSprite->addChild(leftBall); ScaleTo* scaleTo = ScaleTo::create(2.0, 0.5); ScaleTo* scaleBack = ScaleTo::create(2.0, 1.0); - parent->runAction(RepeatForever::create(Sequence::create(scaleTo, scaleBack, nullptr))); + _parentSprite->runAction(RepeatForever::create(Sequence::create(scaleTo, scaleBack, nullptr))); auto normal = Sprite::create("Images/YellowSquare.png"); normal->setPosition(300, 100); normal->setScale(0.25, 0.5); - auto size = parent->getContentSize(); + auto size = _parentSprite->getContentSize(); size.width *= normal->getScaleX(); size.height *= normal->getScaleY(); normal->setPhysicsBody(PhysicsBody::createBox(size, PhysicsMaterial(0.1f, 1.0f, 0.0f))); normal->getPhysicsBody()->setTag(DRAG_BODYS_TAG); - addChild(normal); + _rootLayer->addChild(normal); auto bullet = Sprite::create("Images/ball.png"); bullet->setPosition(200, 200); bullet->setPhysicsBody(PhysicsBody::createCircle(bullet->getContentSize().width/2, PhysicsMaterial(0.1f, 1.0f, 0.0f))); bullet->getPhysicsBody()->setVelocity(Vect(100, 100)); - this->addChild(bullet); + _rootLayer->addChild(bullet); MoveBy* move = MoveBy::create(2.0f, Vec2(100, 100)); @@ -1781,9 +1783,9 @@ void PhysicsTransformTest::onEnter() RotateBy* rotate = RotateBy::create(6.0f, 360); - this->runAction(RepeatForever::create(Sequence::create(move, move2, move3, nullptr))); - this->runAction(RepeatForever::create(Sequence::create(scale, scale2, nullptr))); - this->runAction(RepeatForever::create(rotate)); + _rootLayer->runAction(RepeatForever::create(Sequence::create(move, move2, move3, nullptr))); + _rootLayer->runAction(RepeatForever::create(Sequence::create(scale, scale2, nullptr))); + _rootLayer->runAction(RepeatForever::create(rotate)); } std::string PhysicsTransformTest::title() const diff --git a/tests/cpp-tests/Classes/PhysicsTest/PhysicsTest.h b/tests/cpp-tests/Classes/PhysicsTest/PhysicsTest.h index f9f91ebe8e..0e3646c9fd 100644 --- a/tests/cpp-tests/Classes/PhysicsTest/PhysicsTest.h +++ b/tests/cpp-tests/Classes/PhysicsTest/PhysicsTest.h @@ -259,6 +259,9 @@ public: bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event); +private: + cocos2d::Sprite* _parentSprite; + cocos2d::Layer* _rootLayer; }; class PhysicsIssue9959 : public PhysicsDemo