目录一、游戏介绍1. 玩法指南2.运行效果二、用到的js库三、游戏开发思路四、核心功能点1. 绘制棋盘2. 小偷的移动逻辑3. 警察的移动逻辑4. 胜利逻辑五、待实现的创意玩法六、代
之前一直用原生canvas写小游戏,很多逻辑都需要自己一点点封装,最近看了下p5.js,哇哦,好的很嘞!就用它开发了一款名为“警察抓小偷”的游戏。这是一款非常有趣的游戏,玩家扮演警察追捕小偷,通过大声喊话来控制警察的速度,抓住小偷。游戏中,小偷会在棋盘上移动,而警察则会向小偷靠近。如果警察追上小偷,就算警察胜利;如果小偷逃脱到棋盘的最下方,那么小偷胜利。老少皆宜哦😀。
点击开始游戏按钮小偷会在棋盘上移动,游戏会调用麦克风,玩家通过大声狂呼乱叫,此时警察则会向小偷靠近,你喊得越大声,警察移动速度越快,当警察追上小偷,就算警察胜利;如果小偷跑到的右下方出口处逃脱,那么小偷胜利。
游戏体验链接:警察抓小偷游戏 (forrestyuan.GitHub.io)由于gitHub page的缘故,页面加载可能会慢一点,大家耐心等待哈!记得要同意麦克风的调用权限申请哦。
代码仓库地址:forrestyuan/catchThief警察抓小偷(github.com)
p5.js
库基于 Processing,是一个 javascript 库,可以轻松地创建交互式图形和动态图像。它的核心是使用 HTML5 Canvas 元素来创建图形。p5.js 提供了简单易用的 api,让编写和理解代码变得更加容易。p5.sound.min.js
是 p5.js 库的音频处理插件,它基于 WEB Audio API,可以加载、播放和操纵音频文件。Web Audio API 是 html5 中的一项标准,提供了低延迟、高品质的音频处理能力,可以通过 JavaScript 对音频进行混合、变形、过滤等操作。核心功能点主要包括绘制棋盘、小偷的移动逻辑、警察的移动逻辑以及胜利逻辑函数。大家着重理解这些开发思路。
在 p5.js 的 draw()
函数中,调用rect()
方法来绘制20X15的游戏棋盘,image()
方法来绘制小偷、警察元素,并实时绘制小偷和警察的新位置和动态更新页面上玩家的麦克风音量刻度条,通过whoWin
变量检查是否胜利,以及执行相应的文案绘制效果。
//循环体
function draw() {
//.....
// 绘制格子盘
for (let i = 1; i < col + 1; i++) {
for (let j = 0; j < row; j++) {
fill('pink')
rect(i * gridW, j * gridH, gridW, gridH - 1); // 将高度改为gridH-1
}
}
// 绘制小偷和警察
image(thiefImg, thiefXPos, thiefYPos - 5, gridW, gridH + 5);
image(policeImg, policeXPos, policeYPos - 20, gridW, gridH + 25);
if (whoWin === 'police') {
fill(255, 0, 0);
textSize(30)
text("恭喜你抓住小偷啦,警察胜利喽!", col * gridW / 6, 60);
}
if (whoWin === 'thief') {
fill(255, 0, 0);
textSize(30)
text("小样,还想抓住我,哈哈哈", col * gridW / 5, 60);
}
//.....
// 显示当前玩家数据
thiefVoiceValnode.innerText = thiefProgressNode.value = policeDir / policeStepLen;
policeValNode.innerText = policeProgressNode.value = Math.abs(policeDir);
}
小偷的移动逻辑是每隔一定时间在 X 轴方向上以固定的步长移动,步长由stepLen
常量控制,在到达棋盘边界if (thiefXPos >= (col + 1) * gridW)
改变运动方向dir = -stepLen
,并在 Y 轴方向上增加一个棋盘格子高thiefYPos += gridH
。小偷的移动步长可以通过调整 stepLen
的值来改变,值越大,小偷移动的越快。
警察的移动逻辑比较复杂,需要根据下面代码中getVoiceSize()
获取的音量值来动态调整警察的速度policeDir = Math.floor(voiceSize * 100) / 10 * policeStepLen
。通过 setInterval()
函数来定时改变警察的横坐标的位置policeXPos += policeDir;
,并根据警察的位置和小偷的位置来判断是否胜利。
//获取音量大小
async function getVoiceSize() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true } });// echoCancellation: true以减少回音和噪声的影响
const audiocontext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
const processor = audioContext.createScriptProcessor(1024, 1, 1);
source.connect(processor);
processor.connect(audioContext.destination);
processor.onaudioprocess = function (event) {
const buffer = event.inputBuffer.getChannelData(0);
const sum = buffer.reduce((acc, val) => acc + val ** 2, 0);//计算音量大小
voiceSize = Math.sqrt(sum / buffer.length);
};
} catch (err) {
console.log('getUserMedia error: ', err);
}
}
在开始游戏点击事件的回调函数中,判断游戏胜利的条件并播放胜利音乐。在 draw()
函数中通过whoWin
来判断游戏胜利的条件绘制页面上的文案。当游戏胜利时,页面上绘制对应文案并播放胜利音乐。
//开始游戏事件
startBtn.addEventListener('click', () => {
//....省略其它逻辑代码
//贼胜利逻辑
if (thiefYPos >= row * gridH) {
winLogicFunc('thief')
}
//警察胜利逻辑
if (policeYPos === thiefYPos && (policeStepLen > 0 && policeXPos >= thiefXPos || policeStepLen < 0 && Math.abs(policeXPos) <= thiefXPos)) {
winLogicFunc('police')
}
}, moveTime);
});
为了增加游戏的趣味性,可以考虑增加更多的道具和障碍物。
除了增加道具和障碍物,我们还可以增加多种不同的游戏模式,例如多人模式、计时模式等等。
还有可以美化页面还有音效、动效等,让游戏更加丰富多彩,让玩家有更多的选择和挑战。
为了避免文章过于臃肿,这里只放js的实现代码,对于页面布局代码可以到我的github上去阅读代码,代码里都有丰富的注释,相信大家可以快速读懂代码,如果我用原生canvas开发,估计工作量会大很多。不过,代码在封装方面还有待提高,除此之外,玩法也还有更多创意有待实现。
各位如果更好玩的实现玩法,不妨把代码fork
过去实现,记得评论区告诉我,我期待你的实现。
const gridW = 30; //棋盘格子宽
const gridH = 30; //棋盘格子高
const row = 15; //棋盘行数
const col = 20; //棋盘列数
let sone = null;//胜利音乐
let whoWin = ''; //谁胜利可选值'police|thief'
let interval = null;//循环句柄
const moveTime = 16;//循环时间
let thiefImg = null;//小偷图片
let thiefXPos = 40;//小偷横坐标
let thiefYPos = 0;//小偷纵坐标
const stepLen = 2; //小偷移动步长(速度)
let policeImg = null;//警察图片
let policeXPos = 0;//警察横坐标
let policeYPos = 0;//警察纵坐标
let policeStepLen = 2;//警察移动步长基数
let policeDir = 0; //警察动态移动步长(速度)
let voiceSize = 0;
//获取DOM节点
const thiefProgressNode = document.getElementById('progress');
const thiefVoiceValNode = document.getElementById('voiceVal');
const policeProgressNode = document.getElementById('policeProgress');
const policeValNode = document.getElementById('policeVal');
const startBtn = document.getElementById('start-btn');
const restartBtn = document.getElementById('restart-btn');
const thiefWinGifNode = document.getElementById('thiefGif'); //小偷胜利动图
const policeWinGifNode = document.getElementById('policeGif'); //警察胜利动图
//预加载图片
function preload() {
song = loadSound('./resources/胜利配乐.mp3');
thiefImg = loadImage('./resources/thief.png');
policeImg = loadImage('./resources/police.png');
}
//初次调用
function setup() {
createCanvas(col * gridW + 60, row * gridH).parent('gameCanvas')
document.querySelector('.page').style.CSSText = `width:${(col + 2) * gridW}px`
}
//循环体
function draw() {
noStroke();
background('#fff');
// 绘制格子盘
for (let i = 1; i < col + 1; i++) {
for (let j = 0; j < row; j++) {
fill('pink')
rect(i * gridW, j * gridH, gridW, gridH - 1); // 将高度改为gridH-1
}
}
// 绘制小偷和警察
image(thiefImg, thiefXPos, thiefYPos - 5, gridW, gridH + 5);
image(policeImg, policeXPos, policeYPos - 20, gridW, gridH + 25);
if (whoWin === 'police') {
fill(255, 0, 0);
textSize(30)
text("恭喜你抓住小偷啦,警察胜利喽!", col * gridW / 6, 60);
}
if (whoWin === 'thief') {
fill(255, 0, 0);
textSize(30)
text("小样,还想抓住我,哈哈哈", col * gridW / 5, 60);
}
//绘制通道
for (let i = 1; i < row; i++) {
fill('pink');
(i % 2 === 0) ? rect((col + 1) * gridW, gridH * i - 1, gridW, 3) : rect(0, gridH * i - 1, gridW, 3);
}
//绘制出口文字
fill(0, 0, 0);
textSize(12)
text("出口", (col + 1) * gridW + 2, row * gridH - 10);
// 显示当前玩家数据
thiefVoiceValNode.innerText = thiefProgressNode.value = policeDir / policeStepLen;
policeValNode.innerText = policeProgressNode.value = Math.abs(policeDir);
}
//获取音量大小
async function getVoiceSize() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true } });// echoCancellation: true以减少回音和噪声的影响
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
const processor = audioContext.createScriptProcessor(1024, 1, 1);
source.connect(processor);
processor.connect(audioContext.destination);
processor.onaudioprocess = function (event) {
const buffer = event.inputBuffer.getChannelData(0);
const sum = buffer.reduce((acc, val) => acc + val ** 2, 0);//计算音量大小
voiceSize = Math.sqrt(sum / buffer.length);
};
} catch (err) {
console.log('getUserMedia error: ', err);
}
}
//胜利逻辑函数
function winLogicFunc(who) {
if (who == 'thief' || who == 'police') {
clearInterval(interval);
song.play();
whoWin = who
noLoop();
(who === 'thief' ? thiefWinGifNode : policeWinGifNode).style.visibility = 'visible';
}
}
//开始游戏事件
startBtn.addEventListener('click', () => {
getVoiceSize()
startBtn.disabled = true;
let dir = stepLen;
// policeDir = Math.floor(voiceSize * 100) / 10 * policeStepLen;
//小偷、警察运动循环体
interval = setInterval(() => {
policeDir = Math.floor(voiceSize * 100) / 10 * policeStepLen
thiefXPos += dir;
policeXPos += policeDir;
//小偷的改变方向逻辑
if (thiefXPos >= (col + 1) * gridW) {
thiefXPos = (col + 1) * gridW - stepLen
dir = -stepLen
thiefYPos += gridH
} else if (thiefXPos < 0) {
thiefXPos = -stepLen
dir = stepLen
thiefYPos += gridH
}
//警察的改变方向逻辑
if (policeXPos >= (col + 1) * gridW) {
policeXPos = (col + 1) * gridW - stepLen
policeStepLen = -policeStepLen
policeYPos += gridH
} else if (policeXPos < 0) {
policeXPos = 1
policeStepLen = -policeStepLen
policeYPos += gridH
}
//贼胜利逻辑
if (thiefYPos >= row * gridH) {
winLogicFunc('thief')
}
//警察胜利逻辑
if (policeYPos === thiefYPos && (policeStepLen > 0 && policeXPos >= thiefXPos || policeStepLen < 0 && Math.abs(policeXPos) <= thiefXPos)) {
winLogicFunc('police')
}
}, moveTime);
});
//重新开始游戏
restartBtn.addEventListener('click', () => {
thiefWinGifNode.style.visibility = 'hidden';
policeWinGifNode.style.visibility = 'hidden';
startBtn.disabled = false;
// 重新开始游戏
clearInterval(interval);
thiefXPos = 40;
thiefYPos = 0;
policeXPos = 0;
policeYPos = 0;
whoWin = '';
loop();
});
页面的布局结构相对简单,大家可以到github页面上去阅读源码。页面布局每个人的审美不同,实现的各有千秋,大家着重理js核心代码,页面实现那是三下五除二的。 代码地址:forrestyuan/catchThief警察抓小偷(github.com)
通过这个小游戏的开发,我们可以了解到如何使用 p5.js 库来开发交互式游戏,包括绘制游戏界面和元素、实现游戏的逻辑和交互、预加载音乐和图片资源的能力和便捷性。我发现开发用原生canvas开发突然不香了😂,好吧,我会用原生canvas开发更锻炼人这个点来说服自己。
以上就是p5.js实现声音控制警察抓小偷游戏示例解析的详细内容,更多关于p5.js 警察抓小偷游戏的资料请关注编程网其它相关文章!
--结束END--
本文标题: p5.js实现声音控制警察抓小偷游戏示例解析
本文链接: https://www.lsjlt.com/news/208451.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-01-12
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0