心血来潮想做的一款小游戏,出发点是结合兔子和小恐龙游戏,做一款兔子越过障碍的游戏。然而越做想的越多,慢慢的就成了下面的场景。
众所周知,现在在网上想找一张纯粹的兔子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);
});
},
做完给同床的兄弟看,原以为会收到
结果他眉头一皱: 奇奇怪怪
好嘛,那还是保留原版模式吧。但我想,总会有人跟我一样,喜欢这种奇奇怪怪的东西吧?
码上掘金地址:
项目地址: