天天看點

cocos2d-js的平面世界cocos2d世界的經緯度–坐标系場景組成了cocos2d世界Cocos2d世界物體的祖宗–節點

cocos2d世界的經緯度–坐标系

首先,我們看一個典型的遊戲畫面,這個圖有幾個元素,首先最明顯的應該是一個超人,然後右側有一個’HUNGRY HERO’字樣的圖示,另外還有2個按鈕(PLAY和ABOUT),最後就是底下的背景圖,如圖3-1所示。

在這個遊戲畫面中,超人和位置按鈕是怎麼表示呢,我們知道,地球上某一點的位置可以用經緯度來表示。同理,遊戲的二維世界也可以通過這樣的方式表示,具體來說,就是用x和y分别表示橫向和縱向位置。有沒有似曾相識的感覺,對了,這就是我們中學時代的坐标系,在cocos2d-js遊戲中,畫面左下角是(0,0),x從左往右遞增,而Y是從上往下遞增的,跟數學的二維坐标系一緻。

場景組成了cocos2d世界

來到cocos2d-js世界,我們需要了解一個概念–場景。這跟我們日常生活中的場景很相似。例如,剛進入遊戲的菜單界面是一個場景,遊戲過程一個場景,遊戲的商店也是一個場景。cocos2d-js架構把遊戲拆分為很多場景,當玩家在不同界面切換的時候,架構實際上就是讓遊戲在不同場景中切換。

使用場景拆分遊戲界面,能讓遊戲結構更清晰,代碼更便于維護,而且有利于運作時的性能優化,從一個場景切換到另一個場景時候,cocos2d-js架構會把前者銷毀,該場景包含的所有圖檔、文本餓的那個資源都會被清楚。

接下來我們看如何拆分場景、自定義一個場景。在建立的cocos2d-js工程中,可以找到HelloWorldScene.js,代碼如下:

var helloWorldScene = cc.Scene.extend({
    onEnter:function(){
    this._super();
    var layer = new HelloWorldLayer();
    this.addChild(layer);
}
});
           

這段代碼就定義了一個場景,其中重點是extend函數,他擴充了cc.Scene,得到HelloWorldScene.cc是一個JS對象,目的是實作跟C++的命名空間類似的作用,把各種cocos2d-js原生類型都封裝在這個命名空間中。extend接受一個對象作為參數,在這裡我們姑且了解extend函數就是擴張或者繼承的意思。在這個繼承中,我們重定義了onEnter函數,當遊戲加載進入HelloWorldScene這個場景時,就會執行這個函數。在onEnter函數中,首先執行this._super()調用父類(cc.Scene)原有的onEnter.然後再添加一個名為HelloWorldLayer的子節點。

Cocos2d世界物體的祖宗–節點

既然cocos2d由一個一個的場景組成,那麼場景中的物體又用什麼來表示和實作呢?這一節我們要重點了解節點。因為節點是Cocos2d世界中最基礎的東西,所有顯示在螢幕中的東西實際上都有節點的影子。這就好比現實生活中,一切東西都可以成為物體一樣。

節點封裝了一些基本的操作和功能,例如縮放、坐标變化、縮放變化、透明度、可見性。而其他的顯示類都繼承節點,并按照各自需要擴充節點的功能,例如Layer和Sprite。

節點有一個很重要的屬性–_children,正如這個名字的意思一樣,表示節點的孩子。節點包含節點孩子,節點孩子又可以包含節點孩子,子子孫孫無窮盡也。這個關系就類似于一棵樹。

這樣的包含關系有非常重要的特性:父節點位置變化,它所包含的子節點也會跟着變化,以整體的形式移動:父節點拉伸變大,子節點也會按照一樣的比例變大。除了位置和拉伸,還有一些屬性會有這種整體效應,包括可見性(visible)、透明度(opaque)、旋轉角度(rotation)、傾斜值(skewX和skewY)等。

#讓2d世界階層化—層

節點可以包含子節點,實作一個整體的效果,而在實際開發中,我們用得更多就是層。層繼承了節點,加入了更多功能,例如背景顔色。

##按層管理所有物體

層更符合我們對世界的認識,就好像百貨大廈一樣, 每一層銷售的商品種類都不一樣。相對應的,在一個典型遊戲中,往往會包含一些層:背景層、任務層、道具層、系統資訊層。背景層是固定的圖檔或地圖,人物層中主角和各種可以不斷移動,兩層之間不互不幹擾,各自管理層内的子節點。層繼承節點的特點,可以在層中繼續嵌套子層,例如人物層中又可以繼續拆分為主角層、npc層。

使用層的方式很簡單,首先建立一層:

var layer = new cc.Layer();

把層添加到舞台上:

scene.addChild(layer);

再把子節點添加到這個層上:

layer.addChild(child);

##把層擴充成各種功能的面闆

Cocos2d-js提供了兩個常用的Layer給我們使用:LayerColor和LayerGradient。顧名思義,LayerColor是一個純色背景的層,利用這個類, 我們可以友善地實作純色背景。在自定義Scene的onEnter函數中添加代碼

