2016年4月29日 星期五

0429 cocos2d-x 顯示IOS 上方 狀態列

cocos2d-x 顯示IOS 上方 狀態列

https://www.v2ex.com/t/239103

需求

我現在需要讓我的 cocos2d-x 遊戲一直顯示白底黑字的狀態條。

實現

根據這篇文章,我的做法如下:

Info.plist

  • View controller-based status bar appearance NO
  • Status bar is initially hidden NO
  • Status bar style Gray style (default)

AppController.mm

frameworks/runtime-src/proj.ios_mac/ios/AppController.mm 中的application:didFinishLaunchingWithOptions: 添加如下代碼(完整代碼見後面)。
[[UIApplication sharedApplication].statusBarStyle:UIStatusBarStyleDefault];
[[UIApplication sharedApplication] setStatusBarHidden:NO];

// The OpenGLWindow
_window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] applicationFrame]];

RootViewController.mm

- (BOOL)prefersStatusBarHidden
{
    return NO;
}

- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleDefault;
}

問題

由於上提到的參考 文章 是顯示的黑底白字的狀態條,然而我的需求是白底黑字
最終產生的結果如下:
StatusBar
上面的黑色部分其實就是狀態條,只是背景(由於窗口被下移,沒有繪製那塊區域)也是黑色的,所以就黑成一團了。
cocos2d-x 提供的模板,是這樣創建 _window 的:
_window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
我根據上面的文章,將窗口下移,我修改後的代碼:
_window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] applicationFrame]];
UI 是這樣的結構(完整代碼見『附』):
如上圖,黃色為標識區域為 StatusBar
紅色標識區域為_window
_window 內部為 [_window]---rootViewController--> [viewController] ---view--> [eaglView]
eaglView 是最終 cocos2d-x 繪圖的區域,他填滿整個 _window
現在我想要的結果是讓上圖的黑色區域底色變成白色,由於我不懂 iOS ,我猜想的方案可能是:
  • 用 applicationFrame 創建窗口;新加一個和 _window 平級的白色 UIView ,放到黑色區域。
  • 或者用 bounds 創建窗口,弄一個固定高度為 StatusBar 高度的 UIView 將 eaglView 『擠』下去。
  • 或者能不能設置整個應用的底色為白色?
大致就是這些,請大家指點下我,最好有代碼。我不熟悉 iOS 開發。
感謝了!

application:didFinishLaunchingWithOptions 完整代碼:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    cocos2d::Application *app = cocos2d::Application::getInstance();
    app->initGLContextAttrs();
    cocos2d::GLViewImpl::convertAttrs();

    // Override point for customization after application launch.

    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
    [[UIApplication sharedApplication] setStatusBarHidden:NO];

    // Add the view controller's view to the window and display.
    _window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] applicationFrame]];
    CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [self.window bounds]
                                     pixelFormat: (NSString*)cocos2d::GLViewImpl::_pixelFormat
                                     depthFormat: cocos2d::GLViewImpl::_depthFormat
                              preserveBackbuffer: NO
                                      sharegroup: nil
                                   multiSampling: NO
                                 numberOfSamples: 0 ];

    [eaglView setMultipleTouchEnabled:YES];

    // Use RootViewController manage CCEAGLView
    viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
    viewController.wantsFullScreenLayout = YES;
    viewController.view = eaglView;

    // Set RootViewController to window
    if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)
    {
        // warning: addSubView doesn't work on iOS6
        [self.window addSubview: viewController.view];
    }
    else
    {
        // use this method on ios6
        [_window setRootViewController:viewController];
    }

    [_window makeKeyAndVisible];


    // IMPORTANT: Setting the GLView should be done after creating the RootViewController
    cocos2d::GLView *glview = cocos2d::GLViewImpl::createWithEAGLView(eaglView);
    cocos2d::Director::getInstance()->setOpenGLView(glview);


    app->run();
    return YES;
}

2016年4月28日 星期四

0428 cocos2dx 對話框 範例

1.無法立即編輯 但是確定可以!須自行補上缺失的h
2.按鈕直接引用 button 元件
3. 加入 CC_SYNTHESIZE ,將變數獨立。
   有加入背景變暗功能  m_UI_Background
4.使用方式
init:
Layer* setTimeDialog = DislogSetTime::create();
    //setTimeDialog->setPosition(winSize*0.5);
    setTimeDialog->setVisible(false);
  addChild(setTimeDialog,16);
show:
    setTimeDialog->setVisible(true);
    DislogSetTime*  pDialog=(DislogSetTime*)setTimeDialog;
    pDialog->setDayName(sDayString);
    pDialog->setSetTime(scheduleTime);
    pDialog->showDialog();
Hide:
    setTimeDialog->setVisible(false);
    DislogSetTime*  pDialog=(DislogSetTime*)setTimeDialog;
    pDialog->HideDialog();

程式碼:
////////////////////////////////////////////////////////////
#ifndef __ROBOTDIALOG_H__
#define __ROBOTDIALOG_H__
#include "Def_ImageList.h"
#include "Robot_NumberPicker.h"
#include "extensions/cocos-ext.h"
#include "ui/CocosGUI.h"
using namespace ui;
using namespace cocos2d;
//頁面種類
enum _DIALOGSETTIMEITEM_
{
    DIATIME_LAYER_BK=0,
    DIATIME_SPRITE_CANEL,
    DIATIME_SPRITE_SAVE,
    DIATIME_NOTOUCH,    //此線以下不偵測TOUCH
    DIATIME_LAB_MESSAGE=DIATIME_NOTOUCH,
    DIATIME_PICKER_MIN,
    DIATIME_PICKER_HOUR,
    DIATIME_SIZE,
   
};

