游戏小视频
<html> <body> <canvas id="myCanvas" width="600" height="500" style='position:absolute; left:0;'></canvas> <script> let canvas = document.getElementById("myCanvas"); let context = canvas.getContext("2d"); context.font = 'bold 30px sans-serif'; context.lineWidth = 4; let image = new Image(); image.src = "sprite.png"; const maxStones = 6, size = 40; let angle = -Math.PI / 2; let x, y, frame, currentStone, mode, run, offset, stickLength, stones; function reset() { currentStone = 0; x = 100; y = 360; frame = 0; stones = []; stickLength = 0; offset = 0; run = 0; for (let i = 0; i < maxStones; i++) { stones[i] = { x: i * 300 + Math.floor(Math.random() * 80), width: 50 + Math.floor(Math.random() * 50) }; } stones[0].x = 80; mode = 'wait'; } function animate() { context.clearRect(0, 0, canvas.width, canvas.height); context.fillText('Distance remaining: ' + (maxStones - currentStone - 1), 250, 100); stones.forEach((stone)=>{ context.fillRect(stone.x - offset, 398, stone.width, 600); } ); context.drawImage(image, Math.floor(frame) * size, 0, size, size, x + size / 2, y, size, size); switch (mode) { case 'pointerdown': stickLength++; break; case 'stickFall': angle = angle + Math.PI / 64; if (angle >= 0) mode = 'run'; break; case 'run': offset++; run++; frame = frame + .5; if (frame == 20) frame = 0; if (stickLength == run) { mode = 'wait'; angle = -Math.PI / 2; stickLength = 0; run = 0; let gameOver = true; stones.forEach((stone,index)=>{ if (offset + x + size > stone.x && offset + x < stone.x + stone.width - size) { gameOver = false; currentStone = Math.max(currentStone, index); if (currentStone == maxStones - 1) { mode = 'gameOver'; frame = 21; } } } ); if (gameOver) { mode = 'gameOver'; frame = 20; } } break; case 'gameOver': if (currentStone < maxStones - 1) { y++; context.fillText('Game over. Click to restart', 20, 60); } else context.fillText('You win! Click to restart', 20, 60); } let x2 = x + (stickLength - run) * Math.cos(angle); let y2 = y + (stickLength - run) * Math.sin(angle); context.beginPath(); context.moveTo(x + size - run, y + size); context.lineTo(x2 + size, y2 + size); context.stroke(); window.requestAnimationFrame(animate); } window.onpointerdown = function() { switch (mode) { case 'wait': mode = 'pointerdown'; break; case 'gameOver': mode = 'wait'; reset(); } }; window.onpointerup = function() { if (mode == 'pointerdown') mode = 'stickFall'; }; reset(); animate(); </script> </body> </html>
sprite.png图片
图片下载地址:sprite.png
[1-8] 行设置了 HTML5 Canvas 和包含 spritesheet的 2d 上下文
[9-10] 不可见图像(运行序列的 20 帧,1 次跌倒,1 次庆祝)
[11-13] 声明游戏变量和常量:
[11] 最大石头数(=要覆盖的距离),精灵的大小(40x40 像素)
[12] 棒的初始角度 - 它直线上升
[13] x - x 冠军的坐标
y - y摇杆
框架顶部坐标- 当前动画帧
currentStone - 已经达到了多少石头
mode - 游戏模式(wait/pointerdown/stickFall/gameOver)
run - 当前运行
偏移的长度- 屏幕水平滚动偏移
stickLength - 棍子的当前扩展
stones - 石头对象数组
[15-32] 将所有变量重置为初始值
[24-29] 随机化石头位置和宽度
[34-96] 主游戏循环
[35] 清除框架
[36] 在屏幕上显示剩余距离
[37-40] 绘制石头(即使它们不适合屏幕 - 为简单起见)
[42] 绘制精灵的当前动画帧,如果您需要详细信息关于drawImage 的工作原理,点击这里
[44-46] 如果指针(鼠标/触摸)被按下,增加棍子长度
[47-51] 如果棍子正在下降,增加角度。
[49-50]如果一直往下掉,把游戏模式改成“跑”
[52-80] 如果我们处于“运行”模式:
[53-54] 增加偏移量和运行长度
[55] 精灵帧每 2 个动画帧改变一次
[56-57] 如果我们到达终点在 spritesheet 中的动画序列中,如果运行的长度等于摇杆的末端,则返回开始[58-62],重置摇杆长度、角度、运行并切换到“等待”模式
[63-74] ] 检查我们是否落在石头上:
[63] 假设我们落在一个空的空间
[64-65] 比较每个石头的英雄坐标
[66] 如果英雄落在石头上,更正 [63] 中的悲观假设
[67] 计算当前的石头
[68-71] 如果到达最后一块石头,你就赢了!
[75-78] 如果冠军降落在空地上,切换模式到 gameOver 并
在“gameOver”模式中显示下落姿势[81-87]
[82-84] 如果你输了,你就下落
[85-86] ] 如果你赢了,你就赢了。
[88-89] 计算摇杆末端的坐标
[90-93] 绘制摇杆
[94] 如果点击/触摸屏幕,则触发下一个动画帧[97-107]
[98-101] 如果我们正在等待,如果我们处于“gameOver”模式,我们切换到“pointerdown”模式[102-105],如果释放指针并且我们处于“pointerdown”模式,游戏将重新启动[108-111],我们切换到“stickFall”模式
[112] 启动游戏
[113] 开始动画