壽司快賣:實現遊戲主流程--製作壽司和客戶顯示動畫特效
上一節我們搭建了遊戲的基本框架。遊戲介面被分為若干個板塊,其中一個板塊顯示了各種製作壽司的材料,它的目的是用於玩家根據資訊組裝各種壽司,本節我們進入遊戲的主流程設計階段,這節我們要完成的是如何將讓玩家將各種材料組合成相應的壽司。
首先我們先新增一些輔助函式,在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.hasShowUp = 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.hasShowUp = 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.hasShowUp) {
console.log('show up customer left')
this.customerShowUp(customer)
}
customer = this.queues[1][0]
if (customer && !customer.hasShowUp) {
console.log('show up customer right')
this.customerShowUp(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後自動從頁面上刪除,完成上面程式碼後,我們就可以看到前面所示的動畫特效了。
更多技術資訊,包括作業系統,編譯器,面試演算法,機器學習,人工智慧,請關照我的公眾號: