本篇中主要介紹cocos2d-xna的CCLayer和CCMenu,在游戲開發中,只是使用前面提到的CCSprite和CCScene來完成豐富的交互行為是不夠的,單體的界面元素操作只會增加UI開發的復雜程度,所以像CCLayer這樣的成組操作可以為開發提供很好的便捷。這次拿關卡選擇場景做操作實驗,完成較為復雜的交互開發。
巧用CCMenuItem |
拿出來咱們之前制作的資源文件,游戲設計中,將魏蜀吳三個國家分成不同的幾個關卡,這樣就要求我們在界面上需要對它們進行分開選擇,先看看圖片資源的准本情況,在GameUI01.plist里有這樣的幾個按鈕文件:
用亮紅色的按鈕表示為選定的,而暗紅色的表示可以點擊,在標簽頭上,被選定的一定是不能點擊的,所以可以巧用CCMenuItem的Enabled屬性組合達到效果,打開SceneSelect.cs類,在構造中創建Tab按鈕的地方把原有的代碼修改一下,原來的只是用CCSpirte貼了一張展示圖,詳細的可以看上一篇,
首先創建該類的一個成員:
CCMenu story_tabs;
CCMenu story_tabs一會兒就能用的上了,下面再構造函數里寫代碼如下:
//上面的Tab按鈕創建 CCMenuItemSprite tab1 = CCMenuItemSprite.itemFromNormalSprite( CCSprite.spriteWithSpriteFrameName("tab_shu2.png"), CCSprite.spriteWithSpriteFrameName("tab_shu1.png"), CCSprite.spriteWithSpriteFrameName("tab_shu1.png"), this, click_story_tab); CCMenuItemSprite tab2 = CCMenuItemSprite.itemFromNormalSprite( CCSprite.spriteWithSpriteFrameName("tab_wu2.png"), CCSprite.spriteWithSpriteFrameName("tab_wu1.png"), CCSprite.spriteWithSpriteFrameName("tab_wu1.png"), this, click_story_tab); CCMenuItemSprite tab3 = CCMenuItemSprite.itemFromNormalSprite( CCSprite.spriteWithSpriteFrameName("tab_wei2.png"), CCSprite.spriteWithSpriteFrameName("tab_wei1.png"), CCSprite.spriteWithSpriteFrameName("tab_wei1.png"), this, click_story_tab); story_tabs = CCMenu.menuWithItems(tab1, tab2, tab3); //按照水平方向10個像素間隔分割 story_tabs.alignItemsHorizontallyWithPadding(10); //轉換為界面UI的坐標 story_tabs.position = CCDirector.sharedDirector().convertToUI(new CCPoint(300, 72)); //將第一個設置為不可點擊 tab1.Enabled = false; this.addChild(story_tabs);
該代碼是創建了幾個按鈕並且組合到了一個CCMenu當中,這里需要特別說明的是,CCDirector.sharedDirector().convertToUI()方法,這個方法是將坐標轉換成為UI坐標,要知道,在cocos2d里面坐標是用左下角向上的,而不是一般的左上角,用這個方法可以方便的轉換。
現在運行不了,因為沒有實現click_story_tab方法,下面在該類中實現方法:
private void click_story_tab(CCObject sender) { //遍歷story_tabs foreach (var item in story_tabs.children) { //判斷是否為CCMenuItem,如果是的話,排除sender以外的全部設定為true if(item is CCMenuItem) (item as CCMenuItem).Enabled = item != sender; } }
也許初步看起來不太容易理解,其實這里做一下邏輯判斷就行,用if else實現就是這樣的:
if(item != sender) (item as CCMenuItem).Enabled = false; else (item as CCMenuItem).Enabled = true;
用什么方式都行,現在運行一下看看效果
當點擊任何一個標簽的時候就會呈現激活狀態,而此時也會不能點擊,好了,下面就要考慮如何實現對應的關卡內容了。
CCLayer |
上一篇的代碼中,只是用了for循環繪制了幾個按鈕然后讓按鈕觸發跳轉場景,很顯然,現在有了分類的標簽頁,下面的對應的關卡也需要改變,如果使用很粗暴的方法(每個組單獨寫按鈕,用是否可見管理)雖然能實現,但是管理起來不但麻煩,而且控制動畫也就不方便了,下面我們用CCLayer圖層來將這一大堆的按鈕放在一起,關於層的概念如果玩過PS的朋友應該很容易理解,下面這張圖當作簡要的說明:
魏蜀吳不同的標簽對應一個不同的圖層,當點擊對應時,圖層發生發生變換,所以我們抽象一個關卡的圖層類代碼如下:
public class LayerLevels : CCLayer { public LayerLevels() { //關卡選擇層 CCPoint offset = new CCPoint(170, 180); for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { //關卡的按鈕 CCMenuItemSprite level = CCMenuItemSprite.itemFromNormalSprite( CCSprite.spriteWithSpriteFrameName("btn_level1.png"), CCSprite.spriteWithSpriteFrameName("btn_level2.png"), this, click_level); CCMenu menu = CCMenu.menuWithItems(level); //位置相對於左上的UI界面 menu.position = CCDirector.sharedDirector().convertToUI(new CCPoint(offset.x + 160 * i, offset.y + 85 * j)); this.addChild(menu); //創建一個MenuItem,用作文本內容 CCMenuItem menuitem = new CCMenuItem(); //指定Arial的字體描述,保證fonts里有Arial.spritefont var text = CCLabelTTF.labelWithString((j * 4 + i + 1).ToString(), "Arial", 12); //將顏色指定為黑色 text.Color = new ccColor3B(); menuitem.addChild(text); menu.addChild(menuitem); } } } private void click_level(CCObject sender) { //當關卡點擊的時候會自動跳轉到游戲場景 CCDirector.sharedDirector().pushScene(GameRoot.pSceneGame); } public void Show() { this.visible = true; } public void Hide() { this.visible = false; } }
好了,現在回到SceneSelect.cs,將之前的按鈕for循環全部去掉,只需要一行代碼:
this.addChild(new LayerLevels());
運行看看:
有了關卡,圖層顯示,並且還有了關卡名字,其實這里你可以發揮一下,以后將來能自定義關卡名,但謹記漢字是顯示不了的,這需要另外一個漢字解決方案,以后咱們再說。
為了更好了和將來拓展,我加入了Show()和Hide()兩個方法,用來控制是否顯示,下面我們馬上就用的上啦。
但是點擊對應的魏蜀吳還不能實現切換,下面考慮如何實現頁面的切換,將標簽都對應起來,建立一個字典:
Dictionary<CCMenuItem, LayerLevels> dictLayerLevels = new Dictionary<CCMenuItem, LayerLevels>(); LayerLevels currentlayerlevers = null;
在SceneSelect構造函數中:
//添加測試層 //this.addChild(new LayerLevels()); //將標簽對應到不同的LyerLevers 類中 dictLayerLevels.Add(tab1, new LayerLevels() { visible = false }); dictLayerLevels.Add(tab2, new LayerLevels() { visible = false }); dictLayerLevels.Add(tab3, new LayerLevels() { visible = false }); //將tab1顯示為當前的關卡層 showLayerLevels(dictLayerLevels[tab1]); //遍歷並添加到界面中 foreach (var item in dictLayerLevels.Values) { this.addChild(item); }
注意將之前的注釋掉,現在這個沒用,好了,然后實現showLayerLevels方法和在標簽點擊的事件中加入代碼,變成這個樣子:
private void click_story_tab(CCObject sender) { //遍歷story_tabs foreach (var item in story_tabs.children) { //判斷是否為CCMenuItem,如果是的話,排除sender以外的全部設定為true if(item is CCMenuItem) (item as CCMenuItem).Enabled = item != sender; } showLayerLevels(dictLayerLevels[sender as CCMenuItem]); } private void showLayerLevels(LayerLevels layer) { if (currentlayerlevers != null) currentlayerlevers.Hide(); layer.Show(); currentlayerlevers = layer; }
showLayerLevels可以保證之前的關卡層隱蔽掉,將新的顯示出來。
但是現在點擊上面的標簽是沒用的,因為沒有任何的變化,只是可見和不可見,所以為了更清晰,下面將用CCAction來實現動畫效果。
CCMoveTo |
CCMoveTo意思很簡單,讓一個元素從當前的位置移動到指定的新位置,它的靜態方法可以通過時間和坐標產生一個移動的CCAction,下面用它來實現圖層的移動變化,這樣當標簽切換的時候,對應的更換也就可以直接看得到。那么打開LayerLevels類,把Show方法做一下修改:
public void Show() { //將其顯示出來 this.visible = true; //把位置設置到最右邊出屏幕外 this.position = new CCPoint(CCDirector.sharedDirector().getWinSize().width,0); //指定移動到0,0點 CCMoveTo move = CCMoveTo.actionWithDuration(0.5f,new CCPoint(0,0)); //運行這個Action this.runAction(move); }
好了,運行一下,看到了滑動效果了嗎?這個效果,當然了,這個效果還不能通過手勢來控制,只能點擊上面的魏蜀吳。
更進一步,我們現在看這個效果還是有點別扭,就是之前頁面是直接消失掉的,能不能跟滑動滾動走呢,這里就需要另外一個行為CCSequence隊列行為來實現,隊列行為在之前的一篇中有詳細的說明:
Cocos2d-x for WindowsPhone:開發一個打地鼠游戲(下):
我們將用上這個Action來實現Hide方法,當隱蔽動畫完成之后將本圖層隱蔽,改造Hide方法並添加回調方法:
public void Hide() { //指定移動到最左邊並超出屏幕 CCMoveTo move = CCMoveTo.actionWithDuration(0.5f,new CCPoint(-CCDirector.sharedDirector().getWinSize().width,0)); //執行一個隊列行為,當移動完成后就會調用HideAniCompled this.runAction(CCSequence.actionOneTwo(move, CCCallFunc.actionWithTarget(this, HideAniCompled))); } void HideAniCompled() { this.visible = false; }
現在再運行一下:
當點擊對應的標簽時候就會發生變化,交互是否立即豐富了很多呢?現在你可以嘗試自己做一些行為效果發揮想象力。
本篇主要對CCLayer和CCAction結合應用達到關卡選擇場景的交互開發,沒有做太多的其他考慮,在正式開發中,關卡選擇界面和關卡設計緊密相關,對應數據的構建和設計至關重要,該部分在后面我們一步一步的完成它。
關於Action有很多的方法,有興趣的朋友可以參看OpenXLive移植的cocos2d-xna里的test工程樣本,里面展示了非常多的行為效果,例如放縮、切變、不透明等等,在這里就不再太多的講解。
本篇例子工程:https://github.com/Nowpaper/SanguoCommander_cocos2dxna_Sample
本例工程名為:SanguoCommander4