手记

116行纯 JavaScript 的 Stick Champ 游戏

<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] 开始动画

0人推荐
随时随地看视频
慕课网APP