Im Sommer gibt es nicht nur Wassermelonen und Klimaanlagen, sondern auch Sport und Schwitzen, „Run! Programmer" js-Spiel ist heiß, komm und lauf zusammen

Ich nehme am „Early Summer Creative Contest“ teil. Weitere Informationen finden Sie unter: Early Summer Creative Contest

Adresse des Online-Erlebnisses: summer.pkec.net/Quelladresse
: gitee.com/ihope_top/j…

Vorwort

Ehe man sich versieht, ist der Sommer wieder da. Woran denken Sie, wenn es um Sommer geht? Klimaanlage, Wassermelone und Eis? Aber im Sommer gibt es mehr als nur Sport, Schwitzen und Körper zeigen.Erinnern Sie sich an das Sporttreffen jeden Sommer? Erinnerst du dich an das kühle Laufen jeden Tag in diesem Sommer? Heute werde ich js verwenden, um Ihnen ein Parkour-Minispiel zu bringen – „Lauf! Programmierer", ich hoffe es gefällt euch

Spieleinführung

Spezialfunktion

Die Sonderfunktion betrifft die Unterstützung der Back-End-Schnittstelle und wird daher nur während des Ereignisses wirksam. Sie kann nach dem Schreibereignis ungültig werden, aber das Spiel kann weiterhin normal aufgerufen werden. Wenn es auf böswillige Angriffe oder andere unkontrollierbare Faktoren stößt, Diese Funktion kann im Voraus fehlschlagen.

Noten-Upload

Bild.png

Das ist richtig, dieses Mal haben wir das Front- und Backend geöffnet, um das Hochladen von Spielständen zu unterstützen. Nachdem das Spiel vorbei ist, wird es eine Option geben, Spielstände hochzuladen. Sie müssen nur Ihre eigene Nuggets-ID eingeben, um diesen Spielstand hochzuladen Hochladen der Nuggets-IDs Der Hauptzweck besteht darin, Benutzer daran zu hindern, zufällig illegale Spitznamen einzugeben, daher ist es hier notwendig, den Nuggets-Spitznamen des Benutzers gemäß der Nuggets-ID des Benutzers zu erhalten.

Das Minispiel dient nur der Unterhaltung, daher gibt es keine Anti-Cheat-Maßnahme, jeder darf nicht schummeln, aber um Fehlbedienungen zu verhindern, ist es immer noch eingeschränkt, dass dieselben Ergebnisse nicht wiederholt hochgeladen werden können, dh sie sind zweimal 500 Meter gelaufen und das zweite Mal Die Ergebnisse können nicht hochgeladen werden Wenn Sie zum ersten Mal 500 Meter und zum zweiten Mal 100 Meter laufen, können Sie beide Ergebnisse hochladen.

So erhalten Sie die Nuggets-ID:

Bild.png

Geben Sie die persönliche Homepage ein, die Nummer hinter der Adressleiste ist Ihre ID

Alle Mitarbeiter laufen

Bild.png

Nachdem das Spiel beendet ist, werden die von den Benutzern hochgeladenen Punktzahlen in der Datenbank gespeichert, und auf der Homepage wird die Summe der von allen Benutzern hochgeladenen Punktzahlen angezeigt

Bestenliste

Bild.png

Auf der Homepage wird der Eintrag der Bestenliste angezeigt, und die Bestenliste zeigt das Einzelprozess-Ranking aller Benutzer an. Ein Benutzer kann mehrmals auf der Liste stehen. Die höchste Punktzahl beträgt 800 Meter, dann sind die ersten und zweiten Plätze Zhang San.

Regeln Einführung

Bild.png

Nach dem Start des Spiels rennt die Spielfigur automatisch vorwärts und der kleine Teufel wird im laufenden Bild angetroffen. Der Benutzer muss dem kleinen Teufel ausweichen und weiter vorwärts rennen. Wenn der kleine Teufel angetroffen wird, ist das Spiel vorbei.

Betriebsmethode:

跳跃:按 w 键或 键进行跳跃躲避下方的小恶魔

Bild.png

下滑:按 s 键或 键进行下滑躲避上方的小恶魔

Bild.png

随着里程的增加,人物奔跑的速度会越来越快,小恶魔的数量也会越来越多(有上限),规则介绍就到这里啦,快去体验一下游戏吧。

游戏开发

场景开发

白云开发

Bild.png

单个的白云其实就是一个圆角矩形,然后用伪类元素做两个圆叠加起来形成的,代码如下

.cloud-item {
  position: absolute;
  width: 175px;
  height: 55px;
  margin: 50px;
  border-radius: 100px;
  background: #fff;
}
.cloud-item::before, .cloud-item::after {
  content: '';
  display: block;
  background: #fff;
  position: absolute;
}
.cloud-item::before {
  content: '';
  display: block;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  top: -90%;
  right: 10%;
}
.cloud-item::after {
  content: '';
  display: block;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  top: -54%;
  left: 14%;
  transform: rotate(-25deg);
}
复制代码

下面就是随机生成白云,之后给白云一个从右往左移动的动画,为了生成的白云更符合观感,我们给它一个随机的大小,然后根据这个大小再来一个对应的移动速度,就变成了下面这样

2.gif

附上代码

<div class="cloud-wrap" ref="cloudWrap"></div>
复制代码
screenWidth: document.documentElement.clientWidth,
lastCreateTime: 0,
cloudFrequency: 10,
cloudSpeed: 1

// 生成云朵
createCloud() {
  let now = new Date().getTime()
  if (now - this.lastCreateTime > 3000) {
    // 创建云朵
    let cloudItem = document.createElement('div')
    cloudItem.className = 'cloud-item'
    // 设置云朵变化系数
    cloudItem.cloudScale = Math.random()
    // 设置云朵大小
    cloudItem.style.transform = 'scale('+cloudItem.cloudScale+')'
    // 设置云朵透明度
    cloudItem.style.opacity = cloudItem.cloudScale
    // 设置云朵位置
    let _left = this.screenWidth
    cloudItem.style.left = _left + 'px'
    let _top = Math.random() * 400
    cloudItem.style.top = _top + 'px'

    this.$refs.cloudWrap.appendChild(cloudItem)
    // 云朵移动
    let cloudMove = () => {
      // 云朵越大,移动速度越快
      let moveX = this.cloudSpeed * cloudItem.cloudScale
      let _left = +cloudItem.style.left.slice(0, -2)
      cloudItem.style.left = _left - moveX + 'px'

      // 如果云朵距离屏幕顶部距离大于等于屏幕高度,则移除此云朵
      if (cloudItem.offsetLeft < (-cloudItem.offsetWidth)) {
        this.$refs.cloudWrap.removeChild(cloudItem)
      } else {
        requestAnimationFrame(cloudMove)
      }
    }
    cloudMove()
    cloudMove()
    this.lastCreateTime = now
  }
  requestAnimationFrame(this.createCloud)
},
复制代码

下面是代码块版本

地面开发

Boden.png

地面部分就是用这样的小方块平铺形成的,如果高度不够的话,就再来一个颜色相近的背景色

<!-- 地面 -->
<div class="ground-wrap"></div>
复制代码
.ground-wrap {
  height: 150px;
  box-sizing: border-box;
  background: url('./assets/images/ground.png') #685166;
  background-repeat: repeat-x;
  background-size: 50px;
}
复制代码

我们都知道,游戏里的人物运动其实并不是人物本身在动,而是场景的移动衬托出人物在运动,这里我们需要地面也在不断的运动,衬托出人物在运动。由于我们这里使用的是背景图片,所以只需要控制背景图片的定位即可

因为随着游戏的进行,人物会移动的越来越快,所以这里地面的移动速度也需要随着speed(全局速度控制变量)变化,另外我们对于人物的奔跑距离也在这里进行计算,当人物奔跑到一定距离时改变速度

 // 地面背景横向滚动
    groundScroll() {
      let ground = document.querySelector('.ground-wrap');
      let _left = 0
      
      ground.style.backgroundPositionX = _left +'px';
      let cityMove = () => {
        if (_left <= -600) {
          _left = 0
        }
        _left -= this.speed * 3
        this.total += (this.speed / 10)
        if (this.total >= 40000) {
          this.speed = 6
        } else if (this.total >= 4000) {
          this.speed = 5.5
        } else if (this.total >= 2000) {
          this.speed = 5
        } else if (this.total >= 1000) {
          this.speed = 4.5
        } else if (this.total >= 500) {
          this.speed = 4
        } else if (this.total >= 200) {
          this.speed = 3.5
        }
        ground.style.backgroundPositionX = _left +'px';
        this.groundMoveInterval = requestAnimationFrame(cityMove)
      }
      cityMove()
    },
复制代码

人物开发

Bild.png

人物跑动其实就是来回切换这样的几张静态图片,之所以没有用gif,是因为我还要控制人物跑动的速度,gif我没找到怎么控制速度的,我们先来看一下不同速度的跑动动画

3.gif

4.gif

下面是代码

先加载一下跑动的图片数组

for (let index = 0; index < 10; index++) {
  let img = require('@/assets/images/user/run/Run_00'+index+'.png')
  this.userRunList.push(img)
}
复制代码

跑步时切换图片

