1. 核心玩法
长沙麻将——核心玩法
本章你将掌握
可以学到的 Godot 技能
| 技能 | 说明 | 难度 |
|---|---|---|
| 牌面渲染系统 | 使用 TextureRect 和 Sprite2D 渲染 108 张麻将牌,实现牌的显示、排序、高亮 | ⭐⭐ |
| 规则引擎 | 实现吃碰杠胡的判定逻辑,包括顺子、刻子、将的识别算法 | ⭐⭐⭐⭐ |
| 手牌排序算法 | 按花色和数字自动排序手牌,方便玩家查看和操作 | ⭐⭐ |
| 网络多人同步 | 使用 MultiplayerAPI 实现 4 人联网对战,同步摸牌、出牌、吃碰杠等操作 | ⭐⭐⭐⭐ |
| AI 决策系统 | 实现麻将 AI 的出牌策略、吃碰杠决策、听牌判断 | ⭐⭐⭐⭐⭐ |
| 动画系统 | 实现牌的移动、翻转、消失等动画效果,提升游戏体验 | ⭐⭐⭐ |
| 房间系统 | 实现游戏房间的创建、加入、退出、准备等功能 | ⭐⭐⭐ |
| 计分系统 | 实现自摸加底、扎鸟等长沙麻将特色计分规则 | ⭐⭐⭐ |
关键 Node 节点
游戏核心节点
点击节点可跳转到对应文档查看详细说明。
游戏系统结构图
┌─────────────────────────────────────────────────────────┐
│ MahjongGame 主控制器 │
└────────────┬────────────────────────────────────────────┘
│
┌────────┴────────┐
│ │
┌───▼────┐ ┌────▼─────┐ ┌──────────┐
│ 游戏管理 │◄────►│ 牌管理器 │◄────►│ 玩家管理 │
│ Manager│ │ TileMgr │ │ PlayerMgr│
└───┬────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ ┌─────────────┴──────┐ │
│ │ │ │
┌───▼──▼───┐ ┌────▼─────┐ ┌──▼──────┐
│ 规则引擎 │ │ 牌池系统 │ │ AI系统 │
│RuleEngine│ │ TilePool │ │AIDecision│
└──────────┘ └──────────┘ └─────────┘
│ │ │
└──────────┬───────────┴─────────────┘
│
┌──────▼──────┐
│ UI & 渲染 │
│ UIManager │
└─────────────┘
│
┌──────▼──────┐
│ 网络同步 │
│NetworkManager│
└─────────────┘系统说明:
- 游戏管理器:控制游戏流程(发牌→摸牌→出牌→响应→结算)
- 规则引擎:判断吃碰杠胡是否合法,计算番数
- 牌管理器:管理 108 张牌的创建、分配、回收
- 玩家管理器:管理 4 个玩家的手牌、操作、状态
- AI 系统:为电脑玩家提供决策支持
- UI 管理器:显示牌面、按钮、分数等界面元素
- 网络管理器:同步多人游戏状态
1.1 什么是长沙麻将?
长沙麻将是流行于湖南长沙地区的一种麻将玩法。和中国最常见的"国标麻将"相比,长沙麻将有几个鲜明的特点,让它节奏更快、更刺激:
| 特点 | 国标麻将 | 长沙麻将 |
|---|---|---|
| 牌的数量 | 136张 | 108张(无字牌) |
| 字牌 | 有东南西北中发白 | 没有 |
| 吃牌 | 可以吃 | 可以吃(仅上家) |
| 番数计算 | 非常复杂 | 相对简单 |
| 特殊规则 | 多种 | 自摸加底、扎鸟 |
简单来说,长沙麻就像是一个"精简版"的麻将——去掉了字牌,规则更简洁,但加入了"自摸加底"(自己摸到胡牌牌额外加分)和"扎鸟"(翻牌决定加番)等独特玩法,反而更有趣。
1.2 麻将基本概念
如果你从来没打过麻将,先了解几个基础概念:
牌面
长沙麻将只有三种花色,每种花色有1到9共9个数字:
| 花色 | 符号 | 示例 | 说明 |
|---|---|---|---|
| 万 (万子) | 一万 ~ 九万 | 一万、二万...九万 | 像人民币的"万" |
| 筒 (饼子) | 一筒 ~ 九筒 | 圆形图案,像一个饼 | 像铜钱 |
| 条 (索子) | 一条 ~ 九条 | 竹子形状的图案 | 像一串铜钱 |
每种数字有4张牌(因为总共4个玩家),所以:
- 每种花色:9个数字 x 4张 = 36张
- 三种花色:36 x 3 = 108张
牌的组合
麻将中,几张牌可以组成以下组合:
| 组合名称 | 数量 | 说明 | 比喻 |
|---|---|---|---|
| 顺子 | 3张 | 同花色连续3张 | 1万+2万+3万(像走楼梯,一阶一阶上) |
| 刻子 | 3张 | 完全相同的3张 | 三张五万(像三胞胎) |
| 杠 | 4张 | 完全相同的4张 | 四张五万(像四胞胎,更强) |
| 将 (对子) | 2张 | 完全相同的2张 | 两张五万(一对,是胡牌必需的) |
胡牌的基本条件
要"胡牌"(赢),你的手牌必须满足以下条件:
手牌 = N个顺子/刻子 + 1个将(对子)
示例(14张牌):
[一万][二万][三万] + [五万][五万][五万] + [一条][二条][三条]
+ [七筒][八筒][九筒] + [东风][东风]
顺子 刻子 顺子 顺子 将(对子)1.3 长沙麻将核心规则
1. 无字牌
长沙麻只有万、筒、条三种花色,没有东南西北中发白。这让牌型判断更简单,但也意味着可以用来凑顺子的牌更多了。
2. 吃碰杠
| 操作 | 说明 | 限制 |
|---|---|---|
| 吃 | 拿上家打出的牌,和自己手中的两张组成顺子 | 只能吃上家的牌 |
| 碰 | 拿任意玩家打出的牌,和自己手中两张相同的牌组成刻子 | 任意玩家均可 |
| 杠 | 拿任意玩家打出的牌,和自己手中三张相同的牌组成杠 | 任意玩家均可 |
3. 自摸加底
这是长沙麻将最刺激的规则之一:
| 胡牌方式 | 说明 | 加分 |
|---|---|---|
| 点炮 (放炮) | 别人打出的牌让你胡了 | 正常计分 |
| 自摸 | 自己摸到的牌让自己胡了 | 额外加底(基础分翻倍) |
自摸意味着胡牌的人获得更多分数,同时其他三家都要付钱。这鼓励玩家更积极地听牌(只差一张就胡),而不是消极地等别人点炮。
4. 扎鸟
扎鸟是长沙麻将的特色玩法:
| 术语 | 说明 |
|---|---|
| 扎鸟 | 胡牌后,从牌墙中翻开指定数量的牌 |
| 中鸟 | 翻开的牌的花色和数字与胡牌相关,额外加番 |
| 鸟的数量 | 通常翻1张(自摸翻2张) |
扎鸟的规则让胡牌的分数更加不确定——有时候胡了一个小牌,但扎鸟翻得好,分数反而很高。这种"惊喜感"是长沙麻将的魅力之一。
1.4 核心游戏循环
麻将的一局游戏流程如下:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 洗牌发牌 │ ──→ │ 摸牌出牌 │ ──→ │ 判断胡牌 │
└──────────┘ └──────────┘ └──────────┘
↑ │
│ ┌──────┴──────┐
│ │ │
│ 胡牌了 没胡
│ │ │
│ ┌─────┴─────┐ │
│ │ 计分结算 │ │
│ └───────────┘ │
│ │
└─────────────────────────┘每一轮(一个玩家的回合)的详细流程:
1. 从牌墙摸一张牌
↓
2. 检查是否自摸胡牌
↓ 是 ↓ 否
3. 胡牌!结算 4. 检查其他玩家是否要碰/杠
↓ 否
5. 选择打出一张牌
↓
6. 检查其他玩家是否吃/碰/杠/胡
↓ 否
7. 下一个玩家摸牌 → 回到步骤11.5 游戏状态枚举
和消消乐一样,我们用状态机来管理麻将的游戏流程。
/// <summary>
/// 麻将游戏状态
/// </summary>
public enum MahjongGameState
{
/// <summary>等待发牌</summary>
WaitingForDeal,
/// <summary>当前玩家正在摸牌</summary>
Drawing,
/// <summary>等待当前玩家出牌</summary>
WaitingForDiscard,
/// <summary>等待其他玩家响应(吃/碰/杠/胡)</summary>
WaitingForResponse,
/// <summary>有人吃/碰/杠了,正在处理</summary>
MeldProcessing,
/// <summary>有人胡牌了,正在结算</summary>
Scoring,
/// <summary>流局(牌墙摸完了没人胡)</summary>
Draw,
/// <summary>游戏暂停</summary>
Paused
}## 麻将游戏状态
enum MahjongGameState {
WAITING_FOR_DEAL, ## 等待发牌
DRAWING, ## 当前玩家正在摸牌
WAITING_FOR_DISCARD, ## 等待当前玩家出牌
WAITING_FOR_RESPONSE, ## 等待其他玩家响应(吃/碰/杠/胡)
MELD_PROCESSING, ## 有人吃/碰/杠了,正在处理
SCORING, ## 有人胡牌了,正在结算
DRAW_GAME, ## 流局(牌墙摸完了没人胡)
PAUSED ## 游戏暂停
}1.6 玩家方位
麻将四家玩家分别坐在东南西北四个方位:
| 方位 | 编号 | 说明 |
|---|---|---|
| 东 | 0 | 庄家,第一个摸牌出牌 |
| 南 | 1 | 东家的下家 |
| 西 | 2 | 南家的下家 |
| 北 | 3 | 西家的下家(东家的上家) |
"上家"和"下家"的概念很重要:
- 上家:在你之前出牌的人(逆时针方向的前一个)
- 下家:在你之后出牌的人(逆时针方向的后一个)
- 对家:坐在你对面的人
在长沙麻将中,只有上家出的牌才能被"吃"。如果东家出了一张牌,只有北家(东家的上家)不能吃,南家可以吃。
关于顺序
麻将的出牌顺序是逆时针的:东 → 南 → 西 → 北 → 东...
但在我们的代码中,为了简化计算,我们用数组索引 0、1、2、3 来表示四个玩家,出牌顺序就是 0 → 1 → 2 → 3 → 0...
1.7 本章小结
| 概念 | 说明 |
|---|---|
| 牌面 | 108张,万/筒/条各36张(无字牌) |
| 顺子 | 同花色连续3张 |
| 刻子 | 相同的3张 |
| 杠 | 相同的4张 |
| 将 | 相同的2张(胡牌必需) |
| 吃 | 只能吃上家的牌,组成顺子 |
| 碰 | 任意玩家均可,组成刻子 |
| 杠 | 任意玩家均可,组成杠 |
| 自摸加底 | 自己摸到胡牌,分数翻倍 |
| 扎鸟 | 胡牌后翻牌,决定额外加分 |
| 游戏循环 | 摸牌 → 出牌 → 响应 → 下一家 |
下一章,我们将搭建长沙麻将的项目结构,创建 GameManager、定义牌的数据结构,并准备好场景骨架。
