之前写过了ETMutiMenu
实现了多点触控的菜单类,突然想起来,就来写篇文章来分析下cocos2dx-3.0
中的Menu
。
用法
我刚刚开始的时候,很笨的先创建好MenuItem*
然后用Menu
的某个静态方法来创建。
后来阅读源码之后,发现Menu
是遍历所有子儿子的,所以只要创建一个空的Menu
,之后addChild
便是。
这里需要注意的是Menu
的子儿子必须是MenuItem
的子类,不然在运行过程中会崩溃。
路径
1
| cocos/2d/menu-nodes/CCMenu.h
cocos/2d/menu-nodes/CCMenu.cpp
|
源码分析
先贴上头文件,删去了一些我认为不是很重要的类声明,想要自己仔细看看的,还是自己去看源码吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| #ifndef __CCMENU_H_ #define __CCMENU_H_
#include "CCMenuItem.h" #include "CCLayer.h" #include "CCVector.h" #include "CCEventTouch.h" #include "CCValue.h"
NS_CC_BEGIN
class CC_DLL Menu : public Layer { public: enum class State { WAITING, TRACKING_TOUCH, };
static Menu* create();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) #else static Menu* create(MenuItem* item, ...) CC_REQUIRES_NULL_TERMINATION; #endif
virtual bool isEnabled() const { return _enabled; } virtual void setEnabled(bool value) { _enabled = value; };
virtual bool onTouchBegan(Touch* touch, Event* event); virtual void onTouchEnded(Touch* touch, Event* event); virtual void onTouchCancelled(Touch* touch, Event* event); virtual void onTouchMoved(Touch* touch, Event* event);
virtual void removeChild(Node* child, bool cleanup) override;
virtual void addChild(Node * child) override; virtual void addChild(Node * child, int zOrder) override; virtual void addChild(Node * child, int zOrder, int tag) override;
virtual void onEnter() override; virtual void onExit() override;
virtual void setOpacityModifyRGB(bool bValue) override {CC_UNUSED_PARAM(bValue);} virtual bool isOpacityModifyRGB(void) const override { return false;}
virtual std::string getDescription() const override;
CC_CONSTRUCTOR_ACCESS:
Menu() : _selectedItem(nullptr) {} virtual ~Menu();
bool init();
bool initWithArray(const Vector<MenuItem*>& arrayOfItems);
protected: bool _enabled;
MenuItem* getItemForTouch(Touch * touch);
State _state;
MenuItem *_selectedItem;
private: CC_DISALLOW_COPY_AND_ASSIGN(Menu); };
NS_CC_END
#endif
|
当然是先看看初始化的相关的函数initWithArray
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| bool Menu::initWithArray(const Vector<MenuItem*>& arrayOfItems) { if (Layer::init()) { _enabled = true;
int z=0; for (auto& item : arrayOfItems) { this->addChild(item, z); z++; }
_selectedItem = nullptr; _state = Menu::State::WAITING;
setCascadeColorEnabled(true); setCascadeOpacityEnabled(true); auto touchListener = EventListenerTouchOneByOne::create(); touchListener->setSwallowTouches(true);
touchListener->onTouchBegan = CC_CALLBACK_2(Menu::onTouchBegan, this); touchListener->onTouchMoved = CC_CALLBACK_2(Menu::onTouchMoved, this); touchListener->onTouchEnded = CC_CALLBACK_2(Menu::onTouchEnded, this); touchListener->onTouchCancelled = CC_CALLBACK_2(Menu::onTouchCancelled, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
return true; } return false; }
|
接着当然是看看触摸事件绑定的那四个函数怎么写了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| bool Menu::onTouchBegan(Touch* touch, Event* event) { if (_state != Menu::State::WAITING || ! _visible || !_enabled) { return false; }
for (Node *c = this->_parent; c != nullptr; c = c->getParent()) { if (c->isVisible() == false) { return false; } }
_selectedItem = this->getItemForTouch(touch); if (_selectedItem) { _state = Menu::State::TRACKING_TOUCH; _selectedItem->selected();
return true; }
return false; }
void Menu::onTouchEnded(Touch* touch, Event* event) { CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchEnded] -- invalid state"); this->retain();
if (_selectedItem) { _selectedItem->unselected(); _selectedItem->activate(); }
_state = Menu::State::WAITING; this->release(); }
void Menu::onTouchCancelled(Touch* touch, Event* event) { CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchCancelled] -- invalid state"); this->retain();
if (_selectedItem) { _selectedItem->unselected(); }
_state = Menu::State::WAITING; this->release(); }
void Menu::onTouchMoved(Touch* touch, Event* event) { CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchMoved] -- invalid state");
MenuItem *currentItem = this->getItemForTouch(touch);
if (currentItem != _selectedItem) { if (_selectedItem) { _selectedItem->unselected(); }
_selectedItem = currentItem; if (_selectedItem) { _selectedItem->selected(); } } }
|
上面讲到的一个很关键的函数getItemForTouch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| MenuItem* Menu::getItemForTouch(Touch *touch) { Point touchLocation = touch->getLocation();
if (!_children.empty()) { for (auto iter = _children.crbegin(); iter != _children.crend(); ++iter) { MenuItem* child = dynamic_cast<MenuItem*>(*iter); if (child && child->isVisible() && child->isEnabled()) { Point local = child->convertToNodeSpace(touchLocation); Rect r = child->rect(); r.origin = Point::ZERO;
if (r.containsPoint(local)) { return child; } } } }
return nullptr; }
|
本来想最开始讲的onEneter
和onExit
现在放到了最后说,也是醉了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
void Menu::onEnter() { Layer::onEnter(); }
void Menu::onExit() { if (_state == Menu::State::TRACKING_TOUCH) { if (_selectedItem) { _selectedItem->unselected(); _selectedItem = nullptr; } _state = Menu::State::WAITING; }
Layer::onExit(); }
void Menu::removeChild(Node* child, bool cleanup) { MenuItem *menuItem = dynamic_cast<MenuItem*>(child); CCASSERT(menuItem != nullptr, "Menu only supports MenuItem objects as children");
if (JselectedItem == menuItem) { _selectedItem = nullptr; }
Node::removeChild(child, cleanup); }
|
最后看到onExit
会在Menu
正在处理触摸状态下对以选中的MenuItem
释放。
而且removeChild
自然也是会对正在选中的MenuItem
释放=。=
到此,Menu
的分析已经结束了,下篇文章还是整理下Node
和Ref
这些基类吧。
Ending
帅哥最近貌似也挺忙的,自己这几天也懈怠了,批评下自己,帅哥啊,求提醒
啊,求监督
啊。