I am currently working on subdividing my cocos2dx-cpp game into a more modular system. I want to have one layer to receive all Touches and direct those touches to the affected CCSprite-derived objects.
The derived objects are stored in a CCArray in an EntityManager (which helps me create and manage the entities).
The problem I am facing is that I can't seem to access the correct virtual method for my derived CCSprites.
Here is the code from my Touch layer (called TouchManager):
void TouchManager::ccTouchesBegan( cocos2d::CCSet* pTouches , cocos2d::CCEvent* pEvents )
{
cocos2d::CCSetIterator i;
cocos2d::CCTouch* touch;
cocos2d::CCPoint tap;
auto entities = EntityManager::sharedManager()->getVisibleEntities();
for ( i = pTouches->begin() ; i != pTouches->end() ; ++i )
{
touch = ( cocos2d::CCTouch* ) ( *i );
if ( touch )
{
tap = touch->getLocation();
for ( unsigned int entityIndex = 0 ; entityIndex < entities->size() ; ++entityIndex )
{
auto entity = entities->at( entityIndex );
// OLD: auto entity = ( TouchableSprite* )entities->objectAtIndex( entityIndex );
if ( entity->boundingBox().containsPoint( tap ) )
{
entity->setTouch( touch );
entity->onTouch( tap );
}
}
}
}
}
I want to have the TouchManager detect the entity that has been touched, and send the Touch to it. But there is my problem: it detects the touch but doesn't send it further. Either I have a crash or nothing at all.
I have created a Touchable interface class:
#include "cocos2d.h"
class Touchable : public cocos2d::CCSprite
{
cocos2d::CCTouch* m_pTouch;
public:
virtual cocos2d::CCTouch* getTouch();
virtual void setTouch( cocos2d::CCTouch* touch );
virtual void onTouch( cocos2d::CCPoint location ) = 0 ;
virtual void onMoved( cocos2d::CCPoint location ) = 0 ;
virtual void onEnded( cocos2d::CCPoint location ) = 0 ;
};
as well as a TouchableSprite base class:
#include "cocos2d.h"
#include "Touchable.h"
class TouchableSprite : public Touchable
{
//cocos2d::CCTouch* m_pTouch;
public:
//virtual cocos2d::CCTouch* getTouch();
//virtual void setTouch( cocos2d::CCTouch* touch );
static TouchableSprite* createSpriteWithFile( const char* fileName );
void resetPosition( float positionX = 0.0f , float positionY = 0.0f );
virtual void onTouch( cocos2d::CCPoint location ) ;
virtual void onMoved( cocos2d::CCPoint location ) ;
virtual void onEnded( cocos2d::CCPoint location ) ;
TouchableSprite(void);
~TouchableSprite(void);
};
with simple implementation (TouchableSprite.cpp):
#include "TouchableSprite.h"
TouchableSprite::TouchableSprite(void)
{
}
TouchableSprite::~TouchableSprite(void)
{
}
TouchableSprite* TouchableSprite::createSpriteWithFile( const char* fileName )
{
auto sprite = new TouchableSprite();
if ( sprite && sprite->initWithFile( fileName ) )
{
sprite->autorelease();
return ( TouchableSprite* )sprite;
}
CC_SAFE_DELETE( sprite );
// should not reach this point
return NULL;
}
void TouchableSprite::resetPosition( float positionX , float positionY )
{
this->setPosition( ccp( positionX , positionY ) );
}
void TouchableSprite::onTouch( cocos2d::CCPoint location )
{
}
void TouchableSprite::onMoved( cocos2d::CCPoint location )
{
}
void TouchableSprite::onEnded( cocos2d::CCPoint location )
{
}
And finally, here's my derived class (in this case, ControlStickSprite):
#include "cocos2d.h"
#include "RenderSystem.h"
#include "EntityManager.h"
#include "TouchableSprite.h"
class ControlStickSprite : public TouchableSprite
{
ControlStickSprite* m_sprite;
public:
cocos2d::CCNode* create( cocos2d::CCNode* parent );
void onTouch( cocos2d::CCPoint location ) ;
void onMoved( cocos2d::CCPoint location ) ;
void onEnded( cocos2d::CCPoint location ) ;
ControlStickSprite(void);
~ControlStickSprite(void);
};
with simple implementation for testing (skipping the Create part because it works):
void ControlStickSprite::onTouch( cocos2d::CCPoint location )
{
this->setScale( 0.5f );
}
void ControlStickSprite::onMoved( cocos2d::CCPoint location )
{
this->setPosition( location );
}
void ControlStickSprite::onEnded( cocos2d::CCPoint location )
{
}
Please help me get this working! I'm not too familiar with the usage of virtual methods so maybe I missed something there. I'm also relatively new to C++ and cocos2dx programming.
Thanks in advance!
EDIT: Thanks to @musikov for fixing the first part! I updated the above code to reflect the changes. I replaced the CCArray with std::vector< TouchableSprite* > to eliminate the need for casting the from CCObject*.
Now, I am facing the problem that when touched, ControlStickSprite::onTouch() is never chosen; it's always TouchableSprite::onTouch(). Added ControlStickSprite::create and EntityManager::createEntity methods:
My ControlStickSprite::create() method is like this:
ControlStickSprite* ControlStickSprite::create( cocos2d::CCNode* parent )
{
// auto parent = this->getParent();
auto entityType = "control-stick";
auto scale = 6.0f;
auto rotation = 0.0f;
auto positionX = RenderSystem::sharedRenderSystem()->getScreenWidth() * 0.9f ;
auto positionY = RenderSystem::sharedRenderSystem()->getScreenHeight() * 0.25f ;
auto sprite = EntityManager::sharedManager()->createEntity(
parent ,
entityType ,
scale ,
rotation ,
positionX ,
positionY
);
m_sprite = ( ControlStickSprite* )sprite;
return m_sprite;
}
which makes use of my EntityManager:
cocos2d::CCNode* EntityManager::createEntity( cocos2d::CCNode* parent , const char* entityType , float scale , float rotation , float positionX , float positionY )
{
std::string extension = ".png";
std::string fileName = entityType + extension;
auto entity = TouchableSprite::createSpriteWithFile( fileName.c_str() );
entity->setRotation( rotation );
entity->setScale( scale );
entity->resetPosition( positionX , positionY );
parent->addChild( entity );
// add to VisibleEntities vector
this->addEntity( entity , true );
return entity;
}
The only thing I can think of is that the createEntity() method creates a TouchableSprite* but returns a CCNode*, which I then cast to a ControlStickSprite*. Am I doing this wrong again? :)
Thanks for all your help!
You have wrong create method implementation in TouchableSprite and maybe in ControlStickSprite too. Your create method creates Sprite instance and casts it to TouchableSprite class. It's completely wrong :) That's why your program crashed on setTouch method call - because your calling this method in Sprite instance. You need to change your create method:
TouchableSprite* TouchableSprite::createSpriteWithFile( const char* fileName )
{
auto sprite = new TouchableSprite();
if ( sprite && sprite->initWithFile( fileName ) )
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE( sprite );
// should not reach this point
return NULL;
}
Added controlstick implementation
ControlStickSprite.h
#include "cocos2d.h"
#include "RenderSystem.h"
#include "EntityManager.h"
#include "TouchableSprite.h"
class ControlStickSprite : public TouchableSprite
{
public:
static ControlStickSprite* create();
bool init();
void onTouch( cocos2d::CCPoint location ) ;
void onMoved( cocos2d::CCPoint location ) ;
void onEnded( cocos2d::CCPoint location ) ;
ControlStickSprite(void);
~ControlStickSprite(void);
};
ControlStickSprite.cpp
bool ControlStickSprite::init()
{
auto entityType = "control-stick";
std::string extension = ".png";
std::string fileName = entityType + extension;
if (initWithFile( fileName.c_str() )) {
// auto parent = this->getParent();
auto scale = 6.0f;
auto rotation = 0.0f;
auto positionX = RenderSystem::sharedRenderSystem()->getScreenWidth() * 0.9f ;
auto positionY = RenderSystem::sharedRenderSystem()->getScreenHeight() * 0.25f ;
sprite->setRotation( rotation );
sprite->setScale( scale );
sprite->resetPosition( positionX , positionY );
return true;
}
return false;
}
ControlStickSprite* ControlStickSprite::create()
{
auto sprite = new ControlStickSprite();
if ( sprite && sprite->init() )
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE( sprite );
// should not reach this point
return NULL;
}
EntityManager:
void EntityManager::addEntity( cocos2d::CCNode* parent , TouchableSprite* entity )
{
parent->addChild( entity );
// add to VisibleEntities vector
this->addEntity( entity , true );
}
Remember to call EntityManager::addEntity(parent, entity) after ControlStickSprite::create(), if you decide to use this solution