上一節我們搭建了遊戲的基本框架。遊戲界面被分爲若干個板塊,其中一個板塊顯示了各種製作壽司的材料,它的目的是用於玩家根據信息組裝各種壽司,本節我們進入遊戲的主流程設計階段,這節我們要完成的是如何將讓玩家將各種材料組合成相應的壽司。
首先我們先添加一些輔助函數,在gamescenecomponent.vue中添加代碼如下:
resizeCanvas () { // change 2 var customerView = document.getElementById('customer-view') var w = this.getBorderView(customerView) this.canvas = document.getElementById('canvas') this.canvas.width = customerView.offsetWidth - w.left - w.right this.canvas.height = customerView.offsetHeight - w.top - w.bottom }, ... //change 1 initDomElement () { .... this.others = document.getElementById('others') this.rices = document.getElementById('rice') this.seaweeds = document.getElementById('seaweed') // 設置相應壽司的材料組合 this.recipes['sushiSalmonRoe'] = ['rice', 'seaweed', 'seaweed', 'salmon-roe'].sort() this.recipes['sushiOctopus'] = ['rice', 'octopus'].sort() this.recipes['sushiSalmon'] = ['rice', 'salmon'].sort() this.recipes['sushiEgg'] = ['rice', 'egg', 'seaweed'].sort() }, getBorderWidths (element) { var style = document.getComputedStyled(element) return { top: parseInt(style.borderTopWidth), right: parseInt(style.borderRightWidth), bottom: parseInt(style.borderBottomWidth), left: parseInt(style.borderLeftWidth) } }, // change 3 arrayIsEqual (array1, array2) { if (array1.length !== array2.length) { return false } for (var i = 0, len=array1.length; i < len; i++) { if (array1[i] !== array2[i]) { return false } } return true }, clearChild (node) { while (node.lastChild) { node.removeChild(node.lastChild) } }, clearAllIngredients () { this.clearChild(others) this.clearChild(rices) this.clearChild(seaweeds) } }
上面代碼用於計算可知Dom元素的大小位置,以及在Dom中添加或刪除各種元素。在製作壽司時,玩家通過選取相應材料組合起來形成所需要的壽司,相應代碼如下:
initDOMElements () { // change 6 var ingredients = document.querySelectorAll('.ingredient') for (var i = 0, len = ingredients.length; i < len; i++) { var element = ingredients[i] element.onclick = this.ingredentOnclick.bind(this) } var deleteButton = document.getElementById('delete-sushi-btn') deleteButton.onclick = this.deleteButtonOnclick.bind(this) this.ingredientsNode = document.getElementById('ingredient') }, // change 7 deleteButtonOnclick () { this.trashSushi() }, ... // change 5 trashSushi () { this.sushiOnHand.length = 0 this.clearAllIngredients() }
當玩家選取若干種製作壽司的材料後,界面要做相應變化,對應代碼如下:
// change 8 ingredentOnclick (ingredient) { console.log('ingredient click:', ingredient) var type = ingredient.toElement.dataset.type this.sushiOnHand = this.sushiOnHand.sort() this.addIngredientToScreen(type) }, addIngredientToScreen (type) { var isEqualToAnySushi = false var sushiName = '' for (var key in this.recipes) { if (this.recipes.hasOwnProperty()) { // 當前選中的材料是不是屬於某個指定的壽司菜單裏 isEqualToAnySushi = this.arrayIsEqual(this.sushiOnHand, this.recipes[key]) sushiName = key if (isEqualToAnySushi) { break } } } // 把所有選中的材料組合起來形成一個壽司 if (isEqualToAnySushi) { this.clearAllIngredients() var sushi = document.createElement('div') sushi.classList.add(sushiName, 'sushi') this.others.appendChild(sushi) } else { // 把選擇材料拷貝到壽司板塊 var node = this.ingredientsNode.querySelector('.ingredient[data-type=' + type + ']').cloneNode(true) node.style.height = '80px' if (type === 'rice') { console.log('append to rice:', this.rices) this.rices.appendChild(node) } else if (type === 'seaweed') { console.log('append to seaweeds:', this.seaweeds) this.seaweeds.appendChild(node) } else { console.log('append to others:', this.others) this.others.appendChild(node) } }
當上面代碼完成後,玩家在壽司面板點擊一個圖片代表的元素時,如果它屬於某個壽司組合菜單中的一部分,那麼它就會顯示在右邊面板上,如下圖所示:
當我們點擊右上角的trash按鈕時,下面選中的元素會被刪除掉。接着我們繼續添加顧客動畫特效,客戶將隨機的出現在場景中央區域,根據一個隨機值它會出現在左上方或右下方,一開始客戶出現時它會顯示出愉快的表情,如下圖:
此時玩家應該根據客戶的要求,點擊左下方的材料圖片組裝出客戶想要的壽司,如果時間過長沒能及時將壽司製作出了,客戶就會顯示出憤怒的表情,如下圖:
我們看看相應代碼的實現:
data () { return { canvas: null, // change 4 sushiOnHand: [], recipes: [], // change 10: view: {}, queues: [], queueIndex: 0, leftPos: 0.40, rightPos: 0.8 } }, .... // change 9 initCustomerView () { console.log('this.cjs: ', this.cjs) this.stage = new this.cjs.Stage(this.canvas) this.cjs.Ticker.setFPS(60) this.cjs.Ticker.addEventListener('tick', this.stage) this.cjs.Ticker.addEventListener('tick', this.tick) // 實現客戶隊列 this.view.queueLeft = new this.cjs.Container() this.stage.addChild(this.view.queueLeft) this.view.queueRight = new this.cjs.Container() this.stage.addChild(this.view.queueRight) },
通過上面代碼,爲程序添加一個時鐘,我們將根據時鐘變化來設置遊戲的動畫效果,接着我們編寫構造客戶動畫的代碼:
// change 11 設置顧客對象 Customer (number, leftOrRight) { var obj = new this.cjs.Container() obj.number = number // 隨機構造客戶想要吃的壽司 obj.wants = this.randomWants() // 客戶是否吃到指定壽司 obj.hasEaten = false // 是否把客戶放到隊列前頭 obj.hasShownUp = false // 客戶等待了多久 obj.hasWaitForTicks = 0 // 在左隊列還是右隊列 obj.queueIndex = 0 if (leftOrRight === 'right') { obj.queueIndex = 1 } return obj }, randomWants () { var options = ['sushiSalmonRoe', 'sushiOctopus', 'sushiSalmon', 'sushiEgg'] var index = Math.floor(Math.random() * options.length) return options[index] }, customerTick (customer) { if (customer.hasShowUp === false) { return } customer.hasWaitForTicks += 1 if (customer.hasShownUp === true && customer.hasWaitForTicks === 300) { // 顯示憤怒的顧客圖片 console.log('customer angry') customer.graphics.gotoAndStop('angry') } // 如果等待太久,將顧客從畫面上刪除 if (customer.hasWaitForTicks > 500) { this.removeCustomer(customer) } // 如果成功吃到壽司,也將客戶圖片從頁面刪除 if (customer.hasEaten) { this.removeCustomer(customer) } }, removeCustomer (customer) { if (customer.parent === null) { console.log('remove customer with null parent:', customer) } customer.parent.removeChild(customer) this.removeFromQueue(customer.queueIndex) },
上面代碼構建客戶對象,並且初始化它相關信息,customerTick用來根據時鐘變化調整客戶動畫的顯示,當經過一定時長,如果相關條件沒有滿足,那麼我們就將客戶的愉悅動畫,通過調用gotoAndStop(‘angry’)來時實現將客戶動畫轉變爲憤怒表情,當時長超過500 tick後,我們將客戶動畫從頁面上刪除,客戶在頁面上的顯示需要執行下面代碼:
// 將客戶圖片顯示到頁面上 customerShowUp (customer) { customer.graphics = new this.assetsLib['Customer' + customer.number]() customer.graphics.gotoAndStop('normal') customer.graphics.on('click', this.customerOnClick) customer.addChild(customer.graphics) var bubble = new this.assetsLib.Bubble() bubble.x = -40 bubble.y = -120 customer.addChild(bubble) bubble.sushiType.gotoAndStop(customer.wants) customer.hasShownUp = true this.customer = customer }, customerOnClick () { var isEqual = this.arrayIsEqual(this.sushiOnHand, this.receipes[this.customer.wants]) if (isEqual) { this.customer.hasEaten = true } this.trashSushi() }, removeFromQueue (index) { this.queues[index].shift() }, tick () { var durationForNewCustomer = 500 if (this.cjs.Ticker.getTicks() % durationForNewCustomer === 0) { console.log('summon customer') this.summonNewCustomer() var customer = this.queues[0][0] if (customer && !customer.hasShownUp) { console.log('show up customer left') this.customerShowUp(customer) } customer = this.queues[1][0] if (customer && !customer.hasShownUp) { console.log('show up customer right') this.customerShowUp(customer) this.customerTick(customer) } } if (this.queues[0][0] !== undefined) { this.customerTick(this.queues[0][0]) } if (this.queues[1][0] !== undefined) { this.customerTick(this.queues[1][0]) } }, summonNewCustomer () { var leftOrRight = 'left' var queueIndex = 0 if (Math.random() >= 0.5) { leftOrRight = 'right' queueIndex = 1 } var customer = this.Customer(1, leftOrRight) this.queues[queueIndex].push(customer) this.queueIndex = queueIndex if (leftOrRight === 'left') { this.view.queueLeft.addChild(customer) customer.parent = this.view.queueLeft } else { this.view.queueRight.addChild(customer) customer.parent = this.view.queueRight } },
summonNewCustomer函數用來創建一個客戶對象,並根據一個隨機數決定客戶是出現在頁面的左上角還是右下角,時鐘每次觸發時,函數tick會被調用,在裏面代碼根據ticks來決定是否創建一個客戶對象,每500個ticks就創建一個客戶對象,當客戶對象出現300個tick後顯示憤怒表情,500個tick後自動從頁面上刪除,完成上面代碼後,我們就可以看到前面所示的動畫特效了。