En verano, no solo hay sandías y aires acondicionados, sino también deportes y sudoración, "¡Corre! El juego Programmer" js está de moda, vengan y corran juntos

Participo en el "Concurso creativo de principios de verano" para obtener más información, consulte: Concurso creativo de principios de verano

Dirección de la experiencia en línea: summer.pkec.net/dirección
fuente : gitee.com/ihope_top/j…

prefacio

Antes de que te des cuenta, el verano está aquí de nuevo. ¿Qué piensas cuando se trata de verano? ¿Aire acondicionado, sandía y helado? Pero hay más que estos en verano, hay deporte, sudar y mostrar el cuerpo ¿Recuerdas la reunión deportiva de cada verano? ¿Recuerdas el fresco correr todos los días ese verano? Hoy usaré js para traerte un minijuego de parkour: "¡Corre! Programador", espero que les guste

introducción del juego

funcion especial

La función especial implica el soporte de la interfaz de back-end, por lo que solo tiene efecto durante el evento. Puede volverse inválido después del evento de escritura, pero aún se puede acceder al juego normalmente. Si encuentra ataques maliciosos u otros factores incontrolables, esta función puede fallar de antemano.

Carga de calificaciones

imagen.png

Así es, esta vez hemos abierto los extremos delantero y trasero para admitir la carga de puntajes. Después de que termine el juego, habrá una opción para cargar puntajes. Solo necesita completar su propia ID de Nuggets para cargar este puntaje. Cargar los ID de Nuggets El objetivo principal es evitar que los usuarios introduzcan al azar apodos ilegales, por lo que aquí es necesario obtener el apodo de Nuggets del usuario de acuerdo con el ID de Nuggets del usuario.

El mini juego es solo para entretenimiento, por lo que no existe una medida anti-trampa, todos no deben hacer trampa, pero para evitar un mal funcionamiento, todavía está restringido que los mismos resultados no se pueden cargar repetidamente, es decir, corrieron 500 metros dos veces. , y la segunda vez Los resultados no se pueden cargar. Si corre 500 metros por primera vez y corre 100 metros por segunda vez, puede cargar ambos resultados.

Cómo obtener la identificación de Nuggets:

imagen.png

Ingrese a la página de inicio personal, el número detrás de la barra de direcciones es su identificación

Todo el personal corre

imagen.png

Una vez finalizado el juego, las puntuaciones cargadas por los usuarios se almacenarán en la base de datos y la página de inicio mostrará la suma de las puntuaciones cargadas por todos los usuarios.

Tabla de clasificación

imagen.png

La página de inicio mostrará la entrada de la tabla de clasificación, y la tabla de clasificación mostrará la clasificación de proceso único de todos los usuarios. Un usuario puede estar en la lista varias veces. La puntuación más alta es de 800 metros, luego el primer y segundo lugar son Zhang San.

Reglas Introducción

imagen.png

Después de comenzar el juego, el personaje correrá automáticamente hacia adelante y el pequeño diablo se encontrará en la imagen de carrera. El usuario debe evitar al pequeño diablo y continuar corriendo hacia adelante. Si se encuentra con el pequeño diablo, el juego termina.

Método de operación:

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

imagen.png

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

imagen.png

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

游戏开发

场景开发

白云开发

imagen.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)
},
复制代码

下面是代码块版本

地面开发

terreno.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()
    },
复制代码

人物开发

imagen.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(全局速度变量)来随机生成的,最少一个,最多四个,数量越多,当然难度也就越大。

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

imagen.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'
    }
  }
复制代码

玩法开发

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

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

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

imagen.png

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

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

Por lo tanto, la distancia a la izquierda del obstáculo a juzgar es > la mitad del ancho de la pantalla - la mitad del ancho del personaje - el ancho del propio obstáculo

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
      }
    }
  }
复制代码

Para el objetivo de detección encontrado, también necesitamos saber cuándo detectarlo, es decir, cuando la distancia entre el obstáculo y el lado izquierdo de la pantalla < (la mitad del ancho de la pantalla + la mitad del carácter), el obstáculo apenas comienza para superponerse con el obstáculo Al juzgar según la posición superior e inferior del obstáculo (que se guarda al generar) y el estado del personaje, se puede juzgar si hay una colisión.

// 碰撞检测
  // 当距离最近的障碍物处于检测区时,进行碰撞检测
  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
      }
    }
  }
复制代码

La detección de colisión se realiza en el movimiento de obstáculos, y el código completo es el siguiente.

// 整体障碍物移动
    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)
    },
复制代码

Esta es la introducción al minijuego. Espero que a todos les guste, y establece una bandera para ti. Si puedes entrar entre los tres primeros, cómprate una Mac. Si puedes entrar entre los diez primeros, cómprate un iPad. Si puedes entrar en el top ten, cómprate un iPad, si no vas, no comprarás nada.

Supongo que te gusta

Origin juejin.im/post/7103423600660578341
Recomendado
Clasificación