摘要:序言你是否想過單純使用也可以制作一款游戲甚至可以雙人對決這是一篇非常有趣的文章,作者詳細講解了使用純制作四子連珠游戲的思路以及使用奇淫巧技解決困難問題的方法。在本文中,我將介紹使用純制作的四子連珠游戲的關鍵思想。
序言:你是否想過單純使用 CSS 也可以制作一款游戲?甚至可以雙人對決!這是一篇非常有趣的文章,作者詳細講解了使用純 CSS 制作四子連珠游戲的思路以及使用奇淫巧技解決困難問題的方法。因為案例本身比較復雜,而本人水平有限,翻譯必有不恰當之處,歡迎留言評論。
實驗是學習新技巧、思考新想法、并突破自身極限的有趣的方式。“純 CSS”演示很早就有了,但是隨著瀏覽器和CSS的發(fā)展,新的挑戰(zhàn)又出現(xiàn)了。CSS 和 HTML 預處理器也促進了純 CSS 演示的發(fā)展。有時候,預處理程序用于每個可能的硬編碼場景,比如 :checked 的長字符串和相鄰兄弟選擇器。
在本文中,我將介紹使用純 CSS 制作的四子連珠游戲的關鍵思想。在我的實驗中,我盡量避免硬編碼,并且不使用預處理器,專注于保持代碼的簡潔。以下是游戲的所有代碼以及演示:
See the Pen Pure CSS Connect 4 by Bence Szabó (@finnhvman) on CodePen.
基本概念我認為在“純 CSS”類型中有一些概念是必不可少的。通常,表單元素用于管理狀態(tài)和捕獲用戶操作。當我發(fā)現(xiàn)有人使用 重置或者重新開始新游戲時,我非常興奮。只需要將元素包裹在
第一步就是創(chuàng)建表單元素,再在表單中創(chuàng)建一些用作圓孔(the slots)的 input,然后添加重置按鈕。以下是使用 的基本演示:
See the Pen Pure HTML Form Reset by Bence Szabó (@finnhvman) on CodePen.
為了讓演示好看一些,我使用 radial-gradient(),而不是在游戲板(the board)或者圓盤(the discs)上貼一張圖片。我經(jīng)常使用 Lea Verou 制作的 CSS3 圖案庫。它是使用漸變制作的圖案集,而且很容易編輯。我使用了currentcolor,非常適合圓盤的圖案。我添加了頭部,并且復用了自己制作的純 CSS 波紋按鈕。
現(xiàn)在,布局和圓盤已經(jīng)設計好了,只是還不能游戲
接下來,需要讓用戶輪流將圓盤放到四子連珠的游戲板上。在四子連珠游戲中,玩家(一個紅色,一個黃色)輪流將圓盤放置在面板的列中。游戲板有 7 列 6 行(一共有 42 個圓孔)。每一個圓孔可以為空或者被一個紅色或黃色的圓盤占用。所以,一個圓孔可以有三種狀態(tài)(空、紅色或者黃色)。在同一列中掉落的圓盤會堆疊在一起。
首先我為每個圓孔放置了兩個 checkbox 。當它們都沒有被選中時,圓孔就被認為是空的,當其中一個被選中時,相應的玩家就會把他的圓盤放進去。
當其中任何一個被選中之后,應該把它隱藏起來,避免出現(xiàn)兩者都被選中的狀態(tài)。這些 checkbox 是直接的兄弟類,所以如果選中第一個之后,可以使用 :checked 偽類和相鄰兄弟選擇器(+)來隱藏兩個元素。但是如果選中第二個呢?你可以隱藏第二個,但是怎么才能影響第一個呢?可惜沒有選擇前一個的兄弟選擇器,這不是 CSS 選擇器的工作方式。我不得不拒絕這個想法。
實際上,一個 checkbox 本身可以有三個狀態(tài),可以使用 indeterminate 狀態(tài)。問題是,僅僅使用 HTML 不能將其置于不確定狀態(tài)。即使可以,當再次點擊復選框時,它也會轉換成選中狀態(tài)。強迫第二個玩家在移動圓盤時進行雙擊是不現(xiàn)實的。
我仔細閱讀了 MDN 上關于 :indeterminate 的文檔后發(fā)現(xiàn) radio input 通用都有 indeterminate 狀態(tài)。名稱相同的 radio 按鈕在未選中時都處于這種狀態(tài)。哇,這是一個真正的初始狀態(tài)!真正有用的是,選中后一個同胞元素也會對前者產(chǎn)生影響!于是我在游戲板上放置了 42 對 radio input。
從以往的經(jīng)歷來看,使用 label ,并通過合理的順序搭配 checkbox 或 radio 可以解決問題,但我認為 label 不能使代碼更簡潔。
為了獲得更好的用戶體驗,我希望交互區(qū)域可以更大一些,所以合理的做法是讓玩家點擊一個列來移動圓盤。通過在合適的元素上添加絕對和相對位置,我將同一列的控件相互疊加。這樣,在每一列中只能選擇最下面的圓孔。我仔細地設置了每一行的圓盤下降的時間,它們的時間函數(shù)近似于一個二次曲線,與現(xiàn)實中的自由落體相似。到目前為止,游戲的各部分都做好了,但是下圖清晰地顯示出只有紅色的玩家才能操作。
盡管已經(jīng)設置了所有的控件,但只有紅色的圓盤可以落在游戲板上
我用彩色且半透明的矩形對 Radio input 的可點擊區(qū)域用進行了可視化顯示。黃色和紅色的 input 在每列上重疊 6 次(= 6 行),將最下面一行的紅色的 input 放在頂部。紅色和黃色的混合形成了橙黃色,可以在游戲板上看到。每一列中可用的圓孔越少,這種橙黃色就越不強烈,因為 radio input 只有在 :indeterminate 狀態(tài)時才會顯示。由于在每個圓孔上,紅色 input 總是蓋住黃色 input,所以只有紅色的玩家能夠移動。
輪流游戲我只有一個模糊的想法,就是能不能使用普通的兄弟選擇器解決玩家輪流游戲的問題。這個想法就是統(tǒng)計選中的 input 的數(shù)量,為偶數(shù)(0、2、4等)時紅色玩家移動,為奇數(shù)時黃色玩家移動。很快我就意識到一般的兄弟選擇器不能(也不應該!)按照我想要的方式工作。
還有一種方式就是使用 nth 選擇器。盡管我喜歡使用even和odd這樣的關鍵詞,但我還是走進了死胡同。:nth-child 選擇器 “統(tǒng)計”父類中的子元素,包括所有類型,類、偽類等等。:nth-of-type 選擇器 “統(tǒng)計”在父類中某類型的子類,不包括類或偽類。所以問題就在于無法通過 :checked 狀態(tài)去統(tǒng)計。
CSS counters 也可以統(tǒng)計,所以為什么不試試呢?計數(shù)器的一個常見用法是在文檔中對標題(甚至多個級別)進行編號。它們由 CSS 規(guī)則控制,可以在任何時候被重置,其增加(或遞減!)值可以是任意整數(shù)。計數(shù)器“counter()”函數(shù)顯示在 content 屬性中。
所以最簡單的方法就是設置計數(shù)器,然后統(tǒng)計四子連珠游戲中 :checked 的 input 的數(shù)量。這種方法只有兩個困難。首先,你不能在一個計數(shù)器上執(zhí)行算術運算來檢測它是偶數(shù)還是奇數(shù)。其次,你不能基于計數(shù)器的值在元素上應用 CSS 規(guī)則。
我使用二進制解決了第一個問題。計數(shù)器的初始值設為 0 。當紅色玩家選中 radio 按鈕時,計數(shù)器加 1。當黃色玩家選中 radio 按鈕時,計數(shù)器就減 1,以此類推。因此,計數(shù)器的值始終是 0 或 1,偶數(shù)或奇數(shù)。
解決第二個問題需要更多的創(chuàng)造力(read: hack)。如上所述,計數(shù)器只能顯示在 ::before 和 ::after 偽元素中。這是顯而易見的,但它們如何影響其他元素呢?至少計數(shù)器值可以改變偽元素的寬度。不同的數(shù)有不同的寬度。字符 1 通常比 0 纖細,但這是很難控制的。如果改變的是字符的數(shù)量,而不是字符本身,那么由此產(chǎn)生的寬度變化就是可控的。在 CSS 計數(shù)器中使用羅馬數(shù)字并不少見。用羅馬數(shù)字表示的 1 和 2 與字符 1 和 2 是相同的,它們的像素寬度也是相同的。
我的想法是將一個玩家(黃色)的單選按鈕貼著左邊放置,并將另一個玩家(紅色)的單選按鈕貼著共享父容器的右邊放置。最初,紅色的按鈕被覆蓋在黃色的按鈕上,然后容器的寬度變化會導致紅色的按鈕“消失”,顯示黃色的按鈕。可以將其比作現(xiàn)實中有兩個窗格的滑動窗口,一個窗格是固定的(黃色按鈕),另一個是可滑動的(紅色按鈕)。區(qū)別在于,在游戲中只有一半的窗口是可見的。
到目前為止,還不錯,但我并不滿意使用 font-size (以及其他 font 屬性)間接控制寬度。更好的方式是使用 letter-spacing,因為它只在一個維度上改變了大小。出乎意料的是,即使是一個字母也有字母間距(在字母后面呈現(xiàn)),兩個字母就有兩個字母間距。可靠性的關鍵就是保證寬度是可預知的。寬度為 0 的字符加上單字母和雙字母間距都可以,但是將 font-size 設置為 0 是存在風險的。為了兼容所有瀏覽器,可以將 letter-spacing (以像素為單位)設置的大一些并且將 font-size 設置的小一點(1px),是的,我說的是子像素。
我需要容器的寬度在初始大小(=w)與至少兩倍以上大小(>=2w)之間交替變換,以便能夠完全隱藏和顯示黃色按鈕。假設 v 是 "i" 字符的渲染寬度(小寫羅馬字母表示,在不同的瀏覽器中不同),c 是 letter-spacing 的渲染寬度(常量)。我需要 v + c = w 為真,但這是不可能的,因為 c 和 w 是整數(shù),而 v 是非整數(shù)。最后我使用了 min-width 和 max-width 屬性來約束可能的寬度值,因此我還將可能的計數(shù)器值更改為 "i" 和 "iii" ,以確保文本在流下變寬并溢出約束。通過方程 v + c < w, 3v + 3c > 2w,,v << c,可以得到2/3w < c < w。結論就是“字母間距”必須比初始寬度小一些。
我一直以為偽元素顯示的計數(shù)值是 radio 按鈕的父元素,可惜不是。但是,我注意到偽元素的寬度改變了其父元素的寬度,在本例中父元素是 radio 按鈕的容器。
如果你在想,難道不能用阿拉伯數(shù)字來解決嗎?你說得對,計數(shù)器的值在 "1" 和 "111" 之間交替變換也是可以的。盡管如此,羅馬數(shù)字最先給了我啟示,它們也是點擊器標題的不錯的方式,所以我保留了它們。
從紅色玩家開始,然后輪流游戲
應用所討論的技術使 radio input 的父容器在選中紅色 input 時寬度加倍,在選中黃色 input 寬度變?yōu)樵瓉淼膶挾取T谠紝挾鹊娜萜髦校t色 input 位于黃色 input 之上,而在雙寬度容器中,紅色 input 被移開。
識別模式在現(xiàn)實生活中,四子連珠游戲并不會告訴你是贏了還是輸了,但是提供適當?shù)姆答伿侨魏诬浖己糜脩趔w驗的一部分。下一個目標是檢測玩家是否贏得了游戲。要想贏得比賽,玩家必須在一列、一行或對角線上放四個圓盤。在許多編程語言中,這是一個非常簡單的任務,但是在純 CSS 世界中,這是一個巨大的挑戰(zhàn)。將它分解成子任務是系統(tǒng)地處理這個問題的方法。
我使用一個 flex 容器作為 radio 按鈕和圓盤的父類。一個黃色的 radio 按鈕、一個紅色的 radio 按鈕和一個代表圓盤并與圓孔重疊的 div 。這樣的圓孔重復了42 次,并排列成多列。因此,列中的圓孔是相鄰的,這使得使用相鄰選擇器識別列中的四個是最容易的:
... ...
/* Red four in a column selector */ input:checked + .disc + input + input:checked + .disc + input + input:checked + .disc + input + input:checked ~ .outcome /* Yellow four in a column selector */ input:checked + input + .disc + input:checked + input + .disc + input:checked + input + .disc + input:checked ~ .outcome
這是一個簡單但丑陋的解決方案。為了檢測一列中四子相連的情況,每個玩家都有 11 個類型和類選擇符鏈接在一起。在圓孔元素后面添加一個類名為 .outcome 的 div 可以展示輸出的信息。在被列包裹的一列中,檢測四子相連存在問題,但是我們先把這個問題放到一邊。
如果采用類似的方法判斷一行中是否有四子相連,那將是一個可怕的想法。每個玩家將會有 56 個選擇器(如果我算對了的話),更不用說他們會有類似的檢測錯誤的情況。在將來,[:nth-child(An+B [of S])](https://drafts.csswg.org/sele... 或者 column combinators 會派得上用場.
為了更好的語義化,可以為每個列添加一個新的 div,并在其中排列圓孔元素。這一修改也將消除上述檢測錯誤的情況。然后,檢測一行中的有四子相連可以用以下方法:選擇第一個紅色 radio input 被選中的一個列,然后再選擇第一個紅色 radio input 被選中的相鄰同胞列,重復兩次。這聽起來很麻煩,需要"parent"選擇器。
選擇父節(jié)點是不可行的,但是選擇子節(jié)點是可行的。如何用選擇器及其組合方式檢測一行中的四子相連? 選擇一個列,再選擇它的第一個被選中的紅色 radio input,然后選擇相鄰的列,再選擇它的第一個被選中的紅色 radio input ,以此類推,再重復兩次。這聽起來仍然很麻煩,但卻是可行的。訣竅不僅在 CSS 中,而且在 HTML 中,下一列必須是上一列中創(chuàng)建嵌套結構的單選按鈕的同胞元素。
.........
/* Red four in a row selectors */ input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column::after, input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column::after, ... input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column::after
語義混亂了,這些選擇器只適用于紅色的玩家(黃色的玩家有另一輪),但是它確實有用。有一個好處是不會出現(xiàn)檢測錯誤的列或行。結果的顯示也必須進行修改,任何匹配列使用的 ::after 偽元素都應該是一致的。因此,必須在最后一個位置之后添加一個偽第八列。
如上面的代碼片段所示,列的特殊的位置關系可以檢測一行中的四子相連。可以使用同樣的技術并通過調整這些位置來檢測對角線上的四子相連。注意對角線可以在兩個方向上。
input:nth-of-type(2):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(6):checked ~ .column > input:nth-of-type(8):checked ~ .column::after, input:nth-of-type(4):checked ~ .column > input:nth-of-type(6):checked ~ .column > input:nth-of-type(8):checked ~ .column > input:nth-of-type(10):checked ~ .column::after, ... input:nth-of-type(12):checked ~ .column > input:nth-of-type(10):checked ~ .column > input:nth-of-type(8):checked ~ .column > input:nth-of-type(6):checked ~ .column::after
在最終的代碼中,選擇器的數(shù)量非常龐大,如果使用 CSS 預處理器則可以顯著減少聲明長度。盡管如此,我認為演示的代碼還是比較短的。它應該是在中間的某個地方,從硬編碼一個選擇器到使用 4 個神奇的選擇器(列,行,兩個對角線)。
當有玩家獲得勝利就會顯示一條信息
任何軟件都有邊緣情況需要處理。四子相連游戲的可能結果不僅是紅色或黃色的玩家獲勝,而且會出現(xiàn)游戲板被填滿的平局。從技術上講,這種情況不會破壞游戲或產(chǎn)生任何錯誤,所缺少的是對玩家的反饋。
我們的目標是檢測出黑板上有 42 個 :checked 的單選按鈕,并且它們都沒有處于 :indeterminate 狀態(tài)。這就要求為每個單選按鈕做一個選擇。單選按鈕處于 :indeterminate 時是 invalid ,否則是 valid 。因此,我為每個 input 添加了 required 屬性,然后在表單上使用 :valid 偽類來檢測平局。
當游戲板被填滿時會顯示平局的信息
檢測平局結果出現(xiàn)了一個 bug。在極少數(shù)的情況下會出現(xiàn)黃色玩家最終勝利的情況,勝利和平局的消息都顯示出來了。這是因為這些結果的檢測和顯示方法是正交的。我解決了這個問題,確保獲勝消息有一個白色的背景,并在平局消息之上。還必須延遲平局消息的過渡,這樣它就不會與獲勝消息混合出現(xiàn)了。
黃方勝利的信息蓋住了平局結果
雖然許多單選按鈕是通過絕對定位隱藏在彼此后面的,但是所有處于不確定狀態(tài)的按鈕仍然可以通過 tab 鍵來訪問。這使得玩家可以將他們的圓盤放入任意的圓孔中。處理這個問題的一種方法是簡單地禁止使用 tabindex 屬性進行鍵盤交互:將其設置為 -1 意味著不應該通過連續(xù)的鍵盤導航來訪問它。為了解決這個問題,必須在每個單選按鈕上添加這一屬性。
...限制
最實質性的缺點是,由于輪流游戲的解決方案不可靠,游戲板沒有響應,并且可能在小的視圖窗口上出現(xiàn)故障。我不敢冒險重構響應式的解決方案,由于實現(xiàn)的本質,硬編碼看起來更安全。
另一個問題是觸摸設備上的 sticky hover 。在正確的位置添加一些媒體查詢是解決這個問題最簡單的方法,但是這會消除自由落體動畫。
有人可能認為 :indeterminate 偽類已經(jīng)得到了廣泛的支持,事實的確如此。問題是它只在一些瀏覽器中得到部分支持。注意兼容性表中的注釋1:MS IE 和 Edge 在單選按鈕上不支持它。如果您在這些瀏覽器中查看演示程序,您的光標將變成 not-allowed 的光標,這是無意的,但有點優(yōu)雅的降級。
不是所有瀏覽器都支持 radio 按鈕的 :indeterminate 屬性
感謝閱讀到最后一部分!讓我們看看這個游戲的一些數(shù)據(jù):
140 個 HTML 元素
350 行 (合理地) CSS
0 行 JavaScript
0 個外部資源
總的來說,我對結果很滿意,反饋也很好。做這個演示我確實學到了很多,我希望可以分享更多這樣的文章!
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/113934.html
摘要:序言你是否想過單純使用也可以制作一款游戲甚至可以雙人對決這是一篇非常有趣的文章,作者詳細講解了使用純制作四子連珠游戲的思路以及使用奇淫巧技解決困難問題的方法。在本文中,我將介紹使用純制作的四子連珠游戲的關鍵思想。 序言:你是否想過單純使用 CSS 也可以制作一款游戲?甚至可以雙人對決!這是一篇非常有趣的文章,作者詳細講解了使用純 CSS 制作四子連珠游戲的思路以及使用奇淫巧技解決困難問...
摘要:過往項目年月份項目匯總共個項目年月份項目匯總共個項目年月份項目匯總共個項目年月份項目匯總共個項目年月份項目匯總共個項目年月份項目匯總共個項目年月至年月發(fā)布的項目前端每日實戰(zhàn)專欄每天分解一個前端項目,用視頻記錄編碼過程,再配合詳細的代碼解讀, 過往項目 2018 年 9 月份項目匯總(共 26 個項目) 2018 年 8 月份項目匯總(共 29 個項目) 2018 年 7 月份項目匯總(...
摘要:深度學習現(xiàn)在被視為能夠超越那些更加直接的機器學習的關鍵一步。的加入只是谷歌那一季一系列重大聘任之一。當下谷歌醉心于深度學習,顯然是認為這將引發(fā)下一代搜索的重大突破。移動計算的出現(xiàn)已經(jīng)迫使谷歌改變搜索引擎的本質特征。 Geoffrey Hiton說:我需要了解一下你的背景,你有理科學位嗎?Hiton站在位于加利福尼亞山景城谷歌園區(qū)辦公室的一塊白板前,2013年他以杰出研究者身份加入這家公司。H...
摘要:按下右側的點擊預覽按鈕可以在當前頁面預覽,點擊鏈接可以打開原始頁面。 按下右側的點擊預覽按鈕可以在當前頁面預覽,點擊鏈接可以打開原始頁面。 1. 一個正 20 面體的骰子https://codepen.io/chrisvfrit... 2. 純 css 寫的夜間景色的視差滾動效果https://codepen.io/danbhala/p... 3. 機器人喝油的動畫https://co...
閱讀 1714·2021-11-22 15:33
閱讀 2085·2021-10-08 10:04
閱讀 3542·2021-08-27 13:12
閱讀 3418·2019-08-30 13:06
閱讀 1467·2019-08-29 16:43
閱讀 1390·2019-08-29 16:40
閱讀 785·2019-08-29 16:15
閱讀 2745·2019-08-29 14:13