他們說有些人就是喜歡混亂。
大家好👋🏼,我是格雷厄姆「喜歡混亂」TheDev,這次我帶著另一個愚蠢的網路實驗回來了(如果你願意,你可以直接跳到遊戲)。
一切都是那麼天真地開始,「我可以寫一個貪吃蛇遊戲嗎?」。
但是,一如既往,我肩膀上的小惡魔低聲說「讓它變得更難」......所以我想「沒有 JavaScript,用純 CSS 來做」。
他再次嘰嘰喳喳地說:「噗,還是太簡單了,而且你最近做了太多 CSS 東西,用原始的、無樣式的 HTML 來做吧」。
我轉向另一邊肩膀,想聽聽天使的想法,希望得到更明智的結果。
然後我想起天使從來沒有在我身邊...
所以這就是,snake,純 HTML 格式(用一點 PHP 技巧來支援它)。
這是正確的!
沒有 JavaScript
沒有圖片
沒有CSS
沒有 Cookie
不過,我想澄清一下(因為我不想被指責為點擊誘餌),我正在使用 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,610,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,848,436,415,591,229,454,275,384,803,558,374,5 18,022,675,900,061,399,560,145,595,206,127,211,192,918,105,032,491,008,000,000,000,000,000,000,000,000,000,000,000,000,000,00 0,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 個文件。
所以請原諒使用 PHP 產生下一頁的「捷徑」(對於那些在技術 Twitter 上花費太多時間的人,也請原諒我使用「死」語言!😱🤣)。
不管怎樣,序言太多了,我知道你為什麼來這裡,你想看到它的實際效果!
在桌面 PC 上的 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 Snake 。
當你玩完後,回來看看我用了什麼技巧來完成這個工作(並在評論中分享你的最高分!)。
對於遊戲,您通常需要有一個“遊戲勾選”。每個刻度是當一個動作發生或您計算一個新的遊戲狀態然後渲染新的遊戲狀態。
但這提出了我們的第一個問題,我們如何能夠在沒有 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]">
附註:這是我之前提到的它不適用於其他瀏覽器的地方。他們不排除部分第二次刷新時間,因此刷新是即時的,使得遊戲太快而無法玩。
但僅元刷新不足以使遊戲正常執行,我們需要某種方法來保存遊戲狀態並將蛇方向的變化傳達給伺服器。
為此,我們使用另一個直接的技巧:URL 編碼的 GET 參數。
因為我們不能使用 POST 請求或類似的東西,所以我們需要另一個機制來管理瀏覽器和伺服器之間的遊戲狀態。
起初,我使用多個 GET 參數來管理狀態,因此 URL 如下所示:
url?playing=1&x=2&y=6&foodx=3&foody=6&dir=left.
這一直工作得很好,直到我需要為蛇儲存多個點(它佔據的每個方塊的 x,y 座標)。
雖然我確實讓它與一些 hacky x,y 座標列表和解析一起工作(例如snake=1,1,2,1
,蛇位於x=1,y=1 和x=2,y=1),但這是凌亂的。
因此,我們轉向我們的好朋友: urlencode()
和json_encode()
。
一起使用時,我可以取得一個陣列(或在本例中為多維陣列),將其轉換為 JSON,然後將其轉換為 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
);
要將這些資料儲存在 URL 中,我們可以使用以下命令:
$url = urlencode(json_encode($state));
透過 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
現在我們有一個機制可以將遊戲狀態傳遞到瀏覽器並備份到伺服器。
那些了解自己的東西的人會知道這裡有一個問題。 URL 長度有最大限制!
在 Chrome 中是 2083 個字元。
如果您玩遊戲的時間足夠長,您實際上會達到字元限制,因為為了儲存 x,y 位置對,我們每次使用超過 10 個字元。
但這是一個愚蠢的演示,所以我只想說:讓我知道如果你讓你的蛇足夠長,會發生什麼錯誤!
哦,在現實世界中,您不應該對 URL 中的參數進行 JSON 編碼,我們就這樣吧!
這就對了!
嗯,差不多了。
我們需要將按鍵資訊傳達給伺服器。
這是最後一個問題(也是我們最終在 URL 中顯示遊戲狀態的原因),我們需要向伺服器傳達按鍵訊息以更改蛇的方向。
在我們將按鍵傳達給伺服器之前,我們需要某種方法來實際捕獲它們。
請記住,我們沒有 JS 來捕獲按鍵操作。
我們也不能使用<button>
元素,因為它們需要 JS 才能運作。
所以我們剩下的就是不起眼的錨元素( <a>
)。
但讓某人點擊錨點會讓遊戲變得很難玩。
幸運的是,HTML 中內建了一種名為accesskey
的東西。
它們允許我們將一個字元指派給一個錨點,然後可以透過捷徑(ALT + Windows 上 Chrome 中的字元)存取這些字元。
這為我們提供了允許鍵盤控制的機制,我們只需要 4 個具有不同方向的連結(錨點)作為 URL,然後為每個連結分配一個accesskey
。
重要提示:應謹慎使用accesskey
,如果您選擇輔助科技 (AT) 使用者使用的按鍵,則可能會產生幹擾。
現在我們有了一種按鍵方法,以及一種將按鍵操作傳達給伺服器的方法,我們需要一種方法來管理按鍵操作,以便它們更新蛇的方向。
幸運的是,我們透過 URL 傳遞的狀態物件中已經有了一個direction
屬性。
因此,我們要做的就是建立 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>
現在,例如,當您按ALT + K時,將單擊第四個連結,我們將當前狀態+新方向發送到伺服器!
現在剩下的就是獲取該資訊併計算下一個遊戲狀態。
最後,謎題的最後一部分是一些遊戲邏輯。
例如,在生成食物位置時,我們需要檢查它是否不在蛇已經佔據的圖塊上,因此我們有以下函數:
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;
}
還有一個移動蛇的功能
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;
}
以及建構遊戲板的循環。
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 '⬜';
}
}
}
但我不會詳細介紹這些內容,因為您可以輕鬆找到(並找到更簡潔的方法)、找到其他人編寫的(更好的)程式碼並適應您的需求。
現在你已經知道了,我們使用元刷新、存取鍵和在 URL 中編碼複雜資料的技巧建立了一個遊戲。
這些東西對你的日常生活有用嗎?不,可能不會。
他們是否會在一個奇怪的邊緣情況下拯救你的屁股,完成這個工作,有能力使用黑客來交付產品情況?可能吧。
什麼?您沒想到我會提供您有用的教學嗎?你現在應該更清楚了。
但是,話雖如此,如果您確實喜歡這篇文章,或者奇蹟般地學到了一些新東西,請在下面給我留言,這真的意義重大!
祝大家週末愉快! 💗
原文出處:https://dev.to/grahamthedev/snakein-pure-html-no-js-no-css-no-images-2ccg