mirror of https://github.com/axmolengine/axmol.git
1963 lines
70 KiB
C++
1963 lines
70 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
https://axis-project.github.io/
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "PhysicsTest.h"
|
|
|
|
#if AX_USE_PHYSICS
|
|
|
|
# include <cmath>
|
|
# include "ui/CocosGUI.h"
|
|
# include "../testResource.h"
|
|
|
|
USING_NS_AX;
|
|
|
|
PhysicsTests::PhysicsTests()
|
|
{
|
|
ADD_TEST_CASE(PhysicsDemoLogoSmash);
|
|
ADD_TEST_CASE(PhysicsDemoPyramidStack);
|
|
ADD_TEST_CASE(PhysicsDemoClickAdd);
|
|
ADD_TEST_CASE(PhysicsDemoRayCast);
|
|
ADD_TEST_CASE(PhysicsDemoActions);
|
|
ADD_TEST_CASE(PhysicsDemoJoints);
|
|
ADD_TEST_CASE(PhysicsDemoPump);
|
|
ADD_TEST_CASE(PhysicsDemoOneWayPlatform);
|
|
ADD_TEST_CASE(PhysicsDemoSlice);
|
|
ADD_TEST_CASE(PhysicsDemoBug3988);
|
|
ADD_TEST_CASE(PhysicsContactTest);
|
|
ADD_TEST_CASE(PhysicsPositionRotationTest);
|
|
ADD_TEST_CASE(PhysicsSetGravityEnableTest);
|
|
ADD_TEST_CASE(PhysicsDemoBug5482);
|
|
ADD_TEST_CASE(PhysicsFixedUpdate);
|
|
ADD_TEST_CASE(PhysicsTransformTest);
|
|
ADD_TEST_CASE(PhysicsIssue9959);
|
|
ADD_TEST_CASE(PhysicsIssue15932);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
Color4F STATIC_COLOR(1.0f, 0.0f, 0.0f, 1.0f);
|
|
const int DRAG_BODYS_TAG = 0x80;
|
|
} // namespace
|
|
|
|
void PhysicsDemo::toggleDebug()
|
|
{
|
|
# if AX_USE_PHYSICS
|
|
_debugDraw = !_debugDraw;
|
|
_physicsWorld->setDebugDrawMask(_debugDraw ? PhysicsWorld::DEBUGDRAW_ALL : PhysicsWorld::DEBUGDRAW_NONE);
|
|
# endif
|
|
}
|
|
|
|
PhysicsDemo::PhysicsDemo() : _spriteTexture(nullptr), _ball(nullptr), _debugDraw(false) {}
|
|
|
|
bool PhysicsDemo::init()
|
|
{
|
|
TestCase::init();
|
|
return initWithPhysics();
|
|
}
|
|
|
|
PhysicsDemo::~PhysicsDemo() {}
|
|
|
|
std::string PhysicsDemo::title() const
|
|
{
|
|
return "PhysicsComponentTest";
|
|
}
|
|
|
|
void PhysicsDemo::onEnter()
|
|
{
|
|
TestCase::onEnter();
|
|
|
|
_spriteTexture = SpriteBatchNode::create("Images/grossini_dance_atlas.png", 100)->getTexture();
|
|
|
|
// menu for debug layer
|
|
MenuItemFont::setFontSize(18);
|
|
auto item = MenuItemFont::create("Toggle debug", AX_CALLBACK_1(PhysicsDemo::toggleDebugCallback, this));
|
|
|
|
auto menu = Menu::create(item, nullptr);
|
|
this->addChild(menu);
|
|
menu->setPosition(Vec2(VisibleRect::right().x - item->getContentSize().width / 2 - 10,
|
|
VisibleRect::top().y - item->getContentSize().height / 2 - 10));
|
|
}
|
|
|
|
Sprite* PhysicsDemo::addGrossiniAtPosition(Vec2 p, float scale /* = 1.0*/)
|
|
{
|
|
AXLOG("Add sprite %0.2f x %02.f", p.x, p.y);
|
|
|
|
int posx, posy;
|
|
|
|
posx = AXRANDOM_0_1() * 200.0f;
|
|
posy = AXRANDOM_0_1() * 200.0f;
|
|
|
|
posx = (posx % 4) * 85;
|
|
posy = (posy % 3) * 121;
|
|
|
|
auto sp = Sprite::createWithTexture(_spriteTexture, Rect(posx, posy, 85, 121));
|
|
|
|
sp->setScale(scale);
|
|
sp->setPosition(p);
|
|
sp->addComponent(PhysicsBody::createBox(Size(48.0f, 108.0f)));
|
|
this->addChild(sp);
|
|
|
|
return sp;
|
|
}
|
|
|
|
void PhysicsDemo::toggleDebugCallback(Ref* /*sender*/)
|
|
{
|
|
toggleDebug();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
const int LOGO_WIDTH = 188;
|
|
const int LOGO_HEIGHT = 35;
|
|
const int LOGO_RAW_LENGTH = 24;
|
|
const int LOGO_IMAGE[] = {
|
|
15, -16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
7, -64, 15, 63, -32, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 31, -64, 15, 127, -125, -1, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 127, -64, 15, 127, 15, -1, -64, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -64, 15, -2, 31, -1, -64, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -64, 0, -4, 63, -1, -32, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -64, 15, -8, 127, -1,
|
|
-32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -64, 0,
|
|
-8, -15, -1, -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -31,
|
|
-1, -64, 15, -8, -32, -1, -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 7, -15, -1, -64, 9, -15, -32, -1, -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 31, -15, -1, -64, 0, -15, -32, -1, -32, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 63, -7, -1, -64, 9, -29, -32, 127, -61, -16, 63, 15, -61, -1,
|
|
-8, 31, -16, 15, -8, 126, 7, -31, -8, 31, -65, -7, -1, -64, 9, -29, -32, 0, 7, -8, 127,
|
|
-97, -25, -1, -2, 63, -8, 31, -4, -1, 15, -13, -4, 63, -1, -3, -1, -64, 9, -29, -32, 0,
|
|
7, -8, 127, -97, -25, -1, -2, 63, -8, 31, -4, -1, 15, -13, -2, 63, -1, -3, -1, -64, 9,
|
|
-29, -32, 0, 7, -8, 127, -97, -25, -1, -1, 63, -4, 63, -4, -1, 15, -13, -2, 63, -33, -1,
|
|
-1, -32, 9, -25, -32, 0, 7, -8, 127, -97, -25, -1, -1, 63, -4, 63, -4, -1, 15, -13, -1,
|
|
63, -33, -1, -1, -16, 9, -25, -32, 0, 7, -8, 127, -97, -25, -1, -1, 63, -4, 63, -4, -1,
|
|
15, -13, -1, 63, -49, -1, -1, -8, 9, -57, -32, 0, 7, -8, 127, -97, -25, -8, -1, 63, -2,
|
|
127, -4, -1, 15, -13, -1, -65, -49, -1, -1, -4, 9, -57, -32, 0, 7, -8, 127, -97, -25, -8,
|
|
-1, 63, -2, 127, -4, -1, 15, -13, -1, -65, -57, -1, -1, -2, 9, -57, -32, 0, 7, -8, 127,
|
|
-97, -25, -8, -1, 63, -2, 127, -4, -1, 15, -13, -1, -1, -57, -1, -1, -1, 9, -57, -32, 0,
|
|
7, -1, -1, -97, -25, -8, -1, 63, -1, -1, -4, -1, 15, -13, -1, -1, -61, -1, -1, -1, -119,
|
|
-57, -32, 0, 7, -1, -1, -97, -25, -8, -1, 63, -1, -1, -4, -1, 15, -13, -1, -1, -61, -1,
|
|
-1, -1, -55, -49, -32, 0, 7, -1, -1, -97, -25, -8, -1, 63, -1, -1, -4, -1, 15, -13, -1,
|
|
-1, -63, -1, -1, -1, -23, -49, -32, 127, -57, -1, -1, -97, -25, -1, -1, 63, -1, -1, -4, -1,
|
|
15, -13, -1, -1, -63, -1, -1, -1, -16, -49, -32, -1, -25, -1, -1, -97, -25, -1, -1, 63, -33,
|
|
-5, -4, -1, 15, -13, -1, -1, -64, -1, -9, -1, -7, -49, -32, -1, -25, -8, 127, -97, -25, -1,
|
|
-1, 63, -33, -5, -4, -1, 15, -13, -1, -1, -64, -1, -13, -1, -32, -49, -32, -1, -25, -8, 127,
|
|
-97, -25, -1, -2, 63, -49, -13, -4, -1, 15, -13, -1, -1, -64, 127, -7, -1, -119, -17, -15, -1,
|
|
-25, -8, 127, -97, -25, -1, -2, 63, -49, -13, -4, -1, 15, -13, -3, -1, -64, 127, -8, -2, 15,
|
|
-17, -1, -1, -25, -8, 127, -97, -25, -1, -8, 63, -49, -13, -4, -1, 15, -13, -3, -1, -64, 63,
|
|
-4, 120, 0, -17, -1, -1, -25, -8, 127, -97, -25, -8, 0, 63, -57, -29, -4, -1, 15, -13, -4,
|
|
-1, -64, 63, -4, 0, 15, -17, -1, -1, -25, -8, 127, -97, -25, -8, 0, 63, -57, -29, -4, -1,
|
|
-1, -13, -4, -1, -64, 31, -2, 0, 0, 103, -1, -1, -57, -8, 127, -97, -25, -8, 0, 63, -57,
|
|
-29, -4, -1, -1, -13, -4, 127, -64, 31, -2, 0, 15, 103, -1, -1, -57, -8, 127, -97, -25, -8,
|
|
0, 63, -61, -61, -4, 127, -1, -29, -4, 127, -64, 15, -8, 0, 0, 55, -1, -1, -121, -8, 127,
|
|
-97, -25, -8, 0, 63, -61, -61, -4, 127, -1, -29, -4, 63, -64, 15, -32, 0, 0, 23, -1, -2,
|
|
3, -16, 63, 15, -61, -16, 0, 31, -127, -127, -8, 31, -1, -127, -8, 31, -128, 7, -128, 0, 0};
|
|
|
|
int getPixel(int x, int y)
|
|
{
|
|
return (LOGO_IMAGE[(x >> 3) + y * LOGO_RAW_LENGTH] >> (~x & 0x7)) & 1;
|
|
}
|
|
|
|
float frand()
|
|
{
|
|
return rand() / RAND_MAX;
|
|
}
|
|
} // namespace
|
|
|
|
Sprite* PhysicsDemo::makeBall(Vec2 point, float radius, PhysicsMaterial material)
|
|
{
|
|
Sprite* ball = nullptr;
|
|
if (_ball != nullptr)
|
|
ball = Sprite::createWithTexture(_ball->getTexture());
|
|
else
|
|
ball = Sprite::create("Images/ball.png");
|
|
|
|
ball->setScale(0.13f * radius);
|
|
|
|
ball->addComponent(PhysicsBody::createCircle(ball->getContentSize().width / 2, material));
|
|
ball->setPosition(Vec2(point.x, point.y));
|
|
|
|
return ball;
|
|
}
|
|
|
|
Sprite* PhysicsDemo::makeBox(Vec2 point, Size size, int color, PhysicsMaterial material)
|
|
{
|
|
bool yellow = false;
|
|
if (color == 0)
|
|
{
|
|
yellow = AXRANDOM_0_1() > 0.5f;
|
|
}
|
|
else
|
|
{
|
|
yellow = color == 1;
|
|
}
|
|
|
|
auto box = yellow ? Sprite::create("Images/YellowSquare.png") : Sprite::create("Images/CyanSquare.png");
|
|
box->setScaleX(size.width / 100.0f);
|
|
box->setScaleY(size.height / 100.0f);
|
|
|
|
box->addComponent(PhysicsBody::createBox(box->getContentSize(), material));
|
|
|
|
box->setPosition(Vec2(point.x, point.y));
|
|
|
|
return box;
|
|
}
|
|
|
|
Sprite* PhysicsDemo::makeTriangle(Vec2 point, Size size, int color, PhysicsMaterial material)
|
|
{
|
|
bool yellow = false;
|
|
if (color == 0)
|
|
{
|
|
yellow = AXRANDOM_0_1() > 0.5f;
|
|
}
|
|
else
|
|
{
|
|
yellow = color == 1;
|
|
}
|
|
|
|
auto triangle = yellow ? Sprite::create("Images/YellowTriangle.png") : Sprite::create("Images/CyanTriangle.png");
|
|
|
|
if (size.height == 0)
|
|
{
|
|
triangle->setScale(size.width / 100.0f);
|
|
}
|
|
else
|
|
{
|
|
triangle->setScaleX(size.width / 50.0f);
|
|
triangle->setScaleY(size.height / 43.5f);
|
|
}
|
|
|
|
Vec2 vers[] = {Vec2(0.0f, triangle->getContentSize().height / 2),
|
|
Vec2(triangle->getContentSize().width / 2, -triangle->getContentSize().height / 2),
|
|
Vec2(-triangle->getContentSize().width / 2, -triangle->getContentSize().height / 2)};
|
|
|
|
triangle->addComponent(PhysicsBody::createPolygon(vers, 3, material));
|
|
triangle->setPosition(Vec2(point.x, point.y));
|
|
|
|
return triangle;
|
|
}
|
|
|
|
bool PhysicsDemo::onTouchBegan(Touch* touch, Event* event)
|
|
{
|
|
auto location = touch->getLocation();
|
|
auto arr = _physicsWorld->getShapes(location);
|
|
|
|
PhysicsBody* body = nullptr;
|
|
for (auto& obj : arr)
|
|
{
|
|
if ((obj->getBody()->getTag() & DRAG_BODYS_TAG) != 0)
|
|
{
|
|
body = obj->getBody();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (body != nullptr)
|
|
{
|
|
Node* mouse = Node::create();
|
|
auto physicsBody = PhysicsBody::create(PHYSICS_INFINITY, PHYSICS_INFINITY);
|
|
physicsBody->setDynamic(false);
|
|
mouse->addComponent(physicsBody);
|
|
mouse->setPosition(location);
|
|
this->addChild(mouse);
|
|
PhysicsJointPin* joint = PhysicsJointPin::construct(physicsBody, body, location);
|
|
joint->setMaxForce(5000.0f * body->getMass());
|
|
_physicsWorld->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->setPosition(touch->getLocation());
|
|
}
|
|
}
|
|
|
|
void PhysicsDemo::onTouchEnded(Touch* touch, Event* /*event*/)
|
|
{
|
|
auto it = _mouses.find(touch->getID());
|
|
|
|
if (it != _mouses.end())
|
|
{
|
|
this->removeChild(it->second);
|
|
_mouses.erase(it);
|
|
}
|
|
}
|
|
|
|
// Implementation of PhysicsComponentDemoLogoSmash
|
|
|
|
void PhysicsDemoLogoSmash::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
_physicsWorld->setGravity(Vec2(0.0f, 0.0f));
|
|
_physicsWorld->setUpdateRate(1);
|
|
|
|
_ball = SpriteBatchNode::create("Images/ball.png", sizeof(LOGO_IMAGE) / sizeof(LOGO_IMAGE[0]));
|
|
addChild(_ball);
|
|
for (int y = 0; y < LOGO_HEIGHT; ++y)
|
|
{
|
|
for (int x = 0; x < LOGO_WIDTH; ++x)
|
|
{
|
|
if (getPixel(x, y))
|
|
{
|
|
float xJitter = 0.05 * frand();
|
|
float yJitter = 0.05 * frand();
|
|
|
|
Node* ball =
|
|
makeBall(Vec2(2 * (x - LOGO_WIDTH / 2 + xJitter) + VisibleRect::getVisibleRect().size.width / 2,
|
|
2 * (LOGO_HEIGHT - y + yJitter) + VisibleRect::getVisibleRect().size.height / 2 -
|
|
LOGO_HEIGHT / 2),
|
|
0.95f, PhysicsMaterial(0.01f, 0.0f, 0.0f));
|
|
|
|
auto physicsBody = ball->getPhysicsBody();
|
|
physicsBody->setMass(1.0);
|
|
physicsBody->setMoment(PHYSICS_INFINITY);
|
|
|
|
_ball->addChild(ball);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto bullet = makeBall(Vec2(400.0f, 0.0f), 10, PhysicsMaterial(PHYSICS_INFINITY, 0, 0));
|
|
bullet->getPhysicsBody()->setVelocity(Vec2(200.0f, 0.0f));
|
|
|
|
bullet->setPosition(Vec2(-100.0f, VisibleRect::getVisibleRect().size.height / 2));
|
|
|
|
_ball->addChild(bullet);
|
|
}
|
|
|
|
std::string PhysicsDemoLogoSmash::title() const
|
|
{
|
|
return "Logo Smash";
|
|
}
|
|
|
|
// Implementation of PhysicsComponentDemoClickAdd
|
|
|
|
PhysicsDemoClickAdd::~PhysicsDemoClickAdd()
|
|
{
|
|
Device::setAccelerometerEnabled(false);
|
|
}
|
|
|
|
void PhysicsDemoClickAdd::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
auto touchListener = EventListenerTouchAllAtOnce::create();
|
|
touchListener->onTouchesEnded = AX_CALLBACK_2(PhysicsDemoClickAdd::onTouchesEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
Device::setAccelerometerEnabled(true);
|
|
auto accListener = EventListenerAcceleration::create(AX_CALLBACK_2(PhysicsDemoClickAdd::onAcceleration, this));
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(accListener, this);
|
|
|
|
auto node = Node::create();
|
|
node->addComponent(PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size));
|
|
node->setPosition(VisibleRect::center());
|
|
this->addChild(node);
|
|
|
|
addGrossiniAtPosition(VisibleRect::center());
|
|
}
|
|
|
|
std::string PhysicsDemoClickAdd::subtitle() const
|
|
{
|
|
return "multi touch to add grossini";
|
|
}
|
|
|
|
void PhysicsDemoClickAdd::onTouchesEnded(const std::vector<Touch*>& touches, Event* /*event*/)
|
|
{
|
|
// Add a new body/atlas sprite at the touched location
|
|
|
|
for (auto touch : touches)
|
|
{
|
|
auto location = touch->getLocation();
|
|
|
|
addGrossiniAtPosition(location);
|
|
}
|
|
}
|
|
|
|
void PhysicsDemoClickAdd::onAcceleration(Acceleration* acc, Event* /*event*/)
|
|
{
|
|
static float prevX = 0, prevY = 0;
|
|
|
|
# define FILTER_FACTOR 0.05f
|
|
|
|
float accelX = (float)acc->x * FILTER_FACTOR + (1 - FILTER_FACTOR) * prevX;
|
|
float accelY = (float)acc->y * FILTER_FACTOR + (1 - FILTER_FACTOR) * prevY;
|
|
|
|
prevX = accelX;
|
|
prevY = accelY;
|
|
|
|
auto v = Vec2(accelX, accelY);
|
|
v = v * 200;
|
|
|
|
_physicsWorld->setGravity(v);
|
|
}
|
|
|
|
void PhysicsDemoPyramidStack::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = AX_CALLBACK_2(PhysicsDemoPyramidStack::onTouchBegan, this);
|
|
touchListener->onTouchMoved = AX_CALLBACK_2(PhysicsDemoPyramidStack::onTouchMoved, this);
|
|
touchListener->onTouchEnded = AX_CALLBACK_2(PhysicsDemoPyramidStack::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
auto node = Node::create();
|
|
node->addComponent(PhysicsBody::createEdgeSegment(VisibleRect::leftBottom() + Vec2(0.0f, 50.0f),
|
|
VisibleRect::rightBottom() + Vec2(0.0f, 50.0f)));
|
|
this->addChild(node);
|
|
|
|
auto ball = Sprite::create("Images/ball.png");
|
|
ball->setScale(1);
|
|
ball->setTag(100);
|
|
auto body = PhysicsBody::createCircle(10);
|
|
ball->addComponent(body);
|
|
body->setTag(DRAG_BODYS_TAG);
|
|
ball->setPosition(VisibleRect::bottom() + Vec2(0.0f, 60.0f));
|
|
this->addChild(ball);
|
|
|
|
scheduleOnce(AX_SCHEDULE_SELECTOR(PhysicsDemoPyramidStack::updateOnce), 3.0);
|
|
|
|
for (int i = 0; i < 14; i++)
|
|
{
|
|
for (int j = 0; j <= i; j++)
|
|
{
|
|
auto sp = addGrossiniAtPosition(VisibleRect::bottom() + Vec2((i / 2 - j) * 11, (14 - i) * 23 + 100), 0.2f);
|
|
sp->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PhysicsDemoPyramidStack::updateOnce(float /*delta*/)
|
|
{
|
|
auto ball = getChildByTag(100);
|
|
if (ball)
|
|
ball->setScale(ball->getScale() * 3);
|
|
}
|
|
|
|
std::string PhysicsDemoPyramidStack::title() const
|
|
{
|
|
return "Pyramid Stack";
|
|
}
|
|
|
|
PhysicsDemoRayCast::PhysicsDemoRayCast() : _angle(0.0f), _node(nullptr), _mode(0) {}
|
|
|
|
void PhysicsDemoRayCast::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
auto listener = EventListenerTouchAllAtOnce::create();
|
|
listener->onTouchesEnded = AX_CALLBACK_2(PhysicsDemoRayCast::onTouchesEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
|
|
|
|
_physicsWorld->setGravity(Point::ZERO);
|
|
|
|
auto node = DrawNode::create();
|
|
node->addComponent(PhysicsBody::createEdgeSegment(VisibleRect::leftBottom() + Vec2(0.0f, 50.0f),
|
|
VisibleRect::rightBottom() + Vec2(0.0f, 50.0f), PHYSICSBODY_MATERIAL_DEFAULT, 0.5f));
|
|
node->drawSegment(VisibleRect::leftBottom() + Vec2(0.0f, 50.0f), VisibleRect::rightBottom() + Vec2(0.0f, 50.0f), 0.5f, STATIC_COLOR);
|
|
this->addChild(node);
|
|
|
|
MenuItemFont::setFontSize(18);
|
|
auto item = MenuItemFont::create("Change Mode(any)", AX_CALLBACK_1(PhysicsDemoRayCast::changeModeCallback, this));
|
|
|
|
auto menu = Menu::create(item, nullptr);
|
|
this->addChild(menu);
|
|
menu->setPosition(Vec2(VisibleRect::left().x + 100, VisibleRect::top().y - 10));
|
|
|
|
scheduleUpdate();
|
|
}
|
|
|
|
void PhysicsDemoRayCast::changeModeCallback(Ref* sender)
|
|
{
|
|
_mode = (_mode + 1) % 3;
|
|
|
|
switch (_mode)
|
|
{
|
|
case 0:
|
|
((MenuItemFont*)sender)->setString("Change Mode(any)");
|
|
break;
|
|
case 1:
|
|
((MenuItemFont*)sender)->setString("Change Mode(nearest)");
|
|
break;
|
|
case 2:
|
|
((MenuItemFont*)sender)->setString("Change Mode(multiple)");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool PhysicsDemoRayCast::anyRay(PhysicsWorld& /*world*/, const PhysicsRayCastInfo& info, void* data)
|
|
{
|
|
*((Vec2*)data) = info.contact;
|
|
return false;
|
|
}
|
|
|
|
void PhysicsDemoRayCast::update(float /*delta*/)
|
|
{
|
|
float L = 150.0f;
|
|
Vec2 point1 = VisibleRect::center();
|
|
Vec2 d(L * cosf(_angle), L * sinf(_angle));
|
|
Vec2 point2 = point1 + d;
|
|
|
|
removeChild(_node);
|
|
_node = DrawNode::create();
|
|
switch (_mode)
|
|
{
|
|
case 0:
|
|
{
|
|
Vec2 point3 = point2;
|
|
auto func = AX_CALLBACK_3(PhysicsDemoRayCast::anyRay, this);
|
|
|
|
_physicsWorld->rayCast(func, point1, point2, &point3);
|
|
_node->drawSegment(point1, point3, 0.5f, STATIC_COLOR);
|
|
|
|
if (point2 != point3)
|
|
{
|
|
_node->drawDot(point3, 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f));
|
|
}
|
|
addChild(_node);
|
|
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
Vec2 point3 = point2;
|
|
float friction = 1.0f;
|
|
PhysicsRayCastCallbackFunc func = [&point3, &friction](PhysicsWorld& /*world*/, const PhysicsRayCastInfo& info,
|
|
void* /*data*/) -> bool {
|
|
if (friction > info.fraction)
|
|
{
|
|
point3 = info.contact;
|
|
friction = info.fraction;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
_physicsWorld->rayCast(func, point1, point2, nullptr);
|
|
_node->drawSegment(point1, point3, 1, STATIC_COLOR);
|
|
|
|
if (point2 != point3)
|
|
{
|
|
_node->drawDot(point3, 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f));
|
|
}
|
|
addChild(_node);
|
|
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
# define MAX_MULTI_RAYCAST_NUM 5
|
|
Vec2 points[MAX_MULTI_RAYCAST_NUM];
|
|
int num = 0;
|
|
|
|
PhysicsRayCastCallbackFunc func = [&points, &num](PhysicsWorld& /*world*/, const PhysicsRayCastInfo& info,
|
|
void* /*data*/) -> bool {
|
|
if (num < MAX_MULTI_RAYCAST_NUM)
|
|
{
|
|
points[num++] = info.contact;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
_physicsWorld->rayCast(func, point1, point2, nullptr);
|
|
|
|
_node->drawSegment(point1, point2, 1, STATIC_COLOR);
|
|
|
|
for (int i = 0; i < num; ++i)
|
|
{
|
|
_node->drawDot(points[i], 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f));
|
|
}
|
|
|
|
addChild(_node);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_angle += 0.25f * (float)M_PI / 180.0f;
|
|
}
|
|
|
|
void PhysicsDemoRayCast::onTouchesEnded(const std::vector<Touch*>& touches, Event* /*event*/)
|
|
{
|
|
// Add a new body/atlas sprite at the touched location
|
|
|
|
for (auto& touch : touches)
|
|
{
|
|
auto location = touch->getLocation();
|
|
|
|
float r = AXRANDOM_0_1();
|
|
|
|
if (r < 1.0f / 3.0f)
|
|
{
|
|
addChild(makeBall(location, 5 + AXRANDOM_0_1() * 10));
|
|
}
|
|
else if (r < 2.0f / 3.0f)
|
|
{
|
|
addChild(makeBox(location, Size(10 + AXRANDOM_0_1() * 15, 10 + AXRANDOM_0_1() * 15)));
|
|
}
|
|
else
|
|
{
|
|
addChild(makeTriangle(location, Size(10 + AXRANDOM_0_1() * 20, 10 + AXRANDOM_0_1() * 20)));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string PhysicsDemoRayCast::title() const
|
|
{
|
|
return "Ray Cast";
|
|
}
|
|
|
|
void PhysicsDemoActions::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
_physicsWorld->setGravity(Vec2::ZERO);
|
|
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = AX_CALLBACK_2(PhysicsDemoActions::onTouchBegan, this);
|
|
touchListener->onTouchMoved = AX_CALLBACK_2(PhysicsDemoActions::onTouchMoved, this);
|
|
touchListener->onTouchEnded = AX_CALLBACK_2(PhysicsDemoActions::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
auto node = Node::create();
|
|
node->addComponent(PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size));
|
|
node->setPosition(VisibleRect::center());
|
|
this->addChild(node);
|
|
|
|
Sprite* sp1 = addGrossiniAtPosition(VisibleRect::center());
|
|
Sprite* sp2 = addGrossiniAtPosition(VisibleRect::left() + Vec2(50.0f, 0.0f));
|
|
Sprite* sp3 = addGrossiniAtPosition(VisibleRect::right() - Vec2(20.0f, 0.0f));
|
|
Sprite* sp4 = addGrossiniAtPosition(VisibleRect::leftTop() + Vec2(50.0f, -50.0f));
|
|
sp4->getPhysicsBody()->setGravityEnable(false);
|
|
|
|
sp1->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
sp2->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
sp3->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
sp4->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto actionTo = JumpTo::create(2, Vec2(100, 100), 50, 4);
|
|
auto actionBy = JumpBy::create(2, Vec2(300, 0), 50, 4);
|
|
auto actionUp = JumpBy::create(2, Vec2(0, 50), 80, 4);
|
|
auto actionByBack = actionBy->reverse();
|
|
auto rotateBy = RotateBy::create(2, 180);
|
|
auto rotateByBack = RotateBy::create(2, -180);
|
|
|
|
sp1->runAction(RepeatForever::create(actionUp));
|
|
sp2->runAction(RepeatForever::create(Sequence::create(actionBy, actionByBack, nullptr)));
|
|
sp3->runAction(actionTo);
|
|
sp4->runAction(RepeatForever::create(Sequence::create(rotateBy, rotateByBack, nullptr)));
|
|
}
|
|
|
|
std::string PhysicsDemoActions::title() const
|
|
{
|
|
return "Actions";
|
|
}
|
|
|
|
// implementation of PhysicsDemoJoints
|
|
|
|
void PhysicsDemoJoints::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
toggleDebug();
|
|
|
|
auto listener = EventListenerTouchOneByOne::create();
|
|
listener->onTouchBegan = AX_CALLBACK_2(PhysicsDemo::onTouchBegan, this);
|
|
listener->onTouchMoved = AX_CALLBACK_2(PhysicsDemo::onTouchMoved, this);
|
|
listener->onTouchEnded = AX_CALLBACK_2(PhysicsDemo::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
|
|
|
|
float width = (VisibleRect::getVisibleRect().size.width - 10) / 4;
|
|
float height = (VisibleRect::getVisibleRect().size.height - 50) / 4;
|
|
|
|
Node* node = Node::create();
|
|
PhysicsBody* box = PhysicsBody::create();
|
|
node->addComponent(box);
|
|
|
|
box->setDynamic(false);
|
|
node->setPosition(Point::ZERO);
|
|
this->addChild(node);
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
Vec2 offset(VisibleRect::leftBottom().x + 5 + j * width + width / 2,
|
|
VisibleRect::leftBottom().y + 50 + i * height + height / 2);
|
|
box->addShape(PhysicsShapeEdgeBox::create(Size(width, height), PHYSICSSHAPE_MATERIAL_DEFAULT, 0.5, offset));
|
|
|
|
switch (i * 4 + j)
|
|
{
|
|
case 0:
|
|
{
|
|
auto sp1 = makeBall(offset - Vec2(30, 0), 10);
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBall(offset + Vec2(30, 0), 10);
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
PhysicsJointPin* joint = PhysicsJointPin::construct(sp1PhysicsBody, sp2PhysicsBody, offset);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
|
|
auto sp1 = makeBall(offset - Vec2(30.0f, 0.0f), 10);
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
PhysicsJointFixed* joint = PhysicsJointFixed::construct(sp1PhysicsBody, sp2PhysicsBody, offset);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
auto sp1 = makeBall(offset - Vec2(30.0f, 0.0f), 10);
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
PhysicsJointDistance* joint =
|
|
PhysicsJointDistance::construct(sp1PhysicsBody, sp2PhysicsBody, Point::ZERO, Point::ZERO);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
auto sp1 = makeBall(offset - Vec2(30.0f, 0.0f), 10);
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
PhysicsJointLimit* joint = PhysicsJointLimit::construct(sp1PhysicsBody, sp2PhysicsBody, Point::ZERO,
|
|
Point::ZERO, 30.0f, 60.0f);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
auto sp1 = makeBall(offset - Vec2(30.0f, 0.0f), 10);
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
PhysicsJointSpring* joint = PhysicsJointSpring::construct(sp1PhysicsBody, sp2PhysicsBody, Point::ZERO,
|
|
Point::ZERO, 500.0f, 0.3f);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
auto sp1 = makeBall(offset - Vec2(30.0f, 0.0f), 10);
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
PhysicsJointGroove* joint = PhysicsJointGroove::construct(
|
|
sp1PhysicsBody, sp2PhysicsBody, Vec2(30.0f, 15.0f), Vec2(30.0f, -15.0f), Vec2(-30.0f, 0.0f));
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 6:
|
|
{
|
|
auto sp1 = makeBox(offset - Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1PhysicsBody, box, sp1->getPosition()));
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2PhysicsBody, box, sp2->getPosition()));
|
|
PhysicsJointRotarySpring* joint =
|
|
PhysicsJointRotarySpring::construct(sp1PhysicsBody, sp2PhysicsBody, 3000.0f, 60.0f);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 7:
|
|
{
|
|
auto sp1 = makeBox(offset - Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1PhysicsBody, box, sp1->getPosition()));
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2PhysicsBody, box, sp2->getPosition()));
|
|
PhysicsJointRotaryLimit* joint =
|
|
PhysicsJointRotaryLimit::construct(sp1PhysicsBody, sp2PhysicsBody, 0.0f, (float)M_PI_2);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 8:
|
|
{
|
|
auto sp1 = makeBox(offset - Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1PhysicsBody, box, sp1->getPosition()));
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2PhysicsBody, box, sp2->getPosition()));
|
|
PhysicsJointRatchet* joint =
|
|
PhysicsJointRatchet::construct(sp1PhysicsBody, sp2PhysicsBody, 0.0f, (float)M_PI_2);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 9:
|
|
{
|
|
auto sp1 = makeBox(offset - Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1PhysicsBody, box, sp1->getPosition()));
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2PhysicsBody, box, sp2->getPosition()));
|
|
PhysicsJointGear* joint = PhysicsJointGear::construct(sp1PhysicsBody, sp2PhysicsBody, 0.0f, 2.0f);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
case 10:
|
|
{
|
|
auto sp1 = makeBox(offset - Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp1PhysicsBody = sp1->getPhysicsBody();
|
|
sp1PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
auto sp2 = makeBox(offset + Vec2(30.0f, 0.0f), Size(30.0f, 10.0f));
|
|
auto sp2PhysicsBody = sp2->getPhysicsBody();
|
|
sp2PhysicsBody->setTag(DRAG_BODYS_TAG);
|
|
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1PhysicsBody, box, sp1->getPosition()));
|
|
getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2PhysicsBody, box, sp2->getPosition()));
|
|
PhysicsJointMotor* joint = PhysicsJointMotor::construct(sp1PhysicsBody, sp2PhysicsBody, (float)M_PI_2);
|
|
getPhysicsWorld()->addJoint(joint);
|
|
|
|
this->addChild(sp1);
|
|
this->addChild(sp2);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string PhysicsDemoJoints::title() const
|
|
{
|
|
return "Joints";
|
|
}
|
|
|
|
void PhysicsDemoPump::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
toggleDebug();
|
|
|
|
_distance = 0.0f;
|
|
_rotationV = 0.0f;
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = AX_CALLBACK_2(PhysicsDemoPump::onTouchBegan, this);
|
|
touchListener->onTouchMoved = AX_CALLBACK_2(PhysicsDemoPump::onTouchMoved, this);
|
|
touchListener->onTouchEnded = AX_CALLBACK_2(PhysicsDemoPump::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
scheduleUpdate();
|
|
|
|
auto node = Node::create();
|
|
auto nodeBody = PhysicsBody::create();
|
|
node->addComponent(nodeBody);
|
|
nodeBody->setDynamic(false);
|
|
|
|
PhysicsMaterial staticMaterial(PHYSICS_INFINITY, 0, 0.5f);
|
|
nodeBody->addShape(PhysicsShapeEdgeSegment::create(VisibleRect::leftTop() + Vec2(50, 0),
|
|
VisibleRect::leftTop() + Vec2(50, -130), staticMaterial, 2.0f));
|
|
nodeBody->addShape(PhysicsShapeEdgeSegment::create(VisibleRect::leftTop() + Vec2(190, 0),
|
|
VisibleRect::leftTop() + Vec2(100, -50), staticMaterial, 2.0f));
|
|
nodeBody->addShape(PhysicsShapeEdgeSegment::create(VisibleRect::leftTop() + Vec2(100, -50),
|
|
VisibleRect::leftTop() + Vec2(100, -90), staticMaterial, 2.0f));
|
|
nodeBody->addShape(PhysicsShapeEdgeSegment::create(VisibleRect::leftTop() + Vec2(50, -130),
|
|
VisibleRect::leftTop() + Vec2(100, -145), staticMaterial, 2.0f));
|
|
nodeBody->addShape(PhysicsShapeEdgeSegment::create(
|
|
VisibleRect::leftTop() + Vec2(100, -145), VisibleRect::leftBottom() + Vec2(100, 80), staticMaterial, 2.0f));
|
|
nodeBody->addShape(PhysicsShapeEdgeSegment::create(
|
|
VisibleRect::leftTop() + Vec2(150, -80), VisibleRect::leftBottom() + Vec2(150, 80), staticMaterial, 2.0f));
|
|
nodeBody->addShape(PhysicsShapeEdgeSegment::create(
|
|
VisibleRect::leftTop() + Vec2(150, -80), VisibleRect::rightTop() + Vec2(-100, -150), staticMaterial, 2.0f));
|
|
|
|
nodeBody->setCategoryBitmask(0x01);
|
|
node->setPosition(Vec2::ZERO);
|
|
this->addChild(node);
|
|
|
|
// balls
|
|
for (int i = 0; i < 6; ++i)
|
|
{
|
|
auto ball = makeBall(VisibleRect::leftTop() + Vec2(75 + AXRANDOM_0_1() * 90, 0.0f), 22,
|
|
PhysicsMaterial(0.05f, 0.0f, 0.1f));
|
|
ball->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
addChild(ball);
|
|
}
|
|
|
|
Vec2 vec[4] = {VisibleRect::leftTop() + Vec2(102, -148), VisibleRect::leftTop() + Vec2(148, -161),
|
|
VisibleRect::leftBottom() + Vec2(148, 20), VisibleRect::leftBottom() + Vec2(102, 20)};
|
|
|
|
// pump
|
|
auto pump = Node::create();
|
|
auto center = PhysicsShape::getPolygonCenter(vec, 4);
|
|
pump->setPosition(center);
|
|
auto pumpBody = PhysicsBody::createPolygon(vec, 4, PHYSICSBODY_MATERIAL_DEFAULT, -center);
|
|
pump->addComponent(pumpBody);
|
|
this->addChild(pump);
|
|
pumpBody->setCategoryBitmask(0x02);
|
|
pumpBody->setGravityEnable(false);
|
|
|
|
// small gear
|
|
auto sgearBody = PhysicsBody::createCircle(44);
|
|
sgearBody->setCategoryBitmask(0x04);
|
|
sgearBody->setCollisionBitmask(0x04);
|
|
sgearBody->setTag(1);
|
|
auto sgear = Node::create();
|
|
sgear->addComponent(sgearBody);
|
|
sgear->setPosition(VisibleRect::leftBottom() + Vec2(125.0f, 0.0f));
|
|
this->addChild(sgear);
|
|
|
|
_physicsWorld->addJoint(PhysicsJointPin::construct(nodeBody, sgearBody, sgear->getPosition()));
|
|
_physicsWorld->addJoint(PhysicsJointDistance::construct(pumpBody, sgearBody, Vec2(0.0f, 0.0f), Vec2(0.0f, -44.0f)));
|
|
|
|
// big gear
|
|
auto bgearBody = PhysicsBody::createCircle(100);
|
|
bgearBody->setCategoryBitmask(0x04);
|
|
auto bgear = Node::create();
|
|
bgear->addComponent(bgearBody);
|
|
bgear->setPosition(VisibleRect::leftBottom() + Vec2(275.0f, 0.0f));
|
|
this->addChild(bgear);
|
|
|
|
_physicsWorld->addJoint(PhysicsJointPin::construct(bgearBody, nodeBody, bgear->getPosition()));
|
|
_physicsWorld->addJoint(PhysicsJointGear::construct(sgearBody, bgearBody, (float)-M_PI_2, -2.0f));
|
|
|
|
// plugger
|
|
Vec2 seg[] = {VisibleRect::leftTop() + Vec2(75, -120), VisibleRect::leftBottom() + Vec2(75, -100)};
|
|
Vec2 segCenter = (seg[1] + seg[0]) / 2;
|
|
seg[1] -= segCenter;
|
|
seg[0] -= segCenter;
|
|
|
|
auto pluggerBody = PhysicsBody::createEdgeSegment(seg[0], seg[1], PhysicsMaterial(0.01f, 0.0f, 0.5f), 20);
|
|
pluggerBody->setDynamic(true);
|
|
pluggerBody->setMass(30);
|
|
pluggerBody->setMoment(100000);
|
|
pluggerBody->setCategoryBitmask(0x02);
|
|
auto plugger = Node::create();
|
|
plugger->addComponent(pluggerBody);
|
|
plugger->setPosition(segCenter);
|
|
this->addChild(plugger);
|
|
|
|
sgearBody->setCollisionBitmask(0x04 | 0x01);
|
|
_physicsWorld->addJoint(
|
|
PhysicsJointPin::construct(nodeBody, pluggerBody, VisibleRect::leftBottom() + Vec2(75.0f, -90.0f)));
|
|
_physicsWorld->addJoint(PhysicsJointDistance::construct(pluggerBody, sgearBody, Vec2::ZERO, Vec2(44.0f, 0.0f)));
|
|
}
|
|
|
|
void PhysicsDemoPump::update(float delta)
|
|
{
|
|
for (const auto& body : _physicsWorld->getAllBodies())
|
|
{
|
|
if (body->getTag() == DRAG_BODYS_TAG && body->getPosition().y < 0.0f)
|
|
{
|
|
if (body->getNode() != nullptr)
|
|
{
|
|
body->getNode()->setPosition(VisibleRect::leftTop() + Vec2(75 + AXRANDOM_0_1() * 90, 0.0f));
|
|
}
|
|
|
|
body->setVelocity(Vec2(0.0f, 0.0f));
|
|
}
|
|
}
|
|
|
|
PhysicsBody* gear = _physicsWorld->getBody(1);
|
|
|
|
if (gear != nullptr)
|
|
{
|
|
if (_distance != 0.0f)
|
|
{
|
|
_rotationV += _distance / 2500.0f;
|
|
|
|
if (_rotationV > 30)
|
|
_rotationV = 30.0f;
|
|
if (_rotationV < -30)
|
|
_rotationV = -30.0f;
|
|
}
|
|
|
|
gear->setAngularVelocity(_rotationV);
|
|
_rotationV *= 0.995f;
|
|
}
|
|
}
|
|
|
|
bool PhysicsDemoPump::onTouchBegan(Touch* touch, Event* event)
|
|
{
|
|
PhysicsDemo::onTouchBegan(touch, event);
|
|
|
|
_distance = touch->getLocation().x - VisibleRect::center().x;
|
|
|
|
return true;
|
|
}
|
|
|
|
void PhysicsDemoPump::onTouchMoved(Touch* touch, Event* event)
|
|
{
|
|
PhysicsDemo::onTouchMoved(touch, event);
|
|
|
|
_distance = touch->getLocation().x - VisibleRect::center().x;
|
|
}
|
|
|
|
void PhysicsDemoPump::onTouchEnded(Touch* touch, Event* event)
|
|
{
|
|
PhysicsDemo::onTouchEnded(touch, event);
|
|
|
|
_distance = 0;
|
|
}
|
|
|
|
std::string PhysicsDemoPump::title() const
|
|
{
|
|
return "Pump";
|
|
}
|
|
|
|
std::string PhysicsDemoPump::subtitle() const
|
|
{
|
|
return "touch screen on left or right";
|
|
}
|
|
|
|
void PhysicsDemoOneWayPlatform::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = AX_CALLBACK_2(PhysicsDemoOneWayPlatform::onTouchBegan, this);
|
|
touchListener->onTouchMoved = AX_CALLBACK_2(PhysicsDemoOneWayPlatform::onTouchMoved, this);
|
|
touchListener->onTouchEnded = AX_CALLBACK_2(PhysicsDemoOneWayPlatform::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
auto ground = Node::create();
|
|
ground->addComponent(PhysicsBody::createEdgeSegment(VisibleRect::leftBottom() + Vec2(0.0f, 50.0f),
|
|
VisibleRect::rightBottom() + Vec2(0.0f, 50.0f)));
|
|
this->addChild(ground);
|
|
|
|
auto platform = makeBox(VisibleRect::center(), Size(200.0f, 50.0f));
|
|
auto platformBody = platform->getPhysicsBody();
|
|
platformBody->setDynamic(false);
|
|
platformBody->setContactTestBitmask(0xFFFFFFFF);
|
|
this->addChild(platform);
|
|
|
|
auto ball = makeBall(VisibleRect::center() - Vec2(0.0f, 50.0f), 20);
|
|
auto ballBody = ball->getPhysicsBody();
|
|
ballBody->setVelocity(Vec2(0.0f, 150.0f));
|
|
ballBody->setTag(DRAG_BODYS_TAG);
|
|
ballBody->setMass(1.0f);
|
|
ballBody->setContactTestBitmask(0xFFFFFFFF);
|
|
this->addChild(ball);
|
|
|
|
auto contactListener = EventListenerPhysicsContactWithBodies::create(platformBody, ballBody);
|
|
contactListener->onContactBegin = AX_CALLBACK_1(PhysicsDemoOneWayPlatform::onContactBegin, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
|
|
}
|
|
|
|
bool PhysicsDemoOneWayPlatform::onContactBegin(PhysicsContact& contact)
|
|
{
|
|
return contact.getContactData()->normal.y < 0;
|
|
}
|
|
|
|
std::string PhysicsDemoOneWayPlatform::title() const
|
|
{
|
|
return "One Way Platform";
|
|
}
|
|
|
|
void PhysicsDemoSlice::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
toggleDebug();
|
|
|
|
_sliceTag = 1;
|
|
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = [](Touch* /*touch*/, Event* /*event*/) -> bool { return true; };
|
|
touchListener->onTouchEnded = AX_CALLBACK_2(PhysicsDemoSlice::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
auto ground = Node::create();
|
|
ground->addComponent(PhysicsBody::createEdgeSegment(VisibleRect::leftBottom() + Vec2(0, 50),
|
|
VisibleRect::rightBottom() + Vec2(0.0f, 50.0f)));
|
|
this->addChild(ground);
|
|
|
|
auto box = Node::create();
|
|
Vec2 points[4] = {Vec2(-100.0f, -100.0f), Vec2(-100.0f, 100.0f), Vec2(100.0f, 100.0f), Vec2(100.0f, -100.0f)};
|
|
box->addComponent(PhysicsBody::createPolygon(points, 4));
|
|
box->setPosition(VisibleRect::center());
|
|
box->getPhysicsBody()->setTag(_sliceTag);
|
|
addChild(box);
|
|
}
|
|
|
|
bool PhysicsDemoSlice::slice(PhysicsWorld& /*world*/, const PhysicsRayCastInfo& info, void* /*data*/)
|
|
{
|
|
if (info.shape->getBody()->getTag() != _sliceTag)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (!info.shape->containsPoint(info.start) && !info.shape->containsPoint(info.end))
|
|
{
|
|
Vec2 normal = info.end - info.start;
|
|
normal = normal.getPerp().getNormalized();
|
|
float dist = info.start.dot(normal);
|
|
|
|
clipPoly(dynamic_cast<PhysicsShapePolygon*>(info.shape), normal, dist);
|
|
clipPoly(dynamic_cast<PhysicsShapePolygon*>(info.shape), -normal, -dist);
|
|
|
|
info.shape->getBody()->removeFromWorld();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PhysicsDemoSlice::clipPoly(PhysicsShapePolygon* shape, Vec2 normal, float distance)
|
|
{
|
|
PhysicsBody* body = shape->getBody();
|
|
int count = shape->getPointsCount();
|
|
int pointsCount = 0;
|
|
Vec2* points = new Vec2[count + 1];
|
|
|
|
for (int i = 0, j = count - 1; i < count; j = i, ++i)
|
|
{
|
|
Vec2 a = body->local2World(shape->getPoint(j));
|
|
float aDist = a.dot(normal) - distance;
|
|
|
|
if (aDist < 0.0f)
|
|
{
|
|
points[pointsCount] = a;
|
|
++pointsCount;
|
|
}
|
|
|
|
Vec2 b = body->local2World(shape->getPoint(i));
|
|
float bDist = b.dot(normal) - distance;
|
|
|
|
if (aDist * bDist < 0.0f)
|
|
{
|
|
float t = std::fabs(aDist) / (std::fabs(aDist) + std::fabs(bDist));
|
|
points[pointsCount] = a.lerp(b, t);
|
|
++pointsCount;
|
|
}
|
|
}
|
|
|
|
Vec2 center = PhysicsShape::getPolygonCenter(points, pointsCount);
|
|
Node* node = Node::create();
|
|
PhysicsBody* polygon = PhysicsBody::createPolygon(points, pointsCount, PHYSICSBODY_MATERIAL_DEFAULT, -center);
|
|
node->setPosition(center);
|
|
node->addComponent(polygon);
|
|
polygon->setVelocity(body->getVelocityAtWorldPoint(center));
|
|
polygon->setAngularVelocity(body->getAngularVelocity());
|
|
polygon->setTag(_sliceTag);
|
|
addChild(node);
|
|
|
|
delete[] points;
|
|
}
|
|
|
|
void PhysicsDemoSlice::onTouchEnded(Touch* touch, Event* /*event*/)
|
|
{
|
|
auto func = AX_CALLBACK_3(PhysicsDemoSlice::slice, this);
|
|
getPhysicsWorld()->rayCast(func, touch->getStartLocation(), touch->getLocation(), nullptr);
|
|
}
|
|
|
|
std::string PhysicsDemoSlice::title() const
|
|
{
|
|
return "Slice";
|
|
}
|
|
|
|
std::string PhysicsDemoSlice::subtitle() const
|
|
{
|
|
return "click and drag to slice up the block";
|
|
}
|
|
|
|
void PhysicsDemoBug3988::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
toggleDebug();
|
|
_physicsWorld->setGravity(Vec2::ZERO);
|
|
|
|
auto ball = Sprite::create("Images/YellowSquare.png");
|
|
ball->setPosition(VisibleRect::center() - Vec2(100.0f, 0.0f));
|
|
ball->setRotation(30.0f);
|
|
this->addChild(ball);
|
|
|
|
auto physicsBall = makeBox(VisibleRect::center() + Vec2(100.0f, 0.0f), Size(100.0f, 100.0f));
|
|
physicsBall->setRotation(30.0f);
|
|
this->addChild(physicsBall);
|
|
}
|
|
|
|
std::string PhysicsDemoBug3988::title() const
|
|
{
|
|
return "Bug3988";
|
|
}
|
|
|
|
std::string PhysicsDemoBug3988::subtitle() const
|
|
{
|
|
return "All the Rectangles should have same rotation angle";
|
|
}
|
|
|
|
void PhysicsContactTest::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
_physicsWorld->setGravity(Vec2::ZERO);
|
|
auto s = VisibleRect::getVisibleRect().size;
|
|
|
|
_yellowBoxNum = 50;
|
|
_blueBoxNum = 50;
|
|
_yellowTriangleNum = 50;
|
|
_blueTriangleNum = 50;
|
|
|
|
MenuItemFont::setFontSize(65);
|
|
auto decrease1 = MenuItemFont::create(" - ", AX_CALLBACK_1(PhysicsContactTest::onDecrease, this));
|
|
decrease1->setColor(Color3B(0, 200, 20));
|
|
auto increase1 = MenuItemFont::create(" + ", AX_CALLBACK_1(PhysicsContactTest::onIncrease, this));
|
|
increase1->setColor(Color3B(0, 200, 20));
|
|
decrease1->setTag(1);
|
|
increase1->setTag(1);
|
|
|
|
float prevMenuPos = getSubtitleLable()->getPosition().y - getSubtitleLable()->getContentSize().height;
|
|
float menuStep = (getSubtitleLable()->getPosition().y - getRestartTestItem()->getPosition().y) * 0.25f;
|
|
auto menu1 = Menu::create(decrease1, increase1, nullptr);
|
|
menu1->alignItemsHorizontally();
|
|
menu1->setPosition(Vec2(s.width / 2, prevMenuPos));
|
|
addChild(menu1, 1);
|
|
|
|
auto label = Label::createWithTTF("yellow box", "fonts/arial.ttf", 32);
|
|
addChild(label, 1);
|
|
label->setPosition(Vec2(s.width / 2 - 150, prevMenuPos));
|
|
|
|
auto decrease2 = MenuItemFont::create(" - ", AX_CALLBACK_1(PhysicsContactTest::onDecrease, this));
|
|
decrease2->setColor(Color3B(0, 200, 20));
|
|
auto increase2 = MenuItemFont::create(" + ", AX_CALLBACK_1(PhysicsContactTest::onIncrease, this));
|
|
increase2->setColor(Color3B(0, 200, 20));
|
|
decrease2->setTag(2);
|
|
increase2->setTag(2);
|
|
|
|
auto menu2 = Menu::create(decrease2, increase2, nullptr);
|
|
menu2->alignItemsHorizontally();
|
|
menu2->setPosition(Vec2(s.width / 2, prevMenuPos -= menuStep));
|
|
addChild(menu2, 1);
|
|
|
|
label = Label::createWithTTF("blue box", "fonts/arial.ttf", 32);
|
|
addChild(label, 1);
|
|
label->setPosition(Vec2(s.width / 2 - 150, prevMenuPos));
|
|
|
|
auto decrease3 = MenuItemFont::create(" - ", AX_CALLBACK_1(PhysicsContactTest::onDecrease, this));
|
|
decrease3->setColor(Color3B(0, 200, 20));
|
|
auto increase3 = MenuItemFont::create(" + ", AX_CALLBACK_1(PhysicsContactTest::onIncrease, this));
|
|
increase3->setColor(Color3B(0, 200, 20));
|
|
decrease3->setTag(3);
|
|
increase3->setTag(3);
|
|
|
|
auto menu3 = Menu::create(decrease3, increase3, nullptr);
|
|
menu3->alignItemsHorizontally();
|
|
menu3->setPosition(Vec2(s.width / 2, prevMenuPos -= menuStep));
|
|
addChild(menu3, 1);
|
|
|
|
label = Label::createWithTTF("yellow triangle", "fonts/arial.ttf", 32);
|
|
addChild(label, 1);
|
|
label->setPosition(Vec2(s.width / 2 - 150, prevMenuPos));
|
|
|
|
auto decrease4 = MenuItemFont::create(" - ", AX_CALLBACK_1(PhysicsContactTest::onDecrease, this));
|
|
decrease4->setColor(Color3B(0, 200, 20));
|
|
auto increase4 = MenuItemFont::create(" + ", AX_CALLBACK_1(PhysicsContactTest::onIncrease, this));
|
|
increase4->setColor(Color3B(0, 200, 20));
|
|
decrease4->setTag(4);
|
|
increase4->setTag(4);
|
|
|
|
auto menu4 = Menu::create(decrease4, increase4, nullptr);
|
|
menu4->alignItemsHorizontally();
|
|
menu4->setPosition(Vec2(s.width / 2, prevMenuPos -= menuStep));
|
|
addChild(menu4, 1);
|
|
|
|
label = Label::createWithTTF("blue triangle", "fonts/arial.ttf", 32);
|
|
addChild(label, 1);
|
|
label->setPosition(Vec2(s.width / 2 - 150, prevMenuPos));
|
|
|
|
resetTest();
|
|
}
|
|
|
|
void PhysicsContactTest::onDecrease(Ref* sender)
|
|
{
|
|
switch (dynamic_cast<Node*>(sender)->getTag())
|
|
{
|
|
case 1:
|
|
if (_yellowBoxNum > 0)
|
|
_yellowBoxNum -= 50;
|
|
break;
|
|
case 2:
|
|
if (_blueBoxNum > 0)
|
|
_blueBoxNum -= 50;
|
|
break;
|
|
case 3:
|
|
if (_yellowTriangleNum > 0)
|
|
_yellowTriangleNum -= 50;
|
|
break;
|
|
case 4:
|
|
if (_blueTriangleNum > 0)
|
|
_blueTriangleNum -= 50;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
resetTest();
|
|
}
|
|
|
|
void PhysicsContactTest::onIncrease(Ref* sender)
|
|
{
|
|
switch (dynamic_cast<Node*>(sender)->getTag())
|
|
{
|
|
case 1:
|
|
_yellowBoxNum += 50;
|
|
break;
|
|
case 2:
|
|
_blueBoxNum += 50;
|
|
break;
|
|
case 3:
|
|
_yellowTriangleNum += 50;
|
|
break;
|
|
case 4:
|
|
_blueTriangleNum += 50;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
resetTest();
|
|
}
|
|
|
|
void PhysicsContactTest::resetTest()
|
|
{
|
|
removeChildByTag(10);
|
|
auto root = Node::create();
|
|
root->setTag(10);
|
|
this->addChild(root);
|
|
|
|
auto s = VisibleRect::getVisibleRect().size;
|
|
float prevMenuPos = getSubtitleLable()->getPosition().y - getSubtitleLable()->getContentSize().height;
|
|
float menuStep = (getSubtitleLable()->getPosition().y - getRestartTestItem()->getPosition().y) * 0.25f;
|
|
std::string strNum;
|
|
char buffer[10];
|
|
|
|
sprintf(buffer, "%d", _yellowBoxNum);
|
|
auto label = Label::createWithTTF(buffer, "fonts/arial.ttf", 32);
|
|
root->addChild(label, 1);
|
|
label->setPosition(Vec2(s.width / 2, prevMenuPos));
|
|
|
|
sprintf(buffer, "%d", _blueBoxNum);
|
|
label = Label::createWithTTF(buffer, "fonts/arial.ttf", 32);
|
|
root->addChild(label, 1);
|
|
label->setPosition(Vec2(s.width / 2, prevMenuPos -= menuStep));
|
|
|
|
sprintf(buffer, "%d", _yellowTriangleNum);
|
|
label = Label::createWithTTF(buffer, "fonts/arial.ttf", 32);
|
|
root->addChild(label, 1);
|
|
label->setPosition(Vec2(s.width / 2, prevMenuPos -= menuStep));
|
|
|
|
sprintf(buffer, "%d", _blueTriangleNum);
|
|
label = Label::createWithTTF(buffer, "fonts/arial.ttf", 32);
|
|
root->addChild(label, 1);
|
|
label->setPosition(Vec2(s.width / 2, prevMenuPos -= menuStep));
|
|
|
|
auto wall = Node::create();
|
|
wall->addComponent(PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size, PhysicsMaterial(0.1f, 1, 0.0f)));
|
|
wall->setPosition(VisibleRect::center());
|
|
root->addChild(wall);
|
|
|
|
auto contactListener = EventListenerPhysicsContact::create();
|
|
contactListener->onContactBegin = AX_CALLBACK_1(PhysicsContactTest::onContactBegin, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
|
|
|
|
// yellow box, will collide with itself and blue box.
|
|
for (int i = 0; i < _yellowBoxNum; ++i)
|
|
{
|
|
Size size(10 + AXRANDOM_0_1() * 10, 10 + AXRANDOM_0_1() * 10);
|
|
Size winSize = VisibleRect::getVisibleRect().size;
|
|
Vec2 position = Vec2(winSize.width, winSize.height) - Vec2(size.width, size.height);
|
|
position.x = position.x * AXRANDOM_0_1();
|
|
position.y = position.y * AXRANDOM_0_1();
|
|
position = VisibleRect::leftBottom() + position + Vec2(size.width / 2, size.height / 2);
|
|
Vec2 velocity((float)(AXRANDOM_0_1() - 0.5) * 200, (float)(AXRANDOM_0_1() - 0.5) * 200);
|
|
auto box = makeBox(position, size, 1, PhysicsMaterial(0.1f, 1, 0.0f));
|
|
auto boxBody = box->getPhysicsBody();
|
|
boxBody->setVelocity(velocity);
|
|
boxBody->setCategoryBitmask(0x01); // 0001
|
|
boxBody->setContactTestBitmask(0x04); // 0100
|
|
boxBody->setCollisionBitmask(0x03); // 0011
|
|
root->addChild(box);
|
|
}
|
|
|
|
// blue box, will collide with blue box.
|
|
for (int i = 0; i < _blueBoxNum; ++i)
|
|
{
|
|
Size size(10 + AXRANDOM_0_1() * 10, 10 + AXRANDOM_0_1() * 10);
|
|
Size winSize = VisibleRect::getVisibleRect().size;
|
|
Vec2 position = Vec2(winSize.width, winSize.height) - Vec2(size.width, size.height);
|
|
position.x = position.x * AXRANDOM_0_1();
|
|
position.y = position.y * AXRANDOM_0_1();
|
|
position = VisibleRect::leftBottom() + position + Vec2(size.width / 2, size.height / 2);
|
|
Vec2 velocity((float)(AXRANDOM_0_1() - 0.5) * 200, (float)(AXRANDOM_0_1() - 0.5) * 200);
|
|
auto box = makeBox(position, size, 2, PhysicsMaterial(0.1f, 1, 0.0f));
|
|
auto boxBody = box->getPhysicsBody();
|
|
boxBody->setVelocity(velocity);
|
|
boxBody->setCategoryBitmask(0x02); // 0010
|
|
boxBody->setContactTestBitmask(0x08); // 1000
|
|
boxBody->setCollisionBitmask(0x01); // 0001
|
|
root->addChild(box);
|
|
}
|
|
|
|
// yellow triangle, will collide with itself and blue box.
|
|
for (int i = 0; i < _yellowTriangleNum; ++i)
|
|
{
|
|
Size size(10 + AXRANDOM_0_1() * 10, 10 + AXRANDOM_0_1() * 10);
|
|
Size winSize = VisibleRect::getVisibleRect().size;
|
|
Vec2 position = Vec2(winSize.width, winSize.height) - Vec2(size.width, size.height);
|
|
position.x = position.x * AXRANDOM_0_1();
|
|
position.y = position.y * AXRANDOM_0_1();
|
|
position = VisibleRect::leftBottom() + position + Vec2(size.width / 2, size.height / 2);
|
|
Vec2 velocity((float)(AXRANDOM_0_1() - 0.5) * 300, (float)(AXRANDOM_0_1() - 0.5) * 300);
|
|
auto triangle = makeTriangle(position, size, 1, PhysicsMaterial(0.1f, 1, 0.0f));
|
|
auto triangleBody = triangle->getPhysicsBody();
|
|
triangleBody->setVelocity(velocity);
|
|
triangleBody->setCategoryBitmask(0x04); // 0100
|
|
triangleBody->setContactTestBitmask(0x01); // 0001
|
|
triangleBody->setCollisionBitmask(0x06); // 0110
|
|
root->addChild(triangle);
|
|
}
|
|
|
|
// blue triangle, will collide with yellow box.
|
|
for (int i = 0; i < _blueTriangleNum; ++i)
|
|
{
|
|
Size size(10 + AXRANDOM_0_1() * 10, 10 + AXRANDOM_0_1() * 10);
|
|
Size winSize = VisibleRect::getVisibleRect().size;
|
|
Vec2 position = Vec2(winSize.width, winSize.height) - Vec2(size.width, size.height);
|
|
position.x = position.x * AXRANDOM_0_1();
|
|
position.y = position.y * AXRANDOM_0_1();
|
|
position = VisibleRect::leftBottom() + position + Vec2(size.width / 2, size.height / 2);
|
|
Vec2 velocity((float)(AXRANDOM_0_1() - 0.5) * 300, (float)(AXRANDOM_0_1() - 0.5) * 300);
|
|
auto triangle = makeTriangle(position, size, 2, PhysicsMaterial(0.1f, 1, 0.0f));
|
|
auto triangleBody = triangle->getPhysicsBody();
|
|
triangleBody->setVelocity(velocity);
|
|
triangleBody->setCategoryBitmask(0x08); // 1000
|
|
triangleBody->setContactTestBitmask(0x02); // 0010
|
|
triangleBody->setCollisionBitmask(0x01); // 0001
|
|
root->addChild(triangle);
|
|
}
|
|
}
|
|
|
|
bool PhysicsContactTest::onContactBegin(PhysicsContact& contact)
|
|
{
|
|
PhysicsBody* a = contact.getShapeA()->getBody();
|
|
PhysicsBody* b = contact.getShapeB()->getBody();
|
|
PhysicsBody* body = (a->getCategoryBitmask() == 0x04 || a->getCategoryBitmask() == 0x08) ? a : b;
|
|
AX_ASSERT(body->getCategoryBitmask() == 0x04 || body->getCategoryBitmask() == 0x08);
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string PhysicsContactTest::title() const
|
|
{
|
|
return "Contact Test";
|
|
}
|
|
|
|
std::string PhysicsContactTest::subtitle() const
|
|
{
|
|
return "should not crash";
|
|
}
|
|
|
|
void PhysicsPositionRotationTest::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
toggleDebug();
|
|
_physicsWorld->setGravity(Point::ZERO);
|
|
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = AX_CALLBACK_2(PhysicsDemo::onTouchBegan, this);
|
|
touchListener->onTouchMoved = AX_CALLBACK_2(PhysicsDemo::onTouchMoved, this);
|
|
touchListener->onTouchEnded = AX_CALLBACK_2(PhysicsDemo::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
auto wall = Node::create();
|
|
wall->addComponent(PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size));
|
|
wall->setPosition(VisibleRect::center());
|
|
addChild(wall);
|
|
|
|
// anchor test
|
|
auto anchorNode = Sprite::create("Images/YellowSquare.png");
|
|
// anchorNode->setAnchorPoint(Vec2(0.1f, 0.9f));
|
|
anchorNode->setPosition(100, 100);
|
|
anchorNode->setScale(0.25);
|
|
anchorNode->addComponent(PhysicsBody::createBox(anchorNode->getContentSize()));
|
|
anchorNode->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
addChild(anchorNode);
|
|
|
|
anchorNode->getPhysicsBody()->setAngularVelocity(-5.0f);
|
|
|
|
// parent test
|
|
auto parent = Sprite::create("Images/YellowSquare.png");
|
|
parent->setPosition(300, 100);
|
|
parent->setScale(0.5);
|
|
parent->addComponent(PhysicsBody::createBox(parent->getContentSize()));
|
|
parent->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
addChild(parent);
|
|
|
|
auto leftBall = Sprite::create("Images/YellowSquare.png");
|
|
leftBall->setPosition(-50, 0);
|
|
leftBall->Node::setScale(0.5);
|
|
leftBall->addComponent(PhysicsBody::createBox(leftBall->getContentSize()));
|
|
leftBall->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
parent->addChild(leftBall);
|
|
|
|
parent->getPhysicsBody()->setAngularVelocity(5.0f);
|
|
|
|
// offset position rotation test
|
|
auto offsetPosNode = Sprite::create("Images/YellowSquare.png");
|
|
offsetPosNode->setPosition(100, 200);
|
|
auto body = PhysicsBody::createBox(offsetPosNode->getContentSize() / 2);
|
|
offsetPosNode->addComponent(body);
|
|
body->setPositionOffset(-Vec2(offsetPosNode->getContentSize() / 2));
|
|
body->setRotationOffset(45);
|
|
body->setTag(DRAG_BODYS_TAG);
|
|
addChild(offsetPosNode);
|
|
|
|
offsetPosNode->getPhysicsBody()->setAngularVelocity(5.0f);
|
|
|
|
return;
|
|
}
|
|
|
|
std::string PhysicsPositionRotationTest::title() const
|
|
{
|
|
return "Position/Rotation Test";
|
|
}
|
|
|
|
void PhysicsSetGravityEnableTest::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = AX_CALLBACK_2(PhysicsDemo::onTouchBegan, this);
|
|
touchListener->onTouchMoved = AX_CALLBACK_2(PhysicsDemo::onTouchMoved, this);
|
|
touchListener->onTouchEnded = AX_CALLBACK_2(PhysicsDemo::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
// wall
|
|
auto wall = Node::create();
|
|
wall->addComponent(
|
|
PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size, PhysicsMaterial(0.1f, 1.0f, 0.0f)));
|
|
wall->setPosition(VisibleRect::center());
|
|
addChild(wall);
|
|
|
|
// common box
|
|
auto commonBox = makeBox(Vec2(100, 100), Size(50, 50), 1);
|
|
commonBox->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
commonBox->getPhysicsBody()->setGravityEnable(true);
|
|
addChild(commonBox);
|
|
|
|
auto box = makeBox(Vec2(200, 100), Size(50, 50), 2);
|
|
auto boxBody = box->getPhysicsBody();
|
|
boxBody->setMass(20);
|
|
boxBody->setTag(DRAG_BODYS_TAG);
|
|
boxBody->setGravityEnable(false);
|
|
addChild(box);
|
|
|
|
auto ball = makeBall(Vec2(200, 200), 50);
|
|
ball->setTag(2);
|
|
auto ballBody = ball->getPhysicsBody();
|
|
ballBody->setTag(DRAG_BODYS_TAG);
|
|
ballBody->setGravityEnable(false);
|
|
ballBody->setMass(50);
|
|
addChild(ball);
|
|
|
|
scheduleOnce(AX_SCHEDULE_SELECTOR(PhysicsSetGravityEnableTest::onScheduleOnce), 1.0);
|
|
}
|
|
|
|
void PhysicsSetGravityEnableTest::onScheduleOnce(float /*delta*/)
|
|
{
|
|
auto ball = getChildByTag(2);
|
|
ball->getPhysicsBody()->setMass(200);
|
|
|
|
_physicsWorld->setGravity(Vec2(0, -98));
|
|
}
|
|
|
|
std::string PhysicsSetGravityEnableTest::title() const
|
|
{
|
|
return "Set Gravity Enable Test";
|
|
}
|
|
|
|
std::string PhysicsSetGravityEnableTest::subtitle() const
|
|
{
|
|
return "only yellow box drop down";
|
|
}
|
|
|
|
void PhysicsDemoBug5482::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
toggleDebug();
|
|
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = AX_CALLBACK_2(PhysicsDemo::onTouchBegan, this);
|
|
touchListener->onTouchMoved = AX_CALLBACK_2(PhysicsDemo::onTouchMoved, this);
|
|
touchListener->onTouchEnded = AX_CALLBACK_2(PhysicsDemo::onTouchEnded, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
_bodyInA = false;
|
|
|
|
// wall
|
|
auto wall = Node::create();
|
|
wall->addComponent(
|
|
PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size, PhysicsMaterial(0.1f, 1.0f, 0.0f)));
|
|
wall->setPosition(VisibleRect::center());
|
|
addChild(wall);
|
|
|
|
// button
|
|
MenuItemFont::setFontSize(18);
|
|
_button = MenuItemFont::create("Set Body To A", AX_CALLBACK_1(PhysicsDemoBug5482::changeBodyCallback, this));
|
|
|
|
auto menu = Menu::create(_button, nullptr);
|
|
this->addChild(menu);
|
|
|
|
_nodeA = Sprite::create("Images/YellowSquare.png");
|
|
_nodeA->setPosition(VisibleRect::center().x - 150, 100);
|
|
this->addChild(_nodeA);
|
|
|
|
_nodeB = Sprite::create("Images/YellowSquare.png");
|
|
_nodeB->setPosition(VisibleRect::center().x + 150, 100);
|
|
this->addChild(_nodeB);
|
|
|
|
_body = PhysicsBody::createBox(_nodeA->getContentSize());
|
|
_body->setTag(DRAG_BODYS_TAG);
|
|
_body->retain();
|
|
}
|
|
|
|
void PhysicsDemoBug5482::onExit()
|
|
{
|
|
PhysicsDemo::onExit();
|
|
_body->release();
|
|
}
|
|
|
|
void PhysicsDemoBug5482::changeBodyCallback(Ref* /*sender*/)
|
|
{
|
|
Sprite* node = _bodyInA ? _nodeB : _nodeA;
|
|
if (_bodyInA)
|
|
{
|
|
_button->setString("Set Body To A");
|
|
}
|
|
else
|
|
{
|
|
_button->setString("Set Body To B");
|
|
}
|
|
|
|
if (_body->getOwner())
|
|
{
|
|
_body->getOwner()->removeComponent(_body);
|
|
}
|
|
|
|
// very important to have always _body and sprite in sync (Fix: #712)
|
|
float rot = node->getRotation();
|
|
node->setRotation(node->getRotation() - rot);
|
|
|
|
node->addComponent(_body);
|
|
_bodyInA = !_bodyInA;
|
|
}
|
|
|
|
std::string PhysicsDemoBug5482::title() const
|
|
{
|
|
return "bug 5482: setPhysicsBodyTest";
|
|
}
|
|
|
|
std::string PhysicsDemoBug5482::subtitle() const
|
|
{
|
|
return "change physics body to the other.";
|
|
}
|
|
|
|
void PhysicsFixedUpdate::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
toggleDebug();
|
|
_physicsWorld->setGravity(Point::ZERO);
|
|
|
|
// wall
|
|
auto wall = Node::create();
|
|
wall->addComponent(PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size, PhysicsMaterial(0.1f, 1, 0.0f)));
|
|
wall->setPosition(VisibleRect::center());
|
|
this->addChild(wall);
|
|
|
|
addBall();
|
|
|
|
scheduleOnce(AX_SCHEDULE_SELECTOR(PhysicsFixedUpdate::updateStart), 2);
|
|
}
|
|
|
|
void PhysicsFixedUpdate::addBall()
|
|
{
|
|
auto ball = Sprite::create("Images/ball.png");
|
|
ball->setPosition(100, 100);
|
|
auto ballBody = PhysicsBody::createCircle(ball->getContentSize().width / 2, PhysicsMaterial(0.1f, 1, 0.0f));
|
|
ball->addComponent(ballBody);
|
|
ballBody->setTag(DRAG_BODYS_TAG);
|
|
ballBody->setVelocity(Point(1000, 20));
|
|
this->addChild(ball);
|
|
}
|
|
|
|
void PhysicsFixedUpdate::updateStart(float /*delta*/)
|
|
{
|
|
addBall();
|
|
|
|
_physicsWorld->setFixedUpdateRate(180);
|
|
}
|
|
|
|
void PhysicsFixedUpdate::update(float /*delta*/)
|
|
{
|
|
|
|
// use fixed time and calculate 3 times per frame makes physics simulate more precisely.
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
_physicsWorld->step(1 / 180.0f);
|
|
}
|
|
}
|
|
|
|
std::string PhysicsFixedUpdate::title() const
|
|
{
|
|
return "Fixed Update Test";
|
|
}
|
|
|
|
std::string PhysicsFixedUpdate::subtitle() const
|
|
{
|
|
return "The second ball should not run across the wall";
|
|
}
|
|
|
|
bool PhysicsTransformTest::onTouchBegan(Touch* touch, Event* /*event*/)
|
|
{
|
|
_parentSprite->setPosition(_rootLayer->convertTouchToNodeSpace(touch));
|
|
return false;
|
|
}
|
|
|
|
void PhysicsTransformTest::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
toggleDebug();
|
|
_physicsWorld->setGravity(Point::ZERO);
|
|
|
|
auto touchListener = EventListenerTouchOneByOne::create();
|
|
touchListener->onTouchBegan = AX_CALLBACK_2(PhysicsTransformTest::onTouchBegan, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
|
|
|
_rootLayer = Layer::create();
|
|
addChild(_rootLayer);
|
|
|
|
auto wall = Node::create();
|
|
wall->addComponent(
|
|
PhysicsBody::createEdgeBox(VisibleRect::getVisibleRect().size, PhysicsMaterial(0.1f, 1.0f, 0.0f)));
|
|
wall->setPosition(VisibleRect::center());
|
|
_rootLayer->addChild(wall);
|
|
|
|
// parent test
|
|
_parentSprite = Sprite::create("Images/YellowSquare.png");
|
|
_parentSprite->setPosition(200, 100);
|
|
_parentSprite->setScale(0.25);
|
|
_parentSprite->addComponent(
|
|
PhysicsBody::createBox(_parentSprite->getContentSize(), 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->setScale(2);
|
|
leftBall->addComponent(
|
|
PhysicsBody::createCircle(leftBall->getContentSize().width / 2, PhysicsMaterial(0.1f, 1.0f, 0.0f)));
|
|
leftBall->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
_parentSprite->addChild(leftBall);
|
|
|
|
ScaleTo* scaleTo = ScaleTo::create(2.0, 0.5);
|
|
ScaleTo* scaleBack = ScaleTo::create(2.0, 1.0);
|
|
_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);
|
|
normal->addComponent(PhysicsBody::createBox(normal->getContentSize(), PhysicsMaterial(0.1f, 1.0f, 0.0f)));
|
|
normal->getPhysicsBody()->setTag(DRAG_BODYS_TAG);
|
|
_rootLayer->addChild(normal);
|
|
|
|
auto bullet = Sprite::create("Images/ball.png");
|
|
bullet->setPosition(200, 200);
|
|
bullet->addComponent(
|
|
PhysicsBody::createCircle(bullet->getContentSize().width / 2, PhysicsMaterial(0.1f, 1.0f, 0.0f)));
|
|
bullet->getPhysicsBody()->setVelocity(Vec2(100.0f, 100.0f));
|
|
_rootLayer->addChild(bullet);
|
|
|
|
MoveBy* move = MoveBy::create(2.0f, Vec2(100.0f, 100.0f));
|
|
MoveBy* move2 = MoveBy::create(2.0f, Vec2(-200.0f, 0.0f));
|
|
MoveBy* move3 = MoveBy::create(2.0f, Vec2(100.0f, -100.0f));
|
|
ScaleTo* scale = ScaleTo::create(3.0f, 0.3f);
|
|
ScaleTo* scale2 = ScaleTo::create(3.0f, 1.0f);
|
|
|
|
RotateBy* rotate = RotateBy::create(6.0f, 360);
|
|
|
|
_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
|
|
{
|
|
return "Physics transform test";
|
|
}
|
|
|
|
void PhysicsIssue9959::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
auto origin = Director::getInstance()->getVisibleOrigin();
|
|
auto visibleSize = Director::getInstance()->getVisibleSize();
|
|
|
|
auto scale9Sprite1 = ui::Scale9Sprite::create("Images/blocks.png");
|
|
scale9Sprite1->setPosition(origin + visibleSize / 2);
|
|
addChild(scale9Sprite1);
|
|
scale9Sprite1->runAction(RepeatForever::create(
|
|
Sequence::create(MoveBy::create(2.0f, Vec2(100.0f, 0.0f)), MoveBy::create(2.0f, Vec2(-100.0f, 0.0f)), NULL)));
|
|
|
|
auto scale9Sprite2 = ui::Scale9Sprite::create("Images/blocks.png");
|
|
scale9Sprite2->setPosition(origin + visibleSize / 2 + Vec2(0.0f, 50.0f));
|
|
addChild(scale9Sprite2);
|
|
scale9Sprite2->runAction(
|
|
RepeatForever::create(Sequence::create(ScaleTo::create(2.0f, 1.5f), ScaleTo::create(2.0f, 1.0f), NULL)));
|
|
|
|
auto scale9Sprite3 = ui::Scale9Sprite::create("Images/blocks.png");
|
|
scale9Sprite3->setPosition(origin + visibleSize / 2 + Vec2(0.0f, -50.0f));
|
|
addChild(scale9Sprite3);
|
|
scale9Sprite3->runAction(RepeatForever::create(RotateBy::create(2.0f, 360.0f)));
|
|
}
|
|
|
|
std::string PhysicsIssue9959::title() const
|
|
{
|
|
return "Reorder issue #9959";
|
|
}
|
|
|
|
std::string PhysicsIssue9959::subtitle() const
|
|
{
|
|
return "Test Scale9Sprite run scale/move/rotation action in physics scene";
|
|
}
|
|
|
|
//
|
|
void PhysicsIssue15932::onEnter()
|
|
{
|
|
PhysicsDemo::onEnter();
|
|
|
|
PhysicsBody* pb = PhysicsBody::createBox(Size(15, 5), PhysicsMaterial(0.1f, 0.0f, 1.0f));
|
|
this->removeComponent(pb);
|
|
this->addComponent(pb);
|
|
this->removeComponent(pb);
|
|
}
|
|
|
|
std::string PhysicsIssue15932::title() const
|
|
{
|
|
return "Github issue #15932";
|
|
}
|
|
|
|
std::string PhysicsIssue15932::subtitle() const
|
|
{
|
|
return "addComponent()/removeComponent() should not crash";
|
|
}
|
|
|
|
#endif
|