class DislogSetTime: public Layer
{
    //對話框
protected:
    //初始化對話框
    void InitDialog();
    /* 加載對話框
     * @parm:msg 消息 顯示在界面上的提示消息
     * @parm:fun 回調函數 給確定按鈕調用的
     * @parm:isNeedCancel 是否需要取消按鈕
     * @parm:isModalDialog 是否是模態對話框
     */
    void LoadDialog(std::string msg,std::function<void(Ref*)>  fun,bool isNeedCancel = false,bool isModalDialog = true);
    //對話框加載完成
//    void LoadDialogFinish(Armature *a, MovementEventType b, std::string c);
    //隱藏對話框
    void HideDialog();

    //隱藏對話框完成
//    void HideDialogFinish(Armature *a, MovementEventType b, std::string c);
    //對話框取消按鈕callback
    void PressBtnCancel(Ref *pSender, Widget::TouchEventType type);
    //對話框確認按鈕callback
    void PressBtnConfirm(Ref *pSender, Widget::TouchEventType type);
    //virtual void SetScene();
    //標誌變量
public:
    //視窗變數
    Size visibleSize;
    Vec2 origin;
    bool m_IsNeedExit;
    //UI變量
public:
    Sprite* m_spr_ExitTip; //退出提示
    Sprite* Dialog; //對話框
    Label* m_DialogMsg; //對話框消息
    UINumberPicker *m_Hourpicker;
    UINumberPicker *m_Minatepicker;
    Button* m_DialogConfirm; //對話框確認按鈕
    Button* m_DialogCancel;  //對話框取消按鈕
    //層變量
    Layer* m_UI_Dialog;            //對話框層 最高層
    Layer* m_UI_Background;        //背景層 底層
   
public:
    std::function<void(Ref*)> m_Dialog_ConfirmCallback; //對話框確認回調
    //---------------函數---------------------------
    CREATE_FUNC(DislogSetTime);
    DislogSetTime();
    ~DislogSetTime();
    virtual bool init() override;
    virtual void onEnter() override;
    virtual void onExit() override;
    void releaseUI();
    //bool onTouchBegan(Touch* touch, Event* event) override;
    //顯示對話框
    void showDialog();
//特別獨立出來的單一變數
    CC_SYNTHESIZE(string, dayName, DayName);
    CC_SYNTHESIZE(time_t, setTime, SetTime);
    //工具
public:
    Point getCenterPoint() { return Point(origin.x+visibleSize.width/2,origin.y+visibleSize.height/2); };
    Point getOriginPoint() { return Point(origin.x,origin.y); };
};

#endif // __ROBOTDIALOG_H__

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "Def_ImageList.h"
#include "Robot_Dialog.h"
USING_NS_CC;
USING_NS_CC_EXT;
using namespace std;
using namespace cocos2d::ui;

DislogSetTime::DislogSetTime()
{

}
DislogSetTime::~DislogSetTime()
{

}
bool DislogSetTime::init()
{
    if (!Layer::init())
    {
        return false;
    }
    visibleSize = Director::getInstance()->getVisibleSize();
    origin = Director::getInstance()->getVisibleOrigin();
    m_UI_Dialog = Layer::create();
    addChild(m_UI_Dialog,40);
    m_UI_Dialog->setPosition(getOriginPoint());
   
    //第一個參數是顏色ccc4(r,g,b,a) a取值(0~255),越大越不透明 下面兩個參數為寬高,不傳默認為屏幕大小
    m_UI_Background = LayerColor::create(Color4B(0,0,0,153),visibleSize.width,visibleSize.height);
    m_UI_Background->setPosition(getOriginPoint());
    this->addChild(m_UI_Background,10);
   
    InitDialog();
  //  SetScene();
    return true;
}
//初始化對話框
void DislogSetTime::InitDialog()
{
//    auto ImageScale = Director::getInstance()->getContentScaleFactor();
    const float winWidth=950;
    const float winHeight=900;
    float W_unit=1.0f;
    float H_unit=1.0f;

    Point LabSite_UM_MESSAGE=Vec2(winWidth*0.5*W_unit,(winHeight-25)*H_unit);
    Point PickerSiteOnBtn_Minate=Vec2(0.7*winWidth*W_unit,winHeight*0.3*H_unit);
    Point PickerSiteOnBtn_Hour=Vec2(0.3*winWidth*W_unit,winHeight*0.3*H_unit);
    //設定滾輪大小
    Size slotItemSize =Size(210*W_unit,150*H_unit);
    //建立對話框
    Dialog = Sprite::createWithSpriteFrameName(S_BTN_DIALOG_BK);
//    Dialog->setContentSize(Size(winWidth*W_unit,winHeight*H_unit));
    Dialog->setPosition(getCenterPoint());
    m_UI_Dialog->addChild(Dialog);
    Dialog->setVisible(true);
   
    //視窗說明文件
    m_DialogMsg = Label::createWithSystemFont(STR_RESERV_SetTime_CHS, S_FONE, getFontSize(G_FSIZE_RESERV_SetTime));
    m_DialogMsg->setAnchorPoint(Vec2::ANCHOR_MIDDLE_TOP);
    m_DialogMsg->setTextColor(COLOR_STR_BLUE);
    m_DialogMsg->setPosition(LabSite_UM_MESSAGE);
    Dialog->addChild(m_DialogMsg,15);
   
    //加入數字滾輪:分
    m_Minatepicker = UINumberPicker::create();
    m_Minatepicker->setItemSize(slotItemSize.width,slotItemSize.height);
    m_Minatepicker->setDataprovider(3, 0, 59,1);
    m_Minatepicker->setPosition(PickerSiteOnBtn_Minate);     //set the postions
    //    picker->setSkinFrameName(S_RESERVATION_SETTIME_BK, S_SWITCH_BACKGROUNP);
    Dialog->addChild(m_Minatepicker,12);
     m_Minatepicker->setTag(DIATIME_PICKER_MIN);
//    m_Minatepicker->valueChangeHandler = std::bind(&DislogSetTime::pickerChangeHandler,this, std::placeholders::_1);
   
    //加入數字滾輪:時
    m_Hourpicker = UINumberPicker::create();
    m_Hourpicker->setItemSize(slotItemSize.width,slotItemSize.height);
    m_Hourpicker->setDataprovider(3, 0, 23,1);
    m_Hourpicker->setPosition(PickerSiteOnBtn_Hour);     //set the postions
    //    picker->setSkinFrameName(S_RESERVATION_SETTIME_BK, S_SWITCH_BACKGROUNP);
    Dialog->addChild(m_Hourpicker,12);
//    m_Hourpicker->valueChangeHandler = std::bind(&DislogSetTime::pickerChangeHandler,this, std::placeholders::_1);
    m_Hourpicker->setTag(DIATIME_PICKER_HOUR);
   
    //底下兩個按鈕
    //BUTTON元件 版本
    m_DialogConfirm = Button::create();
    m_DialogConfirm->setTouchEnabled(false);
    m_DialogConfirm->loadTextureNormal(S_BTN_DIALOG_RIGHT, Widget::TextureResType::PLIST);
    m_DialogConfirm->setTitleText(STR_BTN_Save_CHS);
    m_DialogConfirm->setTitleFontSize(getFontSize(G_FSIZE_DIALOG_BtnLabel));
    m_DialogConfirm->setTitleColor(COLOR_BG_WHITE);
    m_DialogConfirm->setPosition(Vec2(winWidth*W_unit,0*H_unit));
    m_DialogConfirm->setAnchorPoint(Vec2::ANCHOR_BOTTOM_RIGHT);
    m_DialogConfirm->addTouchEventListener(CC_CALLBACK_2(DislogSetTime::PressBtnConfirm,this));
    Dialog->addChild(m_DialogConfirm,12);
    m_DialogCancel = Button::create();
    m_DialogCancel->loadTextureNormal(S_BTN_DIALOG_LEFT, Widget::TextureResType::PLIST);
    m_DialogCancel->setTitleText(STR_BTN_Canel_CHS);
    m_DialogCancel->setTitleFontSize(getFontSize(G_FSIZE_DIALOG_BtnLabel));
    m_DialogCancel->setTitleColor(COLOR_BG_WHITE);
    m_DialogCancel->setPosition(Vec2(0,0));
    m_DialogCancel->setAnchorPoint(Vec2::ZERO);
    m_DialogCancel->addTouchEventListener(CC_CALLBACK_2(DislogSetTime::PressBtnConfirm,this));
    m_DialogCancel->addTouchEventListener(CC_CALLBACK_2(DislogSetTime::PressBtnCancel, this));
    Dialog->addChild(m_DialogCancel,12);
}

//void DislogSetTime::SetScene()
//{
//    auto bg = ImageView::create();
//    bg->setContentSize(Size(950,900));
//    bg->setPosition(getCenterPoint());
//    auto listen = EventListenerTouchOneByOne::create();
//    listen->onTouchBegan = CC_CALLBACK_2(DislogSetTime::onTouchBegan, this);
//    _eventDispatcher->addEventListenerWithSceneGraphPriority(listen,Dialog);
//    m_UI_Background->addChild(bg);
//}
//bool DislogSetTime::onTouchBegan(Touch* touch, Event* event)
//{
//    auto target = static_cast<Layer*>(event->getCurrentTarget());
//    if(target==nullptr)
//    {
//        return true;
//    }
////    log("%s",__PRETTY_FUNCTION__);
////    Vec2 touchLocation = touch->getLocation();
////    Vec2 nodePosition = target->convertToNodeSpace( touchLocation );
//    if(!isVisible())
//        return false;
//    return true;
//}
void DislogSetTime::releaseUI()
{
    time_t setTime=getSetTime();
    char sShowHelpStr[48];
    sprintf(sShowHelpStr,STR_RESERV_SetTime_CHS, getDayName().c_str());
    m_DialogMsg->setString(sShowHelpStr);
    tm *pTime=localtime(&setTime);
    m_Hourpicker->setValue(pTime->tm_hour);
    m_Minatepicker->setValue(pTime->tm_min);
   
}
//进入
void DislogSetTime::onEnter()
{
    Layer::onEnter();
}
//退出
void DislogSetTime::onExit()
{
    Layer::onExit();
}
//顯示對話框
void DislogSetTime::showDialog()
{
    m_UI_Dialog->setVisible(true);
    m_UI_Background->setVisible(true);
    m_DialogConfirm->setVisible(true);
    m_DialogConfirm->setTouchEnabled(true);
    m_DialogCancel->setVisible(true);
    m_DialogCancel->setTouchEnabled(true);
   
    auto listener1 = EventListenerTouchOneByOne::create();//创建一个触摸监听
    listener1->setSwallowTouches(true);//设置不想向下传递触摸
    listener1->onTouchBegan = [](Touch* touch, Event* event){
        CCLOG("touch Swallow");
        return true;
    };
    //增加遮蔽版
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,m_UI_Dialog);
   
    releaseUI();
}
//隐藏對話框
void DislogSetTime::HideDialog()
{
    m_UI_Dialog->setVisible(false);
    m_UI_Background->setVisible(false);
    m_DialogConfirm->setVisible(false);
    m_DialogConfirm->setTouchEnabled(false);
    m_DialogCancel->setVisible(false);
    m_DialogCancel->setTouchEnabled(false);
    //移除遮蔽版
    _eventDispatcher->removeEventListenersForTarget(m_UI_Dialog);
}
//取消按鈕
void DislogSetTime::PressBtnCancel(Ref *pSender, Widget::TouchEventType type)
{
    switch (type)
    {
        default:
            break;
        case Widget::TouchEventType::ENDED:
        {
            HideDialog();
            break;
        }
    }

}
//確認按鈕
void DislogSetTime::PressBtnConfirm(Ref *pSender, Widget::TouchEventType type)
{
    switch (type)
    {
        default:
            break;
        case Widget::TouchEventType::ENDED:
        {
            HideDialog();
            break;
        }
    }
   
}
//////////////////////////////////////////

