diff --git a/cocos/2d/CCNode.cpp b/cocos/2d/CCNode.cpp index 49472cf822..69a3454cac 100644 --- a/cocos/2d/CCNode.cpp +++ b/cocos/2d/CCNode.cpp @@ -419,7 +419,9 @@ void Node::setPosition(const Point& position) #if CC_USE_PHYSICS if (_physicsBody) { - _physicsBody->setPosition(position); + Node* parent = getParent(); + Point pos = parent != nullptr ? parent->convertToWorldSpace(getPosition()) : getPosition(); + _physicsBody->setPosition(pos); } #endif } @@ -534,6 +536,14 @@ const Point& Node::getAnchorPoint() const void Node::setAnchorPoint(const Point& point) { +#if CC_USE_PHYSICS + if (_physicsBody != nullptr && point.equals(Point::ANCHOR_MIDDLE)) + { + CCLOG("Node warning: This node has a physics body, the anchor must be in the middle, you cann't change this to other value."); + return; + } +#endif + if( ! point.equals(_anchorPoint)) { _anchorPoint = point; @@ -719,6 +729,11 @@ void Node::addChild(Node *child, int zOrder, int tag) this->insertChild(child, zOrder); #if CC_USE_PHYSICS + if (child->getPhysicsBody() != nullptr) + { + child->getPhysicsBody()->setPosition(this->convertToWorldSpace(child->getPosition())); + } + for (Node* node = this->getParent(); node != nullptr; node = node->getParent()) { if (dynamic_cast(node) != nullptr) @@ -1511,6 +1526,14 @@ void Node::setPhysicsBody(PhysicsBody* body) { body->_node = this; body->retain(); + + // physics rotation based on body position, but node rotation based on node anthor point + // it cann't support both of them, so I clear the anthor point to default. + if (!getAnchorPoint().equals(Point::ANCHOR_MIDDLE)) + { + CCLOG("Node warning: setPhysicsBody sets anchor point to Point::ANCHOR_MIDDLE."); + setAnchorPoint(Point::ANCHOR_MIDDLE); + } } if (_physicsBody != nullptr) @@ -1529,7 +1552,9 @@ void Node::setPhysicsBody(PhysicsBody* body) _physicsBody = body; if (body != nullptr) { - _physicsBody->setPosition(getPosition()); + Node* parent = getParent(); + Point pos = parent != nullptr ? parent->convertToWorldSpace(getPosition()) : getPosition(); + _physicsBody->setPosition(pos); _physicsBody->setRotation(getRotation()); } } diff --git a/cocos/2d/CCNode.h b/cocos/2d/CCNode.h index 1d00884cda..bd88ec0b2f 100644 --- a/cocos/2d/CCNode.h +++ b/cocos/2d/CCNode.h @@ -449,6 +449,7 @@ public: * The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner. * But you can use values higher than (1,1) and lower than (0,0) too. * The default anchorPoint is (0.5,0.5), so it starts in the center of the node. + * @note If node has a physics body, the anchor must be in the middle, you cann't change this to other value. * * @param anchorPoint The anchor point of node. */ @@ -1432,6 +1433,7 @@ public: #if CC_USE_PHYSICS /** * set the PhysicsBody that let the sprite effect with physics + * @note This method will set anchor point to Point::ANCHOR_MIDDLE if body not null, and you cann't change anchor point if node has a physics body. */ void setPhysicsBody(PhysicsBody* body); diff --git a/cocos/physics/CCPhysicsBody.cpp b/cocos/physics/CCPhysicsBody.cpp index 46b2ab4637..85a2436292 100644 --- a/cocos/physics/CCPhysicsBody.cpp +++ b/cocos/physics/CCPhysicsBody.cpp @@ -75,6 +75,7 @@ PhysicsBody::PhysicsBody() , _group(0) , _positionResetTag(false) , _rotationResetTag(false) +, _rotationOffset(0) { } @@ -347,7 +348,7 @@ void PhysicsBody::setPosition(Point position) { if (!_positionResetTag) { - cpBodySetPos(_info->getBody(), PhysicsHelper::point2cpv(position)); + cpBodySetPos(_info->getBody(), PhysicsHelper::point2cpv(position + _positionOffset)); } _positionResetTag = false; } @@ -356,7 +357,7 @@ void PhysicsBody::setRotation(float rotation) { if (!_rotationResetTag) { - cpBodySetAngle(_info->getBody(), -PhysicsHelper::float2cpfloat(rotation * M_PI / 180.0f)); + cpBodySetAngle(_info->getBody(), -PhysicsHelper::float2cpfloat((rotation + _rotationOffset) * (M_PI / 180.0f))); } _rotationResetTag = false; @@ -365,12 +366,12 @@ void PhysicsBody::setRotation(float rotation) Point PhysicsBody::getPosition() const { cpVect vec = cpBodyGetPos(_info->getBody()); - return PhysicsHelper::cpv2point(vec); + return PhysicsHelper::cpv2point(vec) - _positionOffset; } float PhysicsBody::getRotation() const { - return -PhysicsHelper::cpfloat2float(cpBodyGetAngle(_info->getBody()) / M_PI * 180.0f); + return -PhysicsHelper::cpfloat2float(cpBodyGetAngle(_info->getBody()) * (180.0f / M_PI)) - _rotationOffset; } PhysicsShape* PhysicsBody::addShape(PhysicsShape* shape, bool addMassAndMoment/* = true*/) @@ -767,27 +768,18 @@ void PhysicsBody::setResting(bool rest) const void PhysicsBody::update(float delta) { - if (_node != nullptr && _dynamic && !isResting()) + if (_node != nullptr) { - cpVect pos = cpBodyGetPos(_info->getBody()); - cpVect prePos = _info->getPosition(); - cpVect rot = cpBodyGetRot(_info->getBody()); - cpVect preRot = _info->getRotation(); + Node* parent = _node->getParent(); - // only reset the node position when body position/rotation changed. - if (std::abs(pos.x - prePos.x) >= 0.3f || std::abs(pos.y - prePos.y) >= 0.3f - || std::abs(rot.x - preRot.x) >= 0.01f || std::abs(rot.y - preRot.y) >= 0.01f) - { - _positionResetTag = true; - _rotationResetTag = true; - _node->setPosition(getPosition()); - _info->setPosition(pos); - _node->setRotation(getRotation()); - _info->setRotation(rot); - } + Point position = parent != nullptr ? parent->convertToNodeSpace(getPosition()) : getPosition(); + _positionResetTag = true; + _rotationResetTag = true; + _node->setPosition(position); + _node->setRotation(getRotation()); // damping compute - if (_isDamping) + if (_isDamping && _dynamic && !isResting()) { _info->getBody()->v.x *= cpfclamp(1.0f - delta * _linearDamping, 0.0f, 1.0f); _info->getBody()->v.y *= cpfclamp(1.0f - delta * _linearDamping, 0.0f, 1.0f); @@ -834,6 +826,46 @@ void PhysicsBody::setGroup(int group) } } +void PhysicsBody::setPositionOffset(const Point& position) +{ + if (!_positionOffset.equals(position)) + { + Point pos = getPosition(); + + _positionOffset = position; + + if (_node!= nullptr) + { + setPosition(pos); + } + } +} + +Point PhysicsBody::getPositionOffset() +{ + return _positionOffset; +} + +void PhysicsBody::setRotationOffset(float rotation) +{ + if (std::abs(_rotationOffset - rotation) > 0.5f) + { + float rot = getRotation(); + + _rotationOffset = rotation; + + if (_node != nullptr) + { + setRotation(rot); + } + } +} + +float PhysicsBody::getRotationOffset() +{ + return _rotationOffset; +} + Point PhysicsBody::world2Local(const Point& point) { return PhysicsHelper::cpv2point(cpBodyWorld2Local(_info->getBody(), PhysicsHelper::point2cpv(point))); diff --git a/cocos/physics/CCPhysicsBody.h b/cocos/physics/CCPhysicsBody.h index ea7a80aba5..4f4f9a2425 100644 --- a/cocos/physics/CCPhysicsBody.h +++ b/cocos/physics/CCPhysicsBody.h @@ -192,6 +192,15 @@ public: /** get the body rotation. */ float getRotation() const; + /** set body position offset, it's the position witch relative to node */ + void setPositionOffset(const Point& position); + /** get body position offset. */ + Point getPositionOffset(); + /** set body rotation offset, it's the rotation witch relative to node */ + void setRotationOffset(float rotation); + /** set the body rotation offset */ + float getRotationOffset(); + /** * @brief test the body is dynamic or not. * a dynamic body will effect with gravity. @@ -332,6 +341,8 @@ protected: bool _positionResetTag; /// To avoid reset the body position when body invoke Node::setPosition(). bool _rotationResetTag; /// To avoid reset the body rotation when body invoke Node::setRotation(). + Point _positionOffset; + float _rotationOffset; friend class PhysicsWorld; friend class PhysicsShape;