# 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*