Wait for an event to happen

I am trying to make a simple board game(Mensch), in my game when your turn comes up, the dice starts to Bounce, this bouncing show that you should touch the dice in order to roll it. and after 2 or 3 seconds the rolling function gives a random number.
and here is the problem : I don’t know how to wait for the player to touch the dice. I mean when the dice starts bouncing the program should almost stop and wait for the player to touch the dice. I don’t know how to do it. here is some code:

Utils::diceLayer()->bounce();
int rolledNumber;
if(Utils::diceLayer()->getIsTouched()){
     rolledNumber = Utils::diceLayer()->roll();
}

there should be a pause between line 2 and 3 till the player touches the dice and isTouched become true, and the program enters if statement.
I hope I could have explained my problem.

take a look at the sample `HelloWorld’ program.

  1. It seems you want an event listener on your layer

  2. player touches the screen

  3. evaluate the touch location to see if they touched inside the bounding box of the dice

  4. if they did, run your roll()

Thanks for your reply, but my problem is still unsolved. I didn’t find anything useful in HelloWorld. I think I should use “schedule” but I dont know how.

A scheduler is not the solution here. You can use a delay action with 2 or 3 seconds and call the dice throw in a lambda function, so the random number is given after 2 or 3 seconds.

In some init function for the layer create the listener:

EventListenerTouchOneByOne *listener = EventListenerTouchOneByOne::create();

and the handler code:

// Create a "one by one" touch event listener (processes one touch at a time)
auto listener = EventListenerTouchOneByOne::create();

// When "swallow touches" is true, then returning 'true' from the onTouchBegan method will "swallow" the touch event, preventing other listeners from using it.
listener->setSwallowTouches(true);

// Using a lambda expression to implement onTouchBegan event callback function
listener->onTouchBegan = [&](Touch* touch, Event* event)
{
    // Assuming your dice is a Sprite object
    // Cast to the correct object your dice is made of
    Sprite* currentTarget = static_cast<Sprite*>(event->getCurrentTarget());

    // Get the position of the current point relative to the target
    Point locationInNode = currentTarget->convertToNodeSpace(touch->getLocation());
    Size s = currentTarget->getContentSize();
    Rect rect = Rect(0, 0, s.width, s.height);

    // Check the click area
    if (!rect.containsPoint(locationInNode))
    {
        return false;
    }

    // Output dice throw; here you would also implement and run the delay action
    int rolledNumber = Utils::diceLayer()->roll();

    return true;
};

// add listener to dispatcher; _eventDispatcher is a member of Layer
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, myDiceSprite);

If you touched the dice, it will trigger the event and the handler handles your touch.

1 Like

Thanks for your reply.

my function now looks like this :

void Game::handleGreenMove(){
    Utils::diceLayer()->bounce();
    int rolledNumber;

    auto listener = EventListenerTouchOneByOne::create();
    listener->setSwallowTouches(true);

    listener->onTouchBegan = [&](Touch* touch, Event* event){
        //Dice is a layer
        Dice* currentTarget = static_cast<Dice*>(event->getCurrentTarget());

        Point locationInNode = currentTarget->returnSprite()->convertToNodeSpace(touch->getLocation());
        Size s = currentTarget->getContentSize();
        Rect rect = Rect(0, 0, s.width, s.height);

        if (!rect.containsPoint(locationInNode)){
            return false;
        }
        rolledNumber = Utils::diceLayer()->roll();
        return true;
    };

    // add listener to dispatcher; _eventDispatcher is a member of Layer
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
} 

But immediately after I touch the screen, the program halts and logcat gives me “fatal signal 11” error.
what am I doing wrong?
I know that the problem comes from

Point locationInNode = currentTarget->returnSprite()->convertToNodeSpace(touch->getLocation());

my Dice is a layer and in that layer there is a function “returnSprite”. I know that “myTarget” should be the Dice layer that I created before but I don’t know how to do it.

(sorry I am new in cocos2d-x)

you are subclassing Layer*? I guess it should still work the same. Also, I’d implement OnTouchEnded even if you aren’t planning to put anything there. I do though.

And just to be sure, you have a returnSprite() in the Dice* class?

I’d say place some break points and see how far you get.

there are several Sprites in my Dice layer so I wrote “returnSprite()” I know its not efficient but it works! (and of course its my first official game! I am a complete beginner ! I think it will took forever to complete :smiley: )

but what does returnSprite() do?

in my Dice layer I have 2 sprites with same size and position, returnSprite() simply returns one of them so I can get its bounding box, and see if its been touched or not.

so why not put your events on those Sprites? That seems better flow and solves some issues for sure.

1 Like
  1. Check the target and the object from returnSprite() for nullptr first.
  2. Why are you using a layer with your sprites on it? Stacking layers got pretty meaningless in cocos2d-x 3.x, as every node can now have a listener and react to events

Btw.: Are you a German speaker, as your game is Mensch ärgere dich nicht? :wink:

thank you, I will try that.
No I’m not German, I’m Persian. this game seemed easy at first. but now I am stuck in it ! :smiley:
I think I will have to rewrite the whole code !
so you say its better to have two sprites on the main layer rather than putting them on a separate layer(here is Dice)?
thanks again for your answers.

Yes, 2 sprites on the main layer. Put your event listeners on the sprites.

1 Like

I see. Prince of Persia was a great game, btw. :wink:

I’m in for 4 player online “Mensch” game, when it’s ready!

Yes. Forget about stacking layers, except you want to make an UI overlay, but even then it’s not necessary,
Sprites/Nodes are rendered based on their zOrder and the order you add them to the layer, like a stack.

In previous versions there were no event listeners you could have added to every node, so you had to deal with layers.

Just make a main layer and add your sprites. Add them in the order you want them to be drawn or set their zOrder.

You’r welcome.

1 Like

I did what you suggested, I got rid of my extra layer and put the dice as a sprite on the main layer. but I still have the first problem. here is some of my code:

int Dice::startBouncing(){
	isBNouncing = true;
	isTouched = false;

	int rolledNumber;

	auto action0 = ScaleTo::create(0.3f, 1.1f, 1.1f);
	auto action1 = ScaleTo::create(0.3f, 0.99f, 0.99f);
	ActionInterval *bouncingAction = Sequence::create(action0, action1, nullptr);
	auto action = RepeatForever::create(bouncingAction);

	this->runAction(action);

	auto listener = EventListenerTouchOneByOne::create();

	listener->setSwallowTouches(true);

	listener->onTouchBegan = [&](Touch* touch, Event* event){
	Dice* currentTarget = static_cast<Dice*>(event->getCurrentTarget());

    Point locationInNode = currentTarget->convertToNodeSpace(touch->getLocation());
    Size s = currentTarget->getContentSize();
    Rect rect = Rect(0, 0, s.width, s.height);

    if (!rect.containsPoint(locationInNode)){
        return false;
    }
    rolledNumber = Dice::roll();
    CCLOG("%s %d","rolledNumber : ",rolledNumber);
    return true;
};

	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
	CCLOG("%s %d","rolledNumber 2 : ",rolledNumber);
	return rolledNumber;
}

int Dice::roll(){
	CCLOG("%s","in roll");
	return 4;
}

and this code is in my main layer :

int a = dice->startBouncing();
    CCLOG("%d",a); 

‘a’ should be ‘4’, because of my roll() function, but its not, before touching the dice

if (!rect.containsPoint(locationInNode)){
            return false;
        }

executes and then startBouncing() returns a random integer. when I touch the Dice everything is ok, roll() executes, but the program continues with that unwanted ‘a’. how can I change this part of my code so that instead of returning false it try’s again and grab some other touch location and see if its in the sprite’s bounding box.
(I am very tired and my English is not good, hope I could have explained the problem)

it looks like you have an extra }; and are returning a bool instead of an int as the return type that is expected. Take a look at the code you posted and make sure it is formatted properly.

I dont think that I have any syntax problem, I have pasted the code here, you may check it again. and returning bool is for :

[&](Touch* touch, Event* event)   

I think onTouchBegan function should return bool.

oh, I see, it is the formatting of the lambda that was throwing me off

have you considered using currentTarget->getBounding Box() instead of the Size and Rect way you are doing it now. You still need these but perhaps this is easier:

cocos2d::Vector2 p = touch->getLocation();
cocos2d::Rect rect = currentTarget->getBoundingBox();

if( ! rect.containsPoint(p))
{
        return false; 
}

thanks, but I still got that problem !
I don’t want this if statement to return false, I want the program to stay in this function till someone touches the dice.