作為前端的你,CC遊戲開發可以上車
1. 初來乍到
開啟 Cocos Creator 點選新建空白專案,在預設佈局的左下區域,一個黃黃assets資料夾映入眼簾。作為前端的你對這個檔案是不是再熟悉不過了。是的,和你想象的一樣,開發遊戲中所有資源,指令碼都會放置到該檔案。
2. 初步探索
專案建立好以後,對各區域的功能大致瞭解下,作為前端的你,主要還是要迅速的掌握cc提供的各種NB的功能。所以,還得趕緊開啟 [官網](https://docs.cocos.com/creator/manual/zh/) 快速瀏覽一遍。官網也寫得很好,提供中文和英文,對於英文能力不好的夥伴來說,簡直是不能太好了。是不是找到了當初學習Vue的感覺。作為前端的你,整天寫了一堆業務控制,處理各種佈局,各種相容,對奇怪的css優先順序搞得雲裡霧裡的。所以是時候換一個更有意思開發場景,給自己做個遊戲解悶多好
cc是一個跨平臺框架,一端編譯多端釋出。想想前端的 mpvue taro uni-app,無不是解決此類問題,再加上gulp,webpack,再來一堆node_modules,啥less sass stylus.各種環境配置那是相當的複雜。所以業界流傳,前端已經進入深水區,真的一點不假。 然而cc依然可以讓你舒適的寫JS或者TS ,並且沒有繁雜的配置,一鍵搞定打包釋出。
3. 小試牛刀
上邊說了一大堆,其實並沒有什麼鳥用。在官網首頁中,給開發者提供了個完整坑爹的遊戲《摘星星》,如果打包到微信小遊戲,需要橫屏,不太友好。本著舉一反三的求學態度,我利用此場景,換了一個遊戲玩法。開發了自己第一款小遊戲《坦克俠》,當然也很坑爹
遊戲開發主要是確定遊戲規則,我新改編的玩法就是在星空中隨機生成不同數量的星星,並一直往下掉落,我的主角坦克必須在星星掉落前接住。丟失一顆星星生命減一,生命為0遊戲結束。當然我們主角每收集一顆星星,根據當前的難度會新增一定的分數。累計到一定的分數,又可以給主角新增一點生命值
在官網 [下載初始專案](https://github.com/cocos-creator/tutorial-first-game/releases/download/v2.0/start_project.zip) 下載一個基礎專案,該專案中只有一些專案基本圖片和聲音。接下來,我們需要建立場景,製作預製資源,新增控制指令碼,編譯釋出微信小遊戲,[快速開始](https://docs.cocos.com/creator/manual/zh/getting-started/quick-start.html)
製作一個遊戲場景,與官網不同的是,我將Canvas的Size屬性,在屬性檢查器中設定為 288 x 512 ,並且勾選了 Fit Height以及 Fit Width 用以適應同的手機螢幕。然後拖動背景圖片到層級管理器中,並在場景編輯器中設定背景Size屬性,使其等於Canvas的Size屬性。然後依次在層級管理器中新建三個Label控制元件,依次拖動到背景圖片左上角和右上角,用以記錄生命值,當前分數,以及最高分數。接著在場景中間新增一個Label控制元件和一個Button按鈕用於顯示遊戲結束和開始遊戲。在場景底部拖動放置我們的主角坦克。所以最新場景的效果應該是如下顯示的那樣
4. 一頓操作猛如虎
遊戲場景設計,看似酷炫,無非就是拖拖拖。依稀找到了當年C#開發winform的感覺,隨便搞整一下,一個介面就出來了。所以導致很多人開發winform,webform很簡單,很傻瓜,其實不是的。重要的還是後邊的業務邏輯,解決方案,這些都是超越語言之上的東西。所以cc的場景編輯,就不多說了,直接分析我們遊戲實現邏輯。開始之前我們先初始一下typescript開發環境,操作如下圖
依次點選安裝vs code 擴充套件外掛,新增 Typescript專案配置。接下來就要編寫指令碼了,所有還是有必要了解下cc指令碼的生命週期
-
onLoad 首次啟用時觸發,一般做一些初始化操作。對比Vue我覺得最合適的應該是beforeMount回撥
-
start 首次啟用時觸發,一般初始化一些中間狀態的資料,改方法在onLoad之後,在第一次update之前,對比Vue自然應該是mounted回撥
-
update 該回調會頻繁呼叫,每一幀呼叫一次。對比Vue應該是beforeUpdate回撥,雖然他們性質不一樣
-
lateUpdate 該回調會頻繁呼叫,也是每幀呼叫一次,對比Vue應該updated回撥
-
onDestroy 根據條件呼叫,當元件呼叫了自身的 destroy()方法,會觸發此回撥
-
onEnable 根據條件呼叫, enabled 屬性從 false 變為 true 或 active 屬性從 false 變為 true 觸發此回撥
-
onDisable 根據條件呼叫, enabled 屬性從 true 變為 false 或active 屬性從 true 變為 false觸發此回撥
4.1 讓主角動起來
做過前端的你一定知道,要想拖動一個DIV,一定是在Body中監聽滑鼠的移動事件。在移動端一定是監聽觸控移動事件。是的,在cc裡邊做遊戲,希望一個元件動起來依然是這麼操作的,那麼cc裡邊是如何註冊事件的呢?兩個方式,一個在場景編輯器下角的屬性中新增腳本里邊的方法,另外一種就是直接在腳本里邊新增。當然我推薦第二種。雖然IDE會幫我們生成很多程式碼,如果不自己寫一遍,就永遠不曉得資料流向。就像當年開發winform時,很多人拖動一個按鈕控制元件,然後雙擊控制元件,IDE就自動幫你註冊好了一個使用者點選事件。殊不知,IDE是在xx.design.cs中通過程式碼替你註冊好的。所以既然剛開始學,一定要了解清楚它的原理。
-
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this) 註冊一個系統事件 ,支援按鍵事件和重力感應事件
-
this.node.parent.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this) 在某個節點註冊一個Node 支援的事件型別
所以,主角移動無非實在TouchMove時改自己的X/Y
// author:herbert qq:464884492 onTouchMove(e: cc.Event.EventTouch) { let deltaX = e.getDeltaX(); //獲取本次和上次移動的增量 let deltaY = e.getDeltaY(); //左移 if (deltaX < 0 && this.node.x <= this.leftMinX) return; if (deltaX > 0 && this.node.x >= this.rightMaxX) return; if (deltaY > 0 && this.node.y >= this.topMaxY) return; if (deltaY < 0 && this.node.y <= this.bottomMinY) return; this.node.x += deltaX; this.node.y += deltaY; }
4.2 生成坑爹的星星
在cc裡邊需要重複生成的物件,我們一般會製作成一個預製資源。然後在基本中通過程式碼例項化。何為預製資源,就權當它是一個模板吧。現在生成我們第一顆小星星
// author:herbert qq:464884492 buildOneStar() { let star = cc.instantiate(this.starPrefab); this.node.addChild(star); return star; }
是的,就是這麼簡單,有沒有class.newInstance()的感覺,當然這個只是在場景的預設位置生成了一個星星而已。我們需要更多的資訊,位置肯定也不是預設位置,所以還得繼續碼程式碼
// author:herbert qq:464884492 buildRandomStar() { let tempX = 0; let tempY = 0; tempX = Math.floor(this.starMaxX - Math.random() * this.starMaxX);//生成一個不大於MaxX的座標 tempY = Math.floor(this.starMaxY - Math.random() * this.starMaxY); if (Math.random() < 0.5) tempX = tempX * -1; let star = this.buildOneStar(); star.setPosition(tempX, tempY); star.zIndex = this.tank.zIndex - 1; star.name = "star"; star.getComponent("Star").index = this; }
這樣感覺好多了,可以生成很多星星了,不過,我們的星星也得往下掉才行,作為前端的你首先想到的是不是更新星星的Y值,是的,我就是這麼做的。利用生命週期中start方法,定義一個從上往最小Y運動的動畫。後來才瞭解到所有的遊戲引擎都有物理特性,開啟了自己就掉下來了。不過原理肯定還是改變y值。何況這麼簡單的遊戲完全沒必要使用
start() { // 定義一個Action let downAction = cc.moveTo(this.index.starFallSpeed, this.node.x, this.minY - 60); downAction.easing(cc.easeSineOut()); this.node.runAction(downAction); }
4.3 是時候接住星星了
只要是遊戲少不了做碰撞檢測,如果在CC中開啟了物理引擎還好,直接跟星星和主角新增一個剛體就好了,不過我們沒開啟,那就自己來了。不過碰撞檢測無非就是判斷兩個區域有沒有重疊地方,簡單判斷就上下左右,左上右上左下右下八個點。不過我這裡偷了個懶,直接判斷星星和主角間向量的距離。
//author:herbert qq:464884492 ... let distance = this.node.position.sub(this.tank.getPosition()).mag(); if (distance < (this.tank.width / 2 - 5)) { console.log("接住了"); } ...
4.4 來點刺激的
遊戲嘛,總不能一成不變那多沒意思,所以隨著時間的推移我們的調整點難度。我這個遊戲難度無非就一下兩個方面
-
生成星星的速度加快
-
星星掉落的速度加快
//author:herbert qq:464884492 ... this.index.scoreNum += this.index.starScoreSpeed; this.index.score.string = "得分:" + this.index.scoreNum; // 降落速度加 if (Math.floor(this.index.scoreNum / 100) == this.index.starScoreSpeed - 4 && this.index.starFallSpeed > 1) { this.index.starFallSpeed -= 0.2; //下降速度加快 if (this.index.starBuildTimeOut > 200) { this.index.starBuildTimeOut -= 100; //生成速度加快 } this.index.lifeNum += 1; if (this.index.starScoreSpeed < 10) { this.index.starScoreSpeed += 1; } } cc.audioEngine.play(this.index.scoreClip, false, 0.2); this.index.allStars.splice(this.index.allStars.indexOf(this.node), 1) this.node.destroy(); ...
4.5 是時候結束了
遊戲嘛,也不能一直玩下去。不然多沒挑戰。自從調整遊戲難度後我的最高分重來就沒有超過4000.
//author:herbert qq:464884492 ... if (this.node.y <= this.minY) { this.index.lifeNum -= 1; this.index.life.string = "生命:" + this.index.lifeNum; this.node.destroy(); this.index.allStars.splice(this.index.allStars.indexOf(this.node), 1) if (this.index.lifeNum <= 0) { this.index.gameOver.node.active = true; this.index.btnPlay.node.active = true; this.index.starIsRunning = false; let storageValue = cc.sys.localStorage.getItem(this.index.HIGHSTORAGEKEY); if (storageValue && parseInt(storageValue) > this.index.scoreNum) { return; } cc.sys.localStorage.setItem(this.index.HIGHSTORAGEKEY, this.index.scoreNum); this.index.highScore.string = "最高分:" + this.index.scoreNum; } } ...
5. 來點實際的
做技術嘛,大多都是 Talk is cheap,Show me your code.做點總結吧
-
開源地址
-
在基本中定義的屬性,切記在編輯器中拖動繫結
-
多看官網api,多開例項程式碼
-
釋出微信小遊戲一定不要有英文,會導致稽核不通過