蛇...纯 HTML⁉️ [无 JS、无 CSS、无图像!!]😱

2025-06-05

蛇...纯 HTML⁉️ [无 JS、无 CSS、无图像!!]😱

他们说有些人就是喜欢混乱。

大家好👋🏼,我是 Graham“喜欢混乱”TheDev,这次我带着另一个愚蠢的互联网实验回来了(如果你愿意,你可以直接跳到游戏)。

一切都开始得如此天真,“我可以写一个贪吃蛇游戏吗?”。

但是,一如既往,我肩膀上的小恶魔低声说“让它更难”......所以我想“不要 JavaScript,用纯 CSS 来做”。

他又一次嘟囔道“噗,还是太简单了,你最近做了太多 CSS 的东西,用原始的、无样式的 HTML 来做吧”。

我转向另一侧肩膀,听听天使的想法,希望听到更明智的答案。

然后我才想起天使从来没有陪伴过我……

这就是纯 HTML 格式的蛇(使用了一些 PHP 技巧来支持它)。

这是正确的!

  • 没有 JavaScript
  • 没有图片
  • 没有 CSS
  • 没有Cookies

但是,我想说清楚(因为我不想被指责为点击诱饵),我使用 PHP 呈现此 HTML。

虽然可以使用纯 HTML 文件来实现,无需后端语言,但它需要640,345,228,142,352,307,046,244,325,015,114,448,670,890,662,773,914,918,117,331,955,996,440,709,549,671,345,290,477,020,322,434,911,210,797,593,280,795,101,545,372,667,251,627,877,890,009,349,763,765,710,326,350,331,533,965,349, 868,386,831,339,352,024,373,788,157,786,791,506,311,858,702,618,270,169,819,740,062,983,025,308,591,298,346,162,272,304,558,339,520,759,611,505,302,236,086,810,433,297,255,194,852,674,432,232,438,669,948,422,404,232,599,805,551,6 10,635,942,376,961,399,231,917,134,063,858,996,537,970,147,827,206,606,320,217,379,472,010,321,356,624,613,809,077,942,304,597,360,699,567,595,836,096,158,715,129,913,822,286,578,579,549,361,617,654,480,453,222,007,825,818,400,84 8,436,415,591,229,454,275,384,803,558,374,518,022,675,900,061,399,560,145,595,206,127,211,192,918,105,032,491,008,0 ...

因此,请原谅使用 PHP 生成下一页的“快捷方式”(对于那些在技术 Twitter 上花费太多时间的人,也请原谅我使用“死”语言!😱🤣)。

无论如何,这都是太多的序言了,我知道你为什么在这里,你想看到它的实际效果!

玩 HTML 贪吃蛇(有警告!)

可在桌面电脑上的 Chrome 上运行...对我来说,在 Firefox 和 iOS 上的任何浏览器上播放速度都太快了.​​..您将在本文后面看到原因。

所以基本上,在桌面上的 Chrome 上播放它!

关键在于:

  • ALT+I表示向上,
  • ALT+J为左边,
  • ALT+K表示向下,
  • ALT+L为右,
  • ALT+O开始新游戏(一旦你输了)。

在 Mac 上它是Control+Option而不是Alt我相信!

如果您想知道为什么是奇怪的键而不是 WASD,不幸的是ALT+D已经在 Windows 上的 Chrome 中使用,所以我不得不选择“安全”键。

最后警告:我们用来实现此功能的技巧之一就是将大量的 URL 充斥到您的浏览器历史记录中...您已经被警告了!

游戏

遗憾的是,这不能在 codepen 中运行,所以你必须在我的网站上玩 HTML 蛇

玩完之后,回来看看我使用了哪些技巧来实现这个功能(并在评论中分享您的最高分!)。

问题 1:获取游戏 tick

对于游戏来说,通常需要一个“游戏刻度”。每个刻度都代表一个动作发生,或者计算一个新的游戏状态,然后渲染新的游戏状态。

但这引发了我们的第一个问题,我们如何才能在没有 JavaScript 的情况下让页面自动更新?

嗯,在 HTML 中做到这一点实际上非常简单,我们只需将<meta http-equiv="refresh"其设置为一个较低的值。

因此,我们从 0.35 秒的刷新时间开始,然后随着您的分数上升,将其加快到 0.2 秒的刷新时间。

“元刷新”允许我们做的是指示浏览器,一旦它加载了页面的 HTML,就等待 X 秒,然后加载另一个 URL。

