I'm cocos2d-x beginner and having trouble with TouchBegan event + function.
I always get EXC_BAD_ACCESS in the [label->setString("Foo Bar");] point when I run this simple code with xCode(5.1.1 + iPhone sim 7.1).
I tried a few ways to solve, but I'm completely stuck up in this problem for 2 or 3 days.
Does anyone have any idea for the wrong point?
HelloWorld.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
class HelloWorld : public cocos2d::LayerGradient
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
cocos2d::Label* label;
CREATE_FUNC(HelloWorld);
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorld.cpp
#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
auto scene = Scene::create();
auto layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}
bool HelloWorld::init()
{
if(!LayerGradient::initWithColor(Color4B(0, 0, 0, 255), Color4B(50, 50, 50, 255))){
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
auto label = Label::create("Hello World", "Arial", 112);
label->setPosition(Vec2(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - label->getContentSize().height));
this->addChild(label, 1);
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
this->getEventDispatcher()->addEventListenerWithFixedPriority(listener, 100);
return true;
}
bool HelloWorld::onTouchBegan(Touch *touch, Event *event){
label->setString("Foo Bar");
return true;
}
Thank you.
Your problem is in this line :
auto label = Label::create("Hello World", "Arial", 112);
In this line you declare a new variable named label whose scope is only for the init function. You also have a class variable also named label and they're not the same. If you declare a variable in a method with the same name as a class variable, it will shadow it. Consider this example :
class A {
int variable;
void method() {
int variable; // this is not the same thing as the one above
variable = 5; //assignment to local variable
this->variable = 3; //asignment to class variable
}
}
So you actually don't assign anything to your class variable label which you later reference in the touch method. The easiest way to solve this would be to remove the auto keyword at auto label from your init() method.
As noted by #Losiowaty, the use of auto can be dangerous sometimes.
cocos2d::Label* label = cocos2d::Label::create("Hello World", "Arial", 112);
Also, there might be a better constructor to use: http://www.cocos2d-x.org/reference/native-cpp/V3.1rc0/db/de4/classcocos2d_1_1_label.html
Related
I am developing nema stepper motor driver interfacing with arduino. I created a class. I named it Axis. I want to create an axis object for each motor with this class. but, I can not call the attachInterrupt function in the arduino.h from the class. It return this error :
In member function 'void Axis::setReverseInt()':
parsstep.cpp:12:77: error: invalid use of non-static member function 'void Axis::revDirection()'
attachInterrupt(digitalPinToInterrupt(reverseDirPin), revDirection, RISING);
Axis::Axis()
{
}
Axis::~Axis()
{
}
void Axis::setReverseInt () {
attachInterrupt(digitalPinToInterrupt(reverseDirPin), revDirection, RISING);
}
void Axis::setStopInt () {
//attachInterrupt(digitalPinToInterrupt(stopPin), stopMotor, RISING);
}
void Axis::revDirection()
{
dirMode = !dirMode;
}
void Axis::stopMotor()
{
moveMotor = !moveMotor;
}
void Axis::startMotor()
{
moveMotor = true;
}
void Axis::moveStep(int pulseRev, byte laps, boolean dir)
{
digitalWrite(dirPin, dir);
int totalPulse = pulseRev * laps;
for (int i = 0; i < totalPulse; i++)
{
speedValue = map((analogRead(speedPin)), 0, 1023, 2000, 10);
digitalWrite(pulsePin, HIGH);
delayMicroseconds(speedValue);
digitalWrite(pulsePin, LOW);
delayMicroseconds(speedValue);
}
}
I faced a similar issue when writing my own libraries for a big project I've been working on. It happened to me while configuring interrupts inside a method. I found a workaround that while it might seem a bit more complex, it does the trick. In my case, I used it for reading a rotary encoder its switch.
What I used is a pointer to an ISR handler and a method to call it. On your setup function you can call the initialization of the ISR service.
First, in your .h file declare the following methods (in addition to your own):
void init();
static void ISRHandler();
void setupISRHandler(uint8_t pin, void (*ISR)(void), int state); // this will configure your ISR
and a pointer:
static Axis *_ISRPointer; // can be declared as private (like in here from the '_' prefix)
Then, in your .cpp file you use it like this:
#include "Axis.h"
Axis *Axis::_ISRPointer = nullptr;
you will initialize the ISR handler during the setup using the init method:
void Axis::init(){
setupISRHandler(reverseDirPin, Axis::ISRHandler, RISING);
}
the setup method looks like:
void Axis::setupISRHandler(uint8_t pin, void (*ISRFunction)(void), int state){
attachInterrupt(digitalPinToInterrupt(pin), ISRFunction, state);
}
the ISRHandler is in charge of calling the ISR routine:
void Axis::ISRHandler(){
_ISRPointer-> revDirection();
}
void Axis::revDirection(void){
// Do not forget to disable interrupts
cli();
// your routine goes here
dirMode= !dirMode;
sei();
}
Therefore, you will do something like this in your main .ino file:
#include "Axis.h"
#include <whateverotherlibraryyouneed.h>
Axis ax = Axis(); // your new object
void setup(){
ax.init();
ax.startMotor();
}
void loop(){
if(day == SUNNY){
ax.moveStep(100, 2 , CLOCKWISE);
} else {
ax.stopMotor();
}
ax.someOtherMethodToControlTheMotor(anyArgumentYouWant);
}
I hope that my convoluted solution helps
Cheers
Dr. Daniel
I am new to cococs2dx and I am working on a code involving PhysicsJointFixed. I am using cocos2d-x-3.6. I am unable to compile the code as I wrote below following the guidelines and the PhysicsTest.cpp.
My GameLayer.h looks like:
class GameLayer : public cocos2d::Layer
{
GameLayer();
virtual ~GameLayer();
virtual bool init();
static Scene* createScene();
void setPhyWorld(PhysicsWorld* world){m_world = world;}
CREATE_FUNC(GameLayer);
private:
PhysicsWorld* m_world;
ShapeSprite* _square; //ShapeSprite extends Sprite
ShapeSprite* _square1;
PhysicsJointFixed* joint;
...
}
The createScene method in GameLayer.cpp:
Scene* GameLayer::createScene()
{
auto scene = Scene::createWithPhysics();
auto layer = GameLayer::create();
layer->setPhyWorld(scene->getPhysicsWorld());
scene->addChild(layer);
return scene;
}
Then inside GameLayer::init()
bool GameLayer::init()
{
if ( !Layer::init() )
{
return false;
}
...
...
_square = ShapeSprite::gameSpriteWithFile("square.png");
auto squareBody = PhysicsBody::createBox(Size(200,200));
_square->setPhysicsBody(squareBody);
_square->setPosition(Vec2(_screenSize.width * 0.5f, _screenSize.height * 0.7f));
_square1 = ShapeSprite::gameSpriteWithFile("square1.png");
auto squareBody1 = PhysicsBody::createBox(Size(100,100));
_square1->setPhysicsBody(squareBody1);
_square1->setPosition(Vec2(_screenSize.width * 0.5f, _screenSize.height * 0.7f));
this->addChild(_square);
this->addChild(_square1);
joint = PhysicsJointFixed::construct(_square->getPhysicsBody(), _square1->getPhysicsBody(),Vec2(100,100));
this->getScene()->getPhysicsWorld()->addJoint(joint);
return true;
}
The code gives EXC_BAD_ACCESS error on the line
this->getScene()->getPhysicsWorld()->addJoint(joint);
because, this->getScene()->getPhysicsWorld() returns NULL.
Please advise, how can I avoid the error. Any suggestion is appreciated. Thanks in advance.
This error occurs because your custom layer is not yet added to scene during init(). One possible solution is to override onEnterTransitionDidFinish() and add the joint there. In GameLayer.h add this:
virtual void onEnterTransitionDidFinish();
And move the joint adding code to GameLayer.cpp like this:
void GameLayer::onEnterTransitionDidFinish() {
joint = PhysicsJointFixed::construct(_square->getPhysicsBody(), _square1->getPhysicsBody(),Vec2(100,100));
this->getScene()->getPhysicsWorld()->addJoint(joint);
}
Hello I am creating an game in cocos2d-x and when I am scheduling an event on splash screen for run game .It shows compilation error (in expansion of macro 'schedule_selector')
Following is my code for this
Splash.h
#ifndef SPLASH_H_
#define SPLASH_H_
#include "cocos2d.h"
class CCSplashLayer : public cocos2d::CCLayer {
private :
void runGame();
public:
static cocos2d::CCScene* createScene();
virtual bool init();
CREATE_FUNC(CCSplashLayer);
};
#endif /* SPLASH_H_ */
And SplashScene.cpp
#include "splash.h"
#include "cocos2d.h"
#include "HelloWorldScene.h"
USING_NS_CC;
bool CCSplashLayer::init() {
if (!Layer::init()) {
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
auto sprite = Sprite::create("splash.png");
sprite->setScale(Director::getInstance()->getContentScaleFactor());
sprite->setPosition(Vec2(visibleSize.width / 2,visibleSize.height/2));
this->addChild(sprite, 0);
//This line cause problem show i symbol on this line in eclipse
this->scheduleOnce(schedule_selector(CCSplashLayer::runGame),4.0f);
return true;
}
Scene* CCSplashLayer::createScene() {
auto scene = CCScene::create();
auto layer = CCSplashLayer::create();
scene->addChild(layer);
return scene;
}
void CCSplashLayer::runGame(){
auto scene = HelloWorld::createScene();
Director::getInstance()->setDepthTest(true);
TransitionScene *transition = TransitionScene::create(0.5f, scene);
Director::getInstance()->pushScene(transition);
}
schedule_selector takes function pointer which needs a float argument for time.
Change method CCSplashLayer::runGame() to CCSplashLayer::runGame(float dt) in defination and declaration.
Also you are pushing a scene over splash scene, which is not recommended way for splash scene. You must replace splash scene with new scene because we never need to display splash again in game unless there is specific design requirement of game.
Try this macro:
#define CCDL_PERFORM_SELECTOR( __OWNER__, __DELAY__, __CALLFUNC_SELECTOR__ ) \
__OWNER__->runAction( cocos2d::Sequence::create( \
cocos2d::DelayTime::create( __DELAY__ ), \
cocos2d::CallFunc::create( CC_CALLBACK_0( __CALLFUNC_SELECTOR__,__OWNER__) ), \
nullptr )); \
(source: https://github.com/duksel/Cocos2dx-DukselLib/blob/master/Cocos2dx-DukselLib/DukselMacros.h#L70-L74)
Allow to use for any CCNode (+subclass) instance.
In your case will be:
bool CCSplashLayer::init()
{
CCDL_PERFORM_SELECTOR( this, 4.f, CCSplashLayer::runGame );
}
try like this...
in init method,
this->runAction(CCSequence::create(CCDelayTime::create(4.0f),
CCCallFuncN::create(this,callfuncN_selector(CCSplashLayer::runGame)),
NULL));
and add this method.
void CCSplashLayer::runGame(){
auto scene = HelloWorld::createScene();
Director::getInstance()->setDepthTest(true);
TransitionScene *transition = TransitionScene::create(0.5f, scene);
Director::getInstance()->pushScene(transition);
}
I want to perform drag and drop operation on these image.
How can i make it possible with the following code.
void storeLocation::changescene()
{
this->removeAllChildren();
//CCDirector::sharedDirector()->replaceScene(storeLocation::scene());
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
//CCScene* scene=CCScene::create();
storeLocation *layer = storeLocation::create();
CCSprite *k=CCSprite::create("background.png");
this->addChild(k,0);
k->setPosition(ccp(visibleSize.width/2+ origin.x, visibleSize.height/2 + origin.y));
CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(storeLocation::menuCloseCallback));
pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width ,
origin.y + pCloseItem->getContentSize().height/2));
pCloseItem->setScale(1.5);
// create menu, it's an autorelease object
CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
pMenu->setPosition(CCPointZero);
this->addChild(pMenu, 1);
this->addChild(pMenu, 1);
this->setTouchEnabled(true);
int l=5;
int posx=0,posy=0;
int count=0,r,j=-1,i=0,flag=0;
int x=20;
int b[30],a[30];
while(count<=5)
{
srand(time(0));
r=rand()%x+1;
flag=checktag(b,r,j);
if(flag==1)
{
b[i]=r;
i++;
count++;
j++;
}
}
int t;
CCObject* jt=NULL;
CCARRAY_FOREACH(images, jt)
{
// CCSize winSize = CCDirector::sharedDirector()->getWinSize();
//float i=winSize.width;
CCSprite *image = dynamic_cast<CCSprite*>(jt);
t=image->getTag();
for(i=0;i<l;i++)
{
if(t==b[i])
{
this->addChild(image);
image->setPosition(ccp(100+posx,100));
posx=posx+120;
}}}
To drag and drop images from one point to another on screen you have to use touch delegate methods
bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
void ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
void ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
Detect image on user touch in ccTouchBegan method, for this you can store all image objects in a array and check if touch is in rect of any image by using for loop.
To move the image with user touch change position of touched image(save touched image object in a global object) in ccTouchMoved.
And in ccTouchEnded method do whatever you want to do on droping image.
the easiest way to catch drag and drop events is by implementing the onTouchBegan and onTouchMoved and onTouchEnded methods like this:
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, sprite);
bool HelloWorld::onTouchBegan(Touch* touch, cocos2d::Event* event){
// this method is not needed but in order to implement the onTouchMoved you have to first implement onTouchBegan then the onTouchMoved
return true;
}
void HelloWorld::onTouchMoved(Touch* touch, cocos2d::Event* event){
if (sprite->getBoundingBox().containsPoint(touch->getLocation()))
{
sprite->setPosition(sprite->getPosition() + touch->getDelta());
}
}
void HelloWorld::onTouchEnded(Touch* touch, cocos2d::Event* event){
if (sprite->getBoundingBox().containsPoint(touch->getLocation()))
{
log("Sprite Drop Event");
}
}
While porting an cocos2d-x project from iOS to Android, I found a problem that will cause crashing on Android but not on iOS, to show this problem, I made a small modification to the HelloWorld sample. To reproduce this problem, just press the close button on the bottom-right corner, on Android it will crash but not on iOS.
The code that cause the crashing is:
void TestNode::test()
{
// This will cause crash on Android, but OK on iOS
CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy));
this->runAction(selector);
// This is ok on both Android and iOS
// CCCallFunc *selector = CCCallFunc::create(scene_, callfunc_selector(HelloWorld::destroyNode));
// scene_->runAction(selector);
// This is ok on both Android and iOS
// destroy();
}
The complete code as the following:
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
class TestNode : public cocos2d::CCNode {
public:
TestNode(cocos2d::CCLayer *scene);
~TestNode();
void test();
void destroy();
cocos2d::CCLayer *scene_;
cocos2d::CCSprite *sprite_;
};
class HelloWorld : public cocos2d::CCLayer
{
private:
TestNode *node_;
public:
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::CCScene* scene();
// a selector callback
void menuCloseCallback(CCObject* pSender);
// implement the "static node()" method manually
CREATE_FUNC(HelloWorld);
void destroyNode();
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp:
#include "HelloWorldScene.h"
#include "AppMacros.h"
USING_NS_CC;
TestNode::TestNode(cocos2d::CCLayer *scene):
scene_(scene)
{
sprite_ = CCSprite::create("CloseNormal.png");
sprite_->setPosition(ccp(200, 200));
scene_->addChild(sprite_, 255);
}
TestNode::~TestNode()
{
scene_->removeChild(sprite_, true);
scene_->removeChild(this, true);
CCLog("+++ ~TestNode");
}
void TestNode::test()
{
// This will cause crash on Android, but OK on iOS
CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy));
this->runAction(selector);
// This is ok on both Android and iOS
// CCCallFunc *selector = CCCallFunc::create(scene_, callfunc_selector(HelloWorld::destroyNode));
// scene_->runAction(selector);
// This is ok on both Android and iOS
// destroy();
}
void TestNode::destroy()
{
CCLog("+++ destroy");
delete this;
}
CCScene* HelloWorld::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::create();
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
/////////////////////////////
// 2. add a menu item with "X" image, which is clicked to quit the program
// you may modify it.
// add a "close" icon to exit the progress. it's an autorelease object
CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(HelloWorld::menuCloseCallback));
pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 ,
origin.y + pCloseItem->getContentSize().height/2));
// create menu, it's an autorelease object
CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
pMenu->setPosition(CCPointZero);
this->addChild(pMenu, 1);
/////////////////////////////
// 3. add your codes below...
// add a label shows "Hello World"
// create and initialize a label
CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", TITLE_FONT_SIZE);
// position the label on the center of the screen
pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - pLabel->getContentSize().height));
// add the label as a child to this layer
this->addChild(pLabel, 1);
// add "HelloWorld" splash screen"
CCSprite* pSprite = CCSprite::create("HelloWorld.png");
// position the sprite on the center of the screen
pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
// add the sprite as a child to this layer
this->addChild(pSprite, 0);
node_ = new TestNode(this);
this->addChild(node_);
return true;
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
// CCDirector::sharedDirector()->end();
if (node_) {
node_->test();
node_ = NULL;
}
//#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
// exit(0);
//#endif
}
void HelloWorld::destroyNode()
{
node_->destroy();
}
I don't know why this works on iOS but you should not do this anyway. When you run the code
CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy));
this->runAction(selector);
which calls the function
void TestNode::destroy()
{
CCLog("+++ destroy");
delete this;
}
you are destroying an object that the CCCallFunc action still keeps a pointer to. Then when the CCCallFunc object is destroyed, it runs the CC_SAFE_RELEASE macro which will call release on your TestNode object. However, by now that TestNode object is already deallocated and that is most likely the reason for your crash.