疫情一波接着一波,学校都不敢放我们出去了,一直关在学校,现在学校特别注重心理健康,不要因为封校出现心理问题。希望疫情早日结束,见到想见到人,去想玩的地方玩,过节回家团圆,早日回到正轨!
游戏在线体验有背景音乐和音效,建议各位去github上体验!
游戏在线体验地址:github在线[2]
游戏不需要引入其它js库。
游戏使用canvas制作。
去除内外边距,网页超出隐藏。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
复制代码
<button class="startgame">开 始 游 戏</button>
复制代码
给开始游戏和重新开始按钮添加样式
.startgame,
.resgame {
width: 200px;
height: 60px;
background-color: black;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
outline: none;
border: none;
border-radius: 20px;
color: white;
font-size: 25px;
cursor: pointer;
transition: all .2s linear;
}
.resgame {
margin-top: 50px;
}
复制代码
image.png
<div class="fs"><span>分数:</span><span class="score">0</span></div>
复制代码
给分数添加样式
.fs {
font-size: 30px;
color: white;
position: fixed;
}
复制代码
image.png
敌人碰到玩家弹出gameover的div。
<div class="gameover">
<h1>分数:<span class="Gscore">0</span></h1>
<button class="resgame">重 新 开 始</button>
</div>
复制代码
重新开始的按钮和开始游戏的按钮样式一样,但是添加了外边距,这样看起来更美观。
.resgame {
margin-top: 50px;
}
复制代码
image.png
先写好,到时候用JavaScript重复调用开启,这样就能保证背景音乐的循环播放。
<audio src="bgsound.mp3"></audio>
复制代码
把浏览器右键打开菜单关闭。
document.oncontextmenu = function () {
return false;
};
复制代码
射击的时候,会选中文字,导致失误,所以这里添加文字无法选中。
document.addEventListener("selectstart", function (e) {
e.preventDefault();
});
复制代码
获取开始游戏按钮。点击之后禁用按钮,防止重复点击。
let btn1 = document.querySelector('.startgame');```
btn1.disabled = true;
复制代码
开始按钮点击后,获取cnavas,设置canvas宽高与浏览器可视宽高一样。
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
复制代码
传入x,y,圆的大小,圆的颜色,移动速度。
使用canvas绘制圆形,写好移动ai函数,每次执行ai都会移动位置并且执行绘制函数,变化位置。
class Item {
constructor(x, y, radius, color, velocity) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.velocity = velocity;
};
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = this.color;
ctx.fill();
};
ai() {
this.draw();
this.x = this.x + this.velocity.x;
this.y = this.y + this.velocity.y;
};
};
复制代码
玩家、敌人、子弹继承通用类。
玩家类自动执行绘制函数,因为玩家点击开始游戏就会出现。
// 玩家
class Player extends Item {
constructor(x, y, radius, color) {
super(x, y, radius, color);
this.draw();
};
};
// 敌人
class Ele extends Item {
constructor(x, y, radius, color, velocity) {
super(x, y, radius, color, velocity);
};
};
// 子弹
class Bullet extends Item {
constructor(x, y, radius, color, velocity) {
super(x, y, radius, color, velocity);
};
};
复制代码
粒子效果函数,射中敌人会出现粒子效果。粒子效果函数名与通用类函数名字一致,会优先执行粒子效果类的函数。
image.png
class Particle extends Item {
constructor(x, y, radius, color, velocity, friction) {
super(x, y, radius, color, velocity);
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.velocity = velocity;
this.alpha = 1;
this.friction = friction;
};
draw() {
ctx.save();
ctx.globalAlpha = this.alpha;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.restore();
};
ai() {
this.draw();
this.velocity.x *= this.friction;
this.velocity.y *= this.friction;
this.x = this.x + this.velocity.x;
this.y = this.y + this.velocity.y;
this.alpha -= 0.01;
};
};
复制代码
定义三个数组:子弹数组、敌人数组、粒子数组。
添加到数组为了更好管理、更好统一操作、性能会更好。
let bulletArray = [];
let eleArray = [];
let particles = [];
let oScore = document.querySelector('.score');
let oGameOver = document.querySelector('.gameover');
let Gscore = document.querySelector('.Gscore');
let score = 0;
let flag = true;
复制代码
函数使用requestAnimationFrame重复调用。
填充背景和创建玩家,填充背景透明度为0.1是为了慢慢的填充,这样可以看到运动轨迹。
后面就是遍历三个数组,把遍历出来的元素进行判断或修改。
function animate() {
if (flag) requestAnimationFrame(animate);
// 填充背景颜色
ctx.fillStyle = 'rgba(0, 0, 0, .1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 创建玩家
let player = new Player(canvas.width / 2, canvas.height / 2, 20, 'white');
// 遍历子弹
bulletArray.map((item, index) => {
item.ai();
// 删除
if (item.x <= 0 - item.radius || item.y <= 0 - item.radius || item.x >= canvas.width || item.y >= canvas.height) {
bulletArray.splice(index, 1);
}
// 判断
eleArray.map((ele, i) => {
let dist = Math.hypot(ele.x - item.x, ele.y - item.y);
// 添加粒子效果
if (dist - item.radius - ele.radius < 1) {
for (let i = 0; i < item.radius * 8; i++) {
particles.push(new Particle(ele.x, ele.y, Math.random() * 4, ele.color, {
x: (Math.random() - 0.5) * (Math.random() * 8),
y: (Math.random() - 0.5) * (Math.random() * 8),
}, 0.97));
}
let a = new Audio();
a.src = 'a.mp3';
a.play();
// 打中后减小
if (ele.radius - 16 > 10) {
ele.radius -= 16;
bulletArray.splice(index, 1);
score += 100;
oScore.innerHTML = score;
} else {
bulletArray.splice(index, 1);
eleArray.splice(i, 1);
score += 250;
oScore.innerHTML = score;
}
}
});
});
// 遍历敌人
eleArray.map((item) => {
item.ai();
// 判断玩家和敌人碰撞 游戏结束
let dist = Math.hypot(player.x - item.x, player.y - item.y);
if (dist - item.radius - player.radius < 1) {
flag = false;
document.querySelector("audio").pause();
Gscore.innerHTML = score;
oGameOver.style.display = 'block';
}
});
particles.map((item, index) => {
if (item.alpha <= 0) {
particles.splice(index, 1);
} else {
item.ai();
}
});
}
animate();
复制代码
鼠标点击之后,创建一个音乐器,播放设计的音效,使用Math获取点击位置,然后转换成角度发射出去,添加到数组之中。
window.addEventListener('mousedown', (e) => {
if (!flag) return;
let b = new Audio();
b.src = 'b.mp3';
b.play();
// 返回原点到点的线段与x轴正方向之间的平面角度
let angle = Math.atan2(e.clientY - canvas.height / 2, e.clientX - canvas.width / 2);
// 把角度转换
let velocity = {
x: Math.cos(angle) * 5,
y: Math.sin(angle) * 5
};
// 添加到数组里
bulletArray.push(new Bullet(canvas.width / 2, canvas.height / 2, 5, 'white', velocity));
});
复制代码
创建敌人的地方重复执行背景音乐的执行操作
QQ录屏20220912210329.gif
1.5秒创建一个敌人,使用随机数制作随机位置,用Math.atan2转换成位置数据,使用Math.cos和Math.sin把位置数据调成x,y。
最后把创建出来的敌人添加到数组当中。
setInterval(() => {
if (!flag) return;
document.querySelector('audio').play();
// 随机大小
let radius = Math.random() * (35 - 15) + 15;
// 随机颜色
let color = `hsl(${Math.random() * 360}, 50%, 50%)`;
let x, y;
// 随机位置
if (Math.random() < 0.5) {
x = Math.random() < 0.5 ? 0 - radius : canvas.width + radius;
y = Math.random() * canvas.height + radius;
} else {
x = Math.random() * canvas.width + radius;
y = Math.random() < 0.5 ? 0 - radius : canvas.height + radius;
}
let angle = Math.atan2(canvas.height / 2 - y, canvas.width / 2 - x);
let velocity = {
x: Math.cos(angle) * 2.5,
y: Math.sin(angle) * 2.5,
};
eleArray.push(new Ele(x, y, radius, color, velocity));
}, 1500);
});
复制代码
本次把所有需要移动,执行的元素放在数组当中,能更好的控制和执行还能节约性能。
我是学生,很多不足,请谅解!
国庆还有18天,回家倒计时18天,已经200多天没回家了,好想爸妈呢。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8