《era 麻雀》开发日志
路线图 Roadmap#
Alpha Version#
完成一个麻将游戏最基础的功能:摸切,鸣牌(吃 / 碰 / 杠),和牌。
Beta Version#
完成日麻规则:立直、振听、宝牌、役种、根据符数 / 翻数计算点数。
任务清单 Todo#
Alpha Version#
- 洗牌
- 摸起始手牌
- 每巡摸牌
- 摸牌后切牌
- 别家切牌后鸣牌
- 和牌
- 自摸:自家摸牌后和牌
- 荣和:别家切牌后和牌
Beta Version#
- 立直
- 立直(听牌)判定
- 立直操作(切牌)
- 立直宣言(特殊渲染牌河 / 自家立直棒标记 / 立直数 +1)
- 振听(听牌判定)
- 舍张振听(换听才能解除,否则只能自摸)
- 同巡振听(临时)
- 立直振听(触发方式类似同巡振听,持续时间类似舍张振听)
- 宝牌
- 表宝牌:宝牌 + 杠宝牌
- 里宝牌:里宝牌 + 杠里宝牌(立直限定)
- 赤宝牌:赤五万 / 赤五饼 / 赤五索
- 役种
- 门清限定
- 高位替换 or 复合
- 数值计算
- 符数(来自手牌牌型)
- 翻数(来自役种 / 宝牌)
- 最终点数(来自符数、翻数、
庄家 / 闲家 、自摸 / 荣和、本场数 / 立直数)
开发思路讲解 Walkthrough#
参考雀魂进行牌谱设计#
|
|
数字:
11
~47
代表 一万 到 红中51
/52
/53
代表赤宝牌60
代表「摸切(摸到的牌当场切掉)」
字母:
- 立直标记:
r立直宣言牌
(作为舍张) - 吃标记:
c吃的牌
(作为进张) - 碰标记:
p位置
(作为进张) - 杠标记:
k位置
(作为舍张,结合前面是否存在p
可以判断暗杠/加杠)
其他碎碎念 Gossip#
这个项目其实是一个大型 era 开发示例工程,从一开始的初衷就是。
我只是想要亲手演示:
开发 era 游戏真的很简单,而且你可以做到很多你本来以为 era 不可能做到的事。
但自从 2022 年 5 月份立项以来,每每在难以入眠的深夜都会追悔莫及。
我他妈为什么要挖这么大一个坑?
复刻日麻需要的「设计量」实在是太多了,多到我很多次只要一想起就气馁。
但自己开的坑,哭着也要填完。
我就是这么一个人,我做事无所谓有没有人认可,我做东西是做给自己的。
我想做,就会去做。
我也会软弱,也无数次产生过弃坑的念头。
但都是一时的畏难情绪,追根究底,我内心深处想不想做完?想。
那没什么好说的,随便哭,哭完继续干活呗。
(已废弃)需求设计 & 任务清单#
fix#
- 设置界面等地方没有做容错处理,防止用户强行手动输入「显示为 disable」的按钮
feature#
- 标题界面的 LOGO:用 SPRITE 绘制,自适应窗口大小
- 研究 G 系列命令的 mask 功能,能否做到简单的 shader?
- 研究
HTML_PRINT
的<img />
标签的srcb
属性,感觉可以做文章 - 研究能不能实现真正的动图(而不是画一张擦一张实现伪动画)
- 按照官方函数的 API 来说是可以实现的
SPRITEANIMECREATE
/SPRITEANIMEADDFRAME
/SETANIMETIMER
- 复刻 EE demo 里的头像旋转 / 抖动功能(第一次看到给我逗乐了)
- 参考《era 俄罗斯方块》的即时刷新画面/判断输入实现
- 无限轮询 ONEINPUT(不需要回车)输入
- 研究它是如何判断游戏进程里经过的时间的(难道是固定帧率做的?)
- 人机 AI 仿真思考延迟
- 房间设置可自定义等待时间
- 类似雀魂的限时设计:长考 20 + 4,两个计时器
前者不可恢复,后者自动重置,优先消耗后者
-
_fixed.config
强制规定:字高(关系到图片大小)、行高(关系到图片显示出来具体占几行)、禁止调整窗口尺寸(宽度/高度)、禁止最大化窗口 - 整理摸切的全部流程,优化业务逻辑
- 完善鸣牌操作:
- 自家摸牌之后(立直/暗杠/加杠/自摸)
- 别家切牌之后(吃/碰/杠/荣和)
- 梳理整个游戏主流程:发牌,开始循环:
- 手牌满时判定是否和牌,询问自家操作(立直,和牌,暗杠/加杠,不能跳过)
- 切牌
- 询问别家操作(下家吃,别家碰/大明杠/和牌,跳过)
- 牌权切换到下家。DRAW SELF_ACTION OTHERS_ACTION 判断是否是玩家
- 考虑用单独的 XML 文件来存麻将游戏用到的全局变量
data/mahjong_config.xml
MAHJONG_CONFIG
- 考虑是否要手写 MD5 来做牌山验证
- 设计牌谱的数据结构:
- 四家
- 点数
- 起始手牌
- 进张记录
- 舍张记录(牌河和舍张都可以从这里判断)被鸣的牌,雀魂是直接删掉,这里为了可读性考虑将其变为负数
- 四家
- 设计副露字符串格式(鸣牌座位
(call_seat + 4 - self_seat) %4
)遍历所有可能性:- 吃 1 / 吃 2 / 吃 3
- 碰下家 / 碰对家 / 碰上家
- 大明杠下家 / 大明杠对家 / 大明杠上家
- 碰下家加杠 / 碰对家加杠 / 碰上家加杠
- 数字牌暗杠 / 字牌暗杠
- 设计和牌分析数据结构,当然还是 XML
- hand_tiles
- win_tile
- fu
- yaku
- han
- 对局信息:立直数 本场数 庄家闲家
- 对局结束自动保存牌谱文件,然后释放「各家手牌 MAP」和「当前牌谱 XML」
- 格式
log/mahjong/mahjong_log_20220616_154759.xml
(对局开始的时间) - 维护一个牌谱记录表:只保留最近 100 场牌谱,防止数据无限膨胀(其实也无所谓啦,但这是一个工程规范问题)
- 格式
-
特殊能力 - 预言(透视)下一张牌,可以加入预言成功率设定,或者只能预言部分信息(万饼索字)
- 透视别家手牌
- 神态系统:别家会透露神态,可以观察微表情来判断对方准备打什么牌
说起来有点玄学,其实主要还是我想加入「明明很 xx 但正在打牌不得不忍住」的设定 - 超算能力:自动计算向听,何切,安牌,危险牌,最可能的役种,听牌改良(剩余和牌张数)
这个很简单,就是直接把人机 AI 的算法搬过来个屁啦,说得你好像已经写完人机 AI 了一样
- 允许把牌谱转换为 天凤 / 雀魂 的牌谱格式(然后就能拿去给各种牌谱分析器读了)
- 处理字符串形式的数组
1,2,3,4
:utils/common.erb
@sarray_has
- 字符串解析功能兼容缩写:
1s2s3s4s5s6s
→123456s
- 手牌拆分:单张 顺子 明刻 暗刻 明杠 暗杠
- 游乐场功能
-
根据 符数/翻数/庄闲/是否自摸 计算打点已完成 - 和牌分析(是否和牌/符数/役种/翻数/最后打点):可以手动输入字符串(
123m444p
),也可以点击图片 - 何切题
-
- 日麻教程:抄雀魂的教学
- 新手入门
- 首次打开生成 sprite:
tutorial_page_1
- 役种一览
- 首次打开生成 sprite:
yaku_sample_page_1
- 数据统计:
- GDRAWRAW 不存在 SPRITE 就绘制
- 具体数据保存在特制的存档(我对 Emuera 原版乱七八糟的实现的信任度为 0)
- 有没有必要加密?感觉没必要,一个小破单机而已,他都这样了,你就让他改吧
todo#
- 业务逻辑
-
版本更新检测22.6.10 done -
标题界面22.6.12 done - 麻将游戏
-
公平洗牌22.5.29 done -
各家(仿真)摸起始手牌22.5.30 done - 主游戏流程:回合结束自动轮换到下家,循环到有人和牌或流局
-
鸣牌判定22.6.14 done -
和牌判定22.6.14 done - 振听判定
- 听牌判定(立直判定)
- 役种判定
- 符数计算
-
翻数计算22.6.13 done -
打点计算22.6.13 done
-
- 牌谱回放
- 基础教程
- 统计数据
-
游戏设置22.6.12 done -
退出游戏22.6.12 done
-
- 操作
-
输入容错:统一处理(理论上)玩家无法进行的操作22.6.1 done -
便捷功能开关(自动和牌 / 自动摸切 / 不吃碰杠 / 自动理牌)22.5.31 done - 自动和牌
- 自动摸切
- 不吃碰杠
-
自动理牌:兼容庄家起手的手牌排序22.5.31 done -
作弊功能:透视别家手牌22.5.31 done -
作弊功能:预测下一张牌22.5.31 done -
玩家手动进行摸切操作22.5.31 done - 玩家手动进行鸣牌操作
-
- 人机
- 简单人机 AI:人机进行摸切操作
- 简单人机 AI:人机进行鸣牌操作
- 完整人机 AI(进张数、向听数、向听前进后的进张数、改良进张、役种预测、速度、打点、和率、
危险度 ) - 多难度人机 AI
- 简单难度:遵循最基本的几条原则
- 普通难度:分析局势后根据权重判断何切
- 显示
-
渲染可交互的玩家手牌22.5.29 done - 渲染副露
-
渲染别家手牌22.6.1 done - 渲染各家牌河(舍张历史 / 现物记录)
- 渲染对局信息(各家点数 /
东四局 / 立直棒 ×n
/ 本场数 ×m
) - 渲染牌桌(把以上图像叠成一整张图)
-
i18n(内置多语言支持)22.6.10 done -
使用开源的等宽字体22.6.11 done 最后选定更纱黑体
-
- 声音
-
操作音效(打牌 / 吃 / 碰 / 杠 / 立直 / 荣和 / 自摸)22.5.29 done - 自动轮换的 BGM
-