基于Java Swing的禁忌岛桌面游戏
- 模板大小:2.05M
- 浏览次数:4次
- 最后更新:2026-05-19
注意:IT技能网提供的所有源代码或模板仅供学习交流使用。
禁忌岛(Forbidden Island)桌游数字化实现系统介绍
一、项目概述
本项目是对经典合作型桌游《禁忌岛》的完整数字化复刻,采用纯 Java 语言开发,基于 Java Swing 构建图形用户界面,支持 2~4 名玩家本地多人协作游玩。游戏完整还原了原版规则:玩家需在小岛持续沉没的压迫下,分工合作收集四件神圣宝藏,并于直升机停机坪集合起飞,方可取得胜利。
二、整体架构设计
系统整体采用 分层架构 + 观察者模式 的设计思路,核心分为三层:
┌─────────────────────────────────────────────┐
│ UI 层(Java Swing) │
│ ForbiddenIslandFrame / GamePanel / Tile │
│ OperatePanel / PlayerPanel / WaterMeter │
└──────────────────┬──────────────────────────┘
│ 事件触发 / 渲染刷新
┌──────────────────▼──────────────────────────┐
│ 控制层(MVC Controller) │
│ GameController(按钮监听 / 动作分发) │
│ GameListener(全局鼠标/键盘监听) │
└──────────────────┬──────────────────────────┘
│ 状态读写
┌──────────────────▼──────────────────────────┐
│ 数据层(Game Engine) │
│ ForbiddenIslandGame(回合状态机) │
│ ElementEngine(游戏元素管理) │
│ TileBoard / Deck / Adventurer 等 │
└─────────────────────────────────────────────┘
核心引擎双轨设计
| 引擎 | 类 | 职责 |
|---|---|---|
| 元素引擎 | ElementEngine |
管理所有游戏数据:牌堆、地图、角色、卡牌选择等 |
| 渲染引擎 | RenderingEngine |
统一调度各 UI 子模块(6个渲染器)的刷新 |
两引擎完全解耦:控制层调用 ElementEngine 修改状态后,再调用 RenderingEngine.getXxxRendering().update() 触发对应 UI 刷新,实现数据与视图的分离。
三、核心技术实现
3.1 回合状态机(ForbiddenIslandGame)
游戏回合由一个静态状态机全局管理,通过若干状态变量驱动整个游戏流程:
private static int roundNum; // 当前回合玩家索引
private static int actionCount; // 本回合已用行动数(上限3)
private static boolean stage23Done; // 是否已完成阶段2、3(抽牌/沉没)
private static boolean need2save; // 是否有玩家落水待救援
private static boolean inFakeRound; // 是否处于"虚拟回合"
回合流程分三个阶段:
- 阶段1:当前玩家执行最多 3 个动作(移动/加固/传卡/捕宝等)
- 阶段2:抽取 2 张宝藏卡,处理"水位上升"特殊卡
- 阶段3:按当前水位抽取相应数量的洪水卡,沉没对应地块
3.2 “虚拟回合”(Fake Round)机制
这是本系统中最具代表性的核心设计之一。当出现以下场景时,系统需要临时"插入"一个不消耗正常回合的操作窗口:
- 玩家落水:地块完全沉没,其上的玩家需立刻游向邻近格,此时插入虚拟回合,限定该玩家最多移动 2 步
- 传牌手牌超限:接收方满 5 张手牌时,临时切换至接收方让其先弃牌
- 信使/导航员特殊能力:需临时切换操控目标玩家
实现方式是将真实状态快照存储在 fakeRoundNum / fakeActionCount 中,处理完成后还原:
// 进入虚拟回合
ForbiddenIslandGame.setFakeRoundNum(currentRound); // 保存当前回合
ForbiddenIslandGame.setFakeActionCount(actionCount);
ForbiddenIslandGame.setRoundNum(targetPlayer); // 切换控制权
ForbiddenIslandGame.setActionCount(2); // 限制行动数
// 虚拟回合结束后恢复
ForbiddenIslandGame.setRoundNum(fakeRoundNum);
ForbiddenIslandGame.setActionCount(fakeActionCount);
3.3 地图系统的坐标双向映射
游戏地图为 6×6 网格,但实际可用格子仅 24 个(菱形分布),其余 12 个为空白占位格。Map 工具类维护两张 HashMap 实现 O(1) 的双向查询:
// 编号 → [行, 列] 坐标
HashMap<Integer, int[]> coordinatesMatcher;
// "[行, 列]"字符串 → 编号
HashMap<String, Integer> numberMatcher;
TileBoard 在初始化时将 24 个地块编号(1~24)随机洗牌后按此坐标布置,保证每局地图随机性。
3.4 多态角色系统
所有角色继承自抽象类 Adventurer,通过多态实现差异化技能:
| 角色 | 特殊技能实现方式 |
|---|---|
| 潜水员(Diver) | 落水后可移动至距离最近的任意存在格(欧氏距离最短格集合) |
| 探索者(Explorer) | canNormalMove() 中允许斜对角方向移动及加固 |
| 飞行员(Pilot) | 可移动到地图上任意存在格,不受邻接限制 |
| 工程师(Engineer) | shoreUpCount 计数器,每回合首次加固后额外获得一次免费加固机会 |
| 信使(Messenger) | 传牌时跳过"同格限制"检查,可跨格传牌 |
| 导航员(Navigator) | 借助虚拟回合机制,临时切换控制其他玩家移动 |
落水后的游泳检测对探索者专门处理了斜向格:
boolean upLeft = isExplorer && x>0 && y>0 && board.getTile(x-1,y-1).isExist();
boolean upRight = isExplorer && x>0 && y<5 && board.getTile(x-1,y+1).isExist();
boolean downLeft = isExplorer && x<5 && y>0 && board.getTile(x+1,y-1).isExist();
boolean downRight= isExplorer && x<5 && y<5 && board.getTile(x+1,y+1).isExist();
return up || down || left || right || upLeft || upRight || downLeft || downRight;
3.5 牌堆系统(Deck 模板 + 两类子类)
Deck 抽象基类统一管理"牌堆"与"弃牌堆",两个子类各自扩展:
FloodDeck(洪水牌堆):
- 初始发 6 张固定数量,之后按水位计动态决定每回合抽牌数
- 触发"水位上升"卡时,将弃牌堆洗牌后置顶(
putBack2Top()),提升后续洪水频率 - 地块永久沉没时调用
removeFloodCard()将对应洪水牌踢出牌池,确保已沉没地块不再被抽到
TreasureDeck(宝藏牌堆):
- 管理 28 张宝藏卡(20 张宝物卡 + 3 张直升机卡 + 2 张沙袋卡 + 3 张水位升卡)
- 发初始手牌时过滤"水位上升"卡(
getNoRiseCards()),保证初始手牌合法
3.6 渲染引擎(分模块渲染 + 统一接口)
所有 UI 子模块实现统一接口:
interface IRendering {
void update(); // 刷新界面
void finish(); // 游戏结束时禁用所有控件
}
RenderingEngine 作为单例容器持有 6 个渲染器实例:
| 渲染器 | 负责区域 |
|---|---|
TileRendering |
游戏地图格子 |
FloodRendering |
洪水卡牌展示区 |
PlayerRendering |
玩家手牌与状态面板 |
TreasureRendering |
宝藏卡牌展示区 |
WaterMeterRendering |
水位计 |
ControllersRendering |
操作按钮区 |
控制层按需精确刷新对应模块,避免全量重绘,提升界面响应效率。
3.7 胜负判定
失败条件(任一触发):
- 任意宝藏的两块神殿均沉没且该宝藏未被收集(
isShrinesFlooded()) - 玩家落水后无任何邻接可移动格(无路可逃)
胜利条件(同时满足):
- 所有玩家集合于直升机停机坪(Tile #14)
- 4 件宝藏全部收集完毕
- 至少持有 1 张直升机起飞卡(编号 20/21/22)
四、测试支持
项目引入 JUnit 4.13.1 进行单元测试,并包含 hamcrest-core-1.3.jar 断言增强库,对各游戏逻辑单元的正确性提供了测试保障。
五、技术栈汇总
| 技术/组件 | 版本 | 用途 |
|---|---|---|
| Java | SE 8+ | 核心开发语言 |
| Java Swing | JDK 内置 | GUI 框架(JFrame/JPanel/JButton 等) |
| JUnit | 4.13.1 | 单元测试 |
| Hamcrest | 1.3 | 测试断言增强 |
| Collections.shuffle | JDK 内置 | 地图/牌堆随机洗牌 |
六、包结构总览
com.forbidden.island
├── Application.java # 程序入口
├── ForbiddenIslandGame.java # 回合状态机(核心逻辑)
├── adventurer/ # 角色模块
│ ├── Adventurer.java # 角色抽象基类
│ ├── Diver / Engineer / Explorer # 各角色具体实现
│ ├── Messenger / Navigator / Pilot
├── cards/ # 牌堆模块
│ ├── Deck.java # 牌堆抽象基类
│ ├── FloodDeck.java # 洪水牌堆
│ └── TreasureDeck.java # 宝藏牌堆
├── enums/ # 枚举定义
│ ├── TileStatus.java # 地块状态(正常/淹没/沉没)
│ └── TreasureFigurines.java # 宝藏雕像类型
├── listener/ # 控制层
│ ├── GameController.java # 按钮事件分发
│ └── GameListener.java # 全局事件监听
├── ui/ # 视图层
│ ├── ElementEngine.java # 元素引擎
│ ├── TileBoard.java # 游戏地图
│ ├── Tile.java # 地块单元
│ ├── WaterMeter.java # 水位计
│ ├── handler/ # 渲染子模块
│ │ ├── IRendering.java # 渲染接口
│ │ ├── RenderingEngine.java # 渲染引擎(统一管理)
│ │ └── TileRendering / FloodRendering / ...
└── utils/ # 工具类
├── Map.java # 坐标双向映射
├── ImageUtil.java # 图片加载工具
├── Constant.java # 全局常量
└── LogUtil.java # 控制台日志
本系统完整实现了《禁忌岛》的全部规则,通过状态机驱动回合流程、虚拟回合处理复杂中断、多态角色封装差异化技能、双引擎解耦数据与视图,工程结构清晰,扩展性良好。