整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          Cocos 高級架構(gòu)師經(jīng)典面試題,你懂幾道?

          Cocos 高級架構(gòu)師經(jīng)典面試題,你懂幾道?

          試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)建腳本

          1. 首先在 資源管理器 中右鍵點擊 assets 文件夾,選擇 新建 -> 文件夾


          1. 右鍵點擊 New Folder,選擇 重命名 將其重命名為 scripts,之后我們所有的腳本都會存放在這里
          2. 右鍵點擊 scripts 文件夾,選擇 新建 -> JavaScript,創(chuàng)建一個 JavaScript 腳本
          3. 將新建腳本的名字改為 Player,雙擊這個腳本,打開代碼編輯器

          注意: 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);
           },
          

          這里需要注意幾個問題:

          1. 節(jié)點下的 y 屬性對應(yīng)的是錨點所在的 y 坐標(biāo),因為錨點默認(rèn)在節(jié)點的中心,所以需要加上地面高度的一半才是地面的 y 坐標(biāo)
          2. instantiate 方法的作用是:克隆指定的任意類型的對象,或者從 Prefab 實例化出新節(jié)點,返回值為 Node 或者 Object
          3. Node 下的 addChild 方法 作用是將新節(jié)點建立在該節(jié)點的下一級,所以新節(jié)點的顯示效果在該節(jié)點之上
          4. Node 下的 setPosition 方法 作用是設(shè)置節(jié)點在父節(jié)點坐標(biāo)系中的位置,可以通過兩種方式設(shè)置坐標(biāo)點。一是傳入兩個數(shù)值 x 和 y,二是傳入 cc.v2(x, y)(類型為 cc.Vec2 的對象)
          5. 通過 Node 下的 getComponent 方法可以得到該節(jié)點上掛載的組件引用

          保存腳本以后點擊 預(yù)覽游戲 按鈕,在瀏覽器中可以看到,游戲開始后動態(tài)生成了一顆星星!用同樣的方法,您可以在游戲中動態(tài)生成任何預(yù)先設(shè)置好的以 Prefab 為模板的節(jié)點。

          添加主角碰觸收集星星的行為

          現(xiàn)在要添加主角收集星星的行為邏輯了,這里的重點在于,星星要隨時可以獲得主角節(jié)點的位置,才能判斷他們之間的距離是否小于可收集距離,如何獲得主角節(jié)點的引用呢?別忘了我們前面做過的兩件事:

          1. Game 組件中有個名叫 player 的屬性,保存了主角節(jié)點的引用。
          2. 每個星星都是在 Game 腳本中動態(tài)生成的。

          所以我們只要在 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é)點:

          1. 將該節(jié)點名字改為 score
          2. 將 score 節(jié)點的位置(position 屬性)設(shè)為 (0, 180)。
          3. 選中該節(jié)點,編輯 屬性檢查器 中 Label 組件的 string 屬性,填入 Score: 0 的文字。
          4. 將 Label 組件的 Font Size 屬性設(shè)為 50。
          5. 資源管理器 中拖拽 assets/mikado_outline_shadow 位圖字體資源(注意圖標(biāo)是

          1. )到 Label 組件的 Font 屬性中,將文字的字體替換成我們項目資源中的位圖字體。

          注意: 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ù)制過來使用。


          主站蜘蛛池模板: 3d动漫精品啪啪一区二区中| 中文字幕人妻无码一区二区三区| 88国产精品视频一区二区三区| 国产在线精品一区二区中文| 波多野结衣久久一区二区| 台湾无码一区二区| 国产在线精品观看一区| 亚洲国产综合无码一区| 欧洲精品一区二区三区| 无码国产精品一区二区免费16| 亚洲欧美国产国产综合一区| 一区二区三区视频观看| 亚洲熟妇AV一区二区三区浪潮 | 中文字幕一区二区免费| 精产国品一区二区三产区| 国精产品一区二区三区糖心 | 一区二区三区在线免费| 精品免费AV一区二区三区| 自拍日韩亚洲一区在线| 人妻无码一区二区三区| 精品国产亚洲一区二区三区| 蜜臀Av午夜一区二区三区| 国产在线精品一区二区在线观看 | 精品人妻AV一区二区三区| 一区五十路在线中出| 亚洲熟妇av一区二区三区漫画| 一级毛片完整版免费播放一区| 亚洲日韩精品一区二区三区无码 | 国产一区三区三区| 老鸭窝毛片一区二区三区| 国产一区风间由美在线观看| 天堂va视频一区二区| 精品无人区一区二区三区在线| 亚洲无删减国产精品一区| 亚洲AV综合色区无码一区爱AV| 国偷自产av一区二区三区| 日本丰满少妇一区二区三区| 无码国产伦一区二区三区视频 | 成人精品视频一区二区| 国产色综合一区二区三区| 久久国产一区二区三区|