JavaScript小游戏实例:简单的键盘练习
键盘是一种常用的输入设备,灵活熟练地使用键盘进行输入是计算机用户需掌握的一门基本功。下面我们编写一个简单的键盘练习游戏。
1.刺破气泡交互式小动画
在编写简单的键盘练习游戏之前,先设计一个简单地刺破气泡交互式小动画。
在面板底部逐个上升一些气泡,用鼠标在某个气泡上单击,该气泡被刺破,刺破后的小气泡逐渐消散在面板中。交互式效果如图1所示。
图1 刺破气泡交互式动画
一个气泡可分为两个状态:(1)气泡从面板底部上升;(2)气泡被鼠标单击刺破成小气泡或气泡上升越过了面板顶部消散了。
为此抽象出两个对象类:Bubbles和miniBubbles。其中,Bubbles用于表示一个未被刺破的气泡,miniBubbles用于表示一个气泡刺破后得到的逐渐消散的小气泡。
Bubbles对象类定义6个属性:表示气泡圆心的坐标(x,y)、气泡的半径radius、上升时垂直方向的位移改变量ySpeed、气泡上升加速度gravity和气泡颜色color。
坐标属性值y的初始值取画布的高度,表示气泡从游戏面板底部开始上升,其余各属性的初始值采用随机数确定或直接指定。具体定义如下:
function Bubbles()
{
this.x = rand(30,canvas.width - 30);
this.y = canvas.height;
this.radius = rand(15, 30);
this.color ='rgba(255, 255, 255, 0.75)';
this.ySpeed= Math.random() * 2;
this.gravity = 0.01;
}
Bubbles对象类定义2个方法:绘制气泡的方法draw()、气泡上升时坐标改变方法update()。
miniBubbles对象类定义8个属性:表示小气泡圆心的坐标(x,y)、小气泡半径radius、散开时水平和垂直方向的位移改变量xSpeed和ySpeed、小气泡的填充color、小气泡的减速度gravity、小气泡的存活时间timeToLive。具体定义如下:
function miniBubbles(x,y,radius)
{
this.x = x;
this.y = y;
this.radius = radius;
this.color = 'rgba(255, 255, 255, 0.5)';
this.xSpeed=(Math.random() - 0.5) * 0.6;
this.ySpeed=(Math.random() - 1) * 0.5;
this.gravity = -0.03;
this.timeToLive = 100;
}
miniBubbles对象类定义2个方法:绘制小气泡的方法draw()、小气泡位置改变改变方法update()。小气泡每次改变位置后,timeToLive减1,当timeToLive值等于0时,从小气泡数组中删除该小气泡,表示该小气泡已消亡。
定义两个数组var bubbles = [];和 var minibubbles = [];分别存储未刺破的大气泡对象和大气泡刺破后散开的小气泡。
为画布添加鼠标按下事件监控canvas.addEventListener('mousedown', function(){ });,在事件处理函数中查找鼠标单击的气泡,然后将该气泡从bubbles数组中删除,向minibubbles数组中添加若干个散开的小气泡。
完整的HTML代码如下。
<html> <head> <title>刺破气泡小游戏</title> </head> <body> <canvas id="myCanvas"></canvas> <script> var canvas = document.getElementById('myCanvas'); var ctx = canvas.getContext('2d'); canvas.height = innerHeight; canvas.width = innerWidth; function rand(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } function Bubbles() { this.x = rand(30,canvas.width - 30); this.y = canvas.height; this.radius = rand(15, 30); this.color ='rgba(255, 255, 255, 0.75)'; this.ySpeed= Math.random() * 2; this.gravity = 0.01; } Bubbles.prototype.draw = function () { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); ctx.fillStyle = this.color; ctx.fill(); ctx.closePath(); } Bubbles.prototype.update = function () { this.y -= this.ySpeed; if (this.y - this.radius > 0) this.ySpeed += this.gravity; this.draw(); } function miniBubbles(x,y,radius) { this.x = x; this.y = y; this.radius = radius; this.color = 'rgba(255, 255, 255, 0.5)'; this.xSpeed=(Math.random() - 0.5) * 0.6; this.ySpeed=(Math.random() - 1) * 0.5; this.gravity = -0.03; this.timeToLive = 100; } miniBubbles.prototype.draw = function () { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); ctx.fillStyle = this.color; ctx.fill(); ctx.closePath(); } miniBubbles.prototype.update = function () { if (this.y - this.radius > 0) this.ySpeed += this.gravity; this.x += this.xSpeed; this.y += this.ySpeed; this.timeToLive --; this.draw(); } var backgroundGradient = ctx.createLinearGradient(0, 0, 0, canvas.height); backgroundGradient.addColorStop(0, '#009cff') backgroundGradient.addColorStop(1, '#007bff') var bubbles = []; var minibubbles = []; var timer = 0; var spawnRate = 70; function animate() { requestAnimationFrame(animate); ctx.fillStyle = backgroundGradient; ctx.fillRect(0, 0, canvas.width, canvas.height); for (var i=bubbles.length-1;i>=0;i--) { bubbles[i].update(); if (bubbles[i].y<0) { bubbles.splice(i, 1); } } for (var i=minibubbles.length-1;i>=0;i--) { minibubbles[i].update(); if (minibubbles[i].timeToLive == 0) { minibubbles.splice(i, 1); } } timer++; if (timer==spawnRate) { bubbles.push(new Bubbles()); timer=0; spawnRate = rand(50, 100); } } canvas.addEventListener('mousedown', function(){ var x = event.pageX - canvas.getBoundingClientRect().left; var y = event.pageY - canvas.getBoundingClientRect().top; // 查找被单击的气泡 for (var i=bubbles.length-1; i>=0; i--) { var bubble = bubbles[i]; var dist = Math.sqrt(Math.pow(bubble.x-x,2)+ Math.pow(bubble.y- y,2)); if (dist<= bubble.radius) { var mx = bubble.x; var my = bubble.y; var mr = rand(2,5); bubbles.splice(i, 1) for (var k = 0; k < bubble.radius/mr; k++) { minibubbles.push(new miniBubbles(mx,my, mr)); } return; } } }); animate(); </script> </body> </html>
2.简单的键盘练习小游戏
有了上面的基础,我们可以编写一个简单的键盘练习小游戏,大小写字母出现在游戏面板中,按键盘某个字母键后,对应的字母消失。游戏过程如图2所示。
图2 键盘练习小游戏
在Bubbles对象类中增加一个属性letter,表示气泡中显示的字母。
为Windows添加键盘按下keypress事件监听,处理键盘按键。
完整的HTML代码如下。
<html> <head> <title>简单键盘练习</title> </head> <body> <canvas id="myCanvas"></canvas> <script> var canvas = document.getElementById('myCanvas'); var ctx = canvas.getContext('2d'); canvas.height = innerHeight; canvas.width = innerWidth; var str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; var cnt1=0; var cnt2=0; var cnt3=0; function rand(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } function Bubbles() { this.x = rand(30,canvas.width - 30); this.y = canvas.height; this.radius = 20; this.color ='rgba(255, 255, 255, 0.75)'; this.ySpeed= Math.random() * 2; this.gravity = 0.01; this.letter=str.charAt(rand(0,str.length-1)); } Bubbles.prototype.draw = function () { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); ctx.fillStyle = this.color; ctx.fill(); ctx.closePath(); ctx.font = "Bold 20px Georgia"; ctx.fillStyle = "Black"; ctx.textAlign = 'center'; ctx.baseline = 'middle'; ctx.fillText(this.letter,this.x, this.y); } Bubbles.prototype.update = function () { this.y -= this.ySpeed; if (this.y - this.radius > 0) this.ySpeed += this.gravity; this.draw(); } function miniBubbles(x,y,radius) { this.x = x; this.y = y; this.radius = radius; this.color = 'rgba(255, 255, 255, 0.5)'; this.xSpeed=(Math.random() - 0.5) * 0.6; this.ySpeed=(Math.random() - 1) * 0.5; this.gravity = -0.03; this.timeToLive = 100; } miniBubbles.prototype.draw = function () { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); ctx.fillStyle = this.color; ctx.fill(); ctx.closePath(); } miniBubbles.prototype.update = function () { if (this.y - this.radius > 0) this.ySpeed += this.gravity; this.x += this.xSpeed; this.y += this.ySpeed; this.timeToLive --; this.draw(); } var backgroundGradient = ctx.createLinearGradient(0, 0, 0, canvas.height); backgroundGradient.addColorStop(0, '#009cff') backgroundGradient.addColorStop(1, '#007bff') var bubbles = []; var minibubbles = []; var timer = 0; var spawnRate = 70; function animate() { requestAnimationFrame(animate); ctx.fillStyle = backgroundGradient; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.font = "Bold 30px Georgia"; ctx.fillStyle = "Black"; ctx.textAlign = 'center'; ctx.baseline = 'middle'; var mess="正确按键次数:"+cnt1+" 无效按键次数:"+cnt2+" 丢失字母个数:"+cnt3; ctx.fillText(mess,canvas.width/2,35); for (var i=bubbles.length-1;i>=0;i--) { bubbles[i].update(); if (bubbles[i].y<30) { cnt3++; bubbles.splice(i, 1); } } for (var i=minibubbles.length-1;i>=0;i--) { minibubbles[i].update(); if (minibubbles[i].timeToLive == 0) { minibubbles.splice(i, 1); } } timer++; if (timer==spawnRate) { bubbles.push(new Bubbles()); timer=0; spawnRate = rand(50, 100); } } window.addEventListener('keypress', function(e){ var keyID = e.keyCode ? e.keyCode :e.which; for (var i=0;i<bubbles.length-1;i++) { var bubble = bubbles[i]; if (keyID== bubble.letter.charCodeAt(0)) { var mx = bubble.x; var my = bubble.y; var mr = rand(2,5); bubbles.splice(i, 1) cnt1++; for (var k = 0; k < bubble.radius/mr; k++) { minibubbles.push(new miniBubbles(mx,my, mr)); } return; } } cnt2++; },true); animate(); </script> </body> </html>