玉兔的中秋冒险:奔向嫦娥之旅

心血来潮想做的一款小游戏,出发点是结合兔子和小恐龙游戏,做一款兔子越过障碍的游戏。然而越做想的越多,慢慢的就成了下面的场景。

游戏视频

众所周知,现在在网上想找一张纯粹的兔子png或者gif图,要么要钱,要么要会员。对比而言,作为一名开发,找源码是最快的也是最好用的,比如纯粹的兔子就可以用代码实现。

这里就不说明具体的兔子实现,毕竟也是借用了掘友的兔子,注意看,小帅用纯css做了一个会跳的兔子,又白又胖,还很活泼,嗯,很适合作为游戏里的玉兔素材

兔子蹦蹦跳跳好像有点单调?是不是得有个前进的目标更好?兔子奔月?兔子奔嫦娥?感觉还是兔子奔嫦娥吧,毕竟兔子的归处是嫦娥的怀里。嫦娥的素材同样也很难找,但在掘金里直接搜会不会搜到?

嘿,还真给搜到了一位掘友提供的满足我会飞的嫦娥的图,嫦娥飞天动画简易版,上掘友的github工程里直接找就能找到我要的飞天嫦娥动态图了,要说不说,就非常合适

白天玉兔要看不见了,况且嫦娥要奔月,所以嫦娥就晚上飞吧?既然都晚上飞了,来个月亮?

既然是黑夜里,月亮都有了,那再来点星星?星星这种实现也不多说了,想来强大的掘友必然能提供好多实现的文,这里就补上这位掘友的文,上面提供嫦娥素材的掘友就提供了完整的星星实现图。

故事感直接拉满,嫦娥要奔月了,宠物玉兔贪玩没在嫦娥上天前回到她身边,玉兔追不上嫦娥,嫦娥舍不得玉兔,又下不来,只能遥遥相望。

故事感有了,那么接下来就要实现我们的初衷了,毕竟产品做着做着还是得保持初心(不是,我瞎说的)

做一款兔子跳跃游戏,那得做障碍吧?既然是中秋,那就越过月饼吧?随机生成一堆月饼做障碍?这个简单,想来都会,那么在地面随机生成月饼堆,其实也很简单。

首先在地面创建一块草坪,毕竟玉兔也是奔跑在黑夜的草坪上嘛。然后在草坪上随机生成月饼堆做障碍,就像下面这样。

html 复制代码
<div id="obstacle-container"></div>

// 样式
<style lang='scss'>
/* 草坪 */
#obstacle-container{
  position: absolute;
  background: linear-gradient(transparent, rgb(12, 121, 75) 60%) no-repeat;
  bottom: 0;
  left: 0;
  height: 100px;
  width: 100%;
}

/* 月饼堆的样式 */
.obstacle {
  position: absolute;
  // height: 20px;
  width: 50px;
  bottom: 0;
  right: 0;
  border-radius: 4px 4px 0 0;
  .mooncake{
    position: relative;
    width: 50px;
    height: 35px;
    background: url('https://raw.githubusercontent.com/xujiujiu/rabbit-change/main/src/images/mooncake2.png') no-repeat;
    background-size: cover;
    background-position: center;
      margin-bottom: -15px;
  }
  :last-child{
    margin-bottom: 0;
  }

}
</style>

在开始游戏后随机生成月饼堆,总体实现思路就是随机时间生成随机高度的月饼堆,然后根据月饼堆高度确认能放几个月饼

js 复制代码
    // 开始游戏时创建月饼堆
    startGame() {
      if (this.state !== 'playing') return;
      // 3s内随机生成月饼堆
      this.timer = setTimeout(() => {
        if (this.state !== 'playing') return;
        this.createObstacle()
        // 根据生成的间隔时间处理月饼堆之间的距离
        this.time = Math.random() * 2 + 1
        this.startGame()
      }, this.time * 1000)
    },
    // 创建月饼堆
    createObstacle() {
      const obstacle = document.createElement('div');
      obstacle.classList.add('obstacle');

      // 确定月饼堆的月饼个数
      const num = Math.ceil((Math.random() * (this.obstacleContainer.clientHeight - 20) + 20)/25)
      for (let i = 0; i < num; i++) {
        const mooncake = document.createElement('div');
        mooncake.classList.add('mooncake')
        mooncake.style.zIndex = num - i
        mooncake.id = 'mooncake' + i
        obstacle.appendChild(mooncake)
      }

      obstacle.style.right = 0; // 初始位置在草坪的右侧

      // 将月饼堆添加到容器中
      this.obstacleContainer.appendChild(obstacle);
      

      // 让障碍物向左移动
      this.moveObstacle(obstacle);
    },
    
    moveObstacle(obstacle) {
      const obstacleSpeed = 3; // 月饼堆移动速度,可以根据需要调整
      const moveInterval = () => {
        if (this.state !== 'playing') return
        const obstacleRight = parseInt(obstacle.style.right);
        // 移动月饼堆
        obstacle.style.right = obstacleRight + obstacleSpeed + 'px';
        // 如果月饼堆移出容器,则移除它并创建新的月饼堆
        if (obstacleRight > this.obstacleContainer.clientWidth) {
            obstacle.remove(); // 移除月饼堆
        } else {
          window.requestAnimationFrame(moveInterval)
        }
      };
      moveInterval()
    },

简单监听一下空格按键就可以开始游戏了

js 复制代码
    document.addEventListener("keydown", (event) => {
      if (event.code === "Space") {
          // 空格按键控制
          this.jump();
      }
    })

下面来实现跳跃,跳跃从根本上讲也比较简单,主要是通过兔子的上下跳动高度来控制跳跃动画,也就是显示上的跳和下落。

js 复制代码
    jump() {
      if (this.state !== 'playing') this.start();
      this.jumping = true;
      clearInterval(this.fallInterval)
      clearInterval(this.jumpInterval)
      this.jumpInterval = setInterval(() => {
          if (this.jumpHeight >= 280 || this.state !== 'playing') {
              clearInterval(this.jumpInterval);
              this.rabbit.style.transform = `rotate(0deg) translateY(-${this.jumpHeight}px)`;
              this.fall();
          } else { 
              this.jumpHeight += 10;
              this.rabbit.style.transform = `rotate(-10deg) translateY(-${this.jumpHeight}px)`;
          }
      }, 20);
    },
    fall() {
      this.fallInterval = setInterval(() => {
        if (this.jumpHeight === 0  || this.state !== 'playing') {
          if(!this.jumpHeight) {
            this.rabbit.style.transform = `rotate(0deg) translateY(0px)`
          }
          clearInterval(this.fallInterval);
          this.jumping = false;
        } else {
            this.rabbit.style.transform = `rotate(10deg) translateY(-${this.jumpHeight}px)`;
            this.jumpHeight -= 10
        }
      }, 20);
    },

玉兔本身也是有动画的,它的小脚脚在跳跃的过程中还是要漏出来的,所以在样式上对小脚脚单独做下处理即可

html 复制代码
  <div class="rabbit" :class="{'jumpRabbit': jumping}"></div>
  <style lang="scss">
  .jumpRabbit {
      &:after{
        animation: kick 1s infinite linear;
      }
    }
  @keyframes kick {
      40% {
          box-shadow: 0.4em 2em 0, 4.2em 1.75em 0 -0.2em, 4.4em 1.9em 0 -0.2em;
      }
  }
  
  </style>

以上,基本的跳跃功能就实现了。

最近在了解声纹频谱,emmm,想了一下,把声纹信息加入到游戏里是不是更有趣一点?想就做。

与原版跳跃不同,声控跳跃与输入声音有关,这里使用了 mediaDevices.getUserMedia 来获取麦克风声音,使用了 AudioContext 来解析声音,然后获取到音量来控制跳跃

js 复制代码
    // 声控模式
    voiceJump() {
      if (this.state === 'playing') return
      if (this.state !== 'playing') this.start();
      // 使用Web浏览器的音频API获取麦克风输入
      navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
        this.audioStream = stream
        if (this.isLisening) {
          return
        } else {
          this.isLisening = true
          this.audioContext = new AudioContext();
          const microphone = this.audioContext.createMediaStreamSource(stream);
          const analyser = this.audioContext.createAnalyser();

          microphone.connect(analyser);
          analyser.connect(this.audioContext.destination);

          analyser.fftSize = 256;
          const bufferLength = analyser.frequencyBinCount;
          const dataArray = new Uint8Array(bufferLength);

          analyser.getByteFrequencyData(dataArray);

          // 检测音量是否超过阈值,并触发跳跃
          const detectJump = () => {
            if (this.state !== 'playing') {
              clearInterval(voiceInterval)
            }
            analyser.getByteFrequencyData(dataArray);
            const volume = dataArray.reduce((acc, val) => acc + val, 0) / bufferLength;
            console.log(volume > 50 ? volume: '')
            if (volume > 50) { // 调整此阈值以匹配声音强度
              if (this.state !== 'playing') {
                this.start();
              }
              this.jump();
            }
          }

          let voiceInterval = setInterval(detectJump, 100);
        }
      })
      .catch(function (err) {
        console.error('获取麦克风权限失败: ', err);
      });
    },

做完给同床的兄弟看,原以为会收到

结果他眉头一皱: 奇奇怪怪

好嘛,那还是保留原版模式吧。但我想,总会有人跟我一样,喜欢这种奇奇怪怪的东西吧?

码上掘金地址:

项目地址:

rabbit-change

相关推荐
码小瑞2 分钟前
某些iphone手机录音获取流stream延迟问题 以及 录音一次第二次不录音问题
前端·javascript·vue.js
weixin_1894 分钟前
‌Vite和Webpack区别 及 优劣势
前端·webpack·vue·vite
半吊子伯爵5 分钟前
开发过程优化·自定义鼠标右键菜单
前端·javascript·自定义鼠标右键菜单
xcLeigh8 分钟前
HTML5实现好看的喜庆圣诞节网站源码
前端·html·html5
Tirzano26 分钟前
vue3 ts 简单动态表单 和表格
前端·javascript·vue.js
杰~JIE33 分钟前
前端工程化概述(初版)
前端·自动化·工程化·前端工程化·sop
程序员_三木34 分钟前
使用 Three.js 创建圣诞树场景
开发语言·前端·javascript·ecmascript·three
赵大仁1 小时前
深入理解 Vue 3 中的具名插槽
前端·javascript·vue.js·react.js·前端框架·ecmascript·html5
一雨方知深秋1 小时前
v-bind 操作 class(对象,数组),v-bind 操作 style
前端·css·vue.js·html·style·class·v-bind
安晴晚风2 小时前
从0开始在linux服务器上部署SpringBoot和Vue
linux·运维·前端·数据库·后端·运维开发