試Cocos Creator (20K~40K薪資)經(jīng)常會被問的20個問題, 看看你懂幾個。
1: 如何優(yōu)化Cocos Creator 包體體積。
2: Cocos creator如何做資源管理。
3: Cocos Creator如何編寫單例模式。
4: Cococs creator 如何使用物理引擎?
5: Cocos Creator Label的原理與如何節(jié)約Drawcall?
6: Cocos Creator 背包系統(tǒng)可能會需要哪些優(yōu)化?
7: Cocos Creator WebSocket與Socket.IO分別是什么?
8: Cocos Creator如何內(nèi)置protobuf JS版本?
9: Cocos Creator 材質(zhì), shader 分別是什么?
10:Cocos creator 固定寬度與固定高度的底層原理是什么?Cocos creator是如何做適配的?
11: Cocos Creator 圖集打包有什么意義,我們一般在項目里面怎么規(guī)劃圖集?
12: Cocos Creator 如何做游戲框架,能讓多人很好的協(xié)作,代碼好維護(hù)?
13: Cocos Creator 2D如何做Drawcall優(yōu)化?
14: Cococs creator 骨骼動畫與幀動畫的優(yōu)缺點是什么?
15: 如何使用Cococs Creator制作一個地圖編輯與尋路導(dǎo)航系統(tǒng)?
16: Cocos Creator 節(jié)點池的基本原理是什么?如何使用?
18: Cocos Creator 如何設(shè)計熱更新系統(tǒng), 如何設(shè)計大廳與子游戲模式?
19: Cocos Android里 runOnGLThread是什么意思?
20: Cocos Creator 如何設(shè)計自動化打包發(fā)布腳本?
Cocos Creator架構(gòu)師,主要考的是扎實的基礎(chǔ),分析問題,解決問題的能力和經(jīng)驗,系統(tǒng)框架設(shè)計, 與項目難題攻關(guān), 如果大家對哪個問題的答案感興趣,可以評論區(qū)留言,Blake老師會根據(jù)大家的需求,來寫文章回復(fù)大家問題。 祝大家早日成長為架構(gòu)師。
源:新京報
從公元前1萬年出現(xiàn)在人類食譜中起,酸奶在這千年間征服著世界各地食客的味蕾。值得一提的是,除了作為一種零食甜點,生活在不同地區(qū)的人們食用酸奶的方式并不相同。比如,77%的土耳其人習(xí)慣將酸奶與熱餐搭配,55%的巴西人則喜歡用它搭配谷物,對法國人而言,酸奶甚至稱得上是一種信仰。人們使用酸奶的方式,折射著不同地區(qū)的文化與習(xí)俗。
我們享用酸奶的方式,其實與我們的祖先享用酸奶的方式有著內(nèi)在的聯(lián)系。可以肯定的是,大多數(shù)有影響力的酸奶制作者,他們和那些傳承了家族傳統(tǒng)及食譜的人有著親緣關(guān)系。在為英國廣播公司旅行博客撰寫的一篇文章中,作者瑪達(dá)薇 ·拉馬尼(Mad hvi Ra mani)引用了保加利亞人艾莉莎·斯托伊洛伐的一段話:
如果兩位來自不同村莊的祖母用相同的原料制作酸奶,其味道也會有所不同。這是因為酸奶是一種個性化產(chǎn)品。它與地域、動物、家庭的特殊口味以及代代相傳的制作知識有關(guān)。
時至今日,在酸奶的起源地區(qū),它仍然是當(dāng)?shù)厝巳粘o嬍持胁豢苫蛉钡囊徊糠帧H欢S著人們四處遷徙,酸奶文化也隨之流傳開來,這樣說毫不夸張,因為對一些人來說,他們現(xiàn)在遵循的家族酸奶文化就是從其他地方傳過來的。和酸奶相關(guān)的傳統(tǒng)與習(xí)俗、口味與香氣、制作與靈感在全球范圍內(nèi)得到共享,這展示了世界是如何無縫擁抱這種食品的。
保加利亞酸奶:
走向全球的酸奶菌種
為了向保加利亞乳桿菌的發(fā)源地致敬,讓我們從保加利亞開始我們的全球酸奶之旅。在保加利亞酸奶的制作過程中,保加利亞乳桿菌和嗜熱鏈球菌這兩種著名的菌株協(xié)同作用,創(chuàng)造了酸奶制作的黃金標(biāo)準(zhǔn),保加利亞人稱這種酸奶為“kiselo mlyako”。這種酸奶具有獨特的酸味、濃郁的口感和特殊的香氣,酸奶愛好者很容易就能辨認(rèn)出這是保加利亞酸奶。
《舔蓋兒:酸奶小史》,[美]瓊·赫什 著,吳嶺 譯,萬川|中國工人出版社 ,2024年4月。
在20世紀(jì)早期和中期,來自保加利亞的菌株以凍干物或藥丸的形式被兜售和運輸。1937年,倫敦《觀察家報》(The Observer)上刊載了一篇文章,文章報道了薩爾茨堡一家小乳品店倒閉的消息,可以說,這是保加利亞酸奶備受消費者推崇的最好證明。據(jù)說,包括意大利大師級指揮家阿爾圖羅·托斯卡尼尼在內(nèi),人們涌向這家維也納商店,享受詩歌和享用正宗的保加利亞酸奶。下面這些詩句節(jié)選自這家店主的詩歌:
為何保加利亞人如此長壽?
為何他們從不感冒?
因為,他們——
喜歡在春冬時節(jié)享用酸奶。
保加利亞酸奶屬于保加利亞的國家專利,保加利亞人將其同名菌株——保加利亞乳桿菌授權(quán)給其他國家使用,如果這些國家想將自己生產(chǎn)的酸奶稱為“保加利亞酸奶”,它們就必須從保加利亞購買這種酸奶菌種。
最好的例證來自保加利亞羅德比山脈(Rhodope Mountains)中的一個小村莊和中國。2009 年,中國的光明乳業(yè)股份有限公司推出了一款名為“莫斯利安”的常溫酸奶飲品,這款酸奶所用的菌株源自保加利亞境內(nèi)的一個同名村莊——莫斯利安村(Momchilovtsi)。這款酸奶的產(chǎn)地在上海。如果你去莫斯利安這個小村莊參觀,你會在這里看到許多中文標(biāo)識,還有不少自學(xué)普通話的村民。每年,這里都會舉辦一個盛大的節(jié)日——莫斯利安酸奶文化節(jié), 而且還會選出一位“酸奶皇后”。這個被稱為“長壽村”的小鎮(zhèn)有1200多名居民,每年都會招待1000多名中國游客。
新疆奶酪和老北京酸奶:
“方便”概念下的情懷
新疆維吾爾自治區(qū)是中國西北部的一個自治區(qū),這里生活著大量的維吾爾族人,酸奶在新疆的出現(xiàn),表明這里是全球不同飲食傳統(tǒng)的另一個交會點,它也影響著中國文化。維吾爾族人已經(jīng)在這里生活了1000多年。與漢族相比,他們的烹飪更接近中東風(fēng)味,“奶酪”酸奶在這里非常受歡迎。
正如美食作家范(Van)在她的個人美食網(wǎng)站中所寫的那樣:
奶酪可以說是早期的酸奶。最早在19世紀(jì)時,宮廷廚師們掌握了這道甜點,后來,奶酪的配方發(fā)生演變,它的味道變得更柔和、更甜。在20世紀(jì)50年代,它開始在北京流行起來,成為注重健康的潮流人士的最愛。
裝在瓷瓶中的老北京特色酸奶,采用傳統(tǒng)的藍(lán)白色薄 紙蓋包裝,還配有吸管。(出版社供圖)
漸漸地,這一食譜在全中國傳播開來,如今,在每個集市和繁華街道的小販那里,都有老北京酸奶銷售。邊喝老北京酸奶邊在市場上漫步,可以說是一種享受。老北京酸奶裝在瓷瓶中,封口是系著繩子的藍(lán)白色薄紙蓋,吃酸奶時,用細(xì)吸管或一次性勺子插進(jìn)去,然后就可以好好享用了,吃完后,記得再將酸奶瓶歸還給小販。
如今,中國消費者更多地生活在城市,他們有了更多的可支配收入,也在尋找便攜式的營養(yǎng)來源。“方便食品或便攜食品”(food on-the-go)這一飲食概念在亞洲市場非常具有吸引力,因此,在中國和韓國,你會發(fā)現(xiàn)在湍急的人流中,有女性騎著自行車兜售酸奶。
韓國的“酸奶女士”穿著標(biāo)志性的杏色夾克,戴著粉色頭盔。她們駕駛著名為“CoCos”的電動冰箱,是“Cold & Cool”的縮寫。這些帶輪子的冰箱可以容納數(shù)量驚人的3300瓶酸奶。在亞洲烹飪文化中,酸奶仍未被視為一種烹飪輔料或主流食品,而是被人們當(dāng)作一種快速補充營養(yǎng)的方式。中國和東南亞市場有望成為全球最大的酸奶消費市場,其中,飲用型酸奶有助于推動市場發(fā)展。
日本酸奶:益生菌的由來
發(fā)現(xiàn)并重新認(rèn)識酸奶的并不只有中國,其他亞洲市場也是如此。日本對酸奶的熱愛始于2 0世紀(jì)30年代,當(dāng)時,一位出生于京都的科學(xué)家——代田稔博士(Dr Minora Shirota),對乳酸桿菌與疾病之間的關(guān)系進(jìn)行了探索。經(jīng)過一番詳盡的研究之后,他分離出了干酪乳桿菌代田株。這是一種由300多種乳酸桿菌組成的益生菌,代田稔博士用它來發(fā)酵牛奶,并將得到的酸奶產(chǎn)品命名為養(yǎng)樂多。他發(fā)明的“養(yǎng)樂多”酸奶和他所說的“只有腸道健康才能延年益壽”的觀點受到了日本民眾的歡迎和支持。
貨架上的“養(yǎng)樂多”。
時至今日,養(yǎng)樂多在日本仍然廣受歡迎,全球每天有3000多萬人享用這種酸奶,因為據(jù)說它可以提高人體免疫力,促進(jìn)腸道消化。
1971年,日本乳制品企業(yè)巨頭明治乳業(yè)(Meiji)推出了日本第一款原味酸奶,從而宣示其全面進(jìn)入酸奶市場。同中國與保加利亞的聯(lián)系一樣,明治乳業(yè)意識到與保加利亞開展合作能促進(jìn)酸奶的銷售,于是,它在1973年獲得了保加利亞的授權(quán),推出了明治保加利亞式酸奶。明治乳業(yè)繼續(xù)堅持創(chuàng)新,1996 年,它又獲得了日本的“特定保健用食品”(Food for Specified Health Use)標(biāo)簽的使用權(quán),這進(jìn)一步提高了酸奶的銷量。
最近,日本又推出了一系列新口味酸奶,其中包括抹茶或柿子等深受消費者歡迎的傳統(tǒng)配料。為了進(jìn)一步提升消費體驗,產(chǎn)品采用了類似古代漆器的杯子進(jìn)行包裝。預(yù)計日本的酸奶市場將會繼續(xù)增長,但與亞洲其他地區(qū)相比,其增長速度有所放緩。
印度次大陸酸奶:
特色菜肴的基礎(chǔ)
在印度次大陸(包括印度、南亞和中亞的部分地區(qū)、巴基斯坦、孟加拉國和喜馬拉雅山區(qū)),酸奶自古以來就是當(dāng)?shù)孛朗巢豢苫蛉钡囊徊糠帧W鳛橐环N影響廣泛的素食文化,生活在這些地區(qū)的居民將酸奶作為補充人體所需蛋白質(zhì)、鈣元素和脂肪的來源。此外,酸奶還是一種清涼食品,可以降低印度菜中常用香料所產(chǎn)生的熱量。
在印度,酸奶被稱為達(dá)希(dahi,印度用語,意為“凝乳”),與傳統(tǒng)酸奶將菌種引入經(jīng)過巴氏殺菌的牛奶中不同,達(dá)希是將乳酸桿菌接種在煮沸的牛奶當(dāng)中。制作達(dá)希是為了促進(jìn)而不是抑制凝乳的發(fā)展,這一點不同于西方的許多酸奶。在印度,制作達(dá)希是一項日常活動,將前一天做好的凝乳加入新的牛奶中,就能制作出美味濃稠的酸奶。
凝乳是許多印度特色菜肴的基礎(chǔ)。它有助于將米飯和小扁豆湯(dal)融合在一起,這樣更容易用右手抓起來食用——印度風(fēng)格的吃法。“aloo palda”是加有凝乳的土豆咖喱飯,這是一道經(jīng)典的帕哈里菜肴,它依靠酸奶來達(dá)到黏合食材所需要的稠度。“mor rasam”是印度南部的一種酸甜口燉菜,在這道菜肴中,用酸凝乳來制作類似酪乳的那種特色味道。“dahi papdi chaat ”是印式酸奶酥脆沙拉,它是將酸奶與各種酸辣醬(如薄荷醬、香菜醬、酸豆醬等)混合之后,作為這道廣受歡迎的小吃的配料。
酸奶還可用于制作松軟的南印度煎餅(dosas)以及克什米爾的招牌菜印度香飯——這是一種用慢火燉煮的食物,傳統(tǒng)做法是將食材放在密封的厚底鍋上進(jìn)行烹制。除了上面這些美食,甚至還有一種印度版的烤奶酪,將酸奶、洋蔥、香料和香草混合在一起,制成達(dá)希吐司(dahi toast), 在印度,這是一種很受歡迎的早餐食品。
香草味濃郁、美味可口的酸奶色拉是一種絕佳的蘸醬和調(diào)味品。(出版社供圖)
在所有使用酸奶的印度菜肴中,最著名的可能是酸奶色拉(raita),它是酸奶和各種配料(如蔬菜、 水果、香草、香料等)的清涼混合物。它既可以當(dāng)調(diào)味品,也可以當(dāng)配菜,制作起來非常簡單。拉西(lassi)酸奶奶昔是印度的國民飲料,這是一種以酸奶為基底的冰沙,其起源可以追溯到公元前1000年左右的旁遮普地區(qū)。這種飲料分咸甜兩種版本:加胡椒粉或紅辣椒粉即為咸味版,加芒果汁或玫瑰汁即為甜味版。
說到甜點,千萬不要忽視馬哈拉施特拉邦的經(jīng)典甜點“shrikhand”,這道甜點簡單絕妙,只需要三種配料就能制作:過濾后的酸奶、糖粉和香脆的堅果。這是一種充滿風(fēng)味的食物,食用時可以加入藏紅花絲,或者撒上豆蔻、開心果等來提味。
在印度尼西亞,人們喜歡食用“dad iah”這種食物,它可以說是印尼版的酸奶,是將未加熱的水牛奶放在竹筍中發(fā)酵而成的。在尼泊爾,人們將食用酸奶作為文化和宗教慶祝活動的一部分。尼泊爾人相信,被稱為“juju dhai ”的酸奶可以帶來好運。因此,在舉行慶典時,人們經(jīng)常會在入口處放置裝滿酸奶的陶罐以迎接慶祝者。現(xiàn)如今,不在這些國家生活的人也可以品嘗到這些特色的酸奶菜肴,因為這些酸奶菜肴的烹飪方式已經(jīng)與其他地區(qū)的烹飪方式相融合,并在中東、東南亞、歐洲、北美、非洲和加勒比地區(qū)廣為流傳。
土耳其酸奶:
作為經(jīng)典配料的咸酸奶
時至今日,酸奶仍然是土耳其美食中不可或缺的一部分,這里是酸奶的發(fā)源地。酸奶是土耳其人最喜愛的配料、調(diào)味品和配菜,也是土耳其最著名的飲料——咸酸奶(ayran)的基礎(chǔ)食材。
據(jù)說,咸酸奶是由突厥游牧民族發(fā)現(xiàn)的,其本質(zhì)是一種含乳酸但不含酒精、加水稀釋過的酸奶。在炎熱的夏季,游牧民族要忍受酷熱的沙漠,對他們來說,食用這種咸酸奶非常有用。它有提神補體的作用,因為按照傳統(tǒng)咸酸奶的做法,會在酸奶中加入(能夠補充能量的)鹽。
傳統(tǒng)上來說,咸酸奶是裝在銅制馬克杯中飲用的,它可以搭配土耳其美食飲用,有提神補體的效用。(出版社供圖)
至今,土耳其人和許多生活在這一地區(qū)的其他人,他們?nèi)匀缓芟矚g喝咸酸奶,以至于當(dāng)你走進(jìn)當(dāng)?shù)氐柠湲?dāng)勞餐廳時,你很有可能會在菜單上看到它。
土耳其人還喜歡一道與印度的酸奶色拉非常相似的菜肴,但它有著自己的地域特色。酸奶經(jīng)稀釋后,依次加入鹽、大蒜末、黃瓜、薄荷、土茴香,通常,還會再往里加入漆樹粉(sumac)、酸橙汁和橄欖油的混合物,最后得到的就是酸味小黃瓜咸奶酪湯(cacik),這是一種類似蘸醬的清新涼爽的調(diào)味品。它是土耳其許多特色菜肴(如“kebabs”和“koftas”,可以將其理解為土耳其版的烤肉串和烤肉丸)的絕佳調(diào)味品。
希臘酸奶:
酸奶黃瓜醬開胃加倍
在酸奶界,希臘酸奶可謂名聲大噪,眾所周知,希臘酸奶經(jīng)常被人模仿。要想品嘗到真正的希臘酸奶,你需要在它的發(fā)源地品嘗一下“straggisto”,這是一種正宗的脫乳清酸奶。在許多希臘菜譜中,都有這種美味食品的身影,其中,最著名的當(dāng)數(shù)希臘酸奶黃瓜醬(tzatz iki)。
在希臘地區(qū),希臘酸奶黃瓜醬既可以單獨作為一道開胃菜,也可以用作做飯時的醬汁。(出版社供圖)
它的做法和酸奶色拉很像。制作方法很簡單,先將瀝干水分的黃瓜磨碎,再加入酸奶、 薄荷香料、橄欖油、鹽和檸檬汁,攪拌均勻。待這些食材的味道充分融合后,即可食用。在許多豐盛的希臘特色美食中,希臘酸奶黃瓜醬都是絕佳的輔料。
雖然希臘酸奶沒有被希臘注冊為商標(biāo),但歐盟會經(jīng)常制裁那些將酸奶名稱標(biāo)注為“希臘”的國家,稱其是故意誤導(dǎo)消費者。
據(jù)報道,在1948年,希臘總理泰米斯托克利·索福利斯臨終前想吃最后一頓飯,風(fēng)卷殘云間,他便喝下了兩杯啤酒、一碗湯,當(dāng)然,還有他心愛的酸奶,這足以進(jìn)一步證明酸奶在希臘的重要地位。
(本文內(nèi)容系獨家內(nèi)容,經(jīng)出版方授權(quán)整理自《舔蓋兒:酸奶小史》。)
原著作者/[美]瓊·赫什
整理/申璐
編輯/走走
校對/柳寶慶
ocos Creator 開發(fā)游戲的一個核心理念就是讓內(nèi)容生產(chǎn)和功能開發(fā)可以流暢的并行協(xié)作,我們在上個部分著重于處理美術(shù)內(nèi)容,而接下來就是通過編寫腳本來開發(fā)功能的流程,之后我們還會看到寫好的程序腳本可以很容易的被內(nèi)容生產(chǎn)者使用。
如果您從沒寫過程序也不用擔(dān)心,我們會在教程中提供所有需要的代碼,只要復(fù)制粘貼到正確的位置就可以了,之后這部分工作可以找您的程序員小伙伴來解決。下面讓我們開始創(chuàng)建驅(qū)動主角行動的腳本吧。
創(chuàng)建腳本
注意: Cocos Creator 中腳本名稱就是組件的名稱,這個命名是大小寫敏感的!如果組件名稱的大小寫不正確,將無法正確通過名稱使用組件!
編寫組件屬性
在打開的 Player 腳本里已經(jīng)有了預(yù)先設(shè)置好的一些代碼塊,如下所示:
cc.Class({ extends: cc.Component, properties: { // foo: { // // ATTRIBUTES: // default: null, // The default value will be used only when the component attaching // // to a node for the first time // type: cc.SpriteFrame, // optional, default is typeof default // serializable: true, // optional, default is true // }, // bar: { // get () { // return this._bar; // }, // set (value) { // this._bar=value; // } // }, }, // LIFE-CYCLE CALLBACKS: // onLoad () {}, start () { }, // update (dt) {}, });
我們來大概了解一下這些代碼的作用。首先我們可以看到一個全局的 cc.Class() 方法,什么是 cc呢?cc 是 Cocos 的簡稱,Cocos 引擎的主要命名空間,引擎代碼中所有的類、函數(shù)、屬性和常量都在這個命名空間中定義。而 Class() 就是 cc 模塊下的一個方法,這個方法用于聲明 Cocos Creator 中的類。為了方便區(qū)分,我們把使用 cc.Class 聲明的類叫做 CCClass。Class() 方法的參數(shù)是一個原型對象,在原型對象中以鍵值對的形式設(shè)定所需的類型參數(shù),就能創(chuàng)建出所需要的類。
例如:
var Sprite=cc.Class({ name: "sprite" });
以上代碼用 cc.Class() 方法創(chuàng)建了一個類型,并且賦給了 Sprite 變量。同時還將類名設(shè)為 sprite。類名用于序列化,一般可以省略。
對于 cc.Class 的詳細(xì)學(xué)習(xí)可以參考 使用 cc.Class 聲明類型。
現(xiàn)在我們回到腳本編輯器看回之前的代碼,這些代碼就是編寫一個組件(腳本)所需的結(jié)構(gòu)。具有這樣結(jié)構(gòu)的腳本就是 Cocos Creator 中的 組件(Component),他們能夠掛載到場景中的節(jié)點上,提供控制節(jié)點的各種功能。我們先來設(shè)置一些屬性,然后看看怎樣在場景中調(diào)整他們。
找到 Player 腳本里的 properties 部分,將其改為以下內(nèi)容并保存:
// Player.js //... properties: { // 主角跳躍高度 jumpHeight: 0, // 主角跳躍持續(xù)時間 jumpDuration: 0, // 最大移動速度 maxMoveSpeed: 0, // 加速度 accel: 0, }, //...
Cocos Creator 規(guī)定一個節(jié)點具有的屬性都需要寫在 properties 代碼塊中,這些屬性將規(guī)定主角的移動方式,在代碼中我們不需要關(guān)心這些數(shù)值是多少,因為我們之后會直接在 屬性檢查器 中設(shè)置這些數(shù)值。以后在游戲制作過程中,我們可以將需要隨時調(diào)整的屬性都放在 properties 中。
現(xiàn)在我們可以把 Player 組件添加到主角節(jié)點上。在 層級管理器 中選中 Player 節(jié)點,然后在 屬性檢查器 中點擊 添加組件 按鈕,選擇 添加用戶腳本組件 -> Player,為主角節(jié)點添加 Player 組件。
現(xiàn)在我們可以在 屬性檢查器 中(需要選中 Player 節(jié)點)看到剛添加的 Player 組件了,按照下圖將主角跳躍和移動的相關(guān)屬性設(shè)置好:
這些數(shù)值除了 jumpDuration 的單位是秒之外,其他的數(shù)值都是以像素為單位的,根據(jù)我們現(xiàn)在對 Player組件的設(shè)置:我們的主角將能夠跳躍 200 像素的高度,起跳到最高點所需的時間是 0.3 秒,最大水平方向移動速度是 400 像素每秒,水平加速度是 350 像素每秒。
這些數(shù)值都是建議,一會等游戲運行起來后,您完全可以按照自己的喜好隨時在 屬性檢查器 中修改這些數(shù)值,不需要改動任何代碼。
編寫跳躍和移動代碼
下面我們添加一個方法,來讓主角跳躍起來,在 properties: {...}, 代碼塊的下面,添加叫做 setJumpAction 的方法:
// Player.js properties: { //... }, setJumpAction: function () { // 跳躍上升 var jumpUp=cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()); // 下落 var jumpDown=cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn()); // 不斷重復(fù) return cc.repeatForever(cc.sequence(jumpUp, jumpDown)); },
這里就需要了解一下 Cocos Creator 的 動作(Action)系統(tǒng) 了。由于動作系統(tǒng)比較復(fù)雜,這里就簡單的介紹一下。
在 Cocos Creator 中,動作 簡單來說就是 節(jié)點的位移、縮放和旋轉(zhuǎn)。
例如在上面的代碼中,moveBy() 方法的作用是在規(guī)定的時間內(nèi)移動指定的一段距離,第一個參數(shù)就是我們之前定義主角屬性中的跳躍時間,第二個參數(shù)是一個 Vec2(表示 2D 向量和坐標(biāo))類型的對象,為了更好的理解,我們可以看看官方給的函數(shù)說明:
/** * !#en * Moves a Node object x,y pixels by modifying its position property. <br/> * x and y are relative to the position of the object. <br/> * Several MoveBy actions can be concurrently called, and the resulting <br/> * movement will be the sum of individual movements. * !#zh 移動指定的距離。 * @method moveBy * @param {Number} duration duration in seconds * @param {Vec2|Number} deltaPos * @param {Number} [deltaY] * @return {ActionInterval} * @example * // example * var actionTo=cc.moveBy(2, cc.v2(windowSize.width - 40, windowSize.height - 40)); */ cc.moveBy=function (duration, deltaPos, deltaY) { return new cc.MoveBy(duration, deltaPos, deltaY); };
可以看到,方法 moveBy 一共可以傳入三個參數(shù),前兩個參數(shù)我們已經(jīng)知道,第三個參數(shù)是 Number 類型的 Y 坐標(biāo),我們可以發(fā)現(xiàn)第二個參數(shù)是可以傳入兩種類型的,第一種是 Number 類型,第二種才是 Vec2類型,如果我們在這里傳入的是 Number 類型,那么默認(rèn)這個參數(shù)就是 X 坐標(biāo),此時就要填第三個參數(shù),為 Y 坐標(biāo)。上面的例子中 cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)) 第二個參數(shù)傳入的是使用 cc.v2 方法構(gòu)建的 Vec2 類型對象,這個類型表示的是一個坐標(biāo),即有 X 坐標(biāo)也有 Y 坐標(biāo),因為不需要再傳入第三個參數(shù)!同時注意官方的一段話 x and y are relative to the position of the object.,這句話的意思是傳入的 X、Y 坐標(biāo)都是相對于節(jié)點當(dāng)前的坐標(biāo)位置,而不是整個坐標(biāo)系的絕對坐標(biāo)。
了解了參數(shù)的含義之后,我們再來關(guān)注 moveBy() 方法的返回值,看官方說明可以知道,這個方法返回的是一個 ActionInterval 類型的對象,ActionInterval 在 Cocos 中是一個表示時間間隔動作的類,這種動作在一定時間內(nèi)完成。到這里我們就可以理解代碼 cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()) 前一部分 的意思了,它的意思就是構(gòu)造一個 ActionInterval 類型的對象,這個對象表示在 jumpDuration 的時間內(nèi),移動到相對于當(dāng)前節(jié)點的 (0,this.jumpHeight) 的坐標(biāo)位置,簡單來說,就是一個向上跳躍的動作。
那么 后半部分 easing(cc.easeCubicActionOut()) 的作用是什么呢?easing 是 ActionInterval 類下的一個方法,這個方法可以讓時間間隔動作呈現(xiàn)為一種緩動運動,傳入的參數(shù)是一個緩動對象,返回一個 ActionInterval 類型對象,這里傳入的是使用 easeCubicActionInOut 方法構(gòu)建的緩動對象,EaseCubicInOut 是按三次函數(shù)緩動進(jìn)入并退出的動作,具體曲線可參考下圖:
詳細(xì)內(nèi)容可參考 API。
接下來在 onLoad 方法里調(diào)用剛添加的 setJumpAction 方法,然后執(zhí)行 runAction 來開始動作:
// Player.js onLoad: function () { // 初始化跳躍動作 this.jumpAction=this.setJumpAction(); this.node.runAction(this.jumpAction); },
onLoad 方法會在場景加載后立刻執(zhí)行,所以我們會把初始化相關(guān)的操作和邏輯都放在這里面。我們首先將循環(huán)跳躍的動作傳給了 jumpAction 變量,之后調(diào)用這個組件掛載的節(jié)點下的 runAction 方法,傳入循環(huán)跳躍的 Action 從而讓節(jié)點(主角)一直跳躍。保存腳本,然后我們就可以開始第一次運行游戲了!
點擊 Cocos Creator 編輯器上方正中的 預(yù)覽游戲 按鈕
,Cocos Creator 會自動打開您的默認(rèn)瀏覽器并開始在里面運行游戲,現(xiàn)在應(yīng)該可以看到我們的主角——紫色小怪獸在場景中間活潑的蹦個不停了。
移動控制
只能在原地傻蹦的主角可沒前途,讓我們?yōu)橹鹘翘砑渔I盤輸入,用 A 和 D 來控制他的跳躍方向。在 setJumpAction 方法的下面添加鍵盤事件響應(yīng)函數(shù):
// Player.js setJumpAction: function () { //... }, onKeyDown (event) { // set a flag when key pressed switch(event.keyCode) { case cc.macro.KEY.a: this.accLeft=true; break; case cc.macro.KEY.d: this.accRight=true; break; } }, onKeyUp (event) { // unset a flag when key released switch(event.keyCode) { case cc.macro.KEY.a: this.accLeft=false; break; case cc.macro.KEY.d: this.accRight=false; break; } },
然后修改 onLoad 方法,在其中加入向左和向右加速的開關(guān),以及主角當(dāng)前在水平方向的速度。最后再調(diào)用 cc.systemEvent,在場景加載后就開始監(jiān)聽鍵盤輸入:
// Player.js onLoad: function () { // 初始化跳躍動作 this.jumpAction=this.setJumpAction(); this.node.runAction(this.jumpAction); // 加速度方向開關(guān) this.accLeft=false; this.accRight=false; // 主角當(dāng)前水平方向速度 this.xSpeed=0; // 初始化鍵盤輸入監(jiān)聽 cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); }, onDestroy () { // 取消鍵盤輸入監(jiān)聽 cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); },
有 Android 開發(fā)經(jīng)驗的同學(xué)比較好理解,這里的監(jiān)聽器實質(zhì)上就和 Android 里的 OnClickListener 差不多,在 cocos 中通過 systemEvent 來監(jiān)聽系統(tǒng) 全局 事件。(鼠標(biāo)、觸摸和自定義事件的監(jiān)聽和派發(fā)的詳細(xì)內(nèi)容請參考 監(jiān)聽和發(fā)射事件。)這里通過向 systemEvent 注冊了一個鍵盤響應(yīng)函數(shù),在函數(shù)中通過 switch 判斷鍵盤上的 A 和 D 是否被按下或松開,若按下就執(zhí)行對應(yīng)的操作。
最后修改 update 方法的內(nèi)容,添加加速度、速度和主角當(dāng)前位置的設(shè)置:
// Player.js update: function (dt) { // 根據(jù)當(dāng)前加速度方向每幀更新速度 if (this.accLeft) { this.xSpeed -=this.accel * dt; } else if (this.accRight) { this.xSpeed +=this.accel * dt; } // 限制主角的速度不能超過最大值 if ( Math.abs(this.xSpeed) > this.maxMoveSpeed ) { // if speed reach limit, use max speed with current direction this.xSpeed=this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed); } // 根據(jù)當(dāng)前速度更新主角的位置 this.node.x +=this.xSpeed * dt; },
update 在場景加載后就會每幀調(diào)用一次,我們一般把需要經(jīng)常計算或及時更新的邏輯內(nèi)容放在這里。在我們的游戲中,根據(jù)鍵盤輸入獲得加速度方向后,就需要每幀在 update 中計算主角的速度和位置。
保存腳本后,點擊 預(yù)覽游戲 來看看我們最新的成果。在瀏覽器打開預(yù)覽后,用鼠標(biāo)點擊一下游戲畫面(這是瀏覽器的限制,要點擊游戲畫面才能接受鍵盤輸入),然后就可以按 A 和 D 鍵來控制主角左右移動了!
感覺移動起來有點遲緩?主角跳的不夠高?希望跳躍時間長一些?沒問題,這些都可以隨時調(diào)整。只要為 Player 組件設(shè)置不同的屬性值,就可以按照您的想法調(diào)整游戲。這里有一組設(shè)置可供參考:
Jump Height: 150 Jump Duration: 0.3 Max Move Speed: 400 Accel: 1000
這組屬性設(shè)置會讓主角變得靈活無比,至于如何選擇,就看您想做一個什么風(fēng)格的游戲了。
制作星星
主角現(xiàn)在可以跳來跳去了,我們要給玩家一個目標(biāo),也就是會不斷出現(xiàn)在場景中的星星,玩家需要引導(dǎo)小怪獸碰觸星星來收集分?jǐn)?shù)。被主角碰到的星星會消失,然后馬上在隨機位置重新生成一個。
制作 Prefab
對于需要重復(fù)生成的節(jié)點,我們可以將他保存成 Prefab(預(yù)制) 資源,作為我們動態(tài)生成節(jié)點時使用的模板。關(guān)于 Prefab 的更多信息,請閱讀 預(yù)制資源(Prefab)。
首先從 資源管理器 中拖拽 assets/textures/star 圖片到場景中,位置隨意,我們只是需要借助場景作為我們制作 Prefab 的工作臺,制作完成后會我們把這個節(jié)點從場景中刪除。
我們不需要修改星星的位置或渲染屬性,但要讓星星能夠被主角碰觸后消失,我們需要為星星也添加一個專門的組件。按照和添加 Player 腳本相同的方法,添加名叫 Star 的 JavaScript 腳本到 assets/scripts/中。
接下來雙擊這個腳本開始編輯,星星組件只需要一個屬性用來規(guī)定主角距離星星多近時就可以完成收集,修改 properties,加入以下內(nèi)容并保存腳本。
// Star.js properties: { // 星星和主角之間的距離小于這個數(shù)值時,就會完成收集 pickRadius: 0, },
將這個腳本添加到剛創(chuàng)建的 star 節(jié)點上,在 層級管理器 中選中 star 節(jié)點,然后在 屬性檢查器 中點擊 添加組件 按鈕,選擇 添加用戶腳本組件 -> Star,該腳本便會添加到剛創(chuàng)建的 star 節(jié)點上。然后在 屬性檢查器中把 Pick Radius 屬性值設(shè)為 60:
Star Prefab 需要的設(shè)置就完成了,現(xiàn)在從 層級管理器 中將 star 節(jié)點拖拽到 資源管理器 中的 assets 文件夾下,就生成了名叫 star 的 Prefab 資源。
現(xiàn)在可以從場景中刪除 star 節(jié)點了,后續(xù)可以直接雙擊這個 star Prefab 資源進(jìn)行編輯。
接下去我們會在腳本中動態(tài)使用星星的 Prefab 資源生成星星。
添加游戲控制腳本
星星的生成是游戲主邏輯的一部分,所以我們要添加一個叫做 Game 的腳本作為游戲主邏輯腳本,這個腳本之后還會添加計分、游戲失敗和重新開始的相關(guān)邏輯。
添加 Game 腳本到 assets/scripts 文件夾下,雙擊打開腳本。首先添加生成星星需要的屬性:
// Game.js properties: { // 這個屬性引用了星星預(yù)制資源 starPrefab: { default: null, type: cc.Prefab }, // 星星產(chǎn)生后消失時間的隨機范圍 maxStarDuration: 0, minStarDuration: 0, // 地面節(jié)點,用于確定星星生成的高度 ground: { default: null, type: cc.Node }, // player 節(jié)點,用于獲取主角彈跳的高度,和控制主角行動開關(guān) player: { default: null, type: cc.Node } },
這里初學(xué)者可能會疑惑,為什么像 starPrefab 這樣的屬性會用 {} 括起來,括號里面還有新的 “屬性” 呢?其實這是屬性的一種完整聲明,之前我們的屬性聲明都是不完整的,有些情況下,我們需要為屬性聲明添加參數(shù),這些參數(shù)控制了屬性在 屬性檢查器 中的顯示方式,以及屬性在場景序列化過程中的行為。例如:
properties: { score: { default: 0, displayName: "Score (player)", tooltip: "The score of player", } }
以上代碼為 score 屬性設(shè)置了三個參數(shù) default、 displayName 和 tooltip。這幾個參數(shù)分別指定了 score的默認(rèn)值(default)為 0,在 屬性檢查器 里,其屬性名(displayName)將顯示為 Score (player),并且當(dāng)鼠標(biāo)移到參數(shù)上時,顯示對應(yīng)的 tooltip。
下面是常用參數(shù):
default:設(shè)置屬性的默認(rèn)值,這個默認(rèn)值僅在組件第一次添加到節(jié)點上時才會用到
type:限定屬性的數(shù)據(jù)類型,詳見 CCClass 進(jìn)階參考:type 參數(shù)
visible:設(shè)為 false 則不在屬性檢查器面板中顯示該屬性
serializable: 設(shè)為 false 則不序列化(保存)該屬性
displayName:在屬性檢查器面板中顯示成指定名字
tooltip:在屬性檢查器面板中添加屬性的 tooltip
所以上面的代碼:
starPrefab: { default: null, type: cc.Prefab },
就容易理解了,首先在 Game 組件下聲明了 starPrefab 屬性,這個屬性默認(rèn)值為 null,能傳入的類型必須是 Prefab 預(yù)制資源類型。這樣之后的 ground、player 屬性也可以理解了。
保存腳本后將 Game 組件添加到 層級管理器 中的 Canvas 節(jié)點上(選中 Canvas 節(jié)點后,拖拽腳本到 屬性檢查器 上,或者點擊 屬性檢查器 的 添加組件 按鈕,并從 添加用戶腳本組件 中選擇 Game。)
接下來從 資源管理器 中拖拽 star 的 Prefab 資源到 Game 組件的 Star Prefab 屬性中。這是我們第一次為屬性設(shè)置引用,只有在屬性聲明時規(guī)定 type 為引用類型時(比如我們這里寫的 cc.Prefab 類型),才能夠?qū)①Y源或節(jié)點拖拽到該屬性上。
接著從 層級管理器 中拖拽 ground 和 Player 節(jié)點到 Canvas 節(jié)點 Game 組件中相對應(yīng)名字的屬性上,完成節(jié)點引用。
然后設(shè)置 Min Star Duration 和 Max Star Duration 屬性的值為 3 和 5,之后我們生成星星時,會在這兩個之間隨機取值,就是星星消失前經(jīng)過的時間。
在隨機位置生成星星
接下來我們繼續(xù)修改 Game 腳本,在 onLoad 方法 后面 添加生成星星的邏輯:
// Game.js onLoad: function () { // 獲取地平面的 y 軸坐標(biāo) this.groundY=this.ground.y + this.ground.height/2; // 生成一個新的星星 this.spawnNewStar(); }, spawnNewStar: function() { // 使用給定的模板在場景中生成一個新節(jié)點 var newStar=cc.instantiate(this.starPrefab); // 將新增的節(jié)點添加到 Canvas 節(jié)點下面 this.node.addChild(newStar); // 為星星設(shè)置一個隨機位置 newStar.setPosition(this.getNewStarPosition()); }, getNewStarPosition: function () { var randX=0; // 根據(jù)地平面位置和主角跳躍高度,隨機得到一個星星的 y 坐標(biāo) var randY=this.groundY + Math.random() * this.player.getComponent('Player').jumpHeight + 50; // 根據(jù)屏幕寬度,隨機得到一個星星 x 坐標(biāo) var maxX=this.node.width/2; randX=(Math.random() - 0.5) * 2 * maxX; // 返回星星坐標(biāo) return cc.v2(randX, randY); },
這里需要注意幾個問題:
保存腳本以后點擊 預(yù)覽游戲 按鈕,在瀏覽器中可以看到,游戲開始后動態(tài)生成了一顆星星!用同樣的方法,您可以在游戲中動態(tài)生成任何預(yù)先設(shè)置好的以 Prefab 為模板的節(jié)點。
添加主角碰觸收集星星的行為
現(xiàn)在要添加主角收集星星的行為邏輯了,這里的重點在于,星星要隨時可以獲得主角節(jié)點的位置,才能判斷他們之間的距離是否小于可收集距離,如何獲得主角節(jié)點的引用呢?別忘了我們前面做過的兩件事:
所以我們只要在 Game 腳本生成 Star 節(jié)點實例時,將 Game 組件的實例傳入星星并保存起來就好了,之后我們可以隨時通過 game.player 來訪問到主角節(jié)點。讓我們打開 Game 腳本,在 spawnNewStar 方法最后面添加一句 newStar.getComponent('Star').game=this;,如下所示:
// Game.js spawnNewStar: function() { // ... // 在星星組件上暫存 Game 對象的引用 newStar.getComponent('Star').game=this; },
保存后打開 Star 腳本,現(xiàn)在我們可以利用 Game 組件中引用的 player 節(jié)點來判斷距離了,在 onLoad 方法后面添加名為 getPlayerDistance 和 onPicked 的方法:
// Star.js getPlayerDistance: function () { // 根據(jù) player 節(jié)點位置判斷距離 var playerPos=this.game.player.getPosition(); // 根據(jù)兩點位置計算兩點之間距離 var dist=this.node.position.sub(playerPos).mag(); return dist; }, onPicked: function() { // 當(dāng)星星被收集時,調(diào)用 Game 腳本中的接口,生成一個新的星星 this.game.spawnNewStar(); // 然后銷毀當(dāng)前星星節(jié)點 this.node.destroy(); },
Node 下的 getPosition() 方法 返回的是節(jié)點在父節(jié)點坐標(biāo)系中的位置(x, y),即一個 Vec2 類型對象。同時注意調(diào)用 Node 下的 destroy() 方法 就可以銷毀節(jié)點。
然后在 update 方法中添加每幀判斷距離,如果距離小于 pickRadius 屬性規(guī)定的收集距離,就執(zhí)行收集行為:
// Star.js update: function (dt) { // 每幀判斷和主角之間的距離是否小于收集距離 if (this.getPlayerDistance() < this.pickRadius) { // 調(diào)用收集行為 this.onPicked(); return; } },
保存腳本,再次預(yù)覽測試,通過按 A 和 D 鍵來控制主角左右移動,就可以看到控制主角靠近星星時,星星就會消失掉,然后在隨機位置生成了新的星星!
添加得分
小怪獸辛辛苦苦的收集星星,沒有獎勵怎么行?現(xiàn)在讓我們來添加在收集星星時增加得分獎勵的邏輯和顯示。
添加分?jǐn)?shù)文字(Label)
游戲開始時得分從 0 開始,每收集一個星星分?jǐn)?shù)就會加 1。要顯示得分,首先要創(chuàng)建一個 Label 節(jié)點。在 層級管理器 中選中 Canvas 節(jié)點,右鍵點擊并選擇菜單中的 創(chuàng)建新節(jié)點 -> 創(chuàng)建渲染節(jié)點 -> Label(文字),一個新的 Label 節(jié)點會被創(chuàng)建在 Canvas 節(jié)點下面,而且順序在最下面。接下來我們要用如下的步驟配置這個 Label 節(jié)點:
注意: Score: 0 的文字建議使用英文冒號,因為 Label 組件的 String 屬性加了位圖字體后,會無法識別中文的冒號。
完成后效果如下圖所示:
在 Game 腳本中添加得分邏輯
我們將會把計分和更新分?jǐn)?shù)顯示的邏輯放在 Game 腳本里,打開 Game 腳本開始編輯,首先在 properties 區(qū)塊的 最后 添加分?jǐn)?shù)顯示 Label 的引用屬性:
// Game.js properties: { // ... // score label 的引用 scoreDisplay: { default: null, type: cc.Label } },
接下來在 onLoad 方法 里面 添加計分用的變量的初始化:
// Game.js onLoad: function () { // ... // 初始化計分 this.score=0; },
然后在 update 方法 后面 添加名叫 gainScore 的新方法:
// Game.js gainScore: function () { this.score +=1; // 更新 scoreDisplay Label 的文字 this.scoreDisplay.string='Score: ' + this.score; },
保存 Game 腳本后,回到 層級管理器,選中 Canvas 節(jié)點,然后把前面添加好的 score 節(jié)點拖拽到 屬性檢查器 里 Game 組件的 Score Display 屬性中。
在 Star 腳本中調(diào)用 Game 中的得分邏輯
下面打開 Star 腳本,在 onPicked 方法中加入 gainScore 的調(diào)用:
// Star.js onPicked: function() { // 當(dāng)星星被收集時,調(diào)用 Game 腳本中的接口,生成一個新的星星 this.game.spawnNewStar(); // 調(diào)用 Game 腳本的得分方法 this.game.gainScore(); // 然后銷毀當(dāng)前星星節(jié)點 this.node.destroy(); },
保存后預(yù)覽,可以看到現(xiàn)在收集星星時屏幕正上方顯示的分?jǐn)?shù)會增加了!
失敗判定和重新開始
現(xiàn)在我們的游戲已經(jīng)初具規(guī)模,但得分再多,不可能失敗的游戲也不會給人成就感。現(xiàn)在讓我們加入星星定時消失的行為,而且讓星星消失時就判定為游戲失敗。也就是說,玩家需要在每顆星星消失之前完成收集,并不斷重復(fù)這個過程完成玩法的循環(huán)。
為星星加入計時消失的邏輯
打開 Game 腳本,在 onLoad 方法的 spawnNewStar 調(diào)用 之前 加入計時需要的變量聲明:
// Game.js onLoad: function () { // ... // 初始化計時器 this.timer=0; this.starDuration=0; // 生成一個新的星星 this.spawnNewStar(); // 初始化計分 this.score=0; },
然后在 spawnNewStar 方法最后加入重置計時器的邏輯,其中 this.minStarDuration 和 this.maxStarDuration 是我們一開始聲明的 Game 組件屬性,用來規(guī)定星星消失時間的隨機范圍:
// Game.js spawnNewStar: function() { // ... // 重置計時器,根據(jù)消失時間范圍隨機取一個值 this.starDuration=this.minStarDuration + Math.random() * (this.maxStarDuration - this.minStarDuration); this.timer=0; },
在 update 方法中加入計時器更新和判斷超過時限的邏輯:
// Game.js update: function (dt) { // 每幀更新計時器,超過限度還沒有生成新的星星 // 就會調(diào)用游戲失敗邏輯 if (this.timer > this.starDuration) { this.gameOver(); return; } this.timer +=dt; },
最后,在 gainScore 方法后面加入 gameOver 方法,游戲失敗時重新加載場景。
// Game.js gameOver: function () { this.player.stopAllActions(); //停止 player 節(jié)點的跳躍動作 cc.director.loadScene('game'); }
這里需要初學(xué)者了解的是,cc.director 是一個管理你的游戲邏輯流程的單例對象。由于 cc.director 是一個單例,你不需要調(diào)用任何構(gòu)造函數(shù)或創(chuàng)建函數(shù),使用它的標(biāo)準(zhǔn)方法是通過調(diào)用 cc.director.methodName(),例如這里的 cc.director.loadScene('game') 就是重新加載游戲場景 game,也就是游戲重新開始。而節(jié)點下的 stopAllActions 方法就顯而易見了,這個方法會讓節(jié)點上的所有 Action 都失效。
以上,對 Game 腳本的修改就完成了,保存腳本,然后打開 Star 腳本,我們需要為即將消失的星星加入簡單的視覺提示效果,在 update 方法最后加入以下代碼:
// Star.js update: function() { // ... // 根據(jù) Game 腳本中的計時器更新星星的透明度 var opacityRatio=1 - this.game.timer/this.game.starDuration; var minOpacity=50; this.node.opacity=minOpacity + Math.floor(opacityRatio * (255 - minOpacity)); }
保存 Star 腳本,我們的游戲玩法邏輯就全部完成了!現(xiàn)在點擊 預(yù)覽游戲 按鈕,我們在瀏覽器看到的就是一個有核心玩法、激勵機制、失敗機制的合格游戲了。
加入音效
盡管很多人玩手游的時候會無視聲音,我們?yōu)榱私坛陶故镜墓ぷ髁鞒瘫M量完整,還是要補全加入音效的任務(wù)。
跳躍音效
首先加入跳躍音效,打開 Player 腳本,添加引用聲音文件資源的 jumpAudio 屬性:
// Player.js properties: { // ... // 跳躍音效資源 jumpAudio: { default: null, type: cc.AudioClip }, },
然后改寫 setJumpAction 方法,插入播放音效的回調(diào),并通過添加 playJumpSound 方法來播放聲音:
// Player.js setJumpAction: function () { // 跳躍上升 var jumpUp=cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()); // 下落 var jumpDown=cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn()); // 添加一個回調(diào)函數(shù),用于在動作結(jié)束時調(diào)用我們定義的其他方法 var callback=cc.callFunc(this.playJumpSound, this); // 不斷重復(fù),而且每次完成落地動作后調(diào)用回調(diào)來播放聲音 return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback)); }, playJumpSound: function () { // 調(diào)用聲音引擎播放聲音 cc.audioEngine.playEffect(this.jumpAudio, false); },
這里需要強調(diào)的是回調(diào)函數(shù)的作用,我們首先來看官方對 callFunc() 方法的定義:
/** * !#en Creates the action with the callback. * !#zh 執(zhí)行回調(diào)函數(shù)。 * @method callFunc * @param {function} selector * @param {object} [selectorTarget=null] * @param {*} [data=null] - data for function, it accepts all data types. * @return {ActionInstant} * @example * // example * // CallFunc without data * var finish=cc.callFunc(this.removeSprite, this); * * // CallFunc with data * var finish=cc.callFunc(this.removeFromParentAndCleanup, this._grossini, true); */ cc.callFunc=function (selector, selectorTarget, data) { return new cc.CallFunc(selector, selectorTarget, data); };
我們可以看到 callFunc 方法可以傳入三個參數(shù),第一個參數(shù)是方法的 selector,我們可以理解為方法名。第二個參數(shù)是 Object 類型,一般填入 this。第三個參數(shù)為帶回的數(shù)據(jù),可以是所有的數(shù)據(jù)類型,可以不填。我們再注意到這個方法的返回值 —— ActionInstant,這是一個瞬間執(zhí)行的動作類。到這里我們就可以理解了,使用 callFunc 調(diào)用回調(diào)函數(shù)可以讓函數(shù)轉(zhuǎn)變?yōu)?cc 中的 Action(動作),這一用法在 cc 的動作系統(tǒng)里非常實用!例如在上面我們將播放聲音的函數(shù)傳入 callFunc 賦值給 callback,讓 callback 成為了一個播放聲音的動作 Action,那么我們之后就能通過 cc.sequence 將跳躍和播放聲音的動作組合起來,實現(xiàn)每跳一次就能播放音效的功能!
得分音效
保存 Player 腳本以后打開 Game 腳本,來添加得分音效,首先仍然是在 properties 中添加一個屬性來引用聲音文件資源:
// Game.js properties: { // ... // 得分音效資源 scoreAudio: { default: null, type: cc.AudioClip } },
然后在 gainScore 方法里插入播放聲音的代碼:
// Game.js gainScore: function () { this.score +=1; // 更新 scoreDisplay Label 的文字 this.scoreDisplay.string='Score: ' + this.score.toString(); // 播放得分音效 cc.audioEngine.playEffect(this.scoreAudio, false); },
保存腳本,回到 層級管理器 ,選中 Player 節(jié)點,然后從 資源管理器 里拖拽 assets/audio/jump 資源到 Player 組件的 Jump Audio 屬性上。
然后選中 Canvas 節(jié)點,把 assets/audio/score 資源拖拽到 Game 組件的 Score Audio 屬性上。
這樣就大功告成了!完成形態(tài)的場景層級和各個關(guān)鍵組件的屬性如下:
現(xiàn)在我們可以盡情享受剛制作完成的游戲了,您能打到多少分呢?別忘了您可以隨時修改 Player 和 Game 組件里的移動控制和星星持續(xù)時間等游戲參數(shù),來快速調(diào)節(jié)游戲的難度。修改組件屬性之后需要保存場景,修改后的數(shù)值才會被記錄下來。
總結(jié)
恭喜您完成了用 Cocos Creator 制作的第一個游戲!希望這篇快速入門教程能幫助您了解 Cocos Creator 游戲開發(fā)流程中的基本概念和工作流程。如果您對編寫和學(xué)習(xí)腳本編程不感興趣,也可以直接從完成版的項目中把寫好的腳本復(fù)制過來使用。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。