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

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

首先我们先添加一些辅助函数,在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)

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

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏知晓程序

有趣的文字千篇一律,加了「下划线」万里挑一 | 晓技巧

1213
来自专栏菜鸟前端工程师

html+css学习笔记007-样式优先级0外链css样式表

962
来自专栏极客猴

RecyclerView还能这么玩

RecyclerView 从诞生至今,因其具有良好的灵活性、可扩展性而深受人们的爱好。目前已经被开发者广泛应用到App中。本文主要针对 RecyclerView...

942
来自专栏程序员的诗和远方

20180728_ARTS_week05

这题有点犯难,上面是 Discuss 中的一个解法,看了之后挺好理解的,要找回环字符串,就从 a 和 aa 一阶和二阶这两种形式往两边找,感觉特别巧妙。

982
来自专栏Sign

web版《合成10》代码分享

变成月更了。 想做的事情很多,继续扩展『格斗节奏』,把『小红帽』文字冒险游戏成立一个童话系列,优化『月千之夜』和『破壳曼』,上线『Boo』。继续完成各个漫画系列...

3436
来自专栏狮乐园

interview record 20160822

问了一些列范围超级广的问题,不过我感觉大部分问题的答案,面试官还是比较满意的,有一些小问题没有答上来,我觉的并不是因为自己没有能力,其实就是所谓的“约书亚树”道...

883
来自专栏十月梦想

css3 flex弹性布局总结

本文涉及内容如下: flexbox的基本概念、容器属性学习、项目属性学习、实战演练。 flexbox 堪称布局神器,但属性实在太多让人无从下手,因此将自己所学的...

1093
来自专栏练小习的专栏

带你轻松打开SVG动画的大门

初学SVG的时候,感觉那一坨一坨的代码难读难懂,现在回过头仔细想想,是因为那时候看文档缺少一些具体的实例,导致学习起来很枯燥。如今SVG已经在前端各个领域都有所...

2246
来自专栏MixLab科技+设计实验室

设计师会编程、程序员懂艺术之设计规范

以下为正文 UI&UX类的设计规范,不像建筑设计、工业设计那样,有比较成熟权威的设计规范。大多是各家发布的设计规范,用于自家产品、品牌的设计复用。 本文从 设计...

4146
来自专栏Thinks

带你轻松打开SVG动画的大门

初学SVG的时候,感觉那一坨一坨的代码难读难懂,现在回过头仔细想想,是因为那时候看文档缺少一些具体的实例,导致学习起来很枯燥。如今SVG已经在前端各个领域都有所...

1512

扫码关注云+社区

领取腾讯云代金券