var layerColor = new cc.LayerColor(cc.color(,,),,);
           

這樣就建立了一個純白色寬和高都是100像素的背景,并添加到舞台上。其中cc.color是一個全局函數,接受0到255的R、G、B三通道的值,産生一個顔色值。如果不設定寬和高,層将預設為全屏大小。

LayerGradient是顔色漸變的層。例如以下代碼可以實作一個由紅色漸變為藍色的背景:

var layerGradient = new cc.LayerGradient(cc.color(,,),cc.color(,,));
this.addChild(layerGradient);
           

除了Cocos2d-js提供固定的Layer外,我們可以自行擴充層,擴充方式跟自定義場景類似。例如我們自定義一個背景層,就可以在不同的場景中重複。在HelloWorld代碼中就有HelloWorldLayer,裡面的代碼較為複雜,這裡我們看一個簡化版本,

var HelloWorldLayer = cc.Layer.extend({
ctor:function(){
this._super();
var bg = new cc.Sprite(res.HelloWorld_png);
var size = cc.director.getWinSize;
bg.x = size.width/,
bg.y = size.height/,
this.addChid(bg,);
return ture;
}
});
           

在上述代碼中,HelloWorldLayer擴充了cc.Layer,預設加載了一張圖作為背景。addChild(bg,1)表示把背景添加到最底層,Layer會根據第二個參數把孩子節點做排序,數字越小越方在越低層次。

在任意一個場景中, 如果要添加同樣的背景,隻需要添加兩句代碼即可,這就是複用的好處。

var layer = new HelloWorldLayer();
this.addChild(layer);
           

二維直接的人物 –精靈(Sprite)

上面出現了cc.Sprite,它就是精靈。精靈也是由節點Node擴充而成的,原來的目的是用于表示遊戲中的人物,但實際上,因為精靈封裝了圖檔加載等常用功能,我們還可以用精靈來加載背景圖、障礙物等非人物内容。

使用Sprite非常簡單,隻需要兩句代碼就可以完成工作。

var ball = new cc.Sprite('res/item_2.png');
  this.addChild(ball,)
           

建立Sprite時,隻要加載構造函數中傳入圖檔的路勁即可。

另外,增加ball坐标大媽,讓ball在螢幕中間顯示:

var size = cc.director.getWinSize();
ball.x = size.width/;
ball.y = size.height/;
           

這裡cc.director.getWinSize可以獲得視窗的設計尺寸,也就是我們在main.js中設定的寬和高。

在遊戲中,無論是主角還是NPC,還是各種飛機大炮,都可以使用精靈來加載。精靈替我們把圖檔處理的繁瑣工作都處理好了,我們隻需要設定精靈的位置和後續動作即可,不過,在HTML5遊戲中,我們還需要更新resource.js,把所有圖檔的路徑添加到數組中,這樣Cocos2d-js在初始化時會預加載這些資源,最後我們才能正常建立精靈。

如果單純加載一個圖檔還無法滿足需求,例如飛機有血量、飛彈數量等資訊,我們可以擴張精靈,定義自己的類,例如代碼清單中。

var Plane = cc.Sprite.extend({
 life:,
 cotor:function(imageUrl){
 this._super(imageUrl);
 this.life = ;
},
onHit:function(){
this.life -=;
}
});
           

當飛機碰到障礙物時,我們調用Plane的onHit函數,控制飛機的生命減20.這些都是面向對象的程式設計方式。

另外,精靈和層一樣,還可以繼續嵌套其他的節點,精靈還可以包含精靈。這個嵌套機制可能在開始時難以了解,但是使用起來将非常友善。舉個例子,例如遊戲中飛機得到了某種加強的能力,畫面上需要表現為加上一個尾翼之類的效果,我們可以選擇用一張全新的圖檔來表現,也可以在原圖檔上疊加一個小的尾翼圖檔。第二個方案可以節省圖檔的大小,而且,在多個飛機都需要做一樣的處理時候,這個方案将明顯更加實用,這個方案的代碼如下:

var plane = new cc.Sprite('res/plane.png');
this.addChild(plane);
var tail = new cc.Sprite('res/tail.png');
plane.addChild(tail);
tail.x = -;
tail.y = ;
           

把tail添加到plane上之後,需要設定tail的位置,而這個坐标并不是相對真個畫面的左下角定位的,而是相對plane自身内部的左邊系。

天外有天–當層和精靈嵌套時怎麼設定坐标

我們在學習了cocos2d-js世界的坐标系,這個坐标系是整個遊戲畫面的坐标成為全局坐标,而當層和精靈不斷嵌套時将産生一個新的坐标系,層添加在舞台上,這時候層的定位使用全局坐标,同時,層的内部也有一個局部坐标,而再把精靈添加到層上時,精靈将以層的局部坐标定位。

var bg = new cc.LayerColor(cc.color(,,),,);
bg.x = ;
bg.y = ;
this.addChild(bg,);

var ball = new cc.Sprite('res/item_2.png');
ball.x = ;
ball.y = ;

