摘要: 主要解決cocos2dx-2.2.2版本中, UIButton顯示不了disable狀態圖的問題. 順便, 理解了一下cocos2dx中UIWidget的渲染原理.
- 博客: http://www.cnblogs.com/jhzhu
- 郵箱: jhzhuustc@gmail.com
- 作者: 知明所以
- 時間: 2014-03-23
發現問題
在cocostudio中添加一個UIButton組件, 我們可以看到通常以一下按鈕的三態:normal
,pressed
,disable
. 但是,當我們設置了disable
狀態之后, 在我們的游戲項目中, 對某個按鈕執行button->setEnable(false)
后, 按鈕居然完全不見了?!
解決方法
修改
Widget::visit()
方法, 改為:void Widget::visit() { CCNodeRGBA::visit(); }
即, 刪除
if
判斷.修改
Widget::setEnable()
方法, 改為:void Widget::setEnabled(bool enabled) { _enabled = enabled; if(_widgetChildren && _widgetChildren->count() > 0) { CCObject* child; CCARRAY_FOREACH(_widgetChildren, child) { ((Widget*)child)->setEnabled(enabled); } } setBright( enabled );//增加此行 }
即, 增加
setBright( enabled );
行.
如果想知其所以然的, 下面是解釋.
解釋
Widget的詭異渲染
Widget::visit()
的實現如下:
void Widget::visit() { if (_enabled) { CCNodeRGBA::visit(); } }
如果您沒時間仔細看過cocos2dx的源碼, 這里可以有一個簡單的解釋:
1. visit()
方法最初是從CCNode
繼承過來的. 也就是說任何顯示對象都有這個方法.
2. CCNode::visit()
就是CCNode的渲染函數, 是視圖渲染中最重要的一個函數, 跟IOS開發中的UIView::draw()
功能類似. 框架在每一幀都會調用這個方法, 而這個方法負責當前組件的openGL
繪制. 我們可以看下CCNode::visit()
的實現來作為驗證:
void CCNode::visit() { ... this->draw(); ... } //算了, 這段代碼有點長, 有些人時間比較寶貴可能沒興趣看. 我就放在***附錄***了.(*^__^*)
看到這里, 你肯定一經發現問題了: 為什么只有在_enabled
為true
的時候才渲染?難道不是坑爹么?disable
狀態就不理了么?
可能cocos2dx另有打算, 我沒理解清楚. 不過這個功能既然不符合我的要求, 我就果斷的把那個if
去掉了. (誰讓咱們是程序員呢?).
但是, 問題並沒有完全結束. 還是沒有顯示disable
狀態.
Widget的enable?
UIButton::setEnable()
方法比較短, 貼出來看看.
void Widget::setEnabled(bool enabled) { _enabled = enabled; if(_widgetChildren && _widgetChildren->count() > 0) { CCObject* child; CCARRAY_FOREACH(_widgetChildren, child) { ((Widget*)child)->setEnabled(enabled); } } }
發現這段代碼除了把_enable
和所有孩子的_enable
狀態設為false
, 沒做任何事情. 通過查看widget
的代碼, 很容易看到, widget
的三態是通過下面的是三個函數實現的:
void Widget::onPressStateChangedToNormal(){} void Widget::onPressStateChangedToPressed(){} void Widget::onPressStateChangedToDisabled(){}
不錯,他們都是空函數.子類根據需要來實現這三個函數.
下面是setBright
的實現:
void Widget::setBright(bool bright) { _bright = bright; if (_bright) { _brightStyle = BRIGHT_NONE; setBrightStyle(BRIGHT_NORMAL); } else { onPressStateChangedToDisabled(); } }
由上代碼可以看出. 而我們調用setBright(false)
, 即可根據widget
當前的狀態, 渲染不同的圖像.
附錄
代碼片段一CCNode::visit()
void CCNode::visit() { // quick return if not visible. children won't be drawn. if (!m_bVisible) { return; } kmGLPushMatrix(); if (m_pGrid && m_pGrid->isActive()) { m_pGrid->beforeDraw(); } this->transform(); CCNode* pNode = NULL; unsigned int i = 0; if(m_pChildren && m_pChildren->count() > 0) { sortAllChildren(); // draw children zOrder < 0 ccArray *arrayData = m_pChildren->data; for( ; i < arrayData->num; i++ ) { pNode = (CCNode*) arrayData->arr[i]; if ( pNode && pNode->m_nZOrder < 0 ) { pNode->visit(); } else { break; } } // self draw this->draw(); for( ; i < arrayData->num; i++ ) { pNode = (CCNode*) arrayData->arr[i]; if (pNode) { pNode->visit(); } } } else { this->draw(); } // reset for next frame m_uOrderOfArrival = 0; if (m_pGrid && m_pGrid->isActive()) { m_pGrid->afterDraw(this); } kmGLPopMatrix(); }
Written with StackEdit.