// 动画
run() {
  let _this = this
  let _index = 0

  let _run = () => {
    let now = new Date().getTime()
    if (now - this.lastRunTime > 120 - (this.speed * 20)) {
      if (_index > (this.userRunList.length - 1) ) {
        _index = 0
      }
      _this.userPic = this.userRunList[_index]
      _index++
      this.lastRunTime = now
    }
    this.runInterval = requestAnimationFrame(_run)
  }
  _run()
},
复制代码

不同于跑步的动画处理,跳跃和下滑我都没有做动画处理,一是因为它的时间不好控制,二是太多的图片也会导致,所以这里我们就用两张静态图片,展示两种状态

slide () {
  this.userPic = this.userSlidePic
},
jump () {
  this.userPic = this.userJumpPic
}
复制代码

障碍物开发

这种飞行物的开发其实原理都一样,从我最开始的年兽大作战中的弹幕到抗疫的汤圆中的柱子,又或者是刚才说过的天空中的云朵,都一样,都是按规定的时间生成一个物体,然后给它一个移动的动画。

这里不一样的地方是每个障碍物里包含的小恶魔数量不一样,障碍物可能在上面也可能在下面。

针对数量这个问题,我是根据speed(全局速度变量)来随机生成的,最少一个,最多四个,数量越多,当然难度也就越大。

针对上下这个问题,无非就是写一个靠上和靠下的样式,然后生成的时候随机进行生成,赋予相应的样式,注意,这里需要将这个状态保存下来,因为进行碰撞检测的时候要用。

Bild.png

// 生成障碍物
    createObstruction () {
      let obsList = document.createElement('div')
      // 让障碍物随机在上方或者下方
      let state = Math.random() > 0.5 ? 'top' : 'bottom'
      obsList.className = 'obs-list-' + state
      obsList.state = state
      // 根据速度等级,随机生成相应数量的小恶魔
      let random = Math.ceil(Math.random() * (this.speed - 2))
      for (let index = 0; index < random; index++) {
        let obsItem = document.createElement('div')
        obsItem.className = 'obs-item'
        obsList.appendChild(obsItem)
      }
      obsList.style.left = this.screenWidth + 'px'
      // obsList.createNext = false // 是否已创建下一个障碍物
      obsList.nextSpace = Math.random() * (this.obsInterval[1] - this.obsInterval[0]) + this.obsInterval[0] // 下一个障碍物间隔

      this.$refs.obstructionWrap.appendChild(obsList)

    },
复制代码

这里障碍物的生成还借鉴了弹幕那里的生成方案,那就是一个障碍物出来多久后,自动加载下一个,而不是定时进行创建,这里有点忘了当初怎么想的了,想起来再补充吧。

之后就是障碍物的移动,这里障碍物的移动速度设置的和地面是一样的,这样有一种障碍物是漂浮在地面上的感觉,另外和白云不一样的是这里障碍物的移动是控制的整体障碍物的移动,而不是给单一障碍物添加的移动动画,原因是因为这里的障碍物不需要给每个障碍物不同的移动速度,另一个原因是我们需要在障碍物碰到人的时候停止所有障碍物的移动,如果是给单一障碍物添加移动动画,显然是很难达到这个需求的。

  // 获取所有障碍物
  let obsDoms = this.$refs.obstructionWrap.children
  let obsList = Array.from(obsDoms)

  let nextItem = null

  // 给每个障碍物添加移动
  for (let index = 0; index < obsList.length; index++) {
    let item = obsList[index]
    if (item.offsetLeft < -item.offsetWidth) {
      this.$refs.obstructionWrap.removeChild(item)
    } else {
      item.style.left = item.offsetLeft - this.speed * 3 + 'px'
    }
  }
复制代码

玩法开发

玩法无非就是人物运动+障碍物运动+碰撞检测+躲避障碍物,人物运动和障碍物运动刚才都说过了,这里主要说的是碰撞检测和躲避障碍物,这个可以放到一起来说,因为只要没碰到那就是躲过去了。

这里的碰撞检测其实相当于抗疫的汤圆中的碰撞检测改版。其实准确的说,这里用的不是碰撞检测,是状态检测,因为这里的任务不能自由的移动,只有三种状态,跳跃、奔跑、下滑,所以我们只需要在合适的时候判断它处于什么状态就可以了,比如当上面的小恶魔过来的时候,判断人物是否处于下滑状态,如果不是,则判定为碰撞

首先我们需要找到和谁进行碰撞,因为同一时间障碍物可能有多个,我们给每一个障碍物添加碰撞检测,显然会浪费性能,所以我们需要找到距离离人物最近且没有完全经过人物的障碍物进行检测如下图所示

Bild.png

我们需要找到第一个自身没有完全经过人物的障碍物进行检测

已知人物宽度为120,人物处于屏幕水平中央

Daher ist der Abstand links vom zu beurteilenden Hindernis > die halbe Bildschirmbreite - die halbe Zeichenbreite - die Breite des Hindernisses selbst

let nextItem = null

// 给每个障碍物添加移动
  for (let index = 0; index < obsList.length; index++) {
    // 由于只需要找到第一个符合条件的障碍物即可,所以这里需要进行判断
    if (!nextItem) {
      // 找到人物右侧最近的障碍物,进行碰撞检测
      // 需要进行检测的障碍物需满足条件:距离屏幕左侧距离>人物距离左侧距离+自身宽度
      // 人物宽度为120,人物距离左侧距离为屏幕的一半减去自身的一半
      if (item.offsetLeft > (this.screenWidth / 2 - 60 - item.offsetWidth)) {
        nextItem = item
      }
    }
  }
复制代码

Für das gefundene Erkennungsziel müssen wir auch wissen, wann es erkannt werden soll, dh wenn der Abstand zwischen dem Hindernis und der linken Seite des Bildschirms < (halbe Bildschirmbreite + halbes Zeichen) ist, beginnt das Hindernis gerade mit dem Hindernis zu überlappen.Wenn nach der oberen und unteren Position des Hindernisses (die beim Erzeugen gespeichertwird) und dem Zustand des Charakters beurteilt wird, kann beurteilt werden, ob es eineKollision gibt.

// 碰撞检测
  // 当距离最近的障碍物处于检测区时,进行碰撞检测
  if (nextItem.offsetLeft < (this.screenWidth / 2 + 60)) {
    if (nextItem.state === 'top') {
      if (this.userStatus !== 'slide') {
        this.$emit('gameOver')
        // 游戏结束
        // alert('游戏结束')
        // this.gameOver()
        return
      }
    } else {
      if (this.userStatus !== 'jump') {
        // 游戏结束
        this.$emit('gameOver')
        //  alert('游戏结束')
        //  this.gameOver()
        return
      }
    }
  }
复制代码

Die Kollisionserkennung wird in der Hindernisbewegung ausgeführt, und der vollständige Code ist wie folgt.

// 整体障碍物移动
    obsMove () {
      // 获取所有障碍物
      let obsDoms = this.$refs.obstructionWrap.children
      let obsList = Array.from(obsDoms)

      let nextItem = null

      // 给每个障碍物添加移动
      for (let index = 0; index < obsList.length; index++) {
        let item = obsList[index]
        if (item.offsetLeft < -item.offsetWidth) {
          this.$refs.obstructionWrap.removeChild(item)
        } else {
          item.style.left = item.offsetLeft - this.speed * 3 + 'px'
        }

        // 由于只需要找到第一个符合条件的障碍物即可,所以这里需要进行判断
        if (!nextItem) {
          // 找到人物右侧最近的障碍物,进行碰撞检测
          // 需要进行检测的障碍物需满足条件:距离屏幕左侧距离>人物距离左侧距离+自身宽度
          // 人物宽度为120,人物距离左侧距离为屏幕的一半减去自身的一半
          if (item.offsetLeft > (this.screenWidth / 2 - 60 - item.offsetWidth)) {
            nextItem = item
          }
        }
      }

      // 碰撞检测
      // 当距离最近的障碍物处于检测区时,进行碰撞检测
      if (nextItem.offsetLeft < (this.screenWidth / 2 + 60)) {
        if (nextItem.state === 'top') {
          if (this.userStatus !== 'slide') {
            this.$emit('gameOver')
            // 游戏结束
            // alert('游戏结束')
            // this.gameOver()
            return
          }
        } else {
          if (this.userStatus !== 'jump') {
            // 游戏结束
            this.$emit('gameOver')
            //  alert('游戏结束')
            //  this.gameOver()
            return
          }
        }
      }

      // 找到最后一个障碍物,创建下一个障碍物
      let lastChild = obsList[obsList.length - 1]
      // console.log(lastChild.nextSpace);
      if (lastChild.offsetLeft < (this.screenWidth - lastChild.offsetWidth - lastChild.nextSpace)) {
        this.createObstruction()
      }
      this.obsMoveInterval = requestAnimationFrame(this.obsMove)
    },
复制代码

Dies ist die Einführung in das Minispiel. Ich hoffe, es gefällt allen, und setzen Sie eine Flagge für sich selbst. Wenn Sie es unter die ersten drei schaffen, kaufen Sie sich einen Mac. Wenn Sie es unter die ersten zehn schaffen, kaufen Sie sich ein iPad Wenn du es unter die Top Ten schaffst, kauf dir ein iPad, wenn du nicht gehst, kaufst du nichts.

Ich denke du magst

Origin juejin.im/post/7103423600660578341
Empfohlen
Rangfolge