Box2d Cocos2d ContactListener detecting collision - cocos2d-iphone

My problem is very simple, but I can't fix it.
I have a radar that is rotating and I have a player that is moving with a Joystick
Well, I just want to detect the collision between the radar and the player. It works perfectly when both are moving but it doesn't when radar is moving and my player is not.
Here you have the code to detect it and how it works:
//moves radar
[self schedule:#selector(loopRadar) interval: 12];
//moves player with joystick
body->SetLinearVelocity(b2Vec2(scaledVelocity.x*dt, scaledVelocity.y*dt));
actor.position = ccp(body->GetPosition().x * PTM_RATIO,
body->GetPosition().y * PTM_RATIO);
// DETECTS COLLISION BETWEEN RADAR AND PLAYER
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
if ((contact.fixtureA == radarBody->GetFixtureList() && contact.fixtureB == body->GetFixtureList()) ||
(contact.fixtureA == body->GetFixtureList() && contact.fixtureB == radarBody->GetFixtureList())) {
//DO SOMETHING LIKE GAME OVER
}
}
//ENDS COLLISION
MycontactListener class:
#import "MyContactListener.h"
MyContactListener::MyContactListener() : _contacts() {
}
MyContactListener::~MyContactListener() {
}
void MyContactListener::BeginContact(b2Contact* contact) {
// We need to copy out the data because the b2Contact passed in
// is reused.
MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() };
_contacts.push_back(myContact);
}
void MyContactListener::EndContact(b2Contact* contact) {
MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() };
std::vector<MyContact>::iterator pos;
pos = std::find(_contacts.begin(), _contacts.end(), myContact);
if (pos != _contacts.end()) {
_contacts.erase(pos);
}
}
void MyContactListener::PreSolve(b2Contact* contact,
const b2Manifold* oldManifold) {
}
void MyContactListener::PostSolve(b2Contact* contact,
const b2ContactImpulse* impulse) {
}
So, the problem is that when radar goes through the player and player isn't moving, it doesn't detect collision, but when both are moving it works perfectly.
Any tips?
Thank you very much!

Worked adding these lines as "LearnCocos2D" said, setting both bodies awake
body->SetAwake(TRUE);
radarBody->SetAwake(TRUE);
Thank you very much!

Related

Why setPosition work in some condition but doesn't work in other condition?

i make a game let's say ThrowBall, the Player can pick up the ball spawned and throw it into target get the score added then the ball return it's position and repeat. The problem is when Ball dragged by Player (i make the Ball as child of Player) into target, the Ball return it's position correctly into desired position, but the odd happens when it collided after i send the Ball into target by applying impulse it won't return the ball into desired position, why is this happening?
i'm running this game for android, i'm running this code in VS'17 using cocos2d-x-3.17. I tried changing impulse into force, moveby. I tried making the ball stop (setVelocity to zero before setPosition). I tried changing Point into Vect, vec2. I tried to break point debug, the code do read setPosition but do nothing.
GameScene.cpp
bool GameScene::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
auto pickupListener = EventListenerPhysicsContact::create();
pickupListener->onContactBegin = CC_CALLBACK_1(GameScene::onContactBegin, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(pickupListener, this);
return true;
}
bool GameScene::onContactBegin(cocos2d::PhysicsContact &contact)
{
PhysicsBody *a = contact.getShapeA()->getBody();
PhysicsBody *b = contact.getShapeB()->getBody();
if (
(BALL_COLLISION_BITMASK == a->getCollisionBitmask() && PLAYER_COLLISION_BITMASK == b->getCollisionBitmask()) ||
(BALL_COLLISION_BITMASK == b->getCollisionBitmask() && PLAYER_COLLISION_BITMASK == a->getCollisionBitmask())
)
{
// make the ball follow Player
isfollow = true;
}
else if (
(BALL_COLLISION_BITMASK == a->getCollisionBitmask() && TARGET_COLLISION_BITMASK == b->getCollisionBitmask()) ||
(BALL_COLLISION_BITMASK == b->getCollisionBitmask() && TARGET_COLLISION_BITMASK == a->getCollisionBitmask())
)
{
isfollow = false;
// add score
score++;
__String *tempScore = __String::createWithFormat("%i", score);
scoreLabel->setString(tempScore->getCString());
// return spawn ball
ball->returnPos();
return false;
}
void GameScene::throwBall() {
if (isfollow == true) {
isfollow = false;
ball->getSprite()->getPhysicsBody()->applyImpulse(Vec2(100000, 100000));
}
}
Ball.h
class Ball
{
public:
Ball(cocos2d::Layer *layer);
cocos2d::Sprite *getSprite() { return randomSpawn; };
void returnPos();
private:
cocos2d::Size visibleSize;
cocos2d::Vec2 origin;
cocos2d::Sprite *randomSpawn;
};
Ball.cpp
Ball::Ball(cocos2d::Layer *layer)
{
visibleSize = Director::getInstance()->getVisibleSize();
origin = Director::getInstance()->getVisibleOrigin();
randomSpawn = Sprite::create("res/ball.png");
randomSpawn->setPosition(Vec2(400, 80));
auto randomBallBody = PhysicsBody::createCircle(randomSpawn->getContentSize().width / 2);
randomBallBody->setCollisionBitmask(BALL_COLLISION_BITMASK);
randomBallBody->setContactTestBitmask(true);
randomBallBody->setGravityEnable(false);
randomSpawn->setPhysicsBody(randomBallBody);
layer->addChild(randomSpawn);
}
void Ball::returnPos()
{
randomSpawn->setPosition(Vec2(400, 80));
}
i want to make the object (Ball) return position when collided into Target and repeat. i'm sorry if the format is a mess, i'm new here, also it's not my full code, it's actually works fine i can run it, but only the setPosition won't work

How do I restore the sprite into original image after swapped it to another one in coco2dx?

In my gaming context, the sprite is a smiling baby, when the baby is touched, the current sprite image changed to a crying baby, then after 3 seconds of crying sound effect ended, the sprite will restore to the smiling baby image.
My problem is
How to swap back with the previous image?
I have changed the smiling baby to a crying one, but i have no idea how could I swap it with the original smiling one?
How to ensure one click at one time?
Each time the baby sprite is touched, the audio will start to play which is not ideal, because I hope the event function is only called after the previous event process is finished.
here is my code, and thank you a lot!
bool HelloWorld::init()
{
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//add background scene
auto backgroundSprite = Sprite :: create("scene001.jpg");
backgroundSprite->setAnchorPoint(Vec2(0,0));
backgroundSprite->setScaleX((visibleSize.width / backgroundSprite->getContentSize().width) * 1);
backgroundSprite->setScaleY((visibleSize.height / backgroundSprite->getContentSize().height) * 1);
addChild(backgroundSprite);
//add smileBaby sprite
auto smileBabySprite = Sprite :: create("figure001.png");
smileBabySprite -> setPosition(Vec2(500,400));
addChild(smileBabySprite);
//add crying audio
auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
auto babyListener = EventListenerTouchOneByOne::create();
babyListener -> onTouchBegan = [smileBabySprite, audio](Touch* touch, Event* event)
{
//change smileBaby sprite to cryingBaby sprite
smileBabySprite->setTexture(CCTextureCache::sharedTextureCache()->addImage("figure002.png"));
audio -> playEffect("babycry.mp3",false,1.0f,1.0f,1.0f);
return true;
};
babyListener -> onTouchEnded=[smileBabySprite](Touch* touch, Event* event )
{
};
_eventDispatcher -> addEventListenerWithSceneGraphPriority(babyListener, this);
return true;
}
What you want is to keep state about if the baby is crying or not. The best thing to do is keep this logic in a custom Node subclass.
Here is some (almost pseudo-)code to get you started:
Baby.h:
#pragma once
#include "cocos2d.h"
class Baby : public cocos2d::Node
{
private:
cocos2d::Sprite *_sprite; // Weak reference
bool _crying;
float _cryingTime;
public:
CREATE_FUNC(Baby);
protected:
virtual bool init() override;
virtual void update(float delta) override;
public:
void touched();
bool isInside(cocos2d::Touch *touch) const
protected:
void setSprite();
};
Baby.cpp:
#include "Baby.h"
USING_NS_CC;
bool Baby::init()
{
if (!Node::init())
return false;
_crying = false;
setSprite();
scheduleUpdate();
return true;
}
void Baby::update(float delta)
{
Node::update(delta);
if (_crying) {
_cryingTime -= delta;
if (_cryingTime <= 0.0f) {
_crying = false;
setSprite();
}
}
}
void Baby::touched()
{
if (_crying)
return; // Already crying
_crying = true;
_cryingTime = 3.0f; // Length of audio, I guess?
setSprite();
// Start crying sound here
}
bool Baby::isInside(Touch *touch) const
{
Vec2 locationInNode = _sprite->convertToNodeSpace(touch->getLocation());
Size size = _sprite->getContentSize();
Rect rect = Rect(0.0f, 0.0f, size.width, size.height);
return rect.containsPoint(locationInNode);
}
void Baby::setSprite()
{
if (_sprite)
_sprite->removeFromParent();
_sprite = Sprite::initWithFile(_crying ? "baby_crying.png" : "baby.png");
Vec2 size = getContentSize();
_sprite->setPosition(size.width * 0.5f, size.height * 0.5f);
addChild(_sprite);
}
You add the Baby node in the parent instead of the sprite, using:
_baby = Baby::create();
_baby->setPosition(Wherever);
addChild(_baby);
Where _baby is an instance variable and use the isInside() method to test if a touch event is within the bounds of the sprite and call its touched() method:
Touch *touch = ...;
if (_baby->isInside(touch)) {
_baby->touched();
}
and the Baby object will ignore the touch depending on state.

C++ Box2D Iterate through vector of bodies and deleting

I have created a game which uses a color coded image to create different bodies/fixtures. So for example if the pixel is red it will get stored into an array as 7 and then the program will create a body called jewel. If there are 10 red pixels, 10 jewels will be created:
else if (array[w][h]==7)
{
b2BodyDef Jewel_BodyDef;
Jewel_BodyDef.position.Set(x, y);
m_collectableJewel = m_world->CreateBody(&Jewel_BodyDef);
b2PolygonShape box;
box.SetAsBox(0.5f, 0.5f);
m_collectableJewelFixture=m_collectableJewel->CreateFixture(&box, 0.0f);
collide.m_jewelFix.push_back(m_collectableJewelFixture);
jewels a;
a.jewel_dim.set(1.0f,1.0f);
a.jewel_pos.set(x,y);
m_jewels.push_back(a);
x+=5.0f;
}
My problem is when calculating the collision between the player and the jewels. The program knows when the player has collided with a jewel. However, I can't get it to delete the fixture. Or rather, it will only delete the last fixture placed, i.e. the last one to be created in the vector. Is there a way to name the fixtures individually? So that then the program can delete the one it has actually collided with, rather than the last one?
edit:
int Collision_with_Player::PickJewel(b2Fixture *player, b2Fixture *foot)
{
int CollisionJewel=0;
std::vector<MyContact>::iterator posi;
for(posi = m_contactListener->m_contacts.begin(); posi != m_contactListener->m_contacts.end(); ++posi)
{
MyContact contact = *posi;
for(std::vector<b2Body*>::iterator iterb = m_jewelBodyVec.begin(); iterb != m_jewelBodyVec.end(); ++iterb)
{
for(std::vector<b2Fixture*>::iterator iter = m_jewelFix.begin(); iter != m_jewelFix.end(); ++iter)
{
if ((contact.fixtureA == player && contact.fixtureB == *iter) ||
(contact.fixtureA ==*iter && contact.fixtureB == player))
{
m_deleteJewels.clear();
std::cout<<"size"<<m_deleteJewels.size()<<std::endl;
CollisionJewel=1;
std::cout<<"Jewel"<<*iter<<std::endl;
deletejewel = *iter;
deletejewelbody= *iterb;
}
else
{
CollisionJewel=0;
}
}
}
}
return CollisionJewel;
}
and in another file
if (collide.PickJewel(m_playerFixture, m_footSensorFixture)==1)
{
collide.deletejewelbody->DestroyFixture(collide.deletejewel);
}

How to make a sprite jump like a frog in cocos2d-x ios game using c++

I am trying to make a sprite as a frog which will come from the top of the screen and will go downwards to the bottom at y axis =0.Its working fine as a normal CCMoveTo but i want that after a jump the frog should rest for 1 second then again jump.Some kind of delay in moving.Can anyone tell me with this.I am attaching my code also.
my frog animations are from fly1.png to fly5.png.I just want a delay after each move or we can say that I just want to call the CCMove after 1 second delay each time until the frog reaches the y axis=0
Any help will be appreciated.Thanks
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
using namespace cocos2d;
using namespace CocosDenshion;
CCScene* HelloWorld::scene()
{
CCScene *scene = CCScene::create();
HelloWorld *layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}
bool HelloWorld::init()
{
if ( !CCLayer::init() )
{
return false;
}
CCSize winSize=CCDirector::sharedDirector()->getWinSize();
_bgNode = CCNode::create();
_bgNode->setPosition(ccp(winSize.width/2, winSize.height/2));
this->addChild(_bgNode, -1);
_bgSprite = CCSprite::create("bg_2.jpg");
_bgNode->addChild(_bgSprite);
float rX = winSize.width/_bgSprite->getContentSize().width;
float rY = winSize.height/_bgSprite->getContentSize().height;
_bgNode->setScaleX(rX);
_bgNode->setScaleY(rY);
z=CCSprite::create("fly1.png");
z->setScaleX(rX);
z->setScaleY(rY);
z->setPosition(ccp(winSize.width/2,winSize.height+1));
this->addChild(z);
CCAction *a=CCRepeatForever::create(HelloWorld::getAnimationWithFrames(1,5));
z->runAction(a);
z->runAction(CCSequence::create(CCMoveTo::create(2.0, ccp(winSize.width/2, 0)), CCCallFuncN::create(this, callfuncN_selector(HelloWorld::setInvisible)), NULL));
return true;
}
cocos2d::CCAnimate* HelloWorld::getAnimationWithFrames(int from, int to)
{
CCArray* frames = CCArray::create();
for (int i = from; i <= to; i++)
{
CCString *str = CCString::createWithFormat("fly2%d.png", i);
CCSpriteFrame *f = CCSpriteFrame::create(str->getCString(), CCRect(0,0,256,400));
frames->addObject(f);
}
//(frames,speedofmovementofanimation);
CCAnimation *animation = CCAnimation::createWithSpriteFrames(frames,0.15f);
CCAnimate *a = CCAnimate::create(animation);
return a;
}
void HelloWorld::setInvisible()
{
this->removeChild(z,true);
}
Firstly, you have to create a frogJump() function. Then,in frogJump() function add the following code:
void HelloWorld::frogJump()
{
CCJumpTo* jumpTo = CCJumpTo::create(1,ccp(x/6,y/8),y/1.2f,1);
z->runAction(jumpTo);
}
Then, in the init() function add the following line:
this->scheduleOnce(SEL_SCHEDULE(&HelloWorld::frogJump),0);
this->schedule(SEL_SCHEDULE(&HelloWorld::frogJump),2);

cocos2d-x: I'm having a EXC_BAD_ACCESS

I'm having a EXC_BAD_ACCESS (code=1 ...) sometimes code=2 using cocos2d-x.
Getting this error when I do for example:
std::cout << this->getChildrenCount() << std::endl;
std::cout << this->getChildrenCount() << std::endl;
On this exact code, the first line works but the second gives me the error.
I was trying to reach all the children so I could do a function like "hey, fade all objects to this value", since they're inside a class extending CCNode.
void BasicElement::fadeTo(int opacity, float duration)
{
CCActionInterval* actionTo = CCFadeTo::create(duration, opacity);
CCArray* pChildren = this->getChildren();
if (pChildren && pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(pChildren, pObject)
{
CCSprite* pChild = (CCSprite*) pObject;
pChild->stopAllActions();
pChild->runAction(actionTo);
}
}
}
Already tried to do my own CCArray adding the elements that I addChild but... Same problems persist.
Can anyone help me with this?
You cannot use same CCAction on more than one sprite. For every sprite you should create new action. So you should create the action inside the loop.
void BasicElement::fadeTo(int opacity, float duration){
CCArray* pChildren = this->getChildren();
if (pChildren && pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(pChildren, pObject)
{
CCSprite* pChild = (CCSprite*) pObject;
pChild->stopAllActions();
//Create the action here
CCActionInterval* actionTo = CCFadeTo::create(duration, opacity);
pChild->runAction(actionTo);
}
}
}