通过设置较低的刷新时间,然后在每次刷新时更改我们重定向到的 URL(稍后会详细介绍),我们可以让页面自行更改,即使您不按任何按钮!

以下是格式的简单示例:

<meta http-equiv="refresh" content="[time];url=[url-to-redirect-to]">
Enter fullscreen mode Exit fullscreen mode

附注:我之前提到过,这个功能在其他浏览器上无法使用。它们不支持部分秒级的刷新时间,所以刷新是即时的,这导致游戏速度太快,无法正常进行。

但单靠元刷新不足以使游戏正常运行,我们需要某种方式来保存游戏状态并将蛇方向的变化传达给服务器。

为此,我们使用另一个简单的技巧:URL 编码的 GET 参数。

问题 2:管理游戏状态

因为我们不能使用 POST 请求或类似的东西,所以我们需要另一种机制来管理浏览器和服务器之间的游戏状态。

起初我使用几个 GET 参数来管理状态,因此 URL 如下所示:

url?playing=1&x=2&y=6&foodx=3&foody=6&dir=left.
Enter fullscreen mode Exit fullscreen mode

一切运行良好,直到我需要为蛇存储多个点(它占据的每个方格的 x、y 坐标)。

snake=1,1,2,1虽然我确实使用了一些 hacky x,y 坐标列表和解析(例如蛇位于 x=1,y=1 和 x=2,y=1)让它工作,但这很混乱。

因此,我们转而向我们的好朋友求助:urlencode()json_encode()

一起使用,我可以获取一个数组(或在本例中为多维数组),将其转换为 JSON,然后将其转换为 URL 的有效字符。

让我解释一下:

在 URL 中存储复杂数据

以下是我用于游戏状态的数据示例:

$state = array(
        'width' => $width,
        'height' => $height,
        'snake' => array(array('x' => 5, 'y' => 5)),
        'food' => array('x' => 7, 'y' => 7),
        'direction' => 'right',
        'score' => 0, 
        'state' => true
    );
Enter fullscreen mode Exit fullscreen mode

要将数据存储在 URL 中,我们可以使用以下命令:

$url = urlencode(json_encode($state));
Enter fullscreen mode Exit fullscreen mode

通过 JSON 编码我们的数组,然后用 URL 友好的字符替换无效字符,这为我们提供了以 URL 友好(尽管不是人类友好!)的方式提供的状态:

%7B%22width%22%3A20%2C%22height%22%3A20%2C%22snake%22%3A%5B%7B%22x%22%3A19%2C%22y%22%3A5%7D%5D%2C%22food%22%3A%7B%22x%22%3A4%2C%22y%22%3A11%7D%2C%22direction%22%3A%22right%22%2C%22score%22%3A0%2C%22state%22%3Afalse%7D
Enter fullscreen mode Exit fullscreen mode

现在我们有一个机制可以将游戏状态传递到浏览器并返回到服务器。

最大 URL 长度

那些懂行的人应该知道这里有个陷阱。URL 的长度是有上限的!

在 Chrome 中,该大小为 2083 个字符。

如果您玩游戏的时间足够长,您实际上会达到该角色的限制,因为为了存储我们的 x,y 位置对,我们每次使用超过 10 个角色。

但这是一个愚蠢的演示,所以我只想说:如果你把蛇做得足够长,请告诉我会发生什么错误!

哦,在现实世界中,您不应该在 URL 中使用 JSON 编码参数,我们就此打住吧!

我们有状态和游戏记录,现在怎么办?

就是这样!

嗯,差不多了。

我们需要将按键信息传达给服务器。

问题 3:改变蛇的方向

这是最后一个难题(也是我们最终在 URL 中使用游戏状态的原因),我们需要向服务器传达按键信息以改变蛇的方向。

问题 3a:按钮按下

在我们能够将按键信息传达给服务器之前,我们需要某种方法来实际捕获它们。

请记住,我们没有 JS 来捕捉按键动作。

我们也不能使用<button>元素,因为它们需要 JS 才能工作。

因此,我们剩下的就只是不起眼的锚元素(<a>)。

但让某人点击锚点会使游戏变得难以玩。

幸运的是,HTML 中内置了一种叫做accesskeys的东西。

它们允许我们将一个字符分配给一个锚点,然后可以通过快捷方式(ALT + Windows 上 Chrome 中的字符)访问这些字符。

这为我们提供了允许键盘控制的机制,我们只需要 4 个具有不同方向的链接(锚点)作为 URL,然后accesskey为每个链接分配一个。

重要提示: accesskey应谨慎使用,如果您选择辅助技术 (AT) 用户使用的键,则可能会产生干扰。

