寿司快卖:实现游戏主流程--制作寿司和客户显示动画特效
上一节我们搭建了游戏的基本框架。游戏界面被分为若干个板块,其中一个板块显示了各种制作寿司的材料,它的目的是用于玩家根据信息组装各种寿司,本节我们进入游戏的主流程设计阶段,这节我们要完成的是如何将让玩家将各种材料组合成相应的寿司。
首先我们先添加一些辅助函数,在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后自动从页面上删除,完成上面代码后,我们就可以看到前面所示的动画特效了。
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- jQuery实现鼠标经过显示动画边框特效
- 寿司开卖:实现寿司制作特效和音响特效
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- 游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- JS实现从顶部下拉显示的带动画QQ客服特效代码
- JS实现从顶部下拉显示的带动画QQ客服特效代码
- DirectX3D游戏制作之---3D场景的渲染及人物动画的显示
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- VUE+WebPack游戏设计:实现盒子动画和键盘特效
- [原]【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- 【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现
- DX11 游戏开发笔记 (五) 假灯光特效 及多个聚光灯的实现
- Android使用ViewFlipper实现Switch动画特效