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

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

游戏视频

众所周知,现在在网上想找一张纯粹的兔子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

相关推荐
有颜有货10 分钟前
PMC生产排产的4种算法,一次讲清
java·服务器·前端
小虎牙00713 分钟前
Android kotlin图片库Coil源码详解
android·前端
随风一样自由22 分钟前
【前端领域】前端开发核心应用场景与落地实践
前端·前端框架
an317421 小时前
弹窗数据流设计的两种高阶架构实践
前端·vue.js·架构
谢尔登1 小时前
【React】 状态管理方案
前端·react.js·前端框架
用户2136610035721 小时前
Vue商品详情与放大镜组件
前端·javascript
半个落月1 小时前
从Tapas小Demo理清localStorage、事件与this
前端·javascript
李明卫杭州1 小时前
Vue2 中 v-model 处理不同数据结构的技巧
前端·javascript·vue.js
李明卫杭州1 小时前
使用 computed 处理 v-model 复杂数据结构
前端·javascript·vue.js
2023自学中2 小时前
imx6ull 开发板, mame 模拟器,运行游戏 测试
linux·游戏·嵌入式·开发板