0428 對話框背景變暗 主體變亮

下面我們就詳細講述這幾個要點:
一.畫面的變化.
對個這個不同的遊戲的做法不盡相同,歸結起來大概有這麼幾種:
1.畫面整體變暗
這個比較簡單,cocos2d就有現成接口:

CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
//第一個參數是顏色ccc4(r,g,b,a) a取值(0~255),越大越不透明 下面兩個參數為寬高,不傳默認為屏幕大小
CCLayerColor* pLayer=CCLayerColor::create(ccc4(0,0,0,200),visibleSize.width,visibleSize.height);
//CCLayerColor* pLayer=CCLayerColor::create(ccc4(0,0,0,200));
addChild(pLayer,100);
效果如下:
20151224161245055.png (300×207)
然後你可以在這層之上添加精靈,最後的效果可能是這個樣子的:
20151224161304281.jpg (300×200)
註:圖片源自網絡,如果有版權問題~你丫去死吧! 請聯繫我!
2.局部高亮
比較簡單的方法是準備幾張張局部高亮的圖片,隨著引導進度變化而切換!優點是可以做的比較精美,缺點是資源量會比較大!另一種方法是在畫面整體變暗的基礎上摳掉部分區域,還好cocos2d提供了CCClippingNode接口(2.1+版)!
CCClippingNode類比較複雜,但我們只用明白兩個東西就好:一個是CCClippingNode的Stencil,一個CCClippingNode的Child,怎麼理解呢?請看下圖:
20151224161337076.jpg (300×225)
這個東西相信大家都有印象吧,上面的圖案(鏤空部分)就是 CCClippingNode的Stencil(模板),實體部分就是CCClippingNode的child(底板),是不是很好明白,讓我們用代碼來實現吧:
//創建cliper對象
CCClippingNode* pClip=CCClippingNode::create();
addChild(pClip);
//加入灰色的底板
CCLayerColor* pColor=CCLayerColor::create(ccc4(0,0,0,200));
pClip->addChild(pColor);
讓我來加入模板:
//創建drawnode對象
CCDrawNode *m_pAA=CCDrawNode::create();
static ccColor4F green = {0, 1, 0, 1};
static CCPoint rect[4]={ccp(-50,50),ccp(50,50),ccp(50,-50),ccp(-50,-50)};
//用上面的數據保存一個100x100的矩形
m_pAA->drawPolygon(rect, 4, green, 0, green);
//設置到屏幕的中心
m_pAA->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
//設置為pclip的模板
pClip->setStencil(m_pAA);
CCDrawNode是用來繪製幾何圖形的,drawPolygon使用繪製多邊形,參數為頂點數組,頂點數量,邊框寬度,和填充顏色!這裡要注意一點:千萬不要addChild(m_pAA);因為一旦addchild,m_pAA就會被繪製出來,我們是要用它做剪裁!他會在CCClippingNode內部被使用!
我們會看到這樣的效果:
20151224161409119.png (246×300)
額,貌似和我們想想的不太一樣啊,為什麼呢?那是因為CCClippingNode有一個選項是是否反向,既顯示Stencil呢還是child減去Stencil的部分!默認為顯示顯示鏤空(Stencil)部分(false),因此我們要改變選項:
//是否反向?
pClip->setInverted(true);
效果如下,是不是很棒:
20151224161439723.png (237×300)
3.組合效果(將2的矩形改成圓,再加上圖片)
讓我們先看這張圖片,是不是很棒?右下角黃色矩形區域是高亮的,在配上圖片說明,很不錯的效果!
20151224161456152.png (300×231)
讓我們來實現類似的效果吧,用到的資源:
20151224161524560.png (101×115)
//創建cliper
CCClippingNode* pClip=CCClippingNode::create();
pClip->setInverted(true);
addChild(pClip);
//添加底板
CCLayerColor* pColor=CCLayerColor::create(ccc4(0,0,0,200));
pClip->addChild(pColor);

//繪製圓形區域
static ccColor4F green = {0, 1, 0, 1};//頂點顏色,這裡我們沒有實質上沒有繪製,所以看不出顏色
float fRadius=55.0f;//圓的半徑
const int nCount=100;//圓形其實可以看做正多邊形,我們這裡用正100邊型來模擬園
const float coef = 2.0f * (float)M_PI/nCount;//計算每兩個相鄰頂點與中心的夾角
static CCPoint circle[nCount];//頂點數組
for(unsigned int i = 0;i <nCount; i++) {
 float rads = i*coef;//弧度
 circle[i].x = fRadius * cosf(rads);//對應頂點的x
 circle[i].y = fRadius * sinf(rads);//對應頂點的y
}
CCDrawNode *pStencil=CCDrawNode::create();
pStencil->drawPolygon(circle, nCount, green, 0, green);//繪製這個多邊形!

//動起來
pStencil->runAction(CCRepeatForever::create(CCSequence::createWithTwoActions(CCScaleBy::create(0.05f, 0.95f),
 CCScaleTo::create(0.125f, 1))));
pStencil->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

//設這模板
pClip->setStencil(pStencil);

//添加圓和手的圖片
CCSprite*pCircle=CCSprite::create("circle.png");
pCircle->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
pCircle->runAction(CCRepeatForever::create(CCSequence::createWithTwoActions(CCScaleBy::create(0.05f, 0.95f),
 CCScaleTo::create(0.125f, 1))));
addChild(pCircle);

CCSprite* pHand=CCSprite::create("hand.png");
pHand->setAnchorPoint(ccp(0.0f,1.0f));
pHand->setPosition(ccp(pCircle->getContentSize().width/2,pCircle->getContentSize().height/2));
pCircle->addChild(pHand);

