Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 91精品国产91,好男人好资源电影在线播放,国产一区中文字幕在线观看

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Cocos 高級架構師經典面試題,你懂幾道?

          Cocos 高級架構師經典面試題,你懂幾道?

          試Cocos Creator (20K~40K薪資)經常會被問的20個問題, 看看你懂幾個。

          1: 如何優化Cocos Creator 包體體積。

          2: Cocos creator如何做資源管理。

          3: Cocos Creator如何編寫單例模式。

          4: Cococs creator 如何使用物理引擎?

          5: Cocos Creator Label的原理與如何節約Drawcall?

          6: Cocos Creator 背包系統可能會需要哪些優化?

          7: Cocos Creator WebSocket與Socket.IO分別是什么?

          8: Cocos Creator如何內置protobuf JS版本?

          9: Cocos Creator 材質, shader 分別是什么?

          10:Cocos creator 固定寬度與固定高度的底層原理是什么?Cocos creator是如何做適配的?

          11: Cocos Creator 圖集打包有什么意義,我們一般在項目里面怎么規劃圖集?

          12: Cocos Creator 如何做游戲框架,能讓多人很好的協作,代碼好維護?

          13: Cocos Creator 2D如何做Drawcall優化?

          14: Cococs creator 骨骼動畫與幀動畫的優缺點是什么?

          15: 如何使用Cococs Creator制作一個地圖編輯與尋路導航系統?

          16: Cocos Creator 節點池的基本原理是什么?如何使用?

          18: Cocos Creator 如何設計熱更新系統, 如何設計大廳與子游戲模式?

          19: Cocos Android里 runOnGLThread是什么意思?

          20: Cocos Creator 如何設計自動化打包發布腳本?

          Cocos Creator架構師,主要考的是扎實的基礎,分析問題,解決問題的能力和經驗,系統框架設計, 與項目難題攻關, 如果大家對哪個問題的答案感興趣,可以評論區留言,Blake老師會根據大家的需求,來寫文章回復大家問題。 祝大家早日成長為架構師。

          源:新京報

          從公元前1萬年出現在人類食譜中起,酸奶在這千年間征服著世界各地食客的味蕾。值得一提的是,除了作為一種零食甜點,生活在不同地區的人們食用酸奶的方式并不相同。比如,77%的土耳其人習慣將酸奶與熱餐搭配,55%的巴西人則喜歡用它搭配谷物,對法國人而言,酸奶甚至稱得上是一種信仰。人們使用酸奶的方式,折射著不同地區的文化與習俗。

          我們享用酸奶的方式,其實與我們的祖先享用酸奶的方式有著內在的聯系。可以肯定的是,大多數有影響力的酸奶制作者,他們和那些傳承了家族傳統及食譜的人有著親緣關系。在為英國廣播公司旅行博客撰寫的一篇文章中,作者瑪達薇 ·拉馬尼(Mad hvi Ra mani)引用了保加利亞人艾莉莎·斯托伊洛伐的一段話:

          如果兩位來自不同村莊的祖母用相同的原料制作酸奶,其味道也會有所不同。這是因為酸奶是一種個性化產品。它與地域、動物、家庭的特殊口味以及代代相傳的制作知識有關。

          時至今日,在酸奶的起源地區,它仍然是當地人日常飲食中不可或缺的一部分。然而,隨著人們四處遷徙,酸奶文化也隨之流傳開來,這樣說毫不夸張,因為對一些人來說,他們現在遵循的家族酸奶文化就是從其他地方傳過來的。和酸奶相關的傳統與習俗、口味與香氣、制作與靈感在全球范圍內得到共享,這展示了世界是如何無縫擁抱這種食品的。

          保加利亞酸奶:

          走向全球的酸奶菌種

          為了向保加利亞乳桿菌的發源地致敬,讓我們從保加利亞開始我們的全球酸奶之旅。在保加利亞酸奶的制作過程中,保加利亞乳桿菌和嗜熱鏈球菌這兩種著名的菌株協同作用,創造了酸奶制作的黃金標準,保加利亞人稱這種酸奶為“kiselo mlyako”。這種酸奶具有獨特的酸味、濃郁的口感和特殊的香氣,酸奶愛好者很容易就能辨認出這是保加利亞酸奶。

          《舔蓋兒:酸奶小史》,[美]瓊·赫什 著,吳嶺 譯,萬川|中國工人出版社 ,2024年4月。

          在20世紀早期和中期,來自保加利亞的菌株以凍干物或藥丸的形式被兜售和運輸。1937年,倫敦《觀察家報》(The Observer)上刊載了一篇文章,文章報道了薩爾茨堡一家小乳品店倒閉的消息,可以說,這是保加利亞酸奶備受消費者推崇的最好證明。據說,包括意大利大師級指揮家阿爾圖羅·托斯卡尼尼在內,人們涌向這家維也納商店,享受詩歌和享用正宗的保加利亞酸奶。下面這些詩句節選自這家店主的詩歌:

          為何保加利亞人如此長壽?

          為何他們從不感冒?

          因為,他們——

          喜歡在春冬時節享用酸奶。

          保加利亞酸奶屬于保加利亞的國家專利,保加利亞人將其同名菌株——保加利亞乳桿菌授權給其他國家使用,如果這些國家想將自己生產的酸奶稱為“保加利亞酸奶”,它們就必須從保加利亞購買這種酸奶菌種。

          最好的例證來自保加利亞羅德比山脈(Rhodope Mountains)中的一個小村莊和中國。2009 年,中國的光明乳業股份有限公司推出了一款名為“莫斯利安”的常溫酸奶飲品,這款酸奶所用的菌株源自保加利亞境內的一個同名村莊——莫斯利安村(Momchilovtsi)。這款酸奶的產地在上海。如果你去莫斯利安這個小村莊參觀,你會在這里看到許多中文標識,還有不少自學普通話的村民。每年,這里都會舉辦一個盛大的節日——莫斯利安酸奶文化節, 而且還會選出一位“酸奶皇后”。這個被稱為“長壽村”的小鎮有1200多名居民,每年都會招待1000多名中國游客。

          新疆奶酪和老北京酸奶:

          “方便”概念下的情懷

          新疆維吾爾自治區是中國西北部的一個自治區,這里生活著大量的維吾爾族人,酸奶在新疆的出現,表明這里是全球不同飲食傳統的另一個交會點,它也影響著中國文化。維吾爾族人已經在這里生活了1000多年。與漢族相比,他們的烹飪更接近中東風味,“奶酪”酸奶在這里非常受歡迎。

          正如美食作家范(Van)在她的個人美食網站中所寫的那樣:

          奶酪可以說是早期的酸奶。最早在19世紀時,宮廷廚師們掌握了這道甜點,后來,奶酪的配方發生演變,它的味道變得更柔和、更甜。在20世紀50年代,它開始在北京流行起來,成為注重健康的潮流人士的最愛。

          裝在瓷瓶中的老北京特色酸奶,采用傳統的藍白色薄 紙蓋包裝,還配有吸管。(出版社供圖)

          漸漸地,這一食譜在全中國傳播開來,如今,在每個集市和繁華街道的小販那里,都有老北京酸奶銷售。邊喝老北京酸奶邊在市場上漫步,可以說是一種享受。老北京酸奶裝在瓷瓶中,封口是系著繩子的藍白色薄紙蓋,吃酸奶時,用細吸管或一次性勺子插進去,然后就可以好好享用了,吃完后,記得再將酸奶瓶歸還給小販。

          如今,中國消費者更多地生活在城市,他們有了更多的可支配收入,也在尋找便攜式的營養來源。“方便食品或便攜食品”(food on-the-go)這一飲食概念在亞洲市場非常具有吸引力,因此,在中國和韓國,你會發現在湍急的人流中,有女性騎著自行車兜售酸奶。

          韓國的“酸奶女士”穿著標志性的杏色夾克,戴著粉色頭盔。她們駕駛著名為“CoCos”的電動冰箱,是“Cold & Cool”的縮寫。這些帶輪子的冰箱可以容納數量驚人的3300瓶酸奶。在亞洲烹飪文化中,酸奶仍未被視為一種烹飪輔料或主流食品,而是被人們當作一種快速補充營養的方式。中國和東南亞市場有望成為全球最大的酸奶消費市場,其中,飲用型酸奶有助于推動市場發展。

          日本酸奶:益生菌的由來

          發現并重新認識酸奶的并不只有中國,其他亞洲市場也是如此。日本對酸奶的熱愛始于2 0世紀30年代,當時,一位出生于京都的科學家——代田稔博士(Dr Minora Shirota),對乳酸桿菌與疾病之間的關系進行了探索。經過一番詳盡的研究之后,他分離出了干酪乳桿菌代田株。這是一種由300多種乳酸桿菌組成的益生菌,代田稔博士用它來發酵牛奶,并將得到的酸奶產品命名為養樂多。他發明的“養樂多”酸奶和他所說的“只有腸道健康才能延年益壽”的觀點受到了日本民眾的歡迎和支持。

          貨架上的“養樂多”。

          時至今日,養樂多在日本仍然廣受歡迎,全球每天有3000多萬人享用這種酸奶,因為據說它可以提高人體免疫力,促進腸道消化。

          1971年,日本乳制品企業巨頭明治乳業(Meiji)推出了日本第一款原味酸奶,從而宣示其全面進入酸奶市場。同中國與保加利亞的聯系一樣,明治乳業意識到與保加利亞開展合作能促進酸奶的銷售,于是,它在1973年獲得了保加利亞的授權,推出了明治保加利亞式酸奶。明治乳業繼續堅持創新,1996 年,它又獲得了日本的“特定保健用食品”(Food for Specified Health Use)標簽的使用權,這進一步提高了酸奶的銷量。

          最近,日本又推出了一系列新口味酸奶,其中包括抹茶或柿子等深受消費者歡迎的傳統配料。為了進一步提升消費體驗,產品采用了類似古代漆器的杯子進行包裝。預計日本的酸奶市場將會繼續增長,但與亞洲其他地區相比,其增長速度有所放緩。

          印度次大陸酸奶:

          特色菜肴的基礎

          在印度次大陸(包括印度、南亞和中亞的部分地區、巴基斯坦、孟加拉國和喜馬拉雅山區),酸奶自古以來就是當地美食不可或缺的一部分。作為一種影響廣泛的素食文化,生活在這些地區的居民將酸奶作為補充人體所需蛋白質、鈣元素和脂肪的來源。此外,酸奶還是一種清涼食品,可以降低印度菜中常用香料所產生的熱量。

          在印度,酸奶被稱為達希(dahi,印度用語,意為“凝乳”),與傳統酸奶將菌種引入經過巴氏殺菌的牛奶中不同,達希是將乳酸桿菌接種在煮沸的牛奶當中。制作達希是為了促進而不是抑制凝乳的發展,這一點不同于西方的許多酸奶。在印度,制作達希是一項日常活動,將前一天做好的凝乳加入新的牛奶中,就能制作出美味濃稠的酸奶。

          凝乳是許多印度特色菜肴的基礎。它有助于將米飯和小扁豆湯(dal)融合在一起,這樣更容易用右手抓起來食用——印度風格的吃法。“aloo palda”是加有凝乳的土豆咖喱飯,這是一道經典的帕哈里菜肴,它依靠酸奶來達到黏合食材所需要的稠度。“mor rasam”是印度南部的一種酸甜口燉菜,在這道菜肴中,用酸凝乳來制作類似酪乳的那種特色味道。“dahi papdi chaat ”是印式酸奶酥脆沙拉,它是將酸奶與各種酸辣醬(如薄荷醬、香菜醬、酸豆醬等)混合之后,作為這道廣受歡迎的小吃的配料。

          酸奶還可用于制作松軟的南印度煎餅(dosas)以及克什米爾的招牌菜印度香飯——這是一種用慢火燉煮的食物,傳統做法是將食材放在密封的厚底鍋上進行烹制。除了上面這些美食,甚至還有一種印度版的烤奶酪,將酸奶、洋蔥、香料和香草混合在一起,制成達希吐司(dahi toast), 在印度,這是一種很受歡迎的早餐食品。

          香草味濃郁、美味可口的酸奶色拉是一種絕佳的蘸醬和調味品。(出版社供圖)

          在所有使用酸奶的印度菜肴中,最著名的可能是酸奶色拉(raita),它是酸奶和各種配料(如蔬菜、 水果、香草、香料等)的清涼混合物。它既可以當調味品,也可以當配菜,制作起來非常簡單。拉西(lassi)酸奶奶昔是印度的國民飲料,這是一種以酸奶為基底的冰沙,其起源可以追溯到公元前1000年左右的旁遮普地區。這種飲料分咸甜兩種版本:加胡椒粉或紅辣椒粉即為咸味版,加芒果汁或玫瑰汁即為甜味版。

          說到甜點,千萬不要忽視馬哈拉施特拉邦的經典甜點“shrikhand”,這道甜點簡單絕妙,只需要三種配料就能制作:過濾后的酸奶、糖粉和香脆的堅果。這是一種充滿風味的食物,食用時可以加入藏紅花絲,或者撒上豆蔻、開心果等來提味。

          在印度尼西亞,人們喜歡食用“dad iah”這種食物,它可以說是印尼版的酸奶,是將未加熱的水牛奶放在竹筍中發酵而成的。在尼泊爾,人們將食用酸奶作為文化和宗教慶祝活動的一部分。尼泊爾人相信,被稱為“juju dhai ”的酸奶可以帶來好運。因此,在舉行慶典時,人們經常會在入口處放置裝滿酸奶的陶罐以迎接慶祝者。現如今,不在這些國家生活的人也可以品嘗到這些特色的酸奶菜肴,因為這些酸奶菜肴的烹飪方式已經與其他地區的烹飪方式相融合,并在中東、東南亞、歐洲、北美、非洲和加勒比地區廣為流傳。

          土耳其酸奶:

          作為經典配料的咸酸奶

          時至今日,酸奶仍然是土耳其美食中不可或缺的一部分,這里是酸奶的發源地。酸奶是土耳其人最喜愛的配料、調味品和配菜,也是土耳其最著名的飲料——咸酸奶(ayran)的基礎食材。

          據說,咸酸奶是由突厥游牧民族發現的,其本質是一種含乳酸但不含酒精、加水稀釋過的酸奶。在炎熱的夏季,游牧民族要忍受酷熱的沙漠,對他們來說,食用這種咸酸奶非常有用。它有提神補體的作用,因為按照傳統咸酸奶的做法,會在酸奶中加入(能夠補充能量的)鹽。

          傳統上來說,咸酸奶是裝在銅制馬克杯中飲用的,它可以搭配土耳其美食飲用,有提神補體的效用。(出版社供圖)

          至今,土耳其人和許多生活在這一地區的其他人,他們仍然很喜歡喝咸酸奶,以至于當你走進當地的麥當勞餐廳時,你很有可能會在菜單上看到它。

          土耳其人還喜歡一道與印度的酸奶色拉非常相似的菜肴,但它有著自己的地域特色。酸奶經稀釋后,依次加入鹽、大蒜末、黃瓜、薄荷、土茴香,通常,還會再往里加入漆樹粉(sumac)、酸橙汁和橄欖油的混合物,最后得到的就是酸味小黃瓜咸奶酪湯(cacik),這是一種類似蘸醬的清新涼爽的調味品。它是土耳其許多特色菜肴(如“kebabs”和“koftas”,可以將其理解為土耳其版的烤肉串和烤肉丸)的絕佳調味品。

          希臘酸奶:

          酸奶黃瓜醬開胃加倍

          在酸奶界,希臘酸奶可謂名聲大噪,眾所周知,希臘酸奶經常被人模仿。要想品嘗到真正的希臘酸奶,你需要在它的發源地品嘗一下“straggisto”,這是一種正宗的脫乳清酸奶。在許多希臘菜譜中,都有這種美味食品的身影,其中,最著名的當數希臘酸奶黃瓜醬(tzatz iki)。

          在希臘地區,希臘酸奶黃瓜醬既可以單獨作為一道開胃菜,也可以用作做飯時的醬汁。(出版社供圖)

          它的做法和酸奶色拉很像。制作方法很簡單,先將瀝干水分的黃瓜磨碎,再加入酸奶、 薄荷香料、橄欖油、鹽和檸檬汁,攪拌均勻。待這些食材的味道充分融合后,即可食用。在許多豐盛的希臘特色美食中,希臘酸奶黃瓜醬都是絕佳的輔料。

          雖然希臘酸奶沒有被希臘注冊為商標,但歐盟會經常制裁那些將酸奶名稱標注為“希臘”的國家,稱其是故意誤導消費者。

          據報道,在1948年,希臘總理泰米斯托克利·索福利斯臨終前想吃最后一頓飯,風卷殘云間,他便喝下了兩杯啤酒、一碗湯,當然,還有他心愛的酸奶,這足以進一步證明酸奶在希臘的重要地位。

          (本文內容系獨家內容,經出版方授權整理自《舔蓋兒:酸奶小史》。)

          原著作者/[美]瓊·赫什

          整理/申璐

          編輯/走走

          校對/柳寶慶

          ocos Creator 開發游戲的一個核心理念就是讓內容生產和功能開發可以流暢的并行協作,我們在上個部分著重于處理美術內容,而接下來就是通過編寫腳本來開發功能的流程,之后我們還會看到寫好的程序腳本可以很容易的被內容生產者使用。

          如果您從沒寫過程序也不用擔心,我們會在教程中提供所有需要的代碼,只要復制粘貼到正確的位置就可以了,之后這部分工作可以找您的程序員小伙伴來解決。下面讓我們開始創建驅動主角行動的腳本吧。

          創建腳本

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


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

          注意: Cocos Creator 中腳本名稱就是組件的名稱,這個命名是大小寫敏感的!如果組件名稱的大小寫不正確,將無法正確通過名稱使用組件!

          編寫組件屬性

          在打開的 Player 腳本里已經有了預先設置好的一些代碼塊,如下所示:

          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 引擎的主要命名空間,引擎代碼中所有的類、函數、屬性和常量都在這個命名空間中定義。而 Class() 就是 cc 模塊下的一個方法,這個方法用于聲明 Cocos Creator 中的類。為了方便區分,我們把使用 cc.Class 聲明的類叫做 CCClass。Class() 方法的參數是一個原型對象,在原型對象中以鍵值對的形式設定所需的類型參數,就能創建出所需要的類。

          例如:

           var Sprite=cc.Class({
           name: "sprite"
           });
          

          以上代碼用 cc.Class() 方法創建了一個類型,并且賦給了 Sprite 變量。同時還將類名設為 sprite。類名用于序列化,一般可以省略。

          對于 cc.Class 的詳細學習可以參考 使用 cc.Class 聲明類型。

          現在我們回到腳本編輯器看回之前的代碼,這些代碼就是編寫一個組件(腳本)所需的結構。具有這樣結構的腳本就是 Cocos Creator 中的 組件(Component),他們能夠掛載到場景中的節點上,提供控制節點的各種功能。我們先來設置一些屬性,然后看看怎樣在場景中調整他們。

          找到 Player 腳本里的 properties 部分,將其改為以下內容并保存:

          // Player.js
           //...
           properties: {
           // 主角跳躍高度
           jumpHeight: 0,
           // 主角跳躍持續時間
           jumpDuration: 0,
           // 最大移動速度
           maxMoveSpeed: 0,
           // 加速度
           accel: 0,
           },
           //...
          

          Cocos Creator 規定一個節點具有的屬性都需要寫在 properties 代碼塊中,這些屬性將規定主角的移動方式,在代碼中我們不需要關心這些數值是多少,因為我們之后會直接在 屬性檢查器 中設置這些數值。以后在游戲制作過程中,我們可以將需要隨時調整的屬性都放在 properties 中。

          現在我們可以把 Player 組件添加到主角節點上。在 層級管理器 中選中 Player 節點,然后在 屬性檢查器 中點擊 添加組件 按鈕,選擇 添加用戶腳本組件 -> Player,為主角節點添加 Player 組件。

          現在我們可以在 屬性檢查器 中(需要選中 Player 節點)看到剛添加的 Player 組件了,按照下圖將主角跳躍和移動的相關屬性設置好:

          這些數值除了 jumpDuration 的單位是秒之外,其他的數值都是以像素為單位的,根據我們現在對 Player組件的設置:我們的主角將能夠跳躍 200 像素的高度,起跳到最高點所需的時間是 0.3 秒,最大水平方向移動速度是 400 像素每秒,水平加速度是 350 像素每秒。

          這些數值都是建議,一會等游戲運行起來后,您完全可以按照自己的喜好隨時在 屬性檢查器 中修改這些數值,不需要改動任何代碼。

          編寫跳躍和移動代碼

          下面我們添加一個方法,來讓主角跳躍起來,在 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());
           // 不斷重復
           return cc.repeatForever(cc.sequence(jumpUp, jumpDown));
           },
          

          這里就需要了解一下 Cocos Creator 的 動作(Action)系統 了。由于動作系統比較復雜,這里就簡單的介紹一下。

          在 Cocos Creator 中,動作 簡單來說就是 節點的位移、縮放和旋轉

          例如在上面的代碼中,moveBy() 方法的作用是在規定的時間內移動指定的一段距離,第一個參數就是我們之前定義主角屬性中的跳躍時間,第二個參數是一個 Vec2(表示 2D 向量和坐標)類型的對象,為了更好的理解,我們可以看看官方給的函數說明:

          /**
           * !#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 一共可以傳入三個參數,前兩個參數我們已經知道,第三個參數是 Number 類型的 Y 坐標,我們可以發現第二個參數是可以傳入兩種類型的,第一種是 Number 類型,第二種才是 Vec2類型,如果我們在這里傳入的是 Number 類型,那么默認這個參數就是 X 坐標,此時就要填第三個參數,為 Y 坐標。上面的例子中 cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)) 第二個參數傳入的是使用 cc.v2 方法構建的 Vec2 類型對象,這個類型表示的是一個坐標,即有 X 坐標也有 Y 坐標,因為不需要再傳入第三個參數!同時注意官方的一段話 x and y are relative to the position of the object.,這句話的意思是傳入的 X、Y 坐標都是相對于節點當前的坐標位置,而不是整個坐標系的絕對坐標。

          了解了參數的含義之后,我們再來關注 moveBy() 方法的返回值,看官方說明可以知道,這個方法返回的是一個 ActionInterval 類型的對象,ActionInterval 在 Cocos 中是一個表示時間間隔動作的類,這種動作在一定時間內完成。到這里我們就可以理解代碼 cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()) 前一部分 的意思了,它的意思就是構造一個 ActionInterval 類型的對象,這個對象表示在 jumpDuration 的時間內,移動到相對于當前節點的 (0,this.jumpHeight) 的坐標位置,簡單來說,就是一個向上跳躍的動作。

          那么 后半部分 easing(cc.easeCubicActionOut()) 的作用是什么呢?easing 是 ActionInterval 類下的一個方法,這個方法可以讓時間間隔動作呈現為一種緩動運動,傳入的參數是一個緩動對象,返回一個 ActionInterval 類型對象,這里傳入的是使用 easeCubicActionInOut 方法構建的緩動對象,EaseCubicInOut 是按三次函數緩動進入并退出的動作,具體曲線可參考下圖:

          詳細內容可參考 API。

          接下來在 onLoad 方法里調用剛添加的 setJumpAction 方法,然后執行 runAction 來開始動作:

          // Player.js
           onLoad: function () {
           // 初始化跳躍動作
           this.jumpAction=this.setJumpAction();
           this.node.runAction(this.jumpAction);
           },
          

          onLoad 方法會在場景加載后立刻執行,所以我們會把初始化相關的操作和邏輯都放在這里面。我們首先將循環跳躍的動作傳給了 jumpAction 變量,之后調用這個組件掛載的節點下的 runAction 方法,傳入循環跳躍的 Action 從而讓節點(主角)一直跳躍。保存腳本,然后我們就可以開始第一次運行游戲了!

          點擊 Cocos Creator 編輯器上方正中的 預覽游戲 按鈕

          ,Cocos Creator 會自動打開您的默認瀏覽器并開始在里面運行游戲,現在應該可以看到我們的主角——紫色小怪獸在場景中間活潑的蹦個不停了。

          移動控制

          只能在原地傻蹦的主角可沒前途,讓我們為主角添加鍵盤輸入,用 A 和 D 來控制他的跳躍方向。在 setJumpAction 方法的下面添加鍵盤事件響應函數:

          // 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 方法,在其中加入向左和向右加速的開關,以及主角當前在水平方向的速度。最后再調用 cc.systemEvent,在場景加載后就開始監聽鍵盤輸入:

          // Player.js
           onLoad: function () {
           // 初始化跳躍動作
           this.jumpAction=this.setJumpAction();
           this.node.runAction(this.jumpAction);
           // 加速度方向開關
           this.accLeft=false;
           this.accRight=false;
           // 主角當前水平方向速度
           this.xSpeed=0;
           // 初始化鍵盤輸入監聽
           cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
           cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); 
           },
           onDestroy () {
           // 取消鍵盤輸入監聽
           cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
           cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
           },
          

          有 Android 開發經驗的同學比較好理解,這里的監聽器實質上就和 Android 里的 OnClickListener 差不多,在 cocos 中通過 systemEvent 來監聽系統 全局 事件。(鼠標、觸摸和自定義事件的監聽和派發的詳細內容請參考 監聽和發射事件。)這里通過向 systemEvent 注冊了一個鍵盤響應函數,在函數中通過 switch 判斷鍵盤上的 A 和 D 是否被按下或松開,若按下就執行對應的操作。

          最后修改 update 方法的內容,添加加速度、速度和主角當前位置的設置:

          // Player.js
           update: function (dt) {
           // 根據當前加速度方向每幀更新速度
           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);
           }
           // 根據當前速度更新主角的位置
           this.node.x +=this.xSpeed * dt;
           },
          

          update 在場景加載后就會每幀調用一次,我們一般把需要經常計算或及時更新的邏輯內容放在這里。在我們的游戲中,根據鍵盤輸入獲得加速度方向后,就需要每幀在 update 中計算主角的速度和位置。

          保存腳本后,點擊 預覽游戲 來看看我們最新的成果。在瀏覽器打開預覽后,用鼠標點擊一下游戲畫面(這是瀏覽器的限制,要點擊游戲畫面才能接受鍵盤輸入),然后就可以按 A 和 D 鍵來控制主角左右移動了!

          感覺移動起來有點遲緩?主角跳的不夠高?希望跳躍時間長一些?沒問題,這些都可以隨時調整。只要為 Player 組件設置不同的屬性值,就可以按照您的想法調整游戲。這里有一組設置可供參考:

          Jump Height: 150
          Jump Duration: 0.3
          Max Move Speed: 400
          Accel: 1000
          

          這組屬性設置會讓主角變得靈活無比,至于如何選擇,就看您想做一個什么風格的游戲了。

          制作星星

          主角現在可以跳來跳去了,我們要給玩家一個目標,也就是會不斷出現在場景中的星星,玩家需要引導小怪獸碰觸星星來收集分數。被主角碰到的星星會消失,然后馬上在隨機位置重新生成一個。

          制作 Prefab

          對于需要重復生成的節點,我們可以將他保存成 Prefab(預制) 資源,作為我們動態生成節點時使用的模板。關于 Prefab 的更多信息,請閱讀 預制資源(Prefab)。

          首先從 資源管理器 中拖拽 assets/textures/star 圖片到場景中,位置隨意,我們只是需要借助場景作為我們制作 Prefab 的工作臺,制作完成后會我們把這個節點從場景中刪除。

          我們不需要修改星星的位置或渲染屬性,但要讓星星能夠被主角碰觸后消失,我們需要為星星也添加一個專門的組件。按照和添加 Player 腳本相同的方法,添加名叫 Star 的 JavaScript 腳本到 assets/scripts/中。

          接下來雙擊這個腳本開始編輯,星星組件只需要一個屬性用來規定主角距離星星多近時就可以完成收集,修改 properties,加入以下內容并保存腳本。

          // Star.js
           properties: {
           // 星星和主角之間的距離小于這個數值時,就會完成收集
           pickRadius: 0,
           },
          

          將這個腳本添加到剛創建的 star 節點上,在 層級管理器 中選中 star 節點,然后在 屬性檢查器 中點擊 添加組件 按鈕,選擇 添加用戶腳本組件 -> Star,該腳本便會添加到剛創建的 star 節點上。然后在 屬性檢查器中把 Pick Radius 屬性值設為 60:

          Star Prefab 需要的設置就完成了,現在從 層級管理器 中將 star 節點拖拽到 資源管理器 中的 assets 文件夾下,就生成了名叫 star 的 Prefab 資源。

          現在可以從場景中刪除 star 節點了,后續可以直接雙擊這個 star Prefab 資源進行編輯。

          接下去我們會在腳本中動態使用星星的 Prefab 資源生成星星。

          添加游戲控制腳本

          星星的生成是游戲主邏輯的一部分,所以我們要添加一個叫做 Game 的腳本作為游戲主邏輯腳本,這個腳本之后還會添加計分、游戲失敗和重新開始的相關邏輯。

          添加 Game 腳本到 assets/scripts 文件夾下,雙擊打開腳本。首先添加生成星星需要的屬性:

          // Game.js
           properties: {
           // 這個屬性引用了星星預制資源
           starPrefab: {
           default: null,
           type: cc.Prefab
           },
           // 星星產生后消失時間的隨機范圍
           maxStarDuration: 0,
           minStarDuration: 0,
           // 地面節點,用于確定星星生成的高度
           ground: {
           default: null,
           type: cc.Node
           },
           // player 節點,用于獲取主角彈跳的高度,和控制主角行動開關
           player: {
           default: null,
           type: cc.Node
           }
           },
          

          這里初學者可能會疑惑,為什么像 starPrefab 這樣的屬性會用 {} 括起來,括號里面還有新的 “屬性” 呢?其實這是屬性的一種完整聲明,之前我們的屬性聲明都是不完整的,有些情況下,我們需要為屬性聲明添加參數,這些參數控制了屬性在 屬性檢查器 中的顯示方式,以及屬性在場景序列化過程中的行為。例如:

          properties: {
           score: {
           default: 0,
           displayName: "Score (player)",
           tooltip: "The score of player",
           }
          }
          

          以上代碼為 score 屬性設置了三個參數 default、 displayName 和 tooltip。這幾個參數分別指定了 score的默認值(default)為 0,在 屬性檢查器 里,其屬性名(displayName)將顯示為 Score (player),并且當鼠標移到參數上時,顯示對應的 tooltip。

          下面是常用參數:

          default:設置屬性的默認值,這個默認值僅在組件第一次添加到節點上時才會用到

          type:限定屬性的數據類型,詳見 CCClass 進階參考:type 參數

          visible:設為 false 則不在屬性檢查器面板中顯示該屬性

          serializable: 設為 false 則不序列化(保存)該屬性

          displayName:在屬性檢查器面板中顯示成指定名字

          tooltip:在屬性檢查器面板中添加屬性的 tooltip

          所以上面的代碼:

          starPrefab: {
           default: null,
           type: cc.Prefab
          },
          

          就容易理解了,首先在 Game 組件下聲明了 starPrefab 屬性,這個屬性默認值為 null,能傳入的類型必須是 Prefab 預制資源類型。這樣之后的 ground、player 屬性也可以理解了。

          保存腳本后將 Game 組件添加到 層級管理器 中的 Canvas 節點上(選中 Canvas 節點后,拖拽腳本到 屬性檢查器 上,或者點擊 屬性檢查器添加組件 按鈕,并從 添加用戶腳本組件 中選擇 Game。)

          接下來從 資源管理器 中拖拽 star 的 Prefab 資源到 Game 組件的 Star Prefab 屬性中。這是我們第一次為屬性設置引用,只有在屬性聲明時規定 type 為引用類型時(比如我們這里寫的 cc.Prefab 類型),才能夠將資源或節點拖拽到該屬性上。

          接著從 層級管理器 中拖拽 ground 和 Player 節點到 Canvas 節點 Game 組件中相對應名字的屬性上,完成節點引用。

          然后設置 Min Star Duration 和 Max Star Duration 屬性的值為 3 和 5,之后我們生成星星時,會在這兩個之間隨機取值,就是星星消失前經過的時間。

          在隨機位置生成星星

          接下來我們繼續修改 Game 腳本,在 onLoad 方法 后面 添加生成星星的邏輯:

          // Game.js
           onLoad: function () {
           // 獲取地平面的 y 軸坐標
           this.groundY=this.ground.y + this.ground.height/2;
           // 生成一個新的星星
           this.spawnNewStar();
           },
           spawnNewStar: function() {
           // 使用給定的模板在場景中生成一個新節點
           var newStar=cc.instantiate(this.starPrefab);
           // 將新增的節點添加到 Canvas 節點下面
           this.node.addChild(newStar);
           // 為星星設置一個隨機位置
           newStar.setPosition(this.getNewStarPosition());
           },
           getNewStarPosition: function () {
           var randX=0;
           // 根據地平面位置和主角跳躍高度,隨機得到一個星星的 y 坐標
           var randY=this.groundY + Math.random() * this.player.getComponent('Player').jumpHeight + 50;
           // 根據屏幕寬度,隨機得到一個星星 x 坐標
           var maxX=this.node.width/2;
           randX=(Math.random() - 0.5) * 2 * maxX;
           // 返回星星坐標
           return cc.v2(randX, randY);
           },
          

          這里需要注意幾個問題:

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

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

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

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

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

          所以我們只要在 Game 腳本生成 Star 節點實例時,將 Game 組件的實例傳入星星并保存起來就好了,之后我們可以隨時通過 game.player 來訪問到主角節點。讓我們打開 Game 腳本,在 spawnNewStar 方法最后面添加一句 newStar.getComponent('Star').game=this;,如下所示:

          // Game.js
           spawnNewStar: function() {
           // ...
           // 在星星組件上暫存 Game 對象的引用
           newStar.getComponent('Star').game=this;
           },
          

          保存后打開 Star 腳本,現在我們可以利用 Game 組件中引用的 player 節點來判斷距離了,在 onLoad 方法后面添加名為 getPlayerDistance 和 onPicked 的方法:

          // Star.js
           getPlayerDistance: function () {
           // 根據 player 節點位置判斷距離
           var playerPos=this.game.player.getPosition();
           // 根據兩點位置計算兩點之間距離
           var dist=this.node.position.sub(playerPos).mag();
           return dist;
           },
           onPicked: function() {
           // 當星星被收集時,調用 Game 腳本中的接口,生成一個新的星星
           this.game.spawnNewStar();
           // 然后銷毀當前星星節點
           this.node.destroy();
           },
          

          Node 下的 getPosition() 方法 返回的是節點在父節點坐標系中的位置(x, y),即一個 Vec2 類型對象。同時注意調用 Node 下的 destroy() 方法 就可以銷毀節點。

          然后在 update 方法中添加每幀判斷距離,如果距離小于 pickRadius 屬性規定的收集距離,就執行收集行為:

          // Star.js
           update: function (dt) {
           // 每幀判斷和主角之間的距離是否小于收集距離
           if (this.getPlayerDistance() < this.pickRadius) {
           // 調用收集行為
           this.onPicked();
           return;
           }
           },
          

          保存腳本,再次預覽測試,通過按 A 和 D 鍵來控制主角左右移動,就可以看到控制主角靠近星星時,星星就會消失掉,然后在隨機位置生成了新的星星!

          添加得分

          小怪獸辛辛苦苦的收集星星,沒有獎勵怎么行?現在讓我們來添加在收集星星時增加得分獎勵的邏輯和顯示。

          添加分數文字(Label)

          游戲開始時得分從 0 開始,每收集一個星星分數就會加 1。要顯示得分,首先要創建一個 Label 節點。在 層級管理器 中選中 Canvas 節點,右鍵點擊并選擇菜單中的 創建新節點 -> 創建渲染節點 -> Label(文字),一個新的 Label 節點會被創建在 Canvas 節點下面,而且順序在最下面。接下來我們要用如下的步驟配置這個 Label 節點:

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

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

          注意: Score: 0 的文字建議使用英文冒號,因為 Label 組件的 String 屬性加了位圖字體后,會無法識別中文的冒號。

          完成后效果如下圖所示:

          在 Game 腳本中添加得分邏輯

          我們將會把計分和更新分數顯示的邏輯放在 Game 腳本里,打開 Game 腳本開始編輯,首先在 properties 區塊的 最后 添加分數顯示 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 節點,然后把前面添加好的 score 節點拖拽到 屬性檢查器 里 Game 組件的 Score Display 屬性中。

          在 Star 腳本中調用 Game 中的得分邏輯

          下面打開 Star 腳本,在 onPicked 方法中加入 gainScore 的調用:

          // Star.js
           onPicked: function() {
           // 當星星被收集時,調用 Game 腳本中的接口,生成一個新的星星
           this.game.spawnNewStar();
           // 調用 Game 腳本的得分方法
           this.game.gainScore();
           // 然后銷毀當前星星節點
           this.node.destroy();
           },
          

          保存后預覽,可以看到現在收集星星時屏幕正上方顯示的分數會增加了!

          失敗判定和重新開始

          現在我們的游戲已經初具規模,但得分再多,不可能失敗的游戲也不會給人成就感。現在讓我們加入星星定時消失的行為,而且讓星星消失時就判定為游戲失敗。也就是說,玩家需要在每顆星星消失之前完成收集,并不斷重復這個過程完成玩法的循環。

          為星星加入計時消失的邏輯

          打開 Game 腳本,在 onLoad 方法的 spawnNewStar 調用 之前 加入計時需要的變量聲明:

          // Game.js
           onLoad: function () {
           // ...
           // 初始化計時器
           this.timer=0;
           this.starDuration=0;
           // 生成一個新的星星
           this.spawnNewStar();
           // 初始化計分
           this.score=0;
           },
          

          然后在 spawnNewStar 方法最后加入重置計時器的邏輯,其中 this.minStarDuration 和 this.maxStarDuration 是我們一開始聲明的 Game 組件屬性,用來規定星星消失時間的隨機范圍:

          // Game.js
           spawnNewStar: function() {
           // ...
           // 重置計時器,根據消失時間范圍隨機取一個值
           this.starDuration=this.minStarDuration + Math.random() * (this.maxStarDuration - this.minStarDuration);
           this.timer=0;
           },
          

          在 update 方法中加入計時器更新和判斷超過時限的邏輯:

          // Game.js
           update: function (dt) {
           // 每幀更新計時器,超過限度還沒有生成新的星星
           // 就會調用游戲失敗邏輯
           if (this.timer > this.starDuration) {
           this.gameOver();
           return;
           }
           this.timer +=dt;
           },
          

          最后,在 gainScore 方法后面加入 gameOver 方法,游戲失敗時重新加載場景。

          // Game.js
           gameOver: function () {
           this.player.stopAllActions(); //停止 player 節點的跳躍動作
           cc.director.loadScene('game');
           }
          

          這里需要初學者了解的是,cc.director 是一個管理你的游戲邏輯流程的單例對象。由于 cc.director 是一個單例,你不需要調用任何構造函數或創建函數,使用它的標準方法是通過調用 cc.director.methodName(),例如這里的 cc.director.loadScene('game') 就是重新加載游戲場景 game,也就是游戲重新開始。而節點下的 stopAllActions 方法就顯而易見了,這個方法會讓節點上的所有 Action 都失效。

          以上,對 Game 腳本的修改就完成了,保存腳本,然后打開 Star 腳本,我們需要為即將消失的星星加入簡單的視覺提示效果,在 update 方法最后加入以下代碼:

          // Star.js
           update: function() {
           // ...
           // 根據 Game 腳本中的計時器更新星星的透明度
           var opacityRatio=1 - this.game.timer/this.game.starDuration;
           var minOpacity=50;
           this.node.opacity=minOpacity + Math.floor(opacityRatio * (255 - minOpacity));
           }
          

          保存 Star 腳本,我們的游戲玩法邏輯就全部完成了!現在點擊 預覽游戲 按鈕,我們在瀏覽器看到的就是一個有核心玩法、激勵機制、失敗機制的合格游戲了。

          加入音效

          盡管很多人玩手游的時候會無視聲音,我們為了教程展示的工作流程盡量完整,還是要補全加入音效的任務。

          跳躍音效

          首先加入跳躍音效,打開 Player 腳本,添加引用聲音文件資源的 jumpAudio 屬性:

          // Player.js
           properties: {
           // ...
           // 跳躍音效資源
           jumpAudio: {
           default: null,
           type: cc.AudioClip
           },
           },
          

          然后改寫 setJumpAction 方法,插入播放音效的回調,并通過添加 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());
           // 添加一個回調函數,用于在動作結束時調用我們定義的其他方法
           var callback=cc.callFunc(this.playJumpSound, this);
           // 不斷重復,而且每次完成落地動作后調用回調來播放聲音
           return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback));
           },
           playJumpSound: function () {
           // 調用聲音引擎播放聲音
           cc.audioEngine.playEffect(this.jumpAudio, false);
           },
          

          這里需要強調的是回調函數的作用,我們首先來看官方對 callFunc() 方法的定義:

          /**
           * !#en Creates the action with the callback.
           * !#zh 執行回調函數。
           * @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 方法可以傳入三個參數,第一個參數是方法的 selector,我們可以理解為方法名。第二個參數是 Object 類型,一般填入 this。第三個參數為帶回的數據,可以是所有的數據類型,可以不填。我們再注意到這個方法的返回值 —— ActionInstant,這是一個瞬間執行的動作類。到這里我們就可以理解了,使用 callFunc 調用回調函數可以讓函數轉變為 cc 中的 Action(動作),這一用法在 cc 的動作系統里非常實用!例如在上面我們將播放聲音的函數傳入 callFunc 賦值給 callback,讓 callback 成為了一個播放聲音的動作 Action,那么我們之后就能通過 cc.sequence 將跳躍和播放聲音的動作組合起來,實現每跳一次就能播放音效的功能!

          得分音效

          保存 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 節點,然后從 資源管理器 里拖拽 assets/audio/jump 資源到 Player 組件的 Jump Audio 屬性上。

          然后選中 Canvas 節點,把 assets/audio/score 資源拖拽到 Game 組件的 Score Audio 屬性上。

          這樣就大功告成了!完成形態的場景層級和各個關鍵組件的屬性如下:

          現在我們可以盡情享受剛制作完成的游戲了,您能打到多少分呢?別忘了您可以隨時修改 Player 和 Game 組件里的移動控制和星星持續時間等游戲參數,來快速調節游戲的難度。修改組件屬性之后需要保存場景,修改后的數值才會被記錄下來。

          總結

          恭喜您完成了用 Cocos Creator 制作的第一個游戲!希望這篇快速入門教程能幫助您了解 Cocos Creator 游戲開發流程中的基本概念和工作流程。如果您對編寫和學習腳本編程不感興趣,也可以直接從完成版的項目中把寫好的腳本復制過來使用。


          主站蜘蛛池模板: 精品少妇一区二区三区在线| 国产一区二区免费| 九九久久99综合一区二区| 无码少妇一区二区浪潮免费| 精品国产AⅤ一区二区三区4区 | 成人精品一区二区三区电影| 无码人妻精品一区二区三区东京热| 日本韩国黄色一区二区三区| 亚洲AV网一区二区三区| 色综合视频一区二区三区44| 国产精品视频一区二区噜噜| 亚洲综合国产一区二区三区| 国产亚洲自拍一区| 亚洲国产老鸭窝一区二区三区| 精品久久国产一区二区三区香蕉| 国产人妖视频一区在线观看| 丰满爆乳无码一区二区三区| 韩国精品一区视频在线播放| 竹菊影视欧美日韩一区二区三区四区五区| 中文字幕一区二区三区永久| 精品日本一区二区三区在线观看| 国产激情з∠视频一区二区| 蜜臀AV免费一区二区三区| 久久福利一区二区| 亚洲日本精品一区二区| 肉色超薄丝袜脚交一区二区| 久久久老熟女一区二区三区| 亚洲AV成人一区二区三区在线看| 精品国产精品久久一区免费式 | 色婷婷亚洲一区二区三区| 女人和拘做受全程看视频日本综合a一区二区视频 | 无码国产精品一区二区免费3p| AV无码精品一区二区三区| 国产精品揄拍一区二区| 亚洲日韩AV一区二区三区中文| 久久一区二区三区精华液使用方法 | 91在线精品亚洲一区二区| 国产经典一区二区三区蜜芽| 成人精品一区二区激情| 亚洲一区免费观看| 亚洲AV无码一区二区三区电影|