当前位置: 首页 > Web前端 > HTML

如何用纯CSS制作四子游戏

时间:2023-04-02 16:49:24 HTML

前言:有没有想过纯CSS也可以制作游戏?你甚至可以成对战斗!这是一篇很有意思的文章,作者详细讲解了用纯CSS制作四足游戏的思路和奇招解难的方法。由于案件本身比较复杂,本人水平有限,翻译肯定有不妥之处,欢迎大家指正。实验是学习新技巧、思考新想法和突破极限的有趣方式。“纯CSS”演示已经存在很长时间了,但是随着浏览器和CSS的发展,新的挑战出现了。CSS和HTML预处理器也有助于开发纯CSS演示文稿。有时预处理器用于所有可能的硬编码场景,例如:checked和相邻兄弟选择器的长字符串。在这篇文章中,我将介绍用纯CSS制作的连连四游戏背后的关键思想。在我的实验中,我尽量避免硬编码并且不使用预处理器,专注于保持代码干净。以下是游戏的全部代码和demo:请参阅BenceSzabó(@finnhvman)在CodePen上的PenPureCSSConnect4。

基本概念我认为有几个概念在“纯CSS”类型中是必不可少的。通常,表单元素用于管理状态和捕获用户操作。当我发现有人使用来重置或重新启动新游戏时,我会非常兴奋。只需将元素包装在
标记中并添加按钮。在我看来,这是比刷新页面更方便的解决方案。第一步是创建表单元素,然后在表单中创建一些输入作为插槽,然后添加重置按钮。这是一个使用的基本演示:查看BenceSzabó(@finnhvman)的PenPureHTMLFormResetCodePen。