上面是全部的代碼,效果圖如下(動態的會更好)沒有加圓圈和手之前和之後:
20151224161556795.png (279×300)20151224161617351.png (300×289)
那麼如何改變高亮區域得位置呢?很簡單,只用改變pStencil的位置即可!可以考慮記為成員變量在ccTouchesBegan中改變位置,這樣就會高亮區域就會跟著你的鼠標走啦!
二.觸摸和按鈕響應
1.開啟觸摸 阻止穿透響應 判斷落點
讓我們繼續看下面的這張圖片:
20151224161655490.png (300×231)
這張圖片中應該只有右下角的」冒險」按鈕能夠響應的到,在這裡說下思路:
為了讓其他區域的按鈕響應不到,我們應該提高上層(CClayer)的響應優先級為-128和按鈕一樣,這樣的話如果上層(CClayer)後加入遊戲addchild的話,會先收到觸摸消息.
在收到觸摸消息ccTouchBegan時,判斷點擊的地方是否在黃色矩形區域中,在的話return false 交給下層的按鈕去響應,否則return true 阻止消息繼續傳遞.
下面是代碼片段:
1).開啟觸摸
bool CTeachLayer::init()
{
 if (!CCLayer::init())
 {
 return false;
 }
 setTouchEnabled(true);
 return true;
}
2).提升觸摸響應優先級為-128
void CTeachLayer::registerWithTouchDispatcher()
{
//使用-128和CCMenu優先級相同,並且吞掉事件true//
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, -128, true);
CCLayer::registerWithTouchDispatcher();
}
3).在ccTouchBegan判斷觸點
bool CTeachLayer::ccTouchBegan( CCTouch* pTouch, CCEvent* pEvent )
{
    //得到觸摸位置
 CCPoint touchPoint = pTouch->getLocation();
    //聲明一個右下角100x50的矩形
 CCRect m_obRect=CCRectMake(
 CCDirector::sharedDirector()->getVisibleSize().width-100,
 CCDirector::sharedDirector()->getVisibleSize().height-50,
 100,
 50);
    //判斷點是否在矩形中
 if (m_obRect.containsPoint(touchPoint))
 {
 return false;
 }
 return true;
}
大功告成,快去試一下吧!
2.精確命中
手機上和電腦上的觸摸事件的區別在於:電腦上我們是用鼠標點擊,而手機上使用手指去摁,觸摸的精度的高低不言而喻!讓我們來看下面這張圖片:
20151224161739882.png (300×187)
讓我們看右上角的X號按鈕,在手機上點擊它來說應該是很困難的!他的真實尺寸應該是紅色矩形的區域(假設:50x50),為了讓玩家在引導時,能夠很方便的點擊到它,我們將實際的可觸摸區域設置為藍色區域(假設:100x100),這樣應該很好點到了吧!
這樣會有一個新的問題,如果點擊到紅色和藍色之間的區域,下面的x按鈕時響應不到的,那麼怎麼辦呢?我們需要修改touch的數據(設置為按鈕的正中心),然後touch事件將會以新的數據向下傳遞!這樣下面的按鈕就能響應到了!
讓我們看看代碼吧:
bool CTeachLayer::ccTouchBegan( CCTouch* pTouch, CCEvent* pEvent )
{
 CCPoint touchPoint = pTouch->getLocation();

 if (!CGlobal::s_bTeach)
 {
 return false;
 }
    //假設按鈕在(300,200)的位置,那麼藍色矩形如下:
 CCRect m_obRect=CCRectMake(
 250,
 150,
 100,
 100);

 if (m_obRect.containsPoint(touchPoint))
 {
        //得到藍色矩形的中心點(300,200)
 CCPoint pos=ccp(m_obRect.getMidX(),m_obRect.getMidY());
        //這裡要轉化為UI坐標系(左上角為0,0點)
 pos=CCDirector::sharedDirector()->convertToUI(pos);
        //設置觸摸信息
 pTouch->setTouchInfo(pTouch->getID(),pos.x,pos.y);
 return false;
 }

 return true;
}

三.邏輯處理及代碼組織
這裡我大概說下新手引導的架構,代碼肯定貼不出來,太多太亂,說的不對的地方,歡迎大家拍磚吐槽!>_<
1.首先創建了一個CTeachLayer繼承自CCLayer,添加到遊戲的最上層(UI層之上),記住,要先添加遊戲的其層,最後添加CTeachLayer,可以保存全局指針!
2.用一個枚舉記下你要引導的所有步驟,在根據枚舉值去設置高亮的位置及觸摸區域!可以記下當前引導的進度,以方便下次繼續引導!
3.遊戲邏輯中免不了要判斷引導(例:出第3波怪的時候引導玩家使用清屏道具),用全局的指針去設置引導的步驟!
4.引導完成後移除CTeachLayer,遊戲開始!

2016年4月27日 星期三

0427 對話框教學 3

我們時常需要這麼些功能,彈出一個層,給與用戶一些提示,這也是一種模態窗口,在沒有對當前對話框進行確認的時候,不能繼續往下操作。

功能分析

我們設計一個對話框,對話框上有幾個按鈕(個數可定製),當然有個標題,會讓別人一眼看出它之功用,裡面可以有些詳細的提示文字,需要是模態窗口,而且窗口的大小可變,這樣能夠更好的適應不同的屏幕的大小。當然還有一個重要的功能,彈出效果 ~ 雖然從技術角度來說,實現起來並不難,或者說非常簡單,但這會以一個很好的用戶體驗展示給用戶。

代碼

