Files
rpg_start/Q&A.md

176 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Q&A
> 学习过程中遇到的问题记录
---
## Q1为什么保留 `rb.linearVelocity.y` 不会导致 Player 上下移动,而改变 `x` 就会左右移动?
### 关键理解
`rb.linearVelocity` 每一帧都被**完整重新赋值**X 和 Y 的来源不同决定了它们的行为不同。
### X 轴:由输入主动控制
```csharp
rb.linearVelocity = new Vector2(xInput * moveSpeed, rb.linearVelocity.y);
// ^^^^^^^^^^^^^^^^
// 这部分的数值由你决定
```
| 操作 | `xInput` | `velocity.x` | 效果 |
|------|:--------:|:------------:|:----:|
| 按 A / ← | -1 | -3.5 | 向左移动 |
| 松开 | 0 | 0 | 停止 |
| 按 D / → | 1 | 3.5 | 向右移动 |
每次赋值X 都被设为**输入决定的明确值**,所以会立刻产生/停止移动。
### Y 轴:物理系统已经在运行
`rb.linearVelocity.y` 不是空的,它已经是物理引擎当前帧算好的结果:
| 状态 | `velocity.y` 实际值 | 保留后效果 |
|------|:-------------------:|:----------:|
| 站地上 | ≈ 0 | 不动(地面碰撞把 y 锁为 0 |
| 下落中 | < 0重力累加 | 继续下落 |
| 跳起后 | > 0 → 逐渐减小 | 上升 → 减速 → 下落 |
保留 Y 的本质是 **"不去干涉"** 物理系统对垂直运动的控制,而不是"主动让角色上下动"。
### 反例:写死 Y 会怎样?
```csharp
rb.linearVelocity = new Vector2(xInput * moveSpeed, 0); // ❌
```
- 重力被清零 → 角色不会下落,飘在空中
- 跳跃被清零 → 跳不起来
- 地面碰撞结果被覆盖 → 可能会卡进地面
### 一句话总结
> X 轴 velocity 是你主动赋值来**命令移动**Y 轴 velocity 是物理系统**正在运行的结果**,保留它只是不去搞破坏。
---
*2026-05-23*
## Q2跳跃时直接覆盖 Y 为 `jumpForce`,和之前说的"保留 Y"不矛盾吗?为什么不用 `y + jumpForce`
### 问题背景
之前的 Q&A 说"保留 `rb.linearVelocity.y` 不去干涉物理系统",但跳跃代码却直接覆盖:
```csharp
// 平时移动:保留 Y不去干扰物理
rb.linearVelocity = new Vector2(xInput * moveSpeed, rb.linearVelocity.y);
// 跳跃时:覆盖 Y
rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);
```
直觉上会觉得应该写成 `rb.linearVelocity.y + jumpForce` 才对。
### 核心解答
**场景不同,原则不同:**
| 场景 | 对 Y 的操作 | 意图 |
|------|:----------:|:----:|
| 左右移动 | 保留 Y | **不管**物理系统的事 |
| 跳跃 | 覆盖 Y = jumpForce | **主动干预**,跳跃本身就是这个干预 |
平时保留 Y 是因为"不需要管 Y",跳跃覆盖 Y 是因为"现在就要动 Y"。
### 为什么用 `= jumpForce` 而不是 `+= jumpForce`
站在平地时 `velocity.y ≈ 0`,两者结果一样。但在以下情况加法会出问题:
| 场景 | velocity.y 当前值 | `= jumpForce` | `+= jumpForce` | 哪个合理 |
|------|:--:|:--:|:--:|:--:|
| 平地站着 | ≈ 0 | = 8 ✅ | = 8 ✅ | 一样 |
| 在上升平台上跳 | > 0如 +3 | = 8 ✅ | = 11 ❌ 异常高 | `=` |
| 下落中误触跳跃 | < 0如 -5 | = 8 ✅ | = 3 ❌ 跳不起来 | `=` |
- **`= jumpForce`** — 每次跳跃高度一致,可预测 ✅
- **`+= jumpForce`** — 跳跃高度受当前 Y 速度影响,结果不稳定 ❌
### 总结
> 保留 Y = **不想管**物理的事
> 覆盖 Y = **正在主动做跳跃这件事**
> 用 `=` 保证每次跳跃可预测,用 `+=` 反而会让跳跃高度飘忽不定
---
## Q3二段跳可以从哪些方面入手设计
### 前提
第九次更新已经实现了接地检测(`isGrounded`),跳跃被限制为"只有在地上才能跳"。在此基础上扩展二段跳,需要考虑以下几个维度:
### 1. 核心机制 —— 如何判断"能不能跳"
**计数器方案(推荐,可扩展性强):**
```
jumpCount = 允许的跳跃次数(例如 2
remainingJumps = 当前剩余次数
想跳时:
1. 检查 remainingJumps > 0
2. 是 → 跳remainingJumps--
3. 否 → 不跳
落地时:
重置 remainingJumps = jumpCount
```
- 优点:改为 3 段跳、4 段跳只需改一个数字,代码不用动
- 问自己:`remainingJumps` 什么时候扣?什么时候重置?
### 2. 重置时机 —— "跳"这个状态何时结束
核心问题:**二段跳的"次数"在什么时候补回来?**
| 方案 | 行为 | 手感 |
|------|------|------|
| 落地重置 | 碰到地面才能再次二段跳 | 标准平台跳跃,最常用 |
| 接触墙壁重置 | 贴墙也能补跳 | 适合有爬墙机制的游戏 |
| 每 X 秒恢复一次 | 空中待一会儿又能跳 | 节奏较怪,很少用 |
> 当前项目有 `isGrounded`,落地重置是最自然的选择。
### 3. 跳跃力设计 —— 二段跳应该和第一段一样高吗?
| 方案 | 效果 | 代表游戏 |
|------|------|---------|
| 相同 jumpForce | 两段跳一样高,手感直接 | 空洞骑士(部分技能)|
| 稍弱(如 jumpForce × 0.8| 第二段更低,更真实 | 蔚蓝 Celeste |
| 固定低值 | 二段跳只用来"续一下" | 大乱斗 |
> 可以先设两个独立参数 `jumpForce` 和 `doubleJumpForce`,分别调。
### 4. 需要考虑的边界问题
| 问题 | 说明 |
|------|------|
| **走边缘掉落** | 从平台边缘走下来(没按跳),空中能二段跳吗?通常可以 |
| **没用第一段跳** | 跳过一次后空中还能跳一次?还是说必须先用一次跳?取决于设计 |
| **Coyote Time** | 离开地面后很短时间(如 0.1s)内按跳还算落地跳吗?和剩余次数如何叠加? |
| **动画反馈** | 二段跳时播放不同的动画(如翻转、翅膀),让玩家感知到"我用了二段跳" |
| **只能一次** | 二段跳用完没落地之前,不能再获得跳跃次数 |
### 5. 建议的切入顺序
1. 先把计数器机制做出来(`jumpCount` / `remainingJumps`
2. `jump()` 中把 `if(isGrounded)` 改为 `if(remainingJumps > 0)`
3. 落地时重置 `remainingJumps = jumpCount`
4.`doubleJumpForce` 找手感
5. 加动画区分一段跳和二段跳
---
*2026-05-25*