---
# System prepended metadata

title: Pico-8 麻将开发 —— Day 0 准备

---

音效。。。
- [11:00](https://youtu.be/6G2StWNTFlo?list=PLea8cjCua_P3LL7J1Q9b6PJua0A-96uUS&t=664) 

2892955】

# Pico-8 麻将开发 —— Day 0 准备

最近吉考斯工业和 Clockwork 合作推出了一款名为 DEOT 的掌机，仔细读了一下说明书，看起来就是 [Gameshell](https://www.clockworkpi.com/) 的基础上加了一个涂装 MOD。不过，[DEOT 网站的设计](https://alioss.gcores.com/site/deot/index.html) 做的是真的酷，不愧是 [一家设计驱动的公司](https://www.gcores.com/articles/100191)。为此，Gcores 还专门邀请了 Clockwork 的 Hal，做了 [一期 Gadio Ad](https://www.gcores.com/radios/114273)，谈了谈极客精神与开源运动以及 Clockwork 背后的故事。

另外就是 Gcores 会与 Clockwork 合作 [进行一场 Game Jam](https://www.gcores.com/articles/114076)，在 21 日在北京有一个线下的 Demo Day，此前我还纠结 22 日到底是去东京参加技术书典还是去秦皇岛打星，这样一来就可以立马下单直接选择后者了。

由于最近在研究麻将，所以很自然的不如先复刻一个 [FC 上的麻雀](https://www.youtube.com/watch?v=z1V5md5l5jA) 到 Pico-8 上，不过再此之前，需要学习一些必要的知识。

## 梦幻主机 Fantasy Console

> In Pico-8, there is no real reason to make a distinction between the player, the consumer and the maker. If you can play, you can also make: the source and assets are always a single image you can share and change as much as you want to.
> [—— Hello everyone, Pico8 Fanzine #2](https://sectordub.itch.io/pico-8-fanzine-2)

> 通过一系列互补工具研究这些格式的过程中，我渐渐开始了解作为一个运行平台或者运行环境应具有的功能。同时，那些为老电脑制作的游戏拥有独特的视觉效果和氛围，理应有这样一个平台来解决这些独特的设计问题，促使人们制作这种游戏。
> [—— Pico-8: 一部梦幻主机的诞生简史](https://indienova.com/indie-game-development/a-brief-history-of-pico-8/)

如同 Donald Knuth 为了 "illustrate machine-level aspects of programming"，而设计了 [MMIX 语言](https://en.wikipedia.org/wiki/MMIX) 一样，Zep 意识到为了 "drive the identity of games made for [retro computers](https://en.wikipedia.org/wiki/Retrocomputing)" 我们同样需要一台特别的主机。

> 对Pico-8最好的解释，就是它是一款并不真实存在的游戏机的模拟器。它的具有非常鲜明的8-bit风格，因此你可以把它像想为类似Game Boy Color这样的小掌机。它的提供一块128x128像素，16色的显示屏，以及四通道的音乐芯片。
> 不过，Pico-8却不仅仅是某种架空历史的实践，它设计的出发点与市面上已有的类似设备有着很大的不同。它并没有试图去最大化的利用有限的逻辑功能，而是提供了一批积木式的代码部件，尽可能的挖掘使用的乐趣。开发商希望随着时间的推移，Pico-8的技术框架将形成一种独特的审美观：极简主义并有着丰富的表现力。
>  [—— Pico-8——神奇的虚构游戏机](https://zhuanlan.zhihu.com/p/30405996)

如同 [这篇文章](https://zhuanlan.zhihu.com/p/30405996) 所提到精神分裂那样，Pico-8 事实上既复古又现代。她的复古体现在它的各种限制之上，而这些限制可以让开发者专注在某些更为重要的事务之上，而她的现代则体现在了她实际上依赖于许多新的技术，并且拥有一个自由分享的社区和文化，而这些是以前的那个时代所没有的。至于为什么叫做 Fantasy Console，或许是因为这台主机并不真实存在（但是如你所见，Gameshell 设计之初，就是为了给这些 Fantasy Console 提供实体承载，breakout hero 的作者的最新一篇帖子就是做了 [一期 Gameshell 的开箱视频](https://www.lexaloffle.com/bbs/?tid=34159)）。


### 蔚蓝 Celeste 

> Pico-8 gained additional attention in 2018 with the release of Celeste. Originally created as a Pico-8 game for a game jam, Celeste Classic became one of the most popular games on the Pico-8 BBS, prompting the developers to expand the concept into a more expansive, fully realized game. The original Pico-8 version of Celeste is fully playable as an easter egg in the full version of the game.
> —— [Wikipedia, Pico-8](https://en.wikipedia.org/wiki/Pico-8)

Pico-8 上目前最著名的游戏应该就是蔚蓝（Celeste）了。

你可能没听说过 DEOT 但是肯定听说过 Gameshell！
你可能没听说过 Gameshell 但是肯定听说过 Pico8！
你可能没听说过 Pico-8 但是肯定听说过 Celeste！

Celeste 最早的原型就是 [发布在 Pico-8](https://mattmakesgames.itch.io/) 上，并且是公认的 Killer App，[上周 Epic 还在免费送](https://www.gamespark.jp/article/2019/08/30/92565.html)。好吧，它们基本的关系，听起来就像是 —— Unix 的 Pipeline 一样 —— DEOT 是 Gameshell 的涂装，Gameshell 是一个开源硬件，可以用来跑 Fantasy Console，Pico-8 是 Fantasy Console 的鼻祖。

这个 8bit 的版本也非常有难度，事实上正式版的蔚蓝里直接放进去作为了彩蛋。

### 打砖块 Breakout 

在准备动手开发麻将之前，不妨先让我们从一些简单的东西入手，在上文提到的 [Pico-8 Fanzine #1](https://sectordub.itch.io/pico-8-fanzine-1) 里，紧接着 [Pico-8: 一部梦幻主机的诞生简史](https://indienova.com/indie-game-development/a-brief-history-of-pico-8/) 的就是一篇 Squashy 的教程，而 Squashy 可以看作是 Breakout 的雏形。

我这边也是先用这个来入了门。

- [Bilibili, 岛娘，Pico-8 开发实战](https://www.bilibili.com/video/av66749016)
- [Github, Pico-8 Workshop](https://github.com/lychees/pico8-workshop)

不过这个教程是 Beginner 向的版本，例如 Pad 和 Ball 都没有采取面向对象，学习了一遍感觉远远不足以帮助我实现麻将，Github 上搜了一圈，果然有一个 [代码风格更好的版本](https://github.com/rustybailey/squashy/blob/master/squashy.p8)。

继续迭代这个游戏，就可以实现大砖块，但是想要完成度高，也是十分不容易的。

不放让我们先在 Pico-8 上搜了一圈，目前看来实现的最好的项目是 Breakout-Hero（[Itch](https://krystman.itch.io/breakout-hero) | [Pico-8](https://www.lexaloffle.com/bbs/?tid=31484) | [Youtube](https://www.youtube.com/watch?v=YQzwVDMIfyU&list=PLea8cjCua_P0qjjiG8G5FBgqwpqMU7rBk&index=1)）。这个版本不仅完成度高，操作流畅，而且还附赠了一个 [75 集的视频教程](https://www.youtube.com/watch?v=YQzwVDMIfyU&list=PLea8cjCua_P0qjjiG8G5FBgqwpqMU7rBk&index=1)，可以说是非常良心了，完美体现了 Pico-8 的精神：If you can play, you can also make。

其次是 [Beta Breakout](https://www.lexaloffle.com/bbs/?tid=30581)，不过手感就显得僵硬很多了。有趣的是下面唯一一个留言还是 Breakout-Hero 的作者...我还发现了一个叫做 [Breakout arcade](https://www.lexaloffle.com/bbs/?tid=28582)，这个版本着实非常逗。。。在 Fantasy Console 里再套一层 Console，结果就是你的像素范围变得更小了。。。

## 下文

下文将会开始在 Pico-8 上实现 Mahjong，如果我能做完的话 ...



---
## 参考资料
- [Pico-8 Wiki](https://pico-8.fandom.com/wiki/Pico-8_Wikia)
- https://medium.com/@btco_code/why-i-spent-almost-a-year-building-an-rpg-game-for-a-fantasy-console-2bbe2e1d8cb8

#### Developing an Open-World Adventure Game in Pico-8
- [Annoucement](https://www.reddit.com/r/pico8/comments/cxukb7/finally_finished_my_openworld_ecs_videos/)
- [Youtube Channel](https://www.youtube.com/playlist?list=PLQaniv5ve2yh6PGgw_Pg7OE6dDLVhpxAA)


# Pico-8 麻将开发 —— Day 1 和了

[Github](https://github.com/lychees/pico8-workshop) | [Itch Demo](https://minakokojima.itch.io/mahjong)

先考虑站在巨人的肩膀，搜一下 Pico-8 的 BBS，目前只有一个三年前的草稿项目，虽然烂尾，但是里面其实已经包含了非常多可以拿来用的东西，不妨先来审查一下里面都有啥。这个链接里一共包含三份代码，第一份代码只做了一个片头动画，第二份做了抓牌的过程，第三份做了轮流出牌，想要让游戏可以运行起来，只要补上游戏规则，做掉鸣牌和宣言，最后加上一个结算的画面就可以了。

首先这份草稿里包含了一份完整的麻将游戏的 Sprite，虽然如 [这位的评论](https://www.lexaloffle.com/bbs/?pid=26738#p) 所说，这个像素图实在有点难以辨识。不过其实我一开始其实考虑是直接上 ASCII 码的，所以画面这块我们不改。

作者做了许多非常有趣的动画和音效，例如 [抓牌、看牌，洗牌、码牌](https://www.bilibili.com/video/av19390801)，甚至还有掷骰子的特写。不过为了调试和开发方便，这些在开发时我们需要跳过。

主要用到的算法有：
- 整理手牌需要使用排序，作者这里实现了一个 [冒泡排序（Bubble Sort）](https://zh.wikipedia.org/zh-hans/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F)。[L321](https://github.com/lychees/pico8-workshop/commit/106be5779ca7f97cdc86ac27a9d8fb00e8ffd3ec#r34972559)
- 生成牌山的时候需要用到洗牌（Random Shuffle）算法，不过代码里的是错的。[L374](https://github.com/lychees/pico8-workshop/commit/106be5779ca7f97cdc86ac27a9d8fb00e8ffd3ec#r34972570)
- 判断胡牌和立直的时候需要搜索，这个作者还没写。

参考资料：
- [云风, 洗牌](https://blog.codingnow.com/2007/09/shuffle.html)
- [C++ Reference，Random Shuffle](http://www.cplusplus.com/reference/algorithm/random_shuffle/)

## 开发计划

制定以下开发的几个 Milestone。

- 完整的日麻规则流程。
    - 可以鸣牌。
    - 可以和了。
    - 可以立直。
        - 可以在立直是显示几面听。
        - 可以标记这些牌的剩余张数。
    - 可以正确计算得点和役。
    - 处理宝牌。
    - 支持东风局和半庄。
- <del>可以保存游戏进度，从中断的地方开始。</del>
- <del>可以回放游戏录像。</del>
- <del>可以联机</del>


### 准备工作

> update: i was having trouble wrapping my head around structuring my code around the _update() and _draw functions, and realized that for a game like this (no camera movement, minimal animation, simple actions taking place on a "turn" basis), it actually makes more sense to take a more simple approach -- ie, the game logic will just be one giant loop that cycles through each players turns until the game is over. Whenever I need to animate something I can just have a little loop that calls flip() after each frame of the animation is drawn. It should be obvious that I kind of have no idea what I'm doing.
[—— Pico8 BBS, mahjong demo (you can draw and discard now (that's all))](https://www.lexaloffle.com/bbs/?tid=3884)

如同作者上面的留言，他注意到对于这种没有太多动画镜头的回合制游戏，自定义的画帧比 `_update()` 要更好用。话虽如此，主循环里还是非常多出现了很多次可以复用的地方，考虑鸣牌的出现会打断游戏的流程，我们最好把需要玩家给反馈的流程都独立出来，这一块是首先需要调整的地方。

接下来会先制作鸣牌的界面，除了鸣牌（吃、碰、大明杠），还有暗杠、加杠、立直和自摸也会用到。鸣牌界面会分两个部分，第一个选单选择进行的动作，然后在某些情况下（吃、杠、立直）会进入二级菜单，不过考虑到八位机 UI 不好做，不如我们就在手牌中进行选择，将相关的牌高亮显示。事实上老任的麻雀就差不多是这样做的，不过因为没有任何提示，又不懂假名，小时候一直被坑。

### 手牌对象

紧接着我们对麻将牌和手牌的对象进行重构，用来给接下来写搜索做准备。

不妨先看一下 [现在作者定义的 tile 对象](https://github.com/lychees/pico8-workshop/commit/d03a8169af46ec7faa31ed1518411117f7abd20c#r34981788)，我们可以看到，使用 Pico-8 开发时我们经常把展示（View）和逻辑（Model，Control）都混在一起，之前我在注释掉所有动画时，就需要仔细找，好在现在代码不多，还能够应付。比如这组 dora_indicator 为什么要放在这里，sprite 相关的代码更是完全不具有可读性，跑一遍才知道在干啥 ... 

> PICO-8 是一个很容易上手的小游戏开发平台，可以用于快速试验一些想法。同时，由于上手简单，或许也可以作为儿童编程教育的一个选择。
简单是 PICO-8 的优点，同时也是它的缺点，经过一两天的折腾，我觉得主要有两个缺点：
> 1. 调试比较困难。目前只能通过 printh() 方法将日志打印出来进行查看，没有断点调试之类的功能支持；
> 2. 对 Lua 的支持有限，比如不支持标准库里的 [table.xxx](https://www.lua.org/pil/19.2.html) 方法，导致很多网上优秀的基于标准 Lua 写的第三方库无法使用。
> 
> [—— 使用 PICO-8 开发迷宫小游戏](https://blog.oldj.net/2019/06/08/pico-8-maze/)

关于手牌的状态我本来想参考，天凤的 [牌理](http://tenhou.net/2/) 页面，用字符串紧凑的实现，但是 Pico-8 内置的 lua 居然不支持各种 string.xxx 方法，因此与其头铁不如还是在现有的基础上先加强吧。

于是第一步首先是增加了一个 raw_index 成员变量，这样排序也可以顺便得到简化。紧接着是加上了 count map，来表示每种牌有多少张，显然看起来每个玩家应该都有一个，不过简单起见，我们第一步先维护玩家自己。

接下来，先实现了判断一般型和了的搜索方法。一个细节是，找顺子和刻子的代码都可以在鸣牌的时候复用，但是找顺子的时候，搜索过程里一定是当前牌是顺子的第一个（因为一定会被第一次出现的时候搜到），而在鸣牌的时候，就有可能是后面两张了。这里我用了 `any_shuntsu()` 和 `any_shuntsu_start_with()` 予以区分。[L738](https://github.com/lychees/pico8-workshop/commit/d03a8169af46ec7faa31ed1518411117f7abd20c#r34981774)

这样吃碰也都有了，接下来会先处理没有二级菜单的碰。

### 

https://www.bilibili.com/video/av6542298/?p=5


Zoom: https://zoom.us/j/722330768
Bilibili: https://live.bilibili.com/844926
Github: https://github.com/lychees/pico8-workshop
Itch Demo: https://minakokojima.itch.io/mahjong
Hackmd Live: https://hackmd.io/@E-5gxTGiSByBOKpvsaKa_g/H12aF5aHr/edit




