JavaScript实现躲避粒子小游戏的完整教程
本文详细介绍如何使用JavaScript开发一款躲避粒子小游戏。通过面向对象编程思维,实现红球拖拽交互、绿球随机生成与弹性碰撞、以及圆心距离碰撞检测。文章涵盖页面结构搭建、样式修饰及核心逻辑实现,帮助开发者掌握前端交互与游戏逻辑的结合技巧。
游戏概述与实现步骤
本文分享了一个使用JavaScript编写的躲避粒子小游戏的完整代码示例,供读者参考学习。
通过这类小型项目的实战演练,可以更有效地巩固和提升JavaScript编程能力。
游戏的核心规则是:玩家需要拖拽红球以躲避绿球的碰撞,且在拖拽过程中红球不能触碰容器内壁,最终目标是尽可能延长游戏持续时间。
以下是游戏运行的实际页面效果展示。

接下来详细解析游戏的具体实现过程。
正如古语所言“不积小流,无以成江海”,我们将页面效果的实现拆解为多个小步骤逐步完成:包括页面结构的构建、样式修饰、小绿球在容器顶部的随机位置生成、小绿球沿非水平非垂直方向的运动、小绿球碰撞容器内壁后的弹性反弹、多个小绿球的生成、红球的拖拽交互、红球的边界判断、红球与绿球的碰撞检测、“坚持n秒”的定时器实现以及游戏结束后的定时器清除。
页面结构与样式搭建
首先是页面结构的搭建阶段。
我们需要在HTML中创建文本元素、游戏容器以及红球元素;需要注意的是,小绿球并非静态存在,而是通过JavaScript动态创建生成的。

其次是样式修饰阶段。
为上述创建的结构元素设置相应的CSS样式,以美化界面并确定初始布局。