为了使演示看起来更好,我使用radial-gradient()而不是将图片粘贴在板或光盘上。我经常使用LeaVerou的CSS3模式库。它是一组使用渐变制作的图案,并且易于编辑。我使用了非常适合光盘图案的currentcolor。我添加了标题并重新使用了我制作的纯CSS波纹按钮。现在布局和碟子都设计好了,但是游戏还不能把碟子放在游戏盘上。接下来,用户需要轮流将光盘放在游戏板上。在ConnectFour中,玩家(一位红色,一位黄色)轮流在棋盘上的列中放置圆盘。游戏板有7列和6行(共42个孔)。每个洞可以是空的,也可以被红色或黄色的圆盘占据。因此,一个洞可以有三种状态(空、红或黄)。落在同一列的圆盘堆叠在一起。首先,我为每个圆孔放置了两个复选框。当一个都没有被选中时,圆孔被认为是空的,当一个被选中时,相应的玩家将他的光盘放入其中。当其中任何一个被选中时,应该将其隐藏,以避免两者都被选中的状态。这些复选框是直接兄弟,因此您可以使用:checked伪类和相邻的兄弟选择器(+)来隐藏两个元素(如果选中第一个元素)。但是如果选择了第二个呢?你可以隐藏第二个,但你如何影响第一个?太糟糕了,没有选择前一个选择器的兄弟选择器,这不是CSS选择器的工作方式。我不得不拒绝这个想法。其实一个checkbox本身可以有三种状态,可以使用indeterminate状态。问题是,仅使用HTML并不会使它变得不确定。即使可以,当再次单击复选框时,它也会转换为选中状态。强迫第二个玩家在移动冰球时双击是不现实的。仔细阅读了MDN上关于:indeterminate的文档,发现radioinput普遍存在不确定状态。具有相同名称的单选按钮在未选中时处于此状态。哇,这才是真正的初始状态!真正有用的是,选择后者的兄弟姐妹也会影响前者!所以我在游戏板上放置了42对无线电输入。从以往的经验来看,以合理的顺序使用标签和匹配复选框或单选框可以解决问题,但我认为标签并不能使代码更简洁。为了更好的用户体验,我希望交互区域更大,所以让玩家点击列来移动冰球是有意义的。我通过在适当的元素上添加绝对和相对位置,将同一列中的控件堆叠在一起。这样每列只能选中最底部的圆孔。我仔细地为每一排圆盘的下落计时,它们的时间函数近似于二次曲线,类似于现实世界中的自由落体。至此,游戏的所有部分都完成了,但是下图很明显只有红色玩家可以操作。即使设置了所有控件,也只有红色冰球可以落在游戏板上。我用彩色半透明矩形可视化了Radio输入的可点击区域。黄色和红色输入在每列(=6行)上重叠6次,将底部行的红色输入放在顶部。红色和黄色的混合创造了黄橙色,可以在游戏板上看到。每列中可用的孔越少,这种橙黄色的强度就越低,因为无线电输入仅在:indeterminate状态下显示。由于红色输入始终覆盖每个洞的黄色输入,因此只有红色玩家可以移动。我对轮流只有一个模糊的想法。能不能用一个普通的siblingselector来解决玩家轮流的问题。思路是统计选中输入的个数,红色玩家在偶数(0、2、4等)时移动,黄色玩家在奇数时移动。没过多久我就意识到兄弟选择器通常不会(也不应该!)按照我希望的方式工作。另一种方法是使用第n个选择器。尽管我喜欢使用偶数和奇数等关键字,但我已经走到了死胡同。:nth-child选择器“统计”父类中的子元素,包括所有类型、类、伪类等。:nth-of-type选择器“统计”父类中某个类型的子类,不包括类或伪类。所以问题是无法通过:checked状态进行计数。CSS计数器也可以计数,何不试试看呢?计数器的一个常见用途是对文档中的标题(甚至多个级别)进行编号。它们由CSS规则控制,可以随时重置,并且可以按任何整数值递增(或递减!)。计数器“counter()”函数显示在内容属性中。所以最简单的方法是设置一个计数器,然后计算4-sub-link游戏中:checked输入的数量。这种方法只有两个困难。首先,您不能对计数器执行算术运算来检测它是偶数还是奇数。其次,您不能根据计数器的值对元素应用CSS规则。我用二进制解决了第一个问题。计数器的初始值设置为0。当红色玩家选择单选按钮时,计数器加1。当黄色玩家选择单选按钮时,计数器减1,依此类推。因此,计数器的值始终为0或1,偶数或奇数。解决第二个问题需要更多的创造力(阅读:hack)。如上所述,计数器只能显示在::before和::after伪元素中。这很明显,但它们如何影响其他元素?至少计数器的值可以改变伪元素的宽度。不同的数字有不同的宽度。字符1通常比0更苗条,但这很难控制。如果改变的是字符数,而不是字符本身,那么由此产生的宽度变化是可控的。在CSS计数器中使用罗马数字并不少见。罗马数字1和2与字符1和2相同,并且具有相同的像素宽度。我的想法是将一个玩家的(黄色)单选按钮放在左侧,另一个玩家的(红色)单选按钮放在共享父容器的右侧。最初,红色按钮覆盖在黄色按钮之上,然后容器的宽度发生变化,导致红色按钮“消失”,黄色按钮出现。它可以比作现实中的滑动窗口,有两个窗格,一个窗格是固定的(黄色按钮),另一个是可滑动的(红色按钮)。所不同的是,在游戏中只有一半的窗口是可见的。到目前为止一切顺利,但我不喜欢使用字体大小(和其他字体属性)间接控制宽度。更好的方法是使用字母间距,因为它只改变一个维度的大小。令人惊讶的是,即使是一个字母也有字母间距(在字母之后呈现),而两个字母有两个字母的间距。可靠性的关键是确保宽度是可预测的。宽度为0的字符加上单双字母间距是可以的,但是将font-size设置为0是有风险的。为了与所有浏览器兼容,请将字母间距(以像素为单位)设置得更大,将字体大小设置得更小(1px),是的,我说的是子像素。我需要容器的宽度在其初始大小(=w)和至少其大小的两倍(>=2w)之间交替,以便能够完全隐藏和显示黄色按钮。假设v是'i'字符(小写罗马字母,不同浏览器不同)的渲染宽度,c是letter-spacing(常量)的渲染宽度。我需要v+c=w为真,但这是不可能的,因为c和w是整数而v是非整数。我最终使用了min-width和max-width属性来约束可能的宽度值,所以我也将可能的计数器值更改为'i'和'iii'以确保文本在流下变宽并溢出约束。通过等式v+c2w,v<
...
.../*红四合一列选择器*/input:checked+.disc+input+input:checked+.disc+input+input:checked+.disc+input+input:checked~.outcome/*黄色四列选择器*/input:checked+input+.disc+input:checked+input+.disc+input:checked+input+.disc+input:checked~.outcome这是一个简单但丑陋的解决方案。为了检测一列中四个连接的情况,每个玩家都有11个类型和类选择器链接在一起。在hole元素后添加一个类名为.outcome的div,用于显示输出信息。在一个列被列包裹的列中,检测四连子存在一个问题,这个问题暂且搁置。如果使用类似的方法来确定是否有四个连续连接,那将是一个糟糕的主意。每个玩家将有56个选择器(如果我没数错的话),更不用说他们会有类似的错误检测。以后,[:nth-child(An+B[ofS])](https://drafts.c??sswg.org/sele...或列组合器会派上用场。为了更好的语义,可以添加为每一列新建一个div,将圆孔元素排列在里面。这样修改也会消除上述检测错误。然后,可以使用以下方法检测连续四个子连接:选择第一个红色单选为一列选择input,然后选择第一个红色radioinput被选中的相邻兄弟列,重复两次。这听起来很麻烦,需要一个“父”选择器。选择一个父节点是不可行的,但它是可行的选择子节点。如何使用选择器及其组合来检测一行中的四个子连接?选择一列,然后选择其第一个选择的红色单选输入,然后选择相邻的列,然后选择It'sthefirstredradioinputthatgetsselected,等等,再重复两次,听起来还是很麻烦,但确实有效。诀窍不仅在CSS中,在HTML中也是如此,下一列必须是前一列中单选按钮的同级元素,这会创建一个嵌套结构。........./*红色连续四个选择器*/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>输入:nth-??of-type(12):checked~.column>input:nth-of-type(12):checked~.column>input:nth-of-type(12):checked~.column::之后的语义被搞乱了,这些选择器只适用于红色玩家(黄色玩家再轮一次),但它确实具有不会被错误检测到的列或行的好处。结果的显示也必须修改,匹配列使用的任何::after伪元素应该是一致的。因此,必须在最后一个位置之后添加一个伪第八列。如上面的代码片段所示,列的特殊位置关系允许检测一行中的四元组。通过调整这些位置,可以使用相同的技术来检测对角线上的四元组。请注意,对角线可以在两个方向上。输入:nth-??of-type(2):checked~.column>input:nth-of-type(4):checked~.column>input:nth-of-type(6):checked~.column>输入:nth-of-type(8):checked~.column::after,input:nth-of-type(4):checked~.column>input:nth-of-type(6):checked~.column>输入:nth-of-type(8):checked~.column>input:nth-of-type(10):checked~.column::after,...input:nth-of-type(12):checked~.column>输入:nth-??of-type(10):checked~.column>input:nth-of-type(8):checked~.column>input:nth-of-type(6):checked~.column::after在最终的代码中,选择器的数量非常多,如果使用CSS预处理器,可以显着减少声明长度。不过,我认为演示代码相对较短。它应该处于中间位置,从硬编码一个选择器到使用4个魔术选择器(列、行、两个对角线)。当玩家获胜时显示消息修复错误任何软件都有边缘情况需要处理。Connect4的可能结果不仅是红色或黄色玩家获胜,而且游戏板被填满的平局。从技术上讲,这种情况不会破坏游戏或产生任何错误,缺少的是对玩家的反馈。我们的目标是检测黑板上有42个:checked单选按钮,并且没有一个处于:indeterminate状态。这需要为每个单选按钮进行选择。单选按钮在:indeterminate时无效,否则有效。因此,我将required属性添加到每个输入,然后在表单上使用:valid伪类来检测平局。抽奖结果的检测存在错误,当游戏板已满时会显示平局消息。在极少数情况下,黄色玩家最终获胜,同时显示获胜和平局消息。这是因为这些结果的检测和显示方法是正交的。我通过确保获胜消息具有白色背景并位于领带消息之上来解决此问题。还需要延迟绘制消息的转换,以免它与获胜消息混合。黄色的胜利覆盖了平局的结果虽然许多单选按钮通过绝对定位相互隐藏,但所有处于不确定状态的按钮仍然可以使用Tab键访问。这允许玩家将他们的光盘放在任何孔中。处理此问题的一种方法是使用tabindex属性简单地禁用键盘交互:将其设置为-1意味着不应通过连续的键盘导航访问它。要解决此问题,必须将此属性添加到每个单选按钮。...限制的最大缺点是游戏手柄没有响应,并且由于对回合制游戏的不可靠解决方案,可能会在小视图窗口上出现故障。我不会冒险重构反应式解决方案,由于实现的性质,硬编码似乎更安全。另一个问题是触摸设备上的粘性悬停。在正确的地方添加一些媒体查询是解决这个问题最简单的方法,但这会消除自由落体动画。有人可能会认为:indeterminate伪类已经得到广泛支持,而且确实如此。问题是某些浏览器仅部分支持它。注意兼容性表中的注释1:MSIE和Edge不支持单选按钮。如果你在这些浏览器中查看演示,你的光标将变成一个不允许的光标,这是一个无意但有点优雅的降级。并非所有浏览器都支持单选按钮的:indeterminate属性总结感谢阅读最后一部分!让我们看看这个游戏的一些统计数据:140个HTML元素350行(合理)0行CSSJavaScript0外部资源总的来说,我对结果很满意,反馈也很好。通过这个演示,我确实学到了很多东西,我希望能分享更多这样的文章!