patterncppMinor
Messy handler for multiple touch events in a game
Viewed 0 times
eventsmessygameformultiplehandlertouch
Problem
I have a mobile game, utilizing cocos2d-x, that has a handler for touch events.
What it does:
-
In some some parts of the loop there are Boolean indicators that say:
and so on...
It's a very long and complex function, and I was wondering if I can rewrite this function with a better structure.
```
void MainGame::onTouchesBegan(const std::vector& touches, Event *event)
{
bool FontSignTouched = false;
for( auto& touch : touches)
{
auto location = touch->getLocation();
//once the next level button is triggered to show all other touchs are disabled
Vector ScoreContainerChildren = pScoreContainer->getChildren();
for (auto iter = ScoreContainerChildren.begin(); iter != ScoreContainerChildren.end(); ++iter)
{
Node childNode = iter;
if(childNode->getTag() == buttons_tags::SOUND_BT)
{
Point thisTouchScoreContainer = pScoreContainer->convertTouchToNodeSpace(touch);
Sprite pSoundBT = static_cast(iter);
if(pSoundBT->getBoundingBox().containsPoint(thisTouchScoreContainer))
{
pScoreContainer->setSoundButtonSpriteFrame(true);
break;
}
}
if(childNode->getTag() == buttons_tags::POINTS_CONTAINER_NODE)
{
Sprite pCoinsBT = (Sprite)childNode->getChildByTag(buttons_tags::COINS_IMG_BT);
Sprite pCoinsCountFrameBT = (Sprite)childNode->getChildByTag(buttons_tags::COINS_COUNT_FRAME);
What it does:
- Main function that gets the touch.
- Loop in module A. All sprites check if the touch coordinates are in the boundary of one of the sprite. If yes, call the function that associated with the action and break the loop.
- The same as 2 with module B.
- Same as 2,3, with module C and so on.
-
In some some parts of the loop there are Boolean indicators that say:
- If
boolInd1is true, ignore touches from Module A.
- If
boolInd2is true, ignore touches from Module B.
and so on...
It's a very long and complex function, and I was wondering if I can rewrite this function with a better structure.
```
void MainGame::onTouchesBegan(const std::vector& touches, Event *event)
{
bool FontSignTouched = false;
for( auto& touch : touches)
{
auto location = touch->getLocation();
//once the next level button is triggered to show all other touchs are disabled
Vector ScoreContainerChildren = pScoreContainer->getChildren();
for (auto iter = ScoreContainerChildren.begin(); iter != ScoreContainerChildren.end(); ++iter)
{
Node childNode = iter;
if(childNode->getTag() == buttons_tags::SOUND_BT)
{
Point thisTouchScoreContainer = pScoreContainer->convertTouchToNodeSpace(touch);
Sprite pSoundBT = static_cast(iter);
if(pSoundBT->getBoundingBox().containsPoint(thisTouchScoreContainer))
{
pScoreContainer->setSoundButtonSpriteFrame(true);
break;
}
}
if(childNode->getTag() == buttons_tags::POINTS_CONTAINER_NODE)
{
Sprite pCoinsBT = (Sprite)childNode->getChildByTag(buttons_tags::COINS_IMG_BT);
Sprite pCoinsCountFrameBT = (Sprite)childNode->getChildByTag(buttons_tags::COINS_COUNT_FRAME);
Solution
You can easily make the code more readable by breaking the large method into three smaller ones. My suggestion:
With more knowledge of the project, you could probably break it down further into other sub-methods, possibly reusable ones too. But the point to note here is: Don't be afraid of breaking down a large function into a few case-specific helper methods. This is what C++ private methods are for.
Also, notice that I've changed all your
void MainGame::ProcessScoreContainerChildren(const Touch * touch)
{
Vector ScoreContainerChildren = pScoreContainer->getChildren();
for (auto childNode : ScoreContainerChildren)
{
if(childNode->getTag() == buttons_tags::SOUND_BT)
{
Point thisTouchScoreContainer = pScoreContainer->convertTouchToNodeSpace(touch);
Sprite* pSoundBT = static_cast(childNode);
if(pSoundBT->getBoundingBox().containsPoint(thisTouchScoreContainer))
{
pScoreContainer->setSoundButtonSpriteFrame(true);
break;
}
}
if(childNode->getTag() == buttons_tags::POINTS_CONTAINER_NODE)
{
Sprite* pCoinsBT = (Sprite*)childNode->getChildByTag(buttons_tags::COINS_IMG_BT);
Sprite* pCoinsCountFrameBT = (Sprite*)childNode->getChildByTag(buttons_tags::COINS_COUNT_FRAME);
Point thisTouchPointsContainerNode = childNode->convertTouchToNodeSpace(touch);
if(pCoinsBT->getBoundingBox().containsPoint(thisTouchPointsContainerNode))
{
setPopUpWindow();
break;
}
if(pCoinsCountFrameBT->getBoundingBox().containsPoint(thisTouchPointsContainerNode))
{
setPopUpWindow();
break;
}
}
}
}
void MainGame::ProcessSelectionChildren(const Touch * touch, bool & FontSignTouched)
{
auto location = touch->getLocation();
Vector FontSelectionContainerChildren = pFontSelectionContainer->getChildren();
for (auto childNode : FontSelectionContainerChildren)
{
if(childNode->getTag() == sign_tags::LETTER_SIGH)
{
Point thisTouchPositionFontSelection = this->convertTouchToNodeSpace(touch);
Sign* pSign = static_cast(childNode);
if(pSign->getBoundingBox().containsPoint(thisTouchPositionFontSelection))
{
Settings::getInstance()->getSoundManager().playEffect(FONT_TO_SOLUTION);
pSolutionContainer->setFontSelectionToSulotionFont(pSign);
FontSignTouched = true;
break;
}
}
}
Vector thisSelectionChildren = this->getChildren();
if(!FontSignTouched)
{
for (auto childNode : thisSelectionChildren)
{
if(childNode->getTag() == sign_tags::LETTER_SIGH)
{
Point thisTouchPositionFontSelection = this->convertTouchToNodeSpace(touch);
Sign* pSign = static_cast(childNode);
if(pSign->getBoundingBox().containsPoint(thisTouchPositionFontSelection))
{
Settings::getInstance()->getSoundManager().playEffect(SOLUTION_TO_FONT);
pFontSelectionContainer->removeFromMainParantAndSetInSprite(pSign);
break;
}
}
if(childNode->getTag() == buttons_tags::NEXT_BT)
{
Point thisTouchPositionFontSelection = this->convertTouchToNodeSpace(touch);
Sign* pSign = static_cast(childNode);
if(pSign->getBoundingBox().containsPoint(thisTouchPositionFontSelection))
{
}
}
}
}
else
{
FontSignTouched = false;
}
}
void MainGame::onTouchesBegan(const std::vector& touches, Event *event)
{
bool FontSignTouched = false;
for (auto& touch : touches)
{
ProcessScoreContainerChildren(touch);
ProcessSelectionChildren(touch, FontSignTouched);
}
}With more knowledge of the project, you could probably break it down further into other sub-methods, possibly reusable ones too. But the point to note here is: Don't be afraid of breaking down a large function into a few case-specific helper methods. This is what C++ private methods are for.
Also, notice that I've changed all your
for() loops to use range based iteration. This also made the code more compact and eliminated a few temp variables.Code Snippets
void MainGame::ProcessScoreContainerChildren(const Touch * touch)
{
Vector<Node *> ScoreContainerChildren = pScoreContainer->getChildren();
for (auto childNode : ScoreContainerChildren)
{
if(childNode->getTag() == buttons_tags::SOUND_BT)
{
Point thisTouchScoreContainer = pScoreContainer->convertTouchToNodeSpace(touch);
Sprite* pSoundBT = static_cast<Sprite*>(childNode);
if(pSoundBT->getBoundingBox().containsPoint(thisTouchScoreContainer))
{
pScoreContainer->setSoundButtonSpriteFrame(true);
break;
}
}
if(childNode->getTag() == buttons_tags::POINTS_CONTAINER_NODE)
{
Sprite* pCoinsBT = (Sprite*)childNode->getChildByTag(buttons_tags::COINS_IMG_BT);
Sprite* pCoinsCountFrameBT = (Sprite*)childNode->getChildByTag(buttons_tags::COINS_COUNT_FRAME);
Point thisTouchPointsContainerNode = childNode->convertTouchToNodeSpace(touch);
if(pCoinsBT->getBoundingBox().containsPoint(thisTouchPointsContainerNode))
{
setPopUpWindow();
break;
}
if(pCoinsCountFrameBT->getBoundingBox().containsPoint(thisTouchPointsContainerNode))
{
setPopUpWindow();
break;
}
}
}
}
void MainGame::ProcessSelectionChildren(const Touch * touch, bool & FontSignTouched)
{
auto location = touch->getLocation();
Vector<Node *> FontSelectionContainerChildren = pFontSelectionContainer->getChildren();
for (auto childNode : FontSelectionContainerChildren)
{
if(childNode->getTag() == sign_tags::LETTER_SIGH)
{
Point thisTouchPositionFontSelection = this->convertTouchToNodeSpace(touch);
Sign* pSign = static_cast<Sign*>(childNode);
if(pSign->getBoundingBox().containsPoint(thisTouchPositionFontSelection))
{
Settings::getInstance()->getSoundManager().playEffect(FONT_TO_SOLUTION);
pSolutionContainer->setFontSelectionToSulotionFont(pSign);
FontSignTouched = true;
break;
}
}
}
Vector<Node *> thisSelectionChildren = this->getChildren();
if(!FontSignTouched)
{
for (auto childNode : thisSelectionChildren)
{
if(childNode->getTag() == sign_tags::LETTER_SIGH)
{
Point thisTouchPositionFontSelection = this->convertTouchToNodeSpace(touch);
Sign* pSign = static_cast<Sign*>(childNode);
if(pSign->getBoundingBox().containsPoint(thisTouchPositionFontSelection))
{
Settings::getInstance()->getSoundManager().playEffect(SOLUTION_TO_FONT);
pFontSelectionContainer->removeFromMainParantAndSetInSprite(pSign);
break;
}
Context
StackExchange Code Review Q#59356, answer score: 7
Revisions (0)
No revisions yet.