专栏首页Coding迪斯尼寿司快卖:实现游戏主流程--制作寿司和客户显示动画特效

寿司快卖:实现游戏主流程--制作寿司和客户显示动画特效

上一节我们搭建了游戏的基本框架。游戏界面被分为若干个板块,其中一个板块显示了各种制作寿司的材料,它的目的是用于玩家根据信息组装各种寿司,本节我们进入游戏的主流程设计阶段,这节我们要完成的是如何将让玩家将各种材料组合成相应的寿司。

首先我们先添加一些辅助函数,在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后自动从页面上删除,完成上面代码后,我们就可以看到前面所示的动画特效了。

本文分享自微信公众号 - Coding迪斯尼(gh_c9f933e7765d)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 实现小球在弹射前的拉伸特效和动态障碍物特效

    当前我们实现小球弹射时,会先用鼠标点击小球,然后移动鼠标,当松开鼠标时,小球会弹射向鼠标松开的位置。我们按住小球的时间越长,小球弹射的力度就越大,但有一个问题是...

    望月从良
  • VUE+WebPack游戏设计:欲望都市城市图层的设计

    望月从良
  • 寿司开卖:实现寿司制作特效和音响特效

    本节我们将继续上一节完成若干个小功能。首先要完成的是,当客户动画在主页面出现时,它左上角会冒泡,显示它想购买何种寿司,此时玩家可以点击左下角面板中各种元素,组合...

    望月从良
  • Canvas 绘制点线相交

    无论是Canvas 还是别的,前端的学习总是妙趣横生,只要思想在不断进步,技术就会一次次的突破。如果你学习过Canvas ,你会更加了解这段代码的神奇,送给你,...

    我不是费圆
  • 分享HTML5-CANVAS相交线动画代码实例

    今天全百科网分享的是HTML5-CANVAS相交线动画代码实例,史基于html、css、js三个方面制作而成,可用于网页背景,效果很是不错。

    于飞云计算
  • Flutter组件学习(三)—— 输入框TextFiled

    Google 前两天发布了 Flutter 1.0 正式版本,正式版发布之后,LZ身边越来越多的人都开始入坑了,不得不说 Flutter 框架的魅力还是很吸引人...

    用户2802329
  • 一款轮播组件的诞生

    早在几个月前,就想自己动手写个轮播图组件,因此也看了许多文章,断断续续过了几个月,今天终于有时间腾出手来给此插件做个总结,因此有了这篇文章。话不多说,先上 De...

    小皮咖
  • tcplayer 源码改造第二弹 -> 加入倍速播放

    由腾讯视频的官方文档可以知道,currentTime方法是暴露给用户,用于获取/设置当前时间的方法,同理,加入获取当前倍速的方法currentRate:

    大洼X
  • Flutter基础widgets教程-Chip篇

    青年码农
  • SpringBoot源码分析 顶

    一:创建SpringApplication对象过程:new SpringApplication(primarySources)

    须臾之余

扫码关注云+社区

领取腾讯云代金券