问题 3b:方向

现在我们有了按下按键的方法,以及将按键操作传达给服务器的方法,我们需要一种方法来管理按键操作,以便它们更新蛇的方向。

幸运的是,我们的状态对象中已经有一个direction通过 URL 传递的属性。

所以我们只需要创建 4 个不同的 URL,每个方向一个。然后我们把它们添加到链接中就完成了。


$encodedState = urlencode(json_encode($state)); 

<a href="index.php?state=<?php echo $encodedState; ?>&direction=up" accesskey="i">up (ALT + I)</a><br/>

<a href="index.php?state=<?php echo $encodedState; ?>&direction=left" accesskey="j">left (ALT + J)</a>

<a href="index.php?state=<?php echo $encodedState; ?>&direction=right"  accesskey="l">right (ALT + L)</a>

<a href="index.php?state=<?php echo $encodedState; ?>&direction=down"  accesskey="k">down (ALT + K)</a>

Enter fullscreen mode Exit fullscreen mode

现在,例如,当您按下ALT+时K,将单击第 4 个链接,并将当前状态 + 新方向发送到服务器!

现在剩下的就是获取信息并计算下一个游戏状态。

游戏逻辑

最后,谜题的最后一部分是一些游戏逻辑。

例如,当生成食物位置时,我们需要检查它不在蛇已经占据的瓷砖上,所以我们有这个函数:

function generateFoodPosition($width, $height, $snake) {
  do {
    $food = array(
      'x' => rand(0, $width - 1), 
      'y' => rand(0, $height - 1));
  } while (
    in_array($food, $snake)
  );

  return $food;
}

Enter fullscreen mode Exit fullscreen mode

另一个函数是移动蛇

function moveSnake($state) {
    $newHead = array('x' => $state['snake'][0]['x'], 'y' => $state['snake'][0]['y']);

    // Update snake's head position based on direction
    switch ($state['direction']) {
        case 'up':
            $newHead['y']--;
            break;
        case 'down':
            $newHead['y']++;
            break;
        case 'left':
            $newHead['x']--;
            break;
        case 'right':
            $newHead['x']++;
            break;
    }

    // Check if snake has collided with the wall or itself
    if ($newHead['x'] < 0 || $newHead['x'] >= $state['width'] || $newHead['y'] < 0 || $newHead['y'] >= $state['height'] || in_array($newHead, array_slice($state['snake'], 1))) {
        $state['state'] = false;
        return $state; // Game over
    }

    // Check if snake has eaten the food
    if ($newHead['x'] == $state['food']['x'] && $newHead['y'] == $state['food']['y']) {
        $state['score'] += 10;
        // Generate new food position
        $state['food'] = generateFoodPosition($state['width'], $state['height'], $state['snake']);
    } else {
        // Remove tail segment
        array_pop($state['snake']);
    }

    // Move snake
    array_unshift($state['snake'], $newHead);
    return $state;
}
Enter fullscreen mode Exit fullscreen mode

以及一个用于构建游戏板的循环。

 for ($y = 0; $y < 20; $y++) {
        echo "\r\n"; 
        for ($x = 0; $x < 20; $x++) {
            if ($x == $state['food']['x'] && $y == $state['food']['y']) {
                echo '🍎';
            } elseif (in_array(array('x' => $x, 'y' => $y), $state['snake'])) {
                echo '🟩';
            }else{
                echo '⬜';
            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

但我不会详细介绍这些内容,因为您可以轻松查找这些内容(并找到更简洁的方法),找到其他人编写的(更好的)代码并适应您的需求。

就这样结束了

好了,我们使用元刷新、访问密钥和黑客技术构建了一个游戏,以便在 URL 中编码复杂数据。

这些东西在你的日常生活中有用吗?没有,可能没有。

他们能不能帮你解决一些奇怪的边缘情况,比如需要完成这件事,或者需要用黑客手段才能把产品发布出去?有可能。

什么?你不会是期待我给你一个有用的教程吧?现在你应该明白了吧。

但是,话虽如此,如果您确实喜欢这篇文章,或者奇迹般地学到了一些新东西,请在下面给我留言,这真的意义重大!

祝大家周末愉快!💗

文章来源:https://dev.to/grahamthedev/snakein-pure-html-no-js-no-css-no-images-2ccg
PREV
所以我找到了一份工作🎉……我和你一样震惊!🤯
NEXT
快速提示:如何修复 Page Speed Insights / Lighthouse 中的“图像元素没有明确的宽度和高度”问题