1.彈出框類
PopupLayer.h
[cpp] view plain copy
  1. //  
  2. //  PopupLayer.h  
  3. //  PopupDemo  
  4. //  
  5. //  Created by IDEA-MAC03 on 13-10-10.  
  6. //  
  7. //  
  8.   
  9. #ifndef __PopupDemo__PopupLayer__  
  10. #define __PopupDemo__PopupLayer__  
  11.   
  12. #include "cocos2d.h"  
  13. #include "cocos-ext.h"  
  14. using namespace cocos2d;  
  15. using namespace cocos2d::extension;  
  16. using namespace std;  
  17.   
  18.   
  19.   
  20. class PopupLayer:public CCLayer  
  21. {  
  22.     
  23. public:  
  24.     PopupLayer();  
  25.     ~PopupLayer();  
  26.       
  27.     virtual bool init();  
  28.     CREATE_FUNC(PopupLayer);  
  29.       
  30.      // 需要重寫觸摸註冊函數,重新給定觸摸級別  
  31.     virtual void registerWithTouchDispatcher(void);  
  32.      // 重寫觸摸函數,永遠返回 true ,屏蔽其它層,達到 「模態」 效果  
  33.     bool ccTouchBegan(cocos2d::CCTouch *pTouch,cocos2d::CCEvent *pEvent);  
  34.     // 構架,並設置對話框背景圖片  
  35.     static PopupLayer* create(const char* backgroundImage);  
  36.       
  37.      // 它可以顯示標題,並且設定顯示文字大小  
  38.     void setTitle(const char*title,int fontsize = 20);  
  39.     // 文本內容,padding 為文字到對話框兩邊預留的距離,這是可控的,距上方的距離亦是如此  
  40.     void setContentText(const char *text, int fontsize = 20, int padding = 50, int paddintTop = 100);  
  41.     // 回調函數,當點擊按鈕後,我們關閉彈出層的同事,需要一個回調函數,以通知我們點擊了哪個按鈕(如果有多個)  
  42.     void setCallbackFunc(CCObject* target, SEL_CallFuncN callfun);  
  43.      // 為了添加按鈕方面,封裝了一個函數,傳入些必要的參數  
  44.     bool addButton(const char* normalImage, const char* selectedImage, const char* title, int tag = 0);  
  45.       
  46.     // 為了在顯示層時之前的屬性生效,選擇在 onEnter 裡動態展示  
  47.     virtual void onEnter();  
  48.     virtual void onExit();  
  49.       
  50. private:  
  51.       
  52.     void buttonCallback(CCObject* pSender);  
  53.       
  54.     // 文字內容兩邊的空白區  
  55.     int m_contentPadding;  
  56.     int m_contentPaddingTop;  
  57.       
  58.     CCObject* m_callbackListener;  
  59.     SEL_CallFuncN m_callback;  
  60.       
  61.     CC_SYNTHESIZE_RETAIN(CCMenu*, m__pMenu, MenuButton);  
  62.     CC_SYNTHESIZE_RETAIN(CCSprite*, m__sfBackGround, SpriteBackGround);  
  63.     CC_SYNTHESIZE_RETAIN(CCScale9Sprite*, m__s9BackGround, Sprite9BackGround);  
  64.     CC_SYNTHESIZE_RETAIN(CCLabelTTF*, m__ltTitle, LabelTitle);  
  65.     CC_SYNTHESIZE_RETAIN(CCLabelTTF*, m__ltContentText, LabelContentText);  
  66.   
  67. };  
  68.   
  69.   
  70.   
  71.   
  72. #endif /* defined(__PopupDemo__PopupLayer__) */  

