diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 5a811fd0..802cfb27 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -20,6 +20,10 @@ concurrency: jobs: build: + if: > + github.event_name == 'push' || + (github.event_name == 'pull_request' && !github.event.pull_request.draft) + runs-on: ubuntu-latest defaults: run: diff --git a/doc/docs/zh-hans/part0_mise_en_place/0.1_welcome.md b/doc/docs/zh-hans/part0_mise_en_place/0.1_welcome.md index cb180104..a8965260 100644 --- a/doc/docs/zh-hans/part0_mise_en_place/0.1_welcome.md +++ b/doc/docs/zh-hans/part0_mise_en_place/0.1_welcome.md @@ -1,6 +1,130 @@ # 欢迎来到皇家厨房 -这里是什么地方?(关于 SoupRune) -看看其他大厨的杰作(Showcase) +```dialogue + +* 欢迎,我的孩子。我看到你对制作游戏充满了热情。 +* 不用紧张,我会一步步教你的,就像烤派一样简单。 +``` -(TODO) \ No newline at end of file +欢迎来到 **SoupRune**——一个为创作 Deltarune / Undertale 风格游戏而生的现代化框架。 + +如果你是第一次来到这里,可能会有些不知所措。这么多文件,这么多术语……别担心,深呼吸,让我慢慢为你讲解。 + +## 🥣 这到底是什么? + +想象一下,你走进了一间厨房。 + +在这间厨房里: +- **炉灶**已经准备好了(Bevy 引擎) +- **锅碗瓢盆**都摆放整齐(核心系统) +- **基础食材**已经备好(配置模板) + +你需要做的,只是: +1. 拿起菜谱(配置文件) +2. 按照步骤添加材料(编写 RON 文件) +3. 偶尔调整火候(修改参数) + +就这样,一道美味的游戏就诞生了! + +```dialogue + +* 最重要的是——你不需要成为专业厨师。 +* 只要会读食谱,就能做出美味的派。 +``` + +## 🍲 S.O.U.P 原则 + +SoupRune 的设计理念可以用一碗热气腾腾的汤来概括: + +### **S - Strong (强劲内核)** +底层使用 **Rust** 和 **Bevy 引擎**,拥有出色的性能和并行计算能力。就像一口结实的铸铁锅,能承受高温和重压。 + +### **O - Open (自由开源)** +采用 **LGPL-3.0** 协议。引擎代码开源,但你的游戏项目完全属于你自己。就像公共厨房——设备是大家的,但你做出的菜是你的。 + +### **U - User-friendly (易于上手)** +提供开箱即用的功能:对话系统、战斗框架、地图编辑器集成。就像预制的半成品——加热就能吃。 + +### **P - Polyglot (多语言支持)** +支持 Rust、C#、Haxe 等多种编程语言。就像准备了多种餐具——筷子、刀叉、勺子,用你最顺手的那个! + +## 🎮 你能做什么? + +使用 SoupRune,你可以创作: + +✅ **回合制战斗系统**(像 Undertale 那样的弹幕战斗) +✅ **开放世界探索**(像 Deltarune 的城镇和迷宫) +✅ **对话和剧情系统**(用 Mortar 脚本语言) +✅ **自定义 UI 界面**(战斗菜单、道具栏等) +✅ **动画和特效**(集成 Alight Motion 动画工具) + +## 🎯 "不写代码"是真的吗? + +是的——**绝大部分内容**都可以通过配置文件完成! + +举个例子,创建一场战斗,你只需要写这样的配置文件: + +```ron +// battle.ron +( + player_hp: 92, + player_speed: 200.0, + battle_box_size: (150.0, 150.0), + chapters: [ + // 玩家选择攻击目标 + UIInteraction(ui_layout: "battle_menu"), + // 播放弹幕攻击 + DanmakuPerformance( + performance: "attacks/flowey_bullets.ron", + position: Some((0.0, 100.0)), + ), + // 等待3秒 + Wait(3.0), + ] +) +``` + +看到了吗?就像填表格一样! + +当然,如果你想实现更复杂的逻辑(比如独特的 Boss 机制),你可以用 Rust、C# 或 Haxe 编写脚本。但对于大部分内容来说,配置文件就够了。 + +```dialogue + +* 别担心,孩子。我们会从最简单的开始。 +* 一步一个脚印,很快你就能烤出自己的派了。 +``` + +## 🏗️ 引擎 vs 项目:游戏机与卡带 + +这个比喻非常重要,让我们用任天堂 Switch 来类比: + +| 部分 | 比喻 | 说明 | 你需要做什么? | +|-----|------|------|-------------| +| **Engine (引擎)** | Switch 主机 | 负责运行游戏的底层系统 | ❌ 不需要改动 | +| **Core (核心库)** | 操作系统 | 提供基础功能(碰撞、渲染等) | ❌ 不需要改动 | +| **Project (项目)** | 游戏卡带 | 你的游戏内容和剧情 | ✅ 这是你的主战场 | +| **Assets (资源)** | 游戏数据 | 图片、音乐、配置文件 | ✅ 这是你的创意空间 | + +所以你要做的,就是**制作游戏卡带**——编写配置文件、准备素材、编排剧情。 + +引擎会帮你处理所有复杂的技术问题(渲染、物理、音频等),你只需要专注于创意! + +## 📚 关于这份文档 + +这份文档分为几个部分: + +- **Part 0: 备菜阶段**(你现在这里)— 环境配置和基础知识 +- **Part 1: 辛辣的主菜** — 战斗系统开发 +- **Part 2: 精致摆盘** — 世界场景与叙事 +- **Part 3: 灵魂甜点** — 视听效果与美化 +- **Part 4: 分子料理** — 高级脚本与底层开发 + +```dialogue + +* 准备好了吗,孩子? +* 让我们开始你的烹饪之旅吧。 +``` + +--- + +**下一步:[挑选你的厨具 →](0.2_setup.md)** \ No newline at end of file diff --git a/doc/docs/zh-hans/part0_mise_en_place/0.2_setup.md b/doc/docs/zh-hans/part0_mise_en_place/0.2_setup.md index 488a340c..16e213ee 100644 --- a/doc/docs/zh-hans/part0_mise_en_place/0.2_setup.md +++ b/doc/docs/zh-hans/part0_mise_en_place/0.2_setup.md @@ -1,6 +1,205 @@ # 挑选你的厨具 -只需三步,点火开灶(快速安装指南) -*特别收录:如果锅炸了怎么办?(常见安装报错)* +```dialogue + +* 好的,孩子。在开始烹饪之前... +* 我们需要确保厨房里有所有必要的工具。 +* 别担心,我会帮你检查每一件的。 +``` -(TODO) \ No newline at end of file +在真正开始制作游戏之前,我们需要准备好开发环境。这就像准备厨具——工欲善其事,必先利其器。 + +## 🔧 你需要什么? + +### 必备工具清单 + +| 工具 | 作用 | 必需? | +|-----|------|--------| +| **Rust** | 编译和运行引擎 | ✅ 必须 | +| **Git** | 版本控制和克隆仓库 | ✅ 必须 | +| **系统依赖库** | 图形和音频支持 | ✅ 必须 | +| **代码编辑器** | 编写配置文件 | 🟡 推荐 | + +```dialogue + +* 让我们一件一件来准备吧。 +``` + +## 📦 Step 1: 安装 Rust + +Rust 是 SoupRune 的核心语言。不用担心,你不需要学习 Rust 编程——它只是用来编译和运行引擎的。 + +### Windows + +1. 访问 [https://rustup.rs/](https://rustup.rs/) +2. 下载并运行 `rustup-init.exe` +3. 按照提示完成安装(默认选项即可) +4. 重启终端 + +**检查安装:** +```bash +rustc --version +cargo --version +``` + +如果看到版本号,说明安装成功了! + +```dialogue + +* 很好!第一步完成了。 +``` + +### Linux (Ubuntu/Debian) + +首先安装系统依赖: +```bash +sudo apt-get update +sudo apt-get install -y \ + g++ \ + pkg-config \ + libx11-dev \ + libasound2-dev \ + libudev-dev \ + libwayland-dev \ + libxkbcommon-dev +``` + +然后安装 Rust: +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +source $HOME/.cargo/env +``` + +**检查安装:** +```bash +rustc --version +cargo --version +``` + +### macOS + +1. 打开终端 +2. 运行以下命令: +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` +3. 按照提示完成安装 +4. 运行 `source $HOME/.cargo/env` + +**检查安装:** +```bash +rustc --version +cargo --version +``` + +## 🌐 Step 2: 安装 Git + +Git 用于克隆 SoupRune 仓库和管理你的项目版本。 + +### Windows +访问 [https://git-scm.com/downloads](https://git-scm.com/downloads) 下载安装包。 + +### Linux +```bash +sudo apt-get install git +``` + +### macOS +```bash +brew install git +# 或者使用 Xcode Command Line Tools +xcode-select --install +``` + +**检查安装:** +```bash +git --version +``` + +## 💻 Step 3: 选择代码编辑器(推荐) + +虽然用记事本也能编辑配置文件,但一个好的编辑器能让你事半功倍。 + +推荐的编辑器: + +### 新手友好型 +- **[Visual Studio Code](https://code.visualstudio.com/)** — 功能强大,有丰富的插件 + - 推荐插件:`Even Better TOML`(用于编辑 RON 文件) + - 推荐插件:`Rust Analyzer`(如果你打算写 Rust 脚本) + +### 专业开发者 +- **[RustRover](https://www.jetbrains.com/rust/)** — JetBrains 出品的 Rust IDE +- **[Neovim](https://neovim.io/)** — 如果你喜欢终端编辑器 + +```dialogue + +* 选择你最熟悉的工具就好。 +* 就像有人喜欢木勺,有人喜欢铁勺一样。 +``` + +## 🍳 准备完成检查清单 + +在继续之前,请确认以下几点: + +- [ ] Rust 已安装(`rustc --version` 能显示版本号) +- [ ] Cargo 已安装(`cargo --version` 能显示版本号) +- [ ] Git 已安装(`git --version` 能显示版本号) +- [ ] (Linux)系统依赖库已安装 +- [ ] 代码编辑器已安装(推荐) + +如果全部打勾,那你就准备好了! + +## ⚠️ 常见问题 + +### "command not found: cargo" + +这通常意味着 Rust 的路径没有被添加到环境变量中。 + +**解决方法:** +```bash +# Linux/macOS +source $HOME/.cargo/env + +# Windows (PowerShell) +$env:PATH += ";$env:USERPROFILE\.cargo\bin" +``` + +然后重启终端。 + +### Linux 编译时报错 "could not find system library" + +确保安装了所有开发库: +```bash +sudo apt-get install -y \ + g++ \ + pkg-config \ + libx11-dev \ + libasound2-dev \ + libudev-dev \ + libwayland-dev \ + libxkbcommon-dev +``` + +### Windows 上编译很慢 + +这是正常的!Rust 第一次编译会需要一些时间(10-30分钟)。后续的编译会快很多。 + +可以考虑: +- 使用 `--release` 模式编译(更慢但运行快) +- 升级到 SSD 硬盘 +- 增加虚拟内存 + +```dialogue + +* 如果遇到其他问题,不要慌张... +* 可以到我们的 Discord 社区寻求帮助。 +* 那里有很多热心的大厨会帮助你的。 +``` + +## 🎉 恭喜! + +你已经准备好了所有的厨具!现在让我们真正点燃炉火吧。 + +--- + +**下一步:[第一次点火 →](0.3_quick_start.md)** \ No newline at end of file diff --git a/doc/docs/zh-hans/part0_mise_en_place/0.3_quick_start.md b/doc/docs/zh-hans/part0_mise_en_place/0.3_quick_start.md index 92edd1db..b817bbf8 100644 --- a/doc/docs/zh-hans/part0_mise_en_place/0.3_quick_start.md +++ b/doc/docs/zh-hans/part0_mise_en_place/0.3_quick_start.md @@ -1,7 +1,232 @@ -# 五分钟热身:煮第一碗速食面 +# 第一次点火 -运行示例工程。 -**第一次恶作剧**:修改 `config.ron`,把游戏窗口标题改成“Sans 的睡觉模拟器”。 -*成就解锁:你现在是 Modder 了!* +```dialogue + +* 很好,孩子!现在让我们点燃炉火吧。 +* 第一次总是令人紧张的,但我会在这里陪着你。 +``` -(TODO) \ No newline at end of file +准备好工具后,是时候真正开始了!在这一节,我们会: +- 下载 SoupRune 项目 +- 运行第一个示例 +- 做第一次小改动 + +让我们开始吧! + +## 🔥 Step 1: 克隆仓库 + +打开终端(或命令提示符),执行: + +```bash +git clone https://github.com/Bli-AIk/souprune.git +cd souprune +``` + +这会把整个项目下载到你的电脑上。 + +```dialogue + +* 等待下载完成...深呼吸,别着急。 +``` + +## 📦 Step 2: 拉取子模块(重要!) + +SoupRune 依赖一些子项目(比如 Mortar 脚本编译器)。我们需要把它们也一起下载下来: + +```bash +git submodule update --init --recursive +``` + +这个命令会下载所有必要的依赖项目。 + +⚠️ **重要提示**:如果跳过这一步,编译会失败!就像做派忘了加黄油一样严重。 + +## 🎮 Step 3: 第一次运行 + +现在,让我们启动引擎! + +```bash +cargo run --package souprune --features debug +``` + +让我解释一下这个命令: +- `cargo run` — 编译并运行项目 +- `--package souprune` — 运行主游戏包 +- `--features debug` — 开启调试模式(会显示性能统计和调试信息) + +**第一次编译会比较慢**(10-30分钟),因为 Cargo 需要下载和编译所有依赖库。这是正常的!后续的编译会快得多。 + +```dialogue + +* 第一次烤派总是需要预热烤箱的。 +* 去泡杯茶吧,回来时应该就好了。 +``` + +## ✨ 成功了! + +如果一切顺利,你会看到一个游戏窗口弹出来! + +你应该能看到: +- 一个像素风格的游戏画面 +- 可以用方向键移动的角色 +- 一些场景和对象 + +**恭喜!你已经成功运行了 SoupRune!** + +## 📁 认识项目结构 + +在开始改东西之前,让我们先熟悉一下"厨房的布局": + +``` +souprune/ +├── crates/ # 核心代码(引擎部分) +│ ├── souprune/ # 主游戏框架 +│ │ ├── src/ # Rust 源代码 +│ │ └── assets/ # 游戏资源文件 +│ │ ├── levels/ # 地图文件(.tmx) +│ │ ├── textures/ # 图片素材 +│ │ ├── audio/ # 音频文件 +│ │ └── config/ # 配置文件(.ron) +│ ├── bevy_mortar_bond/ # Mortar 脚本系统 +│ ├── bevy_ecs_typewriter/ # 打字机效果插件 +│ └── ... # 其他核心组件 +├── projects/ # 你的项目文件夹(未来) +├── Cargo.toml # 项目配置 +└── readme.md # 项目说明 +``` + +对于初学者来说,你主要会在这些地方工作: +- **`crates/souprune/assets/`** — 放置和修改游戏资源 +- **`crates/souprune/assets/config/`** — 编辑配置文件 +- **`projects/`** — 创建你自己的游戏(后续教程) + +```dialogue + +* 记住这些位置,孩子。 +* 就像记住调料柜在哪里一样重要。 +``` + +## 🎨 第一次改动:修改窗口标题 + +让我们做第一个小小的修改!我们来改变游戏窗口的标题。 + +### 找到配置文件 + +打开文件:`crates/souprune/src/config.rs` + +### 修改标题 + +找到类似这样的代码: + +```rust +.set(WindowPlugin { + primary_window: Some(Window { + title: "SoupRune".to_string(), + // ... + }), + // ... +}) +``` + +把 `"SoupRune"` 改成你想要的标题,比如: + +```rust +title: "我的第一个游戏".to_string(), +``` + +或者来点有趣的: + +```rust +title: "Sans 的睡觉模拟器".to_string(), +``` + +### 重新运行 + +保存文件后,重新运行: + +```bash +cargo run --package souprune --features debug +``` + +这次编译会快很多(几秒钟),因为只需要重新编译改动的部分。 + +看!窗口标题变了! + +```dialogue + +* 做得好,孩子!你已经迈出了第一步。 +* 你现在是一个真正的 Modder 了! +``` + +## 🎯 更多实验 + +既然你已经知道怎么修改代码了,不妨试试这些: + +### 修改玩家移动速度 + +找到 `crates/souprune/assets/config/battle_player.ron`,修改 `speed` 参数: + +```ron +( + speed: 500.0, // 原来是 200.0,现在更快了! + // ... +) +``` + +### 修改战斗框大小 + +在同一个文件中,找到 `battle_box_size`: + +```ron +( + battle_box_size: (200.0, 200.0), // 更大的战斗空间! + // ... +) +``` + +每次修改后,重新运行游戏看看效果! + +## 🐛 遇到问题了? + +### 编译错误:"failed to resolve" + +确保你运行了子模块更新命令: +```bash +git submodule update --init --recursive +``` + +### 运行时黑屏 + +检查 `assets/` 文件夹是否存在,资源文件是否完整。 + +### "linker errors" 或 "undefined reference" + +**Linux:** 确保安装了所有开发库(见上一节) +**Windows:** 确保安装了 Visual Studio Build Tools + +```dialogue + +* 如果还是解决不了,不要沮丧... +* 到 Discord 社区求助吧,那里有很多人愿意帮忙。 +``` + +## 🎊 你做到了! + +恭喜你完成了"备菜阶段"的所有步骤!现在你已经: + +✅ 安装了所有必要的工具 +✅ 成功运行了 SoupRune +✅ 做出了第一次修改 +✅ 理解了项目的基本结构 + +```dialogue + +* 我为你感到骄傲,孩子。 +* 你已经准备好进入真正的厨房了。 +* 接下来,Undyne 会教你如何制作战斗系统。 +* 她...非常热情。但别担心,她是个很好的老师! +``` + +--- + +**准备好了吗?让我们开始真正的战斗!→ [Part 1: 辛辣的主菜](../part1_spicy_main_course/1.1_forms.md)** diff --git a/doc/docs/zh-hans/part1_spicy_main_course/1.1_forms.md b/doc/docs/zh-hans/part1_spicy_main_course/1.1_forms.md index ba8bbc55..94fdcfa6 100644 --- a/doc/docs/zh-hans/part1_spicy_main_course/1.1_forms.md +++ b/doc/docs/zh-hans/part1_spicy_main_course/1.1_forms.md @@ -1,6 +1,322 @@ -# 填表大师 +# 编写配置文件 -不用写代码!通过 `battle.ron` 决定你的敌人是谁。 -**实战**:把敌人的血量从 100 改成 99999(并在游戏中被它打爆)。 +```dialogue + +* NGAHHH!你终于来了! +* 听好了!战斗系统的基础就是——配置! +* 不需要什么复杂的代码,只需要填表格! +``` -(TODO) \ No newline at end of file +欢迎来到真正的战场!我是 Undyne,皇家卫队队长!从现在开始,我会教你如何制作一个真正的战斗系统! + +别被"配置文件"这个词吓到——它其实就是一张表格,告诉引擎"这场战斗应该怎么打"。就像战斗计划书一样! + +## 📝 什么是 RON? + +RON 是 **Rusty Object Notation** 的缩写,是一种人类可读的数据格式。它看起来像代码,但其实就是个清单! + +来看一个简单的例子: + +```ron +// 这是注释,不会被执行 +( + name: "Undyne", + hp: 99999, + atk: 50, + def: 20, + can_spare: false, // 不可饶恕! +) +``` + +看到了吗?就像填表格一样! +- 用 `//` 写注释 +- 用 `:` 分隔名称和值 +- 用 `,` 分隔不同的字段 +- 用 `()` 包裹整个结构 + +```dialogue + +* NYEH HEH HEH!记住逗号很重要! +* 少一个逗号,你的配置就会炸掉! +``` + +## ⚔️ 战斗配置文件结构 + +战斗配置文件定义了一场战斗的所有基本参数。让我们看看一个完整的例子: + +打开文件:`crates/souprune/assets/config/battle_player.ron` + +```ron +( + // 玩家血量 + player_hp: 92, + + // 玩家移动速度(像素/秒) + player_speed: 200.0, + + // 战斗框大小(宽, 高) + battle_box_size: (150.0, 150.0), + + // 战斗框位置(x, y,相对于屏幕中心) + battle_box_position: (0.0, -50.0), + + // 无敌时间(秒) + invincibility_duration: 1.0, +) +``` + +让我解释每个字段的作用: + +### `player_hp` (整数) +玩家的生命值。在 Undertale/Deltarune 中,这通常是一个两位数。 +- **默认值**: 92(Kris 的血量) +- **范围**: 建议 1-999 + +### `player_speed` (浮点数) +玩家心形在战斗框中的移动速度,单位是像素/秒。 +- **默认值**: 200.0 +- **建议范围**: 100.0 - 500.0 +- 太慢会让游戏很无聊,太快会让玩家难以控制! + +### `battle_box_size` (宽, 高) +战斗框的尺寸,单位是像素。 +- **默认值**: (150.0, 150.0) +- **标准尺寸**: 150x150(Undertale 标准) +- **建议范围**: (100.0, 100.0) 到 (300.0, 300.0) + +⚠️ 注意:太大的战斗框会让躲避变得太容易! + +### `battle_box_position` (x, y) +战斗框相对于屏幕中心的偏移量。 +- **默认值**: (0.0, -50.0)(略微向下) +- **(0.0, 0.0)** 表示屏幕正中央 +- 正值向右/向上,负值向左/向下 + +### `invincibility_duration` (浮点数) +玩家受到伤害后的无敌时间,单位是秒。 +- **默认值**: 1.0 +- **建议范围**: 0.5 - 2.0 +- 太短会让连击太致命,太长会让战斗失去挑战性! + +```dialogue + +* 这些数字决定了战斗的感觉! +* 想要快节奏?提高速度,缩小战斗框! +* 想要慢节奏?降低速度,增大战斗框! +``` + +## 🔨 实战:创建你的第一个战斗配置 + +让我们动手修改配置,创建一个自定义的战斗体验! + +### 练习1:超音速战斗 + +创建一个快节奏的战斗: + +```ron +( + player_hp: 20, // 低血量,增加紧张感 + player_speed: 400.0, // 快速移动 + battle_box_size: (120.0, 120.0), // 小战斗框 + battle_box_position: (0.0, -30.0), + invincibility_duration: 0.5, // 短无敌时间 +) +``` + +### 练习2:新手友好模式 + +创建一个宽松的练习模式: + +```ron +( + player_hp: 200, // 大量血量 + player_speed: 250.0, // 适中的速度 + battle_box_size: (200.0, 200.0), // 大战斗框 + battle_box_position: (0.0, -50.0), + invincibility_duration: 2.0, // 长无敌时间 +) +``` + +### 练习3:Undyne 式地狱难度 + +```ron +( + player_hp: 1, // 一击必杀! + player_speed: 150.0, // 慢速移动 + battle_box_size: (100.0, 100.0), // 极小的空间 + battle_box_position: (0.0, -50.0), + invincibility_duration: 0.2, // 几乎没有无敌时间 +) +``` + +```dialogue + +* NGAHAHA!第三个才是真正的挑战! +* 我就是这样训练新兵的! +``` + +## 🎯 测试你的配置 + +修改配置后,运行游戏测试: + +```bash +cargo run --package souprune --features debug +``` + +进入战斗场景,观察你的修改效果: +- 移动速度是否合适? +- 战斗框大小是否平衡? +- 无敌时间是否影响游戏难度? + +不断调整数值,直到找到最好的感觉! + +## 📊 常见配置模板 + +这里是一些经典游戏风格的配置参考: + +### Undertale 原版风格 +```ron +( + player_hp: 92, + player_speed: 200.0, + battle_box_size: (150.0, 150.0), + battle_box_position: (0.0, -50.0), + invincibility_duration: 1.0, +) +``` + +### Deltarune 风格(略快) +```ron +( + player_hp: 110, + player_speed: 220.0, + battle_box_size: (160.0, 160.0), + battle_box_position: (0.0, -50.0), + invincibility_duration: 1.2, +) +``` + +### Touhou 弹幕风格(超高难度) +```ron +( + player_hp: 3, // 体力制 + player_speed: 300.0, // 快速闪避 + battle_box_size: (250.0, 250.0), // 大空间,但弹幕密集 + battle_box_position: (0.0, -50.0), + invincibility_duration: 2.0, // 长无敌时间补偿 +) +``` + +## 🐛 常见错误与调试 + +### 错误1:忘记逗号 + +❌ **错误:** +```ron +( + player_hp: 92 + player_speed: 200.0 // 忘记上一行的逗号 +) +``` + +✅ **正确:** +```ron +( + player_hp: 92, // 不要忘记逗号! + player_speed: 200.0, +) +``` + +### 错误2:括号不匹配 + +❌ **错误:** +```ron +( + player_hp: 92, + battle_box_size: (150.0, 150.0, // 少了一个 ) +) +``` + +✅ **正确:** +```ron +( + player_hp: 92, + battle_box_size: (150.0, 150.0), // 括号要匹配 +) +``` + +### 错误3:类型不匹配 + +❌ **错误:** +```ron +( + player_hp: "92", // 这是字符串,不是数字! + player_speed: 200, // 这应该是浮点数(200.0) +) +``` + +✅ **正确:** +```ron +( + player_hp: 92, // 整数 + player_speed: 200.0, // 浮点数(注意 .0) +) +``` + +```dialogue + +* 如果你看到"unexpected token"之类的错误... +* 十有八九是忘记逗号或括号没配对! +* 伟大的 Papyrus 会仔细检查每一个符号! +``` + +## 💡 专业提示 + +### 提示1:使用注释 +```ron +( + // === 基础属性 === + player_hp: 92, + + // === 移动配置 === + player_speed: 200.0, // 调整后的速度,感觉更流畅 + + // === 战斗框设置 === + battle_box_size: (150.0, 150.0), // 标准尺寸 +) +``` + +注释能帮你记住为什么做了某个修改! + +### 提示2:创建多个配置文件 + +你可以为不同的 Boss 创建不同的配置: +- `config/battle_easy.ron` — 简单难度 +- `config/battle_normal.ron` — 正常难度 +- `config/battle_hard.ron` — 困难难度 +- `config/battle_undyne.ron` — Undyne 特殊战斗 + +### 提示3:小步调整 + +不要一次改动太多!每次只调整一个参数,这样你才能知道它的具体影响。 + +```dialogue + +* 好!基础训练完成了! +* 现在你知道如何配置战斗参数了! +* 但光有数字是不够的...我们需要真正的战斗! +* 准备好了吗?下一步我们要学习弹幕演出! +``` + +## 📝 本节小结 + +你学到了: +✅ RON 格式的基本语法 +✅ 战斗配置文件的结构 +✅ 每个参数的作用和建议值 +✅ 如何创建自定义战斗配置 +✅ 常见错误和调试方法 + +--- + +**下一步:[编排弹幕演出 →](1.2_danmaku.md)** diff --git a/doc/docs/zh-hans/part1_spicy_main_course/1.2_danmaku.md b/doc/docs/zh-hans/part1_spicy_main_course/1.2_danmaku.md index 53256e17..b0e18897 100644 --- a/doc/docs/zh-hans/part1_spicy_main_course/1.2_danmaku.md +++ b/doc/docs/zh-hans/part1_spicy_main_course/1.2_danmaku.md @@ -1,6 +1,635 @@ -# 撒点胡椒粉:弹幕编排 +# 编排弹幕演出 -如果你还在手写坐标...(介绍 SoupRune 的弹幕优势)。 -**魔法调料包**:如何用简单的配置组合出“圆形发散”、“狙击玩家”的弹幕。 +```dialogue + +* 好!现在来点刺激的! +* 弹幕!演出!时间轴! +* 这就是战斗的核心! +``` -(TODO) \ No newline at end of file +配置文件设置好了?很好!但一场战斗不仅仅是数字——我们需要真正的攻击! + +在这一节,我会教你如何创建弹幕演出(DanmakuPerformance)——一个完整的时间轴系统,让你可以像编排舞台剧一样编排战斗! + +## 🎭 什么是弹幕演出? + +弹幕演出(Performance)是一个**基于时间轴**的系统,定义了: +- 什么时候生成弹幕 +- 生成什么样的弹幕 +- 弹幕如何移动 +- 弹幕持续多久 + +就像一份剧本——告诉演员(弹幕)什么时候上场、做什么动作、什么时候下场! + +## 📁 演出文件结构 + +让我们看一个完整的演出文件:`demo_attack.performance.ron` + +```ron +( + // 第一部分:弹幕原型定义 + prototypes: { + "spear": (...), + "pellet": (...), + }, + + // 第二部分:行为定义 + behaviors: { + "move_right": Linear(...), + "spiral_in": Orbital(...), + }, + + // 第三部分:时间轴 + timeline: [ + (t: 0.0, spawn: "spear", ...), + (t: 1.5, spawn: "pellet", ...), + ], +) +``` + +让我一块一块地解释! + +## 🔷 Part 1: 弹幕原型(Prototypes) + +弹幕原型定义了弹幕的"外观和属性"——它长什么样、造成多少伤害、存在多久。 + +### 基础示例:长矛 + +```ron +prototypes: { + "spear": ( + // 视觉表现(使用config.toml中定义的精灵) + visual: SpriteRef(module: "battle", name: "spear"), + + // 碰撞体(用于检测弹幕与玩家的碰撞) + collider: BoxCollider(3.0, 12.0), // 宽3像素,高12像素 + + // 伤害值 + damage: 2.0, + + // 生命周期(秒) + lifetime: 4.0, + + // 渲染层级 + z_index: 15.0, + + // 碰撞后行为 + hit_behavior: Persistent, // 碰撞后不消失(像Undyne的长矛) + ), +} +``` + +让我解释每个字段: + +### `visual` - 视觉表现 + +有两种方式: + +**1. 静态精灵 (SpriteRef)** +```ron +visual: SpriteRef(module: "battle", name: "spear"), +``` + +**2. 动画 (Animation)** +```ron +visual: Animation( + module: "battle", + name: "flowey_pellet", + frame_duration: 0.05, // 每帧显示0.05秒 +), +``` + +### `collider` - 碰撞体 + +定义弹幕的"命中判定"。有两种形状: + +**盒形碰撞体:** +```ron +collider: BoxCollider(width, height), +// 例如:BoxCollider(6.0, 6.0) - 6x6像素的正方形 +``` + +**圆形碰撞体:** +```ron +collider: CircleCollider(radius), +// 例如:CircleCollider(5.0) - 半径5像素的圆形 +``` + +⚠️ **重要**:碰撞体应该略小于视觉大小,这样玩家才能"擦弹"! + +### `damage` - 伤害值 + +```ron +damage: 2.0, // 造成2点伤害 +``` + +建议范围: +- 简单弹幕:1.0 +- 普通弹幕:2.0 +- 危险弹幕:3.0-5.0 +- 一击必杀:玩家HP的数值 + +### `lifetime` - 生命周期 + +```ron +lifetime: 4.0, // 4秒后自动消失 +``` + +超过这个时间,弹幕会自动消失(即使没碰到玩家)。 + +### `hit_behavior` - 碰撞行为 + +定义弹幕击中玩家后的表现: + +**Persistent(持续型)** +```ron +hit_behavior: Persistent, +``` +碰撞后不消失,可以多次造成伤害(例如Undyne的长矛) + +**Despawn(消失型)** +```ron +hit_behavior: Despawn, +``` +碰撞后立即消失(大部分弹幕都是这样,默认值) + +**DamageWhenStationary(橙色弹幕)** +```ron +hit_behavior: DamageWhenStationary, +color_tint: (hex: "#FCA600"), +``` +只有玩家静止时才造成伤害! + +**DamageWhenMoving(蓝色弹幕)** +```ron +hit_behavior: DamageWhenMoving, +color_tint: (hex: "#40FEFE"), +``` +只有玩家移动时才造成伤害! + +```dialogue + +* 橙色和蓝色弹幕?哈! +* 那是 Undyne 的招牌技能! +* 你也可以用! +``` + +### `color_tint` - 颜色着色(可选) + +```ron +color_tint: (hex: "#FF0000"), // 红色 +``` + +可以给弹幕染色,用于区分类型。 + +### 完整示例:花颗粒 + +```ron +"pellet": ( + visual: Animation( + module: "battle", + name: "flowey_pellet", + frame_duration: 0.05, + ), + collider: BoxCollider(6.0, 6.0), + damage: 1.0, + lifetime: 3.0, + z_index: 15.0, + // hit_behavior 默认为 Despawn,可以省略 +), +``` + +```dialogue + +* 哇!这些设置好酷! +``` + +## 🎯 Part 2: 行为定义(Behaviors) + +行为定义了弹幕如何**移动**。你可以定义可重用的行为,然后在时间轴中应用它们。 + +### Linear - 线性移动 + +```ron +behaviors: { + "move_right": Linear(( + dir: (1.0, 0.0), // 移动方向(向右) + speed: 200.0, // 速度(像素/秒) + )), +} +``` + +方向向量: +- `(1.0, 0.0)` - 向右 +- `(-1.0, 0.0)` - 向左 +- `(0.0, 1.0)` - 向上 +- `(0.0, -1.0)` - 向下 +- `(0.707, 0.707)` - 向右上(45度) + +### Orbital - 轨道运动 + +```ron +behaviors: { + "spiral_in": Orbital(( + angular_velocity: 0.8, // 角速度(弧度/秒) + radial_velocity: -60.0, // 径向速度(负值=向内收缩) + )), +} +``` + +- `angular_velocity > 0` - 逆时针旋转 +- `angular_velocity < 0` - 顺时针旋转 +- `radial_velocity > 0` - 向外扩散 +- `radial_velocity < 0` - 向内收缩 + +### Tween - 补间动画 + +```ron +behaviors: { + "fade_in": Tween(( + target: Opacity, // 动画目标(透明度) + duration: 2.0, // 持续时间 + ease: QuadOut, // 缓动函数 + range: (0.0, 1.0), // 从0到1(完全透明到完全不透明) + delay: 0.0, // 延迟(秒) + )), +} +``` + +**可用的 `target`:** +- `Opacity` - 透明度 +- `Scale` - 缩放 +- `Rotation` - 旋转 + +**可用的缓动函数(`ease`):** +- `Linear` - 线性 +- `QuadIn`, `QuadOut`, `QuadInOut` - 二次方 +- `CubicIn`, `CubicOut`, `CubicInOut` - 三次方 +- `BackOut` - 回弹效果 + +### Custom - 自定义行为(Mod脚本) + +```ron +behaviors: { + "aimed": Custom( + id: "aimed_spear", + props: { + "speed": 180.0, + "smoothness": 0.8, + }, + ), +} +``` + +这会调用你的 Mod 脚本中定义的自定义行为!(高级内容,我们之后会讲) + +```dialogue + +* 基础行为足够做出很多攻击了! +* 别急着用自定义脚本——先把基础玩透! +``` + +## ⏱️ Part 3: 时间轴(Timeline) + +时间轴定义了"什么时候生成什么弹幕"。这是整个演出的核心! + +### 基础时间轴节点 + +```ron +timeline: [ + ( + t: 0.0, // 时间点(秒) + spawn: "spear", // 生成哪个原型 + pattern: EdgeGenerator(...), // 生成模式 + apply: ["move_right", "fade_in"], // 应用哪些行为 + ), +] +``` + +### 时间值(`t`) + +**默认:相对时间** +```ron +timeline: [ + (t: 0.0, ...), // 在0秒时 + (t: 1.5, ...), // 在上一个节点之后1.5秒(实际是1.5秒) + (t: 2.0, ...), // 在上一个节点之后2.0秒(实际是3.5秒) +] +``` + +**绝对时间** +```ron +timeline: [ + (t: 0.0, absolute: true, ...), // 在0秒时 + (t: 2.5, absolute: true, ...), // 在2.5秒时 + (t: 5.0, absolute: true, ...), // 在5.0秒时 +] +``` + +## 📐 生成模式(Patterns) + +生成模式定义了"在哪里生成多少个弹幕"。 + +### EdgeGenerator - 从边缘生成 + +```ron +pattern: EdgeGenerator( + count: 5, // 生成5个 + side: Left, // 从左边缘 + spacing: 35.0, // 间隔35像素 + margin: 250.0, // 距离战斗框250像素 +), +``` + +**可用的边:** `Left`, `Right`, `Top`, `Bottom` + +### RingGenerator - 环形生成 + +```ron +pattern: RingGenerator( + count: 12, // 生成12个 + radius: 100.0, // 半径100像素 + start_angle: 0.0, // 起始角度(弧度) +), +``` + +角度参考: +- `0.0` - 右侧 +- `1.57` (π/2) - 顶部 +- `3.14` (π) - 左侧 +- `4.71` (3π/2) - 底部 + +### LineGenerator - 直线生成 + +```ron +pattern: LineGenerator( + count: 5, // 生成5个 + spacing: 60.0, // 间隔60像素 + direction: (0.0, -1.0), // 排列方向(向下) +), +``` + +### Single - 单个生成 + +```ron +pattern: Single( + position: (50.0, 100.0), // 在(50, 100)位置 +), +``` + +## 🎬 完整示例:简单的三波攻击 + +让我们创建一个简单但完整的攻击! + +```ron +( + // === 原型定义 === + prototypes: { + "spear": ( + visual: SpriteRef(module: "battle", name: "spear"), + collider: BoxCollider(3.0, 12.0), + damage: 2.0, + lifetime: 4.0, + z_index: 15.0, + hit_behavior: Persistent, + ), + "pellet": ( + visual: Animation( + module: "battle", + name: "flowey_pellet", + frame_duration: 0.05, + ), + collider: BoxCollider(6.0, 6.0), + damage: 1.0, + lifetime: 3.0, + z_index: 15.0, + ), + }, + + // === 行为定义 === + behaviors: { + "move_right": Linear(( + dir: (1.0, 0.0), + speed: 200.0, + )), + "spiral_in": Orbital(( + angular_velocity: 0.8, + radial_velocity: -60.0, + )), + "fade_in": Tween(( + target: Opacity, + duration: 1.0, + ease: QuadOut, + range: (0.0, 1.0), + delay: 0.0, + )), + }, + + // === 时间轴 === + timeline: [ + // 第一波:从左侧射出5根长矛 + ( + t: 0.0, + spawn: "spear", + pattern: EdgeGenerator( + count: 5, + side: Left, + spacing: 35.0, + margin: 250.0, + ), + apply: ["move_right", "fade_in"], + ), + + // 第二波:环形花颗粒(1.5秒后) + ( + t: 1.5, + spawn: "pellet", + pattern: RingGenerator( + count: 12, + radius: 100.0, + start_angle: 0.0, + ), + apply: ["spiral_in", "fade_in"], + ), + + // 第三波:从右侧射出长矛(再过2秒) + ( + t: 2.0, + spawn: "spear", + pattern: EdgeGenerator( + count: 7, + side: Right, + spacing: 30.0, + margin: 250.0, + ), + apply: ["move_left", "fade_in"], + ), + ], +) +``` + +这会创建: +1. **0秒**:5根长矛从左边射来 +2. **1.5秒**:12个花颗粒环形出现并向内收缩 +3. **3.5秒**:7根长矛从右边射来 + +```dialogue + +* 看到了吗?就是这么简单! +* 三个时间节点,三波攻击! +* 现在轮到你了! +``` + +## 🎮 在游戏中使用演出 + +创建好演出文件后,如何在游戏中使用呢? + +在你的 Chapter 文件(例如 `demo.battle.ron`)中: + +```ron +[ + // ... 其他章节 ... + + // 播放弹幕演出 + DanmakuPerformance( + performance: "battle/danmaku/demo_attack.performance.ron", + position: Some((0.0, 100.0)), // 可选:生成位置偏移 + ), + + // ... 其他章节 ... +] +``` + +## 🔥 实战练习 + +### 练习1:简单的左右夹击 + +创建一个攻击,让长矛从左右两边同时射来! + +
+点击查看答案 + +```ron +timeline: [ + // 左边的长矛 + ( + t: 0.0, + spawn: "spear", + pattern: EdgeGenerator(count: 5, side: Left, spacing: 40.0, margin: 250.0), + apply: ["move_right"], + ), + // 右边的长矛(同一时间) + ( + t: 0.0, + absolute: true, // 使用绝对时间,也在0秒 + spawn: "spear", + pattern: EdgeGenerator(count: 5, side: Right, spacing: 40.0, margin: 250.0), + apply: ["move_left"], + ), +] +``` + +
+ +### 练习2:三层环形花颗粒 + +创建三个不同半径的环形花颗粒,从外到内收缩! + +
+点击查看答案 + +```ron +timeline: [ + // 外圈 + ( + t: 0.0, + spawn: "pellet", + pattern: RingGenerator(count: 16, radius: 150.0, start_angle: 0.0), + apply: ["spiral_in"], + ), + // 中圈 + ( + t: 0.3, + spawn: "pellet", + pattern: RingGenerator(count: 12, radius: 100.0, start_angle: 0.5), + apply: ["spiral_in"], + ), + // 内圈 + ( + t: 0.3, + spawn: "pellet", + pattern: RingGenerator(count: 8, radius: 50.0, start_angle: 1.0), + apply: ["spiral_in"], + ), +] +``` + +
+ +## 💡 进阶技巧 + +### 技巧1:内联行为 + +你不需要在 `behaviors` 中预定义所有行为,可以直接在时间轴中内联: + +```ron +timeline: [ + ( + t: 0.0, + spawn: "pellet", + pattern: RingGenerator(count: 8, radius: 80.0, start_angle: 0.0), + behaviors: [ + Orbital((angular_velocity: 1.0, radial_velocity: -50.0)), + Tween((target: Scale, duration: 1.0, ease: QuadOut, range: (0.5, 1.0), delay: 0.0)), + ], + ), +] +``` + +### 技巧2:组合多个行为 + +一个弹幕可以同时应用多个行为: + +```ron +apply: ["move_right", "fade_in", "rotate"], +``` + +它们会同时生效!例如:边移动边淡入边旋转。 + +### 技巧3:使用延迟 + +用 `delay` 让动画延迟开始: + +```ron +behaviors: { + "delayed_fade": Tween(( + target: Opacity, + duration: 1.0, + ease: QuadOut, + range: (0.0, 1.0), + delay: 0.5, // 等0.5秒后才开始淡入 + )), +} +``` + +```dialogue + +* 很好!你已经掌握基础了! +* 但还有一件事—— +* 我们可以用 Alight Motion 做更酷的动画! +* 下一节我会教你! +``` + +## 📝 本节小结 + +你学到了: +✅ 弹幕演出的三大部分(原型、行为、时间轴) +✅ 如何定义弹幕的外观和属性 +✅ 各种移动行为(线性、轨道、补间) +✅ 生成模式(边缘、环形、直线) +✅ 如何编写时间轴 +✅ 橙色/蓝色弹幕的特殊效果 + +--- + +**下一步:[导入动画资源 →](1.3_animation.md)** diff --git a/doc/docs/zh-hans/part1_spicy_main_course/1.3_animation.md b/doc/docs/zh-hans/part1_spicy_main_course/1.3_animation.md index 59226da2..aea8f6f9 100644 --- a/doc/docs/zh-hans/part1_spicy_main_course/1.3_animation.md +++ b/doc/docs/zh-hans/part1_spicy_main_course/1.3_animation.md @@ -1,6 +1,436 @@ -# 视觉盛宴:与 Alight Motion 共舞 +# 导入动画资源 -把 AM 当作你的案板:如何在视频软件里画弹幕轨道。 -导入引擎:见证奇迹的时刻。 +```dialogue + +* 听着!Alight Motion是个超酷的工具! +* 虽然我更喜欢直接用代码……但用手机做动画确实很方便! +``` -(TODO) \ No newline at end of file +用代码写弹幕时间轴很强大,但有时候你想要更直观的方式——比如直接"画"出攻击的动画! + +这就是 **Alight Motion** (简称 AM) 的用武之地。它是一个移动端动画工具,而 SoupRune 完美支持它! + +## 📱 什么是 Alight Motion? + +[Alight Motion](https://www.alightmotion.com/) 是一个手机端的动画/特效制作应用(Android/iOS)。 + +你可以用它: +- 制作关键帧动画 +- 添加视觉特效 +- 设计复杂的运动轨迹 +- 实时预览效果 + +然后直接在 SoupRune 中播放这个动画! + +## 🎯 核心概念:图层命名约定 + +SoupRune 通过**图层名称**来识别不同类型的对象。这是最重要的概念! + +| 前缀 | 含义 | 作用 | +|-----|------|------| +| **`#B`** | Bullet(弹幕) | 带碰撞体,会造成伤害 | +| **`#C`** | Container(战斗框) | 定义玩家可移动的边界 | +| 普通名称 | 视觉效果 | 纯装饰,无碰撞 | + +```dialogue + +* 记住这个!图层名称很重要! +* 以 #B 开头 = 攻击! +* 以 #C 开头 = 战斗框! +``` + +### 详细说明 + +#### #B - 弹幕图层 +任何名称以 `#B` 开头的图层都会被视为**弹幕**: + +- ✅ `#B_spear` +- ✅ `#B_circle1` +- ✅ `#B花颗粒` + +这些图层会自动获得: +- 碰撞体(根据精灵大小自动生成) +- 伤害值(可配置) +- 弹幕行为 + +#### #C - 战斗框图层 +任何名称以 `#C` 开头的图层都会被视为**战斗框边界**: + +- ✅ `#C_box` +- ✅ `#C战斗框` + +这定义了玩家心形可以移动的范围! + +#### 组继承 +如果一个**组(Group)**图层有特殊前缀,那么组内的所有子图层都会继承这个属性! + +``` +#B_bullets ← 组图层(以#B开头) + └─ spear_1 ← 自动成为弹幕 + └─ spear_2 ← 自动成为弹幕 + └─ pellet ← 自动成为弹幕 +``` + +这样你就不需要给每个子图层都加前缀了! + +```dialogue + +* NYEH HEH HEH!记住命名规则! +* 伟大的 Papyrus 从不在命名上犯错! +``` + +## 🛠️ 配置文件 + +AM 动画的行为由配置文件控制。创建 `battle/am_config.ron`: + +```ron +( + // 缩放倍数(相对于原始大小) + scale: 2.0, + + // 偏移位置(x, y) + offset: (0.0, -50.0), + + // 弹幕图层匹配模式(正则表达式) + bullet_pattern: "^#B", + + // 战斗框图层匹配模式 + battle_box_pattern: "^#C", + + // 弹幕伤害值 + bullet_damage: 1.0, + + // 碰撞体缩放比例(相对于精灵大小) + collision_scale: 0.8, // 缩小到80%,方便擦弹 +) +``` + +### 配置说明 + +#### `scale` - 缩放倍数 +```ron +scale: 2.0, // 放大2倍 +``` +调整动画的整体大小。如果你的AM动画太小或太大,修改这个值。 + +#### `offset` - 偏移位置 +```ron +offset: (0.0, -50.0), // 向下偏移50像素 +``` +调整动画在屏幕上的位置。(0, 0) 是屏幕中心。 + +#### `bullet_pattern` - 弹幕匹配模式 +```ron +bullet_pattern: "^#B", // 匹配以 #B 开头的图层 +``` +正则表达式,用于识别哪些图层是弹幕。 + +#### `battle_box_pattern` - 战斗框匹配模式 +```ron +battle_box_pattern: "^#C", // 匹配以 #C 开头的图层 +``` +正则表达式,用于识别哪些图层是战斗框。 + +#### `bullet_damage` - 弹幕伤害 +```ron +bullet_damage: 1.0, // 每次碰撞造成1点伤害 +``` +所有 AM 弹幕的统一伤害值。 + +#### `collision_scale` - 碰撞体缩放 +```ron +collision_scale: 0.8, // 碰撞体是精灵大小的80% +``` +通常设置为 0.7-0.9,这样玩家能"擦弹"而不会太严格。 + +## 🎬 在战斗中使用 AM 动画 + +创建好 AM 项目并配置后,在 Chapter 文件中使用它: + +```ron +[ + // ... 其他章节 ... + + // 播放 AM 动画演出 + AmPerformance( + amproj_path: "demo_turn.amproj", + wait_for_completion: true, // 等待动画播放完毕 + ), + + // ... 其他章节 ... +] +``` + +### `amproj_path` - AM 项目路径 +```ron +amproj_path: "demo_turn.amproj", +``` +相对于你的 mod 根目录的路径。 + +### `wait_for_completion` - 是否等待完成 +```ron +wait_for_completion: true, // 等待动画结束再继续 +wait_for_completion: false, // 立即继续下一个章节(动画在后台播放) +``` + +通常设置为 `true`,让动画完整播放。 + +## 📝 完整工作流程 + +让我们从头到尾走一遍完整的流程! + +### Step 1: 在 AM 中创建动画 + +1. 打开 Alight Motion +2. 创建新项目 +3. 添加图层: + - **#B_spear1** - 长矛弹幕 + - **#B_spear2** - 另一根长矛 + - **#C_box** - 战斗框 + - **background** - 背景效果(无前缀,纯视觉) +4. 为每个图层设置关键帧动画 +5. 导出项目(保存 `.amproj` 文件) + +### Step 2: 导入到 SoupRune + +将 `.amproj` 文件放到你的 mod 目录: + +``` +projects/my_mod/ +├── demo_attack.amproj ← 放在这里 +└── battle/ + ├── am_config.ron ← 配置文件 + └── chapters/ + └── battle.ron ← 引用它 +``` + +### Step 3: 配置 am_config.ron + +```ron +( + scale: 1.5, + offset: (0.0, -50.0), + bullet_pattern: "^#B", + battle_box_pattern: "^#C", + bullet_damage: 2.0, + collision_scale: 0.7, +) +``` + +### Step 4: 在 Chapter 中使用 + +```ron +[ + // 显示初始 UI + UIInteraction(ui_layout: "battle/ui/menu.ui_layout.ron"), + + // 生成玩家 + SetPlayer(Spawn( + config_path: "battle/players/player.battle_player.ron", + position: (0.0, -80.0), + )), + + // 播放 AM 动画攻击 + AmPerformance( + amproj_path: "demo_attack.amproj", + wait_for_completion: true, + ), + + // 动画结束后等待0.5秒 + Wait(0.5), + + // 移除玩家 + SetPlayer(Despawn), +] +``` + +### Step 5: 测试 + +```bash +cargo run --package souprune --features debug +``` + +进入战斗,观察你的动画效果! + +## 💡 设计技巧 + +### 技巧1:使用组来管理多个弹幕 + +``` +#B_bullets ← 弹幕组 + ├─ spear_1 + ├─ spear_2 + ├─ spear_3 + └─ #B_dangerous_one ← 可以覆盖为特殊弹幕(高级用法) +``` + +### 技巧2:战斗框动画 + +战斗框也可以有动画!试试: +- 战斗框缩小/扩大 +- 战斗框旋转 +- 战斗框移动 + +``` +#C_box ← 会随着关键帧动画改变大小和位置 +``` + +### 技巧3:纯视觉图层 + +不是所有图层都要有碰撞!添加一些视觉效果: +- 背景闪光 +- 粒子特效 +- 警告标记 + +这些不需要 `#B` 或 `#C` 前缀。 + +### 技巧4:调整碰撞体大小 + +如果碰撞判定感觉太严格或太松: + +```ron +collision_scale: 0.6, // 更宽松(60%) +collision_scale: 0.9, // 更严格(90%) +``` + +```dialogue + +* 记住——游戏难度的一半在于碰撞判定! +* 太严格会让人沮丧,太松会失去挑战性! +* 不断测试,找到平衡点! +``` + +## 🎨 实战示例:创建一个简单的长矛攻击 + +让我们创建一个完整的AM动画攻击! + +### 在 AM 中: + +1. **图层结构:** + ``` + #B_spears ← 弹幕组 + ├─ spear_left ← 从左边射来的长矛 + ├─ spear_right ← 从右边射来的长矛 + └─ spear_top ← 从上方掉落的长矛 + #C_box ← 战斗框(150x150) + bg_flash ← 背景闪光效果 + ``` + +2. **动画时间轴(3秒):** + - **0.0s**: 战斗框出现(淡入) + - **0.5s**: spear_left 从左侧飞入 + - **1.0s**: spear_right 从右侧飞入 + - **1.5s**: spear_top 从上方掉落 + - **2.5s**: 所有长矛淡出 + - **3.0s**: 战斗框淡出 + +3. **导出**: 保存为 `spear_attack.amproj` + +### 在 SoupRune 中: + +**am_config.ron:** +```ron +( + scale: 2.0, + offset: (0.0, -50.0), + bullet_pattern: "^#B", + battle_box_pattern: "^#C", + bullet_damage: 2.0, + collision_scale: 0.75, +) +``` + +**battle.ron:** +```ron +[ + UIInteraction(ui_layout: "battle/ui/undertale.ui_layout.ron"), + SetPlayer(Spawn( + config_path: "battle/players/player.battle_player.ron", + position: (0.0, -80.0), + )), + + AmPerformance( + amproj_path: "spear_attack.amproj", + wait_for_completion: true, + ), + + Sequence([ + Wait(0.5), + SetPlayer(Despawn), + ]), +] +``` + +完成!你现在有了一个完整的动画攻击! + +## 🐛 常见问题 + +### 问:碰撞判定不准确 + +**解决方案:** 调整 `collision_scale` +```ron +collision_scale: 0.6, // 试试更小的值 +``` + +### 问:动画位置不对 + +**解决方案:** 调整 `offset` +```ron +offset: (0.0, -100.0), // 向下移动更多 +``` + +### 问:动画太小/太大 + +**解决方案:** 调整 `scale` +```ron +scale: 3.0, // 放大3倍 +``` + +### 问:战斗框大小不合适 + +在 AM 中调整 `#C_box` 图层的大小,然后重新导出。 + +### 问:有些弹幕没有碰撞 + +**检查:** 图层名称是否以 `#B` 开头?如果是组,确保组名称有前缀。 + +```dialogue + +* 调试时记得开启 debug 功能! +* cargo run --features debug +* 这样能看到碰撞体的可视化! +``` + +## ⚡ 性能提示 + +### 提示1:控制图层数量 +太多图层会影响性能。建议: +- 弹幕图层:< 50个 +- 视觉效果图层:< 20个 + +### 提示2:优化精灵大小 +使用合适大小的图片,不要太大。建议单个精灵 < 512x512。 + +### 提示3:避免过度动画 +不是每个图层都需要复杂的关键帧动画。简单的直线运动最高效。 + +```dialogue + +* 好!你已经掌握 AM 集成了! +* 现在你可以用手机做动画,直接在游戏里看效果! +* 接下来,我们要学习更高级的东西——交互逻辑! +``` + +## 📝 本节小结 + +你学到了: +✅ Alight Motion 是什么以及为什么使用它 +✅ 图层命名约定(#B, #C) +✅ AM 配置文件(am_config.ron) +✅ 如何在 Chapter 中使用 AM 动画 +✅ 完整的工作流程 +✅ 设计技巧和性能优化 + +--- + +**下一步:[编写交互逻辑 →](1.4_act.md)** diff --git a/doc/docs/zh-hans/part1_spicy_main_course/1.4_act.md b/doc/docs/zh-hans/part1_spicy_main_course/1.4_act.md index 2f1ae861..9502ecc8 100644 --- a/doc/docs/zh-hans/part1_spicy_main_course/1.4_act.md +++ b/doc/docs/zh-hans/part1_spicy_main_course/1.4_act.md @@ -1,6 +1,401 @@ -# 话疗环节 (ACT) +# 编写交互逻辑 -如何定义“调情”选项。 -如果玩家选择“拥抱”,敌人会脸红吗?(配置交互反馈)。 +```dialogue + +* 好!前面都是基础! +* 现在我们来点真正强大的东西! +* 不过别担心,我们会从简单的开始! +``` -(TODO) \ No newline at end of file +到目前为止,你已经学会了如何配置战斗参数和编排弹幕。但一场真正的战斗不仅仅是躲避——玩家需要**互动**! + +在这一节,我们会学习 **ACT 系统**——让玩家可以做出选择、影响战斗进程! + +## 🎯 什么是 ACT 系统? + +在 Undertale/Deltarune 中,玩家在战斗中有四个选项: +- **FIGHT** - 攻击 +- **ACT** - 互动 +- **ITEM** - 使用道具 +- **MERCY** - 饶恕/逃跑 + +**ACT** 是最有趣的部分——你可以: +- 检查敌人 +- 安慰/嘲讽 +- 做出特殊动作 +- 改变战斗状态 + +SoupRune 的 ACT 系统让你可以定义这些互动选项,并为它们编写逻辑! + +## 📋 Chapter 类型:UIInteraction + +UI 互动是通过 `UIInteraction` Chapter 实现的: + +```ron +[ + // 显示战斗菜单 + UIInteraction(ui_layout: "battle/ui/undertale.ui_layout.ron"), +] +``` + +这会显示一个 UI 界面,让玩家做出选择。当玩家选择一个选项后,战斗会继续到下一个 Chapter。 + +## 🎨 UI 布局文件 + +UI 布局文件定义了界面的外观和选项。创建 `battle/ui/menu.ui_layout.ron`: + +```ron +( + // UI 组件定义(简化示例) + // 实际 UI 系统更复杂,这里只展示概念 + + // 战斗菜单选项 + menu_items: [ + ( + id: "fight", + label: "FIGHT", + action: GoToFight, + ), + ( + id: "act", + label: "ACT", + action: GoToActMenu, + ), + ( + id: "item", + label: "ITEM", + action: GoToItemMenu, + ), + ( + id: "mercy", + label: "MERCY", + action: GoToMercyMenu, + ), + ], +) +``` + +## 🎭 ACT 菜单示例 + +当玩家选择 ACT 后,会显示具体的互动选项: + +```ron +( + menu_items: [ + ( + id: "check", + label: "Check", + description: "Check enemy stats", + // 基础互动:显示文本 + action: ShowText("* ENEMY - ATK 5 DEF 2\n* Loves to attack!"), + ), + ( + id: "compliment", + label: "Compliment", + description: "Say something nice", + action: ShowText("* You complimented the enemy.\n* It doesn't seem to care."), + ), + ( + id: "threaten", + label: "Threaten", + description: "Try to scare them", + // 可以触发游戏状态变化(高级) + action: TriggerEvent("enemy_scared"), + ), + ], +) +``` + +```dialogue + +* 简单的互动就是显示文本! +* 不需要写任何代码! +``` + +## 🔄 基于选择的战斗流程 + +你可以根据玩家的选择改变战斗流程: + +```ron +[ + // 第一回合:显示菜单 + UIInteraction(ui_layout: "battle/ui/menu.ui_layout.ron"), + + // 敌人攻击 + DanmakuPerformance( + performance: "battle/danmaku/attack_1.performance.ron", + ), + + // 第二回合:再次显示菜单 + UIInteraction(ui_layout: "battle/ui/menu.ui_layout.ron"), + + // 根据玩家之前的选择,敌人会有不同反应 + DanmakuPerformance( + performance: "battle/danmaku/attack_2.performance.ron", + ), +] +``` + +## 💡 简单的互动逻辑(无需编程) + +对于基础的互动,你可以使用内置的动作类型: + +### ShowText - 显示文本 +```ron +action: ShowText("* 文本内容"), +``` +显示一段对话或描述。 + +### PlaySound - 播放音效 +```ron +action: PlaySound("sounds/select.ogg"), +``` +播放一个音效文件。 + +### SetFlag - 设置标志 +```ron +action: SetFlag("enemy_checked", true), +``` +设置一个布尔标志,可以在其他地方检查。 + +### Sequence - 组合动作 +```ron +action: Sequence([ + ShowText("* You compliment the enemy."), + PlaySound("sounds/heal.ogg"), + SetFlag("enemy_happy", true), +]), +``` +按顺序执行多个动作。 + +```dialogue + +* 哇!这些就够做很多东西了! +``` + +## 🎯 实战示例:简单的 Check 系统 + +让我们创建一个完整的 ACT 菜单,包含 Check 功能! + +### Step 1: 创建主菜单 + +`battle/ui/main_menu.ui_layout.ron`: +```ron +( + menu_items: [ + (id: "fight", label: "FIGHT", action: GoToFight), + (id: "act", label: "ACT", action: GoToActMenu), + (id: "item", label: "ITEM", action: GoToItemMenu), + (id: "mercy", label: "MERCY", action: GoToMercyMenu), + ], +) +``` + +### Step 2: 创建 ACT 子菜单 + +`battle/ui/act_menu.ui_layout.ron`: +```ron +( + menu_items: [ + ( + id: "check", + label: "Check", + action: ShowText("* FLOWEY - ATK 19 DEF 5\n* You know what's going on here, don't you?"), + ), + ( + id: "talk", + label: "Talk", + action: Sequence([ + ShowText("* You tried to talk to Flowey."), + ShowText("* He laughs at you."), + SetFlag("talked_to_flowey", true), + ]), + ), + ( + id: "encourage", + label: "Encourage", + action: Sequence([ + ShowText("* You encouraged Flowey."), + ShowText("* His ATTACK increased!"), + // 这会触发自定义事件(需要脚本支持) + TriggerEvent("flowey_powered_up"), + ]), + ), + ], +) +``` + +### Step 3: 在战斗中使用 + +`battle/chapters/flowey_battle.ron`: +```ron +[ + // 显示主菜单 + UIInteraction(ui_layout: "battle/ui/main_menu.ui_layout.ron"), + + // 根据选择,可能会显示 ACT 菜单 + // (这部分逻辑由引擎自动处理) + + // 敌人攻击 + DanmakuPerformance( + performance: "battle/danmaku/flowey_attack.performance.ron", + ), + + // 重复 + UIInteraction(ui_layout: "battle/ui/main_menu.ui_layout.ron"), + + // ... +] +``` + +```dialogue + +* 看到了吗?不需要写代码! +* 就能做出完整的互动系统! +``` + +## 🔥 进阶:自定义 Mod 脚本(可选) + +如果你想实现更复杂的逻辑——比如: +- 根据玩家HP改变敌人行为 +- 实现独特的 Boss 机制 +- 动态生成弹幕 + +你可以使用 **Mod 脚本系统**!SoupRune 支持: +- **Rust** - 最高性能,完全类型安全 +- **C#** (.NET) - 熟悉的 C# 语法,良好的工具支持 +- **Haxe** - 跨平台脚本语言 + +### Mod 脚本简介 + +Mod 脚本是编译成动态库(.dll / .so)的代码,在运行时加载到引擎中。 + +**工作流程:** +1. 用你选择的语言编写逻辑 +2. 编译成动态库 +3. 放到 mod 目录 +4. 在配置文件中引用它 + +### 简单的 Rust Mod 示例 + +```rust +// 在弹幕演出中引用的自定义行为 +use souprune_api::*; + +#[behavior(id = "aimed_spear")] +pub fn aimed_spear_behavior( + params: BehaviorParams, + bullet: &mut Bullet, + player_pos: Vec2, +) { + // 让弹幕追踪玩家 + let direction = (player_pos - bullet.position).normalize(); + let speed = params.get_f32("speed").unwrap_or(180.0); + bullet.velocity = direction * speed; +} +``` + +然后在 Performance 文件中使用: + +```ron +behaviors: { + "aimed": Custom( + id: "aimed_spear", + props: { + "speed": 180.0, + }, + ), +} +``` + +```dialogue + +* 这是高级内容! +* 如果你是新手,先把基础弄透! +* 基础系统已经足够强大了! +``` + +### 何时需要自定义脚本? + +**不需要脚本:** +✅ 显示文本 +✅ 播放音效 +✅ 设置简单标志 +✅ 线性/轨道弹幕运动 +✅ 标准的 ACT 选项 + +**需要脚本:** +🔧 复杂的 AI 行为 +🔧 动态弹幕生成算法 +🔧 自定义碰撞逻辑 +🔧 独特的游戏机制 +🔧 与外部系统交互 + +大部分 Fangame 都**不需要**自定义脚本!配置文件就够了! + +## 💼 实战练习 + +### 练习1:创建一个 Spare 系统 + +创建一个 ACT 选项,让玩家可以饶恕敌人: + +1. 在 ACT 菜单添加 "Spare" 选项 +2. 点击后显示文本 +3. 设置一个 `can_spare` 标志 +4. 在 MERCY 菜单检查这个标志,决定是否显示 "Spare" 选项 + +### 练习2:多阶段对话 + +创建一个 Talk 选项,每次点击显示不同的对话: + +1. 第一次:显示初始对话 +2. 第二次:显示不同的回应 +3. 第三次:敌人变得生气 + +提示:使用标志来追踪对话次数! + +```dialogue + +* 自己试试看! +* 犯错是学习的一部分! +* 不要怕搞砸——那正是我的训练方式! +``` + +## 📝 本节小结 + +你学到了: +✅ ACT 系统的基础概念 +✅ UIInteraction Chapter 的使用 +✅ 如何创建 UI 布局文件 +✅ 内置的互动动作类型 +✅ 创建完整的 ACT 菜单 +✅ 何时需要(和不需要)自定义脚本 + +## 🎊 Part 1 完成! + +恭喜!你已经完成了 **Part 1: 辛辣的主菜**! + +现在你已经掌握: +✅ 战斗配置文件(1.1) +✅ 弹幕演出系统(1.2) +✅ Alight Motion 集成(1.3) +✅ 交互逻辑(1.4) + +有了这些知识,你已经可以创建一个完整的战斗系统了! + +```dialogue + +* NGAHAHA!做得好! +* 你已经掌握战斗系统的基础了! +* 现在去做点什么酷的东西吧! +* 我期待看到你的作品! +``` + +--- + +**接下来:** +- **Part 2: 精致摆盘** - 学习世界场景和对话系统 +- **Part 3: 灵魂甜点** - 学习 UI 定制和音频管理 +- **Part 4: 分子料理** - 学习高级脚本和引擎模组开发 + +**或者现在就开始做你的第一个战斗!** 💪