核心逻辑与交互实现
动态行为部分由JavaScript代码实现。
在编码过程中,我们采用面向对象的编程思维来组织代码结构。
第一步是实现小绿球在容器顶部的随机位置生成。
利用random函数生成[0,1)范围内的随机小数,再乘以小绿球在水平方向的运动范围,最后通过floor函数取整,得到整数作为小绿球初始时与容器左壁的距离。
第二步是实现小绿球沿非水平、非垂直方向的运动。
设置X方向的速度值和Y方向的速度值,与(1)相同,采用random函数乘以初始化XY方向的速度值就可以得到随机方向 创建定时器获取并更新小绿球与容器的左壁和上壁的距离以实现小球运动
第三步是实现小绿球碰撞容器内壁后的弹性运动效果。
通过判断小绿球的边界位置来实现:当碰撞左壁或右壁时,将X方向的速度值乘以-1以改变水平运动方向;当碰撞上壁或下壁时,将Y方向的速度值乘以-1以改变垂直运动方向。
第四步是实现多个小绿球的生成。
通过定时器不断调用构造函数来生成多个小绿球实例,并将这些实例存储在一个数组中以便统一管理。
第五步是实现红球的拖拽交互功能。
为红球绑定点击、拖动和松开事件。记录红球上一次在页面上的停留位置,与当前停留位置做差,得到红球在XY方向的移动距离;将该距离分别加上上一次停留时红球与容器左壁和上壁的距离,从而计算出当前红球与容器左壁和上壁的距离。通过不断循环更新上次停留位置和当前停留位置,实现红球的平滑拖拽移动。
第六步是实现红球的边界判断逻辑。
红球和绿球的移动范围均受限于容器的宽度高度减去自身球面的宽度和高度。当红球触碰边界时,触发页面重载以结束游戏。为了避免页面重载时因持续触碰边界而导致的问题,代码中增加了一个锁机制。
第七步是实现红球与绿球的碰撞检测。
通过计算两圆圆心之间的距离,判断该距离是否小于两圆半径之和,以此确定是否发生碰撞。
第八步是实现“坚持n秒”的计时器功能。
利用定时器进行计时,并实时更新span标签的innerHTML以显示游戏持续时间。
第九步是实现定时器的清除逻辑。
当游戏结束时,清除所有相关的定时器,释放资源。
以下是完整的代码实现展示。
/*
1.随机生成小绿球在顶部 位置随机
3.小绿球自己运动
4.弹性运动
2.生成多个
5.红球拖拽
6.红球边界判断
7.红球和绿球碰撞检测
8.定时器清除
9.坚持了多久
(但对象编程)
*/
var game = {
name:'游戏开始',
redBall:document.getElementsByClassName('red')[0],
RunTime:document.getElementsByTagName('span')[0],
num:0,
greenArr:[],
flag:true,
movePlus:{
outer:document.getElementsByClassName('outer')[0],
iWidth:document.getElementsByClassName('outer')[0].offsetWidth,
iHeight:document.getElementsByClassName('outer')[0].offsetHeight,
ispeedY:10,//小绿球的速度
ispeedX:10
},
init:function(){
console.log(this.name);
// console.log(this.movePlus.iHeight);
this.createBall(this.movePlus);
this.dragRedBall(this.movePlus);
this.runTime();
},
runTime:function(){
var self = this;
this.Timer = setInterval(function(){
self.num++;
self.RunTime.innerHTML = '坚持了' + self.num + '秒';
},1000);
},
createBall:function(obj){
var self = this;
var plus = obj;
function Green(plus){
this.ball = document.createElement('div');
this.ball.className = 'green';
plus.outer.appendChild(this.ball);
this.subWidth = Math.floor(Math.random()*(plus.iWidth - this.ball.offsetWidth));
this.ball.style.left = this.subWidth + 'px';
// this.subHeight = Math.floor(Math.random()*(plus.iHeight - this.ball.offsetHeight));
// this.ball.style.top = this.subHeight + 'px';
this.ispeedX = Math.floor(Math.random()*plus.ispeedX) + 1;
this.ispeedY = Math.floor(Math.random()*plus.ispeedY) + 1;
// 自定义属性
this.iWidth = plus.iWidth;
this.iHeight = plus.iHeight;
}
//先生出一个
var greenBall = new Green(plus);
this.greenArr.push(greenBall);
this.creatTimer = setInterval(function(){
var greenBall = new Green(plus);
self.greenArr.push(greenBall)
}, 2000);
this.moveBall();
},
moveBall:function(){
//创建定时器
var self = this;
// 保存window的this
this.goTimer = setInterval(function(){
for(var i = 0;i < self.greenArr.length;i ++){
self.crashCheck(self.greenArr[i]);
var newLeft = self.greenArr[i].ball.offsetLeft + self.greenArr[i].ispeedX ;
var newTop = self.greenArr[i].ball.offsetTop + self.greenArr[i].ispeedY ;
if(newLeft<0){
self.greenArr[i].ispeedX *= -1;
}
else if(newLeft > (self.greenArr[i].iWidth - self.greenArr[i].ball.offsetWidth)){
self.greenArr[i].ispeedX *= -1;
}
else if(newTop<0){
self.greenArr[i].ispeedY *= -1;
// self.greenArr[i].ispeedX *= -1;
}
else if(newTop > (self.greenArr[i].iHeight - self.greenArr[i].ball.offsetHeight)){
self.greenArr[i].ispeedY *= -1;
// self.greenArr[i].ispeedX *= -1;
}
// console.log((self.greenArr[i].iWidth - self.greenArr[i].ball.offsetWidth),(greenBall.iHeight - greenBall.ball.offsetHeight),greenBall.ispeedX,greenBall.ispeedY);
self.greenArr[i].ball.style.left = newLeft + 'px';
self.greenArr[i].ball.style.top = newTop + 'px';
}
},50)
},
dragRedBall:function(obj){
var self = this;
this.redBall.onmousedown = function(e){
var lastX = e.pageX,
lastY = e.pageY;
// self.redBall.style.left = lastX;
// self.redBall.style.top = lastY;
document.onmousemove = function(e){
var newX = e.pageX,
newY = e.pageY;
self.redBall.style.left = (newX - lastX) + self.redBall.offsetLeft + 'px';
self.redBall.style.top = (newY - lastY) + self.redBall.offsetTop + 'px';
// this.redBall.style.top = newY;
lastX = newX;
lastY = newY;
//判断边界
if(self.redBall.offsetLeft<0 && self.flag){
alert("坚持了" + self.num + '秒' + "\n" + "游戏结束");
self.flag = false;//加锁
self.clearTimer();
window.location.reload();
}else if(self.redBall.offsetLeft>(obj.iWidth-self.redBall.offsetWidth) && self.flag){
alert("坚持了" + self.num + '秒' + "\n" + "游戏结束");
self.flag = false;
self.clearTimer();
window.location.reload();//刷新页面 游戏重开
}else if(self.redBall.offsetTop<0 && self.flag){
alert("坚持了" + self.num + '秒' + "\n" + "游戏结束");
self.flag = false;
self.clearTimer();
window.location.reload();
}else if(self.redBall.offsetTop>(obj.iHeight-self.redBall.offsetHeight ) && self.flag){
alert("坚持了" + self.num + '秒' + "\n" + "游戏结束");
self.flag = false;
self.clearTimer();
window.location.reload();
}
}
this.onmouseup = function(){
document.onmousemove = null;
}
}
},
crashCheck:function(greenBall){
// var self = this;
//效率球的圆心
var greenX1 = greenBall.ball.offsetLeft + Math.floor(greenBall.ball.offsetWidth / 2),
greenY1 = greenBall.ball.offsetTop + Math.floor(greenBall.ball.offsetHeight / 2),
//小红求的圆心
redX1 = this.redBall.offsetLeft + Math.floor(this.redBall.offsetWidth / 2),
redY1 = this.redBall.offsetTop + Math.floor(this.redBall.offsetHeight / 2);
// console.log(greenX1,greenY1,redX1,redY1);
// debug成功
//x1 - x2,y1 - y2 的绝对值
var dx = Math.abs(greenX1 - redX1),
dy = Math.abs(greenY1 - redY1);
// console.log(dx,dy);
var dis = Math.floor(Math.sqrt(Math.pow(dx,2) + Math.pow(dy,2)));
// console.log(dis);
var R = greenBall.ball.offsetWidth/2 + this.redBall.offsetWidth/2;
if(dis < R && this.flag){
alert("坚持了" + this.num + '秒' + "\n" + "游戏结束");
this.flag = false;
this.clearTimer();
window.location.reload();
}
},
clearTimer:function(){
clearInterval(this.goTimer);
clearInterval(this.creatTimer);
clearInterval(this.Timer);
}
}
game.init();//入口函数
欢迎各位技术大佬对代码提出指正意见。
以上就是本文的全部内容,希望能为各位的学习提供帮助,也欢迎大家多多支持本站。
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28