PopupLayer.cpp
[cpp] view plain copy
  1. //  
  2. //  PopupLayer.cpp  
  3. //  PopupDemo  
  4. //  
  5. //  Created by IDEA-MAC03 on 13-10-10.  
  6. //  
  7. //  
  8.   
  9. #include "PopupLayer.h"  
  10.   
  11.   
  12.   
  13. PopupLayer::PopupLayer():  
  14. m__pMenu(NULL)  
  15. ,m_contentPadding(0)  
  16. ,m_contentPaddingTop(0)  
  17. ,m_callbackListener(NULL)  
  18. ,m_callback(NULL)  
  19. ,m__sfBackGround(NULL)  
  20. ,m__s9BackGround(NULL)  
  21. ,m__ltContentText(NULL)  
  22. ,m__ltTitle(NULL)  
  23. {  
  24.       
  25. }  
  26.   
  27. PopupLayer::~PopupLayer()  
  28. {  
  29.     CC_SAFE_RELEASE(m__pMenu);  
  30.     CC_SAFE_RELEASE(m__sfBackGround);  
  31.     CC_SAFE_RELEASE(m__ltContentText);  
  32.     CC_SAFE_RELEASE(m__ltTitle);  
  33.     CC_SAFE_RELEASE(m__s9BackGround);  
  34. }  
  35.   
  36. bool PopupLayer::init()  
  37. {  
  38.     bool bRef = false;  
  39.     do  
  40.     {  
  41.         CC_BREAK_IF(!CCLayer::init());  
  42.         this->setContentSize(CCSizeZero);  
  43.         // 初始化需要的 Menu  
  44.         CCMenu* menu = CCMenu::create();  
  45.         menu->setPosition(CCPointZero);  
  46.         setMenuButton(menu);  
  47.         setTouchEnabled(true);  
  48.         bRef = true;  
  49.     } while (0);  
  50.     return bRef;  
  51. }  
  52.   
  53.   
  54. void PopupLayer::registerWithTouchDispatcher()  
  55. {  
  56.        // 這裡的觸摸優先級設置為 -128 這保證了,屏蔽下方的觸摸  
  57.     CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, -128, true);  
  58. }  
  59.   
  60. bool PopupLayer::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)  
  61. {  
  62.     CCLog("PopupLayer touch");  
  63.     return true;  
  64. }  
  65.   
  66.   
  67. PopupLayer* PopupLayer::create(const char *backgroundImage)  
  68. {  
  69.     PopupLayer* ml = PopupLayer::create();  
  70.     ml->setSpriteBackGround(CCSprite::create(backgroundImage));  
  71.     ml->setSprite9BackGround(CCScale9Sprite::create(backgroundImage));  
  72.     return ml;  
  73. }  
  74.   
  75.   
  76. void PopupLayer::setTitle(const char*title,int fontsize)  
  77. {  
  78.     CCLabelTTF* ltfTitle = CCLabelTTF::create(title, "", fontsize);  
  79.     setLabelTitle(ltfTitle);  
  80. }  
  81.   
  82. void PopupLayer::setContentText(const char *text, int fontsize, int padding, int paddingTop){  
  83.     CCLabelTTF* ltf = CCLabelTTF::create(text, "", fontsize);  
  84.     setLabelContentText(ltf);  
  85.     m_contentPadding = padding;  
  86.     m_contentPaddingTop = paddingTop;  
  87. }  
  88.   
  89. void PopupLayer::setCallbackFunc(cocos2d::CCObject *target, SEL_CallFuncN callfun)  
  90. {  
  91.     m_callbackListener = target;  
  92.     m_callback = callfun;  
  93. }  
  94.   
  95.   
  96. bool PopupLayer::addButton(const char *normalImage, const char *selectedImage, const char *title, int tag){  
  97.     CCSize winSize = CCDirector::sharedDirector()->getWinSize();  
  98.     CCPoint pCenter = ccp(winSize.width / 2, winSize.height / 2);  
  99.       
  100.     // 創建圖片菜單按鈕  
  101.     CCMenuItemImage* menuImage = CCMenuItemImage::create(normalImage, selectedImage, this, menu_selector(PopupLayer::buttonCallback));  
  102.     menuImage->setTag(tag);  
  103.     menuImage->setPosition(pCenter);  
  104.       
  105.     // 添加文字說明並設置位置  
  106.     CCSize imenu = menuImage->getContentSize();  
  107.     CCLabelTTF* ttf = CCLabelTTF::create(title, "", 20);  
  108.     ttf->setColor(ccc3(0, 0, 0));  
  109.     ttf->setPosition(ccp(imenu.width / 2, imenu.height / 2));  
  110.     menuImage->addChild(ttf);  
  111.       
  112.     getMenuButton()->addChild(menuImage);  
  113.     return true;  
  114. }  
  115.   
  116.   
  117. void PopupLayer::buttonCallback(cocos2d::CCObject *pSender){  
  118.     CCNode* node = dynamic_cast<CCNode*>(pSender);  
  119.     CCLog("touch tag: %d", node->getTag());  
  120.     if (m_callback && m_callbackListener){  
  121.         (m_callbackListener->*m_callback)(node);  
  122.     }  
  123.     this->removeFromParentAndCleanup(true);  
  124. }  
  125.   
  126.   
  127.   
  128. void PopupLayer::onEnter()  
  129. {  
  130.     CCLayer::onEnter();  
  131.       
  132.     CCSize winSize = CCDirector::sharedDirector()->getWinSize();  
  133.     CCPoint pCenter = ccp(winSize.width / 2, winSize.height / 2);  
  134.       
  135.     CCSize contentSize;  
  136.      // 設定好參數,在運行時加載  
  137.     if (getContentSize().equals(CCSizeZero))  
  138.     {  
  139.         getSpriteBackGround()->setPosition(ccp(winSize.width / 2, winSize.height / 2));  
  140.         this->addChild(getSpriteBackGround(),0,0);  
  141.         contentSize = getSpriteBackGround()->getTexture()->getContentSize();  
  142.     }else  
  143.     {  
  144.         CCScale9Sprite *background = getSprite9BackGround();  
  145.         background->setContentSize(getContentSize());  
  146.         background->setPosition(ccp(winSize.width / 2, winSize.height / 2));  
  147.         this->addChild(background,0);  
  148.         contentSize = getContentSize();  
  149.     }  
  150.       
  151.       
  152.      // 添加按鈕,並設置其位置  
  153.     this->addChild(getMenuButton());  
  154.     float btnWidth = contentSize.width/(getMenuButton()->getChildrenCount()+1);  
  155.       
  156.     CCArray* array = getMenuButton()->getChildren();  
  157.     CCObject* pObj = NULL;  
  158.     int i = 0;  
  159.     CCARRAY_FOREACH(array, pObj)  
  160.     {  
  161.         CCNode* node = dynamic_cast<CCNode*>(pObj);  
  162.         node->setPosition(ccp(winSize.width / 2 - contentSize.width / 2 + btnWidth * (i + 1), winSize.height / 2 - contentSize.height / 3));  
  163.         i++;  
  164.     }  
  165.       
  166.     // 顯示對話框標題  
  167.     if (getLabelTitle())  
  168.     {  
  169.         getLabelTitle()->setPosition(ccpAdd(pCenter, ccp(0, contentSize.height / 2 - 35.0f)));  
  170.         this->addChild(getLabelTitle());  
  171.     }  
  172.       
  173.     // 顯示文本內容  
  174.     if (getLabelContentText())  
  175.     {  
  176.         CCLabelTTF* ltf = getLabelContentText();  
  177.         ltf->setPosition(ccp(winSize.width / 2, winSize.height / 2));  
  178.           ltf->setDimensions(CCSizeMake(contentSize.width - m_contentPadding * 2, contentSize.height - m_contentPaddingTop));  
  179.          ltf->setHorizontalAlignment(kCCTextAlignmentLeft);  
  180.         this->addChild(ltf);  
  181.     }  
  182.       
  183.     CCAction* popupLayer = CCSequence::create(CCScaleTo::create(0.0, 0.0),  
  184.                                               CCScaleTo::create(0.06, 1.05),  
  185.                                               CCScaleTo::create(0.08, 0.95),  
  186.                                               CCScaleTo::create(0.08, 1.0), NULL);  
  187.     this->runAction(popupLayer);  
  188.       
  189. }  
  190.   
  191.   
  192. void PopupLayer::onExit()  
  193. {  
  194.     CCLog("popup on exit.");  
  195.     CCLayer::onExit();  
  196. }  

2.測試代碼
HelloWorldScene.h
[cpp] view plain copy
  1. #ifndef __HELLOWORLD_SCENE_H__  
  2. #define __HELLOWORLD_SCENE_H__  
  3.   
  4. #include "cocos2d.h"  
  5.   
  6. class HelloWorld : public cocos2d::CCLayer  
  7. {  
  8. public:  
  9.     // Method 'init' in cocos2d-x returns bool, instead of 'id' in cocos2d-iphone (an object pointer)  
  10.     virtual bool init();  
  11.   
  12.     // there's no 'id' in cpp, so we recommend to return the class instance pointer  
  13.     static cocos2d::CCScene* scene();  
  14.       
  15.     // a selector callback  
  16.     void menuCloseCallback(CCObject* pSender);  
  17.   
  18.     // preprocessor macro for "static create()" constructor ( node() deprecated )  
  19.     CREATE_FUNC(HelloWorld);  
  20.       
  21.     void menuCallback(cocos2d::CCObject *pSender);  
  22.     void popupLayer();  
  23.     void buttonCallback(cocos2d::CCNode *pNode);  
  24. };  
  25.   
  26. #endif // __HELLOWORLD_SCENE_H__  

HelloWorldScene.cpp
[cpp] view plain copy
  1. #include "HelloWorldScene.h"  
  2. #include "SimpleAudioEngine.h"  
  3. #include "PopupLayer.h"  
  4.   
  5. using namespace cocos2d;  
  6. using namespace CocosDenshion;  
  7.   
  8. CCScene* HelloWorld::scene()  
  9. {  
  10.     // 'scene' is an autorelease object  
  11.     CCScene *scene = CCScene::create();  
  12.       
  13.     // 'layer' is an autorelease object  
  14.     HelloWorld *layer = HelloWorld::create();  
  15.   
  16.     // add layer as a child to scene  
  17.     scene->addChild(layer);  
  18.   
  19.     // return the scene  
  20.     return scene;  
  21. }  
  22.   
  23. // on "init" you need to initialize your instance  
  24. bool HelloWorld::init()  
  25. {  
  26.     //////////////////////////////  
  27.     // 1. super init first  
  28.     if ( !CCLayer::init() )  
  29.     {  
  30.         return false;  
  31.     }  
  32.   
  33.     CCSize winSize = CCDirector::sharedDirector()->getWinSize();  
  34.     CCPoint pointCenter = ccp(winSize.width / 2, winSize.height / 2);  
  35.       
  36.     // 添加背景圖片  
  37.     CCSprite* background = CCSprite::create("HelloWorld.png");  
  38.     background->setPosition(pointCenter);  
  39.     background->setScale(1.5f);  
  40.     this->addChild(background);  
  41.       
  42.      // 添加菜單  
  43.     CCMenu* menu = CCMenu::create();  
  44.       
  45.     CCMenuItemFont* menuItem = CCMenuItemFont::create("popup"this, menu_selector(HelloWorld::menuCallback));  
  46.     menuItem->setPosition(ccp(winSize.width / 2, winSize.height / 2));  
  47.     menuItem->setColor(ccc3(0, 0, 0));  
  48.     menu->addChild(menuItem);  
  49.       
  50.       
  51.     menu->setPosition(CCPointZero);  
  52.     this->addChild(menu);  
  53.       
  54.       
  55.       
  56.     return true;  
  57. }  
  58.   
  59.   
  60. void HelloWorld::menuCallback(cocos2d::CCObject *pSender){  
  61.     popupLayer();  
  62. }  
  63.   
  64. void HelloWorld::popupLayer()  
  65. {  
  66.     // 定義一個彈出層,傳入一張背景圖  
  67.     PopupLayer* pl = PopupLayer::create("useDialogBox0u00001.png");  
  68.     // ContentSize 是可選的設置,可以不設置,如果設置把它當作 9 圖縮放  
  69.     pl->setContentSize(CCSizeMake(400, 360));  
  70.     pl->setTitle("吾名一葉");  
  71.     pl->setContentText("嬌蘭傲梅世人賞,卻少幽芬暗裡藏。不看百花共爭豔,獨愛疏櫻一枝香。", 20, 50, 150);  
  72.     // 設置回調函數,回調傳回一個 CCNode 以獲取 tag 判斷點擊的按鈕  
  73.     // 這只是作為一種封裝實現,如果使用 delegate 那就能夠更靈活的控制參數了  
  74.     pl->setCallbackFunc(this, callfuncN_selector(HelloWorld::buttonCallback));  
  75.     // 添加按鈕,設置圖片,文字,tag 信息  
  76.     pl->addButton("shopBtn0s01.png""shopBtn0s02.png""確定", 0);  
  77.     pl->addButton("bagButton0b1.png""bagButton0b2.png""取消", 1);  
  78.     // 添加到當前層  
  79.     this->addChild(pl);  
  80. }  
  81.   
  82.   
  83.   
  84. void HelloWorld::buttonCallback(cocos2d::CCNode *pNode){  
  85.     CCLog("button call back. tag: %d", pNode->getTag());  
  86. }  
  87.   
  88. void HelloWorld::menuCloseCallback(CCObject* pSender)  
  89. {  
  90.     CCDirector::sharedDirector()->end();  
  91.   
  92. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)  
  93.     exit(0);  
  94. #endif  
  95. }  

效果圖



如上,完成了對話框的基本模型,它實現了以下功能:
  • 一個可以彈出的對話框實現
  • 模態窗口的實現(需要邏輯的控制)
  • 多按鈕的支持,位置自適應,提供回調函數
  • 提供標題和內容設置
  • 支持 九圖 ,控制適應彈出框大小
    當然還有許多其它並沒有照顧到的功能,或者不完善的地方,這就需要用戶自己擴展,定製了,如,這樣一個層,至少應該是單例的,任何時候只應該存在一個,可以用單例模式實現,對於彈出層的內容方面,這裡只有標題和內容,並且標題位置固定,按鈕的位置還可以更靈活的設置等。
詳解文章:http://www.ityran.com/archives/4854

cocos2dx-lua 建立滑鼠監聽

重要關鍵字  EVENT_MOUSE_SCROLL addEventListenerWithSceneGraphPriority      if IsPc() then --建立滑鼠監聽         local listener = cc.EventListenerMouse...