[教你做小游戏] 展示斗地主扑克牌,支持按出牌规则排序!支持按大小排序!
我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。
问题描述
我们想做一个斗地主游戏,其中最重要的一点是,把扑克牌展示出来。
一副牌有 54 张,我们给每张牌 1 个编号(id),取值 1-54。如果涉及到 2 副牌,就取 id 为 1-108。
展示牌,其实就是给你一个 id 列表,按需展示列表中的牌即可。
而展示牌有 3 种排序方式:
不排序,列表是什么,就展示什么。(发牌、底牌常用)
按照大小排序。(手牌常用)
按照出牌规则排序。(出牌常用,规则比如顺子、连对、飞机、四带二、炸弹等)
今天,我们就来实现它们!
第 1 步,展示 1 张牌
准备素材
牌有 54 种,加上牌背面,有 55 种图案。我们先准备好素材:
如果要展示 1 张牌,以它为背景,使用background-position
和width
、height
对整个大图片裁剪即可。
不要拆开这个大图,让用户一次性下载 55 张图片,那样速度会肉眼可见的慢。因为大多数浏览器不能并发 55 个请求下载图片,它可能一次最多建立 8 个 TCP 连接来下载,你可能需要 8 次 RTT 才能下载完。(55 除以 8 向上取整=8)
所以,做 Web 开发,一定要尽量拼接多个小素材成为一个大图片,再去裁剪它展示素材。
写好 css 做裁剪
我们利用 class,定义一个.poker
写所有扑克牌共用的样式,再给每个扑克牌定义一个background-position
(裁剪位置)即可。
例如,这是 id 为 1 的扑克牌的样式。每个扑克牌单独的样式很简单,只有 1 行,定义background-position
即可。因为其它样式都是一模一样的,用.poker
复用即可。
不再罗列了,可以参考 style.css 源码: github.com/HullQin/poker_fe
定义 扑克牌 ID->图片 ID 的映射
开头我们提到,可能有 2 幅牌,而他们的图片样式应该是一样的。所以需要通过取余数,把 108 个 ID 映射到 54 个值。
代码中,我用id = 0
表示扑克牌的背面。
封装一个组件
你可以封装为 React 组件或 Vue 组件,或其它你采用框架支持的组件。
我代码使用了 React,所以封装为 React 组件。
这是一个非常简洁的组件,只需要传入扑克牌的 ID,就会展示这张扑克牌了。此外,可以传入className
或者style
,自定义样式。
至此,展示 1 张扑克牌,我们就完成啦!
第 2 步,不排序展示多张牌
目前还比较简单,只需要提供一个扑克牌 ID 列表,我们依次展示即可。我们用ids
参数作为扑克牌 ID 列表,需要组件引用者传入。
我们需要关注一下扑克牌图片的高度,我们定义一个默认高度(159),此外也允许引用组件者通过height
新设置高度。
我们还需要关注扑克牌之间的间隔:如果是底牌,那么间隔大一些;如果是手牌、或者出牌,牌会比较多,间隔应该是负数,有重叠的效果。我们用overlap
参数,表示是否需要重叠。
可以看到,我们引用了Poker
组件,并控制了每一个扑克牌的left
属性,让它们等间距排列。
你可能会问:啊!你为什么用列表的 index 做 Key 呢?为什么不用扑克牌 ID 做 Key 呢?
因为我们这个列表非常小,不超过 108,不会有性能问题,所以采用了最稳妥的方式,以 index 作 Key,是独一无二的,绝不会出错。如果你能保证你传入的扑克牌 ID 唯一,也可以使用扑克牌 ID 作 Key。
第 3 步,按照大小排序
扑克牌是有大小的,顺序是:大王、小王、2、A、K、Q、J、10、9、8、7、6、5、4、3。
此外,为了美观,我们也期望同样大小的数字的花色,也是有顺序的。例如按照♥️、♦️、♠️、♣️的顺序排列,当你有很多炸弹时,会非常漂亮,令玩家舒适。
所以,我们要按数字大小排列,数字相同时,按固定花色顺序排列。
只要修改一下StaticPokerList
,对它的ids
参数做一个排序即可。
排序依据是什么呢?需要手写函数嘛?
答案是:当然不需要!只要我们把 54 个 ID 映射到 54 个数字,再按数字排序,就大功告成了!这是效率非常高的方式!
这定义了映射,传入 ID 为 1-54,即可映射到牌的具体大小。规定四个花色的小数部分不一样,分别为.2 .4 .6 .8,这样数字相同时,就按花色排序啦。
定义好每张牌的数字,再根据大小数值排序即可。
这就是排序函数,传入ids
,输出排好序的ids
。
当然,这调用了mapPokerIdToCardId
,是考虑到 2 幅牌的情况,先把 1-108 映射到 1-54,再映射到具体数值。
第 4 步,按照规则排序
上面按大小排序还是太简单,只有结合了游戏规则的排序,才是最难的!
我根据斗地主规则,总结了这样的排序算法:
输入:ids,即你出的牌的列表(前提:是符合斗地主规则的一串牌)。
输出:sortedIds,按出牌规则排好序的列表。
统计每个数字的出现次数。
按照出现次数排序,出现频次高的,放在前面。
如果频次相同,按照数字大小排序。数字小的,放在在前。
同样的数字,要按照固定花色顺序排序,保证美观。
验证算法正确性:
顺子:3、4、5、6、7。频次都是 1,排序结果是 3、4、5、6、7。
连对:QQ、KK、AA。频次都是 2,排序结果是 QQ、KK、AA。
三带一:KKK2。K 频次是 3,2 频次是 1。排序结果是 KKK、2。
四带两对:44443322。4 频次是 4,3 和 2 频次是 2。排序结果是 44443322。
王炸:大王、小王。频次都是 1,规定大王数字更小,那么排序结果是大王、小王。
这里定义pokerNumberMap
为数字大小,为了让王炸时大王在前小王在后,我们规定大王=53、小王=54 即可。
pokerRuleMap
同样有小数部分,是为了同数字时按花色排序。
下面是排序的函数,可以看到,我们先统计了frequency
,对 ids 排序时,判断了 2 个数字谁出现频次更高,高的靠前。
写在最后
我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。
版权声明: 本文为 InfoQ 作者【HullQin】的原创文章。
原文链接:【http://xie.infoq.cn/article/b6d090709e5a14d9ad516add2】。文章转载请联系作者。
评论