this.addChild(ball,);
var ball2 = new cc.Sprite('res/item_3.png');
ball2.x= ;
ball.y = ;
bg.addChild(ball2,);
           

在舞台上添加一個寬、高為200像素的灰色層,并把層移動到畫面(100,100)位置,再添加一個球,放到畫面(100,300)位置,最後再添加一個球,彈這個區在灰色的層上,坐标設定為(100,100)。

我們可以發現:灰色層以整個畫面為參考,左下角的位置正好就是(100,100),而ball在層的左上角的位置,球的中心跟層的左上角正好重合,球心的位置相對整個畫面來說,正好是(100,300),而層中間的球ball2又正好在層的中間位置,球心相對層的左下角來說位置是(100,100)。

由此我們可以總結出,每個節點都有自己内部的局部坐标系,這個局部坐标系的原點就是該節點的左下角,例如上邊的灰色層,它的局部坐标系原點在全局的位置就是(100,100)。嵌套的子節點都以父節點的左下角為坐标原點,再設定自身的位置。

另外,在上述的例子中,大家可以明顯的發現精靈和層不一樣,同樣設定坐标為(100,100),層的左下角移動到了,而精靈卻是把中心點移動到(100,100)。我們這樣了解:精靈加載圖檔做了特殊的處理,把圖檔忠心移動到了精靈的坐标點位置。不過,無論是層還是精靈,如果它在添加子節點,都以左下角作為子節點的局部坐标系原點,對于LayerColor來說,就是色塊的左下角,而對于Sprite,則是圖檔的左下角。

這裡順便再介紹另一個重要的屬性–錨點(anchor)。層和精靈的archorX和anchorY預設都是0.5,表示以中心作為錨點。錨點的作用是,當這個節點縮放旋轉時,将以這個點作為參考點,錨點為0.5能讓節點縮放和旋轉時位置保持不變,

導演

我們在運作helloWorld時,就知道有cc.director.runScene,但隻是見過一面,不知道這個導演為何方神聖?Cocos2d-js世界拆分為若幹個場景,那麼場景和場景之間的切換工作就交給導演了,顧名思義,這個跟實際拍電影是一樣的,導演說,現在拍第一場,那麼第一場的導演就紛紛各就各位:導演說cut,大家都停下來,導演說下一場,那麼目前的演員就退出而下一場的演員就上台

場景的切換

首先,我們要記住的是cc.director.runScene,這個函數用于加載或切換場景。接着,由于我們已經有HelloWorldScene,我們再建立一個

SecondScene。如代碼如下:
var SendScene = cc.Scene.extend({
onEnter : function(){
this._super();
var layer = new cc.LayerGradient(cc.color(,,),cc.color(,,));
this.addChild(layer);
}
});
           

這個場景隻有一個漸變的層。

另外,再在HelloWorldScene中添加切換場景的定時器,為了友善了解和容易了解,這裡的定時器使用了JS的setTimeout,控制3秒後切換為場景2,但為了原生版本相容性。實際上我們不推薦使用這個函數,而是用Cocos2d-js提供的定時器,代碼清單3-10所示,

var HelloWorldScene = cc.Scene.extend({
onEnter:function(){
this._super();

var layer = new HelloWorldLayer();
this.addChild(layer);
setTimeout(function(){
  cc.director.runScene(new SecondScene());
})
}
})
           

大家運作後發現,從場景1到場景2的過程是瞬間的,沒有任何過度效果,是不是覺得這樣不夠高大上?那我們看看Cocos2d-js給我們預備了什麼高大上的套餐

首先介紹滑動切換(Slide);

跟原來稍有不同的是,傳遞給runScene的參數并不是直接的Scene,而是經過了cc.TransitionSlideInT的包裝。TransitionSlideInt(2,new SecondScene())就表示讓SecondScene在2秒内從上往下滑進舞台,舊場景也同時畫出。

runScene會銷毀舊場景上的所有内容,下次回到該場景時,所有内容都要從建立立,如果頻繁切換場景,我們可以使用pushScene和popScene。pushScene跟runScene用法一樣,但不銷毀場景内容,而是把上一個場景存起來,popScene則把目前場景銷毀,然後快速回到上一個場景中,這時候我們隻需要重新喚起原來的内容,并不需要重建立立。這對方法特别适合一些遊戲設定界面,例如遊戲主界面切換到音頻界面設定界面,由于玩家設定完音效後馬上切換回到遊戲界面,是以這裡把遊戲界面銷毀再重建會浪費效率。

導演可以提供的資訊

導演這個老大不是隻懂得隻會就可以當的,它必須還有求必應,懂得如何解決拍攝中的各種問題。在Cocos2d-js中,導演cc.director可以為我們提供很多資訊或功能,這裡筆者列出一些常用的功能:

視窗的設計尺寸:getWinSize;

視窗的實際尺寸:getVisibleSize;這個資訊在适配手機螢幕的時候十分重要

擷取全局的定時器:getScheduler;

暫停/恢複場景:pause/resume

繼續閱讀