WordFunney — 文字生存恐怖游戏的开发记录

今天突然想做一个文字游戏,就是那种玩家选择选项来控制游戏的走向
游戏是文字游戏,想要有多个死亡结局 和 多个胜利结局,这样感觉很有意思
游戏有[ 生命 | 理智 | 道具]

游戏效果

游戏游玩图
在这里插入图片描述

上图为游戏游玩结果,根据剧情提供的选项,选择1/2/…来影响游戏的剧情走向,灰常有意思。。往下看把

1. 项目概述

WordFunney 是一个基于命令行的文字生存恐怖游戏。
采用 数据驱动 模式——所有剧情、选项、状态变化均从 JSON 配置文件读取,引擎代码不包含任何硬编码内容。

核心原则

原则 说明
数据驱动 引擎代码不含任何剧情硬编码
单向流程 节点到节点的有向图遍历
纯标准库 零外部依赖(仅 json, dataclasses, os
节点类型化 新增节点类型无需修改核心引擎

2. 项目结构

wordfunney/
│
├── story.json               # 剧情配置(唯一数据入口)
├── main.py                  # 启动入口
│
├── engine/
│   ├── __init__.py
│   ├── models.py            # 数据模型 dataclass
│   ├── loader.py            # JSON 加载与校验
│   ├── state.py             # 游戏状态管理 & effect 解析
│   └── core.py              # 游戏主循环 & 节点派发
│
└── renderer/
    ├── __init__.py
    └── cli.py               # CLI 渲染(文本输出 + 输入处理)

模块职责

文件 职责
main.py 初始化 GameState、加载 story、启动 engine
engine/models.py StoryNode, Option, GameState dataclass
engine/loader.py story.json,校验 next_id 引用完整性 → dict[str, StoryNode]
engine/state.py StateManager:管理 health/sanity/flags/inventory,解析 condition & effect
engine/core.py GameEngine:主循环 run(),按 node.type 派发处理器
renderer/cli.py CliRenderershow_text(), show_options(), prompt(), show_status()

3. 数据模型

@dataclass
class GameState:
    health: int = 100           # 生命值,归零即死亡
    sanity: int = 100           # 理智值,归零即疯狂
    inventory: list[str]        # 道具列表
    flags: dict[str, bool]      # 剧情标记
    current_node: str           # 当前所在节点 ID
    visited: set[str]           # 已访问节点

@dataclass
class StoryNode:
    id: str                     # 唯一标识
    text: str                   # 展示文本
    type: str                   # normal / win / lose / check
    options: list[Option]       # 可选分支
    effects: dict | None        # 进入节点时触发

@dataclass
class Option:
    text: str                   # 选项文本
    next_id: str                # 指向的下一个节点
    condition: dict | None      # 显示条件
    effects: dict | None        # 选择后触发

节点类型

type 行为
normal 显示文本 → 展示可用选项 → 玩家选择
win 显示胜利文本 → 游戏结束
lose 显示失败文本 → 游戏结束
check 无玩家交互,根据 condition 自动跳转 true_next / false_next

4. 剧情配置格式 (story.json)

{
  "meta": {
    "title": "深渊回响",
    "start_node": "start"
  },
  "nodes": [
    {
      "id": "start",
      "text": "你从一张冰冷的铁床上醒来。\n四周是无尽的黑暗...",
      "type": "normal",
      "effects": { "sanity": -5 },
      "options": [
        {
          "text": "摸索四周的墙壁",
          "next_id": "explore_wall"
        },
        {
          "text": "大声呼救",
          "next_id": "shout",
          "effects": { "sanity": -10 }
        }
      ]
    }
  ]
}

condition 语法

{ "stat": { "health": { ">": 0 } } }
{ "flag": { "name": "knows_truth", "value": true } }
{ "inventory": { "has": "flashlight" } }
{ "visited": "cellar_dark" }

effects 语法

{ "health": -15, "sanity": 10 }
{ "inventory": { "add": "rusty_key" } }
{ "inventory": { "remove": "flashlight" } }
{ "flags": { "knows_truth": true } }

5. 游戏主循环

┌─ main.py ─────────────────────────────┐
│  state = GameState()                   │
│  nodes = load_story("story.json")      │
│  engine = GameEngine(state, nodes)     │
│  engine.run()                          │
└──────────────────┬────────────────────┘
                   ▼
┌─ engine/core.py ──────────────────────┐
│  while running:                       │
│    node = nodes[state.current_node]   │
│    state_manager.apply(node.effects)  │
│                                       │
│    if game_over := check_game_over(): │
│       handle_ending(game_over)        │
│       break                           │
│                                       │
│    match node.type:                   │
│      "normal" → handle_normal(node)   │
│      "win"    → handle_win(node)      │
│      "lose"   → handle_lose(node)     │
│      "check"  → handle_check(node)    │
│                                       │
│    state.visited.add(node.id)         │
└──────────────────────────────────────┘

6. 渲染器设计

show_intro(meta)        → 显示游戏标题
show_text(text)         → 打印文本 + 分隔线
show_status(state)      → 显示 [生命: 100 ❤ | 理智: 95 🌙 | 道具: 手电筒]
show_options(options)   → 打印编号列表
prompt(options)         → 读取 1-n 或 q 退出,返回 Option
show_ending(type)       → 显示游戏结束画面

7. 剧情内容总览(当前版本)

7.1 数据统计

指标 数值
总节点数 54
普通节点 38
判定节点(check) 5
胜利结局 4
死亡结局 7
可用道具 5(flashlight / rusty_key / scalpel / battery / keycard)
总文字量 ~4600 字
单次通关节点数 8~18(取决于路线)

7.2 故事地图

苏醒 ─── 探索密室 ─── 走廊 ─┬── 左侧通道 ─── 手术室 ─── 手术刀 / 笔记
   │                       │
   │                       ├── 右侧走廊 ─┬── 铁门 → 胜利①(铁门逃脱)
   │                       │             │
   │                       │             └── 储物间 ─── 医疗包 / 地图
   │                       │
   │                       ├── 前方大厅 ─┬── 办公室 ─── 钥匙卡
   │                       │             │
   │                       │             ├── 休息室 ─── 恢复状态
   │                       │             │
   │                       │             └── 病房区 ─┬── 安全楼梯 → 屋顶 → 胜利③(屋顶救援)
   │                       │                        │
   │                       │                        └── 锅炉房 ─┬── 发电机 → 主出口 → 胜利②(主出口逃脱)
   │                       │                                     │
   │                       │                                     └── 通风管 ─── 封印区 ─── 深渊 → 死亡
   │                       │
   │                       └── 巢穴 ─── 集合体 ─┬── 手术刀战斗 → 胜利④(英雄结局)
   │                                             │
   │                                             ├── 逃跑 → 死亡
   │                                             └── 加入 → 死亡
   │
   └── 呼救 ─── 暗影 → 死亡

7.3 胜利路线

# 路线 路径 难度
铁门逃脱 右走廊 → 钥匙 → 开门 ★☆☆ 最简单
主出口逃脱 办公室拿钥匙卡 → 锅炉房启动发电机 → 主出口 ★★★ 完整流程
屋顶救援 病房区 → 安全楼梯 → 直升机 ★★☆ 中等
英雄结局 手术室拿手术刀 → 巢穴 → 击败集合体 ★★★ 高风险高回报

7.4 死亡结局

# 结局 触发方式
暗影扼杀 呼救后走向暗影
精神崩溃 sanity ≤ 30 时进入左通道
深渊坠落 跳入封印区的地洞
锅炉爆炸 启动发电机时 health ≤ 30
被拖入巢穴 在集合体前逃跑
融为一体 主动走向集合体
战斗疯狂 用手术刀战斗时 sanity ≤ 20

7.5 道具一览

道具 位置 用途
flashlight 密室地面 照明、驱赶暗影
rusty_key 右走廊 打开铁门(胜利①)
scalpel 手术室推车 击败集合体(胜利④)
keycard 办公室抽屉 打开主出口(胜利②)
battery 储物间急救箱 手电筒备用

8. 扩展指南

需求 做法
添加剧情 story.json 新增 node
新节点类型 models.py 加类型 → core.py 加 handler
新条件/效果 state.py 解析器加分支
随机事件 node 加 random_next 字段
多章节 多文件,loader 合并
存档 GameState 序列化为 JSON

最后

我将我的游戏做成可运行的软件分享给你们

如下图
在这里插入图片描述
在这里你们可以直接通过打开文本编辑器,或者vscode打开story.json文件,然后修改这个文件内容,创造自己的文字游戏故事
是不是很有意思呢!!!

更多推荐