Game_Num_Basics_And_Calc

🏃 移动与冲刺系统 (Movement & Dash System) 深度研究

研究归属: Project Vampirefall - Tech/Mechanics
创建日期: 2025-12-04
优先级: ⭐⭐⭐⭐ (中高)


📑 目录

  1. 理论基础 (Theoretical Basis)
  2. 实践应用 (Practical Implementation)
  3. 业界优秀案例 (Industry Best Practices)
  4. 参考资料 (References)

📚 1. 理论基础 (Theoretical Basis)

1.1 核心定义

移动与冲刺系统是动作游戏中最基础的交互层。它不仅决定了玩家如何穿越空间,更是战斗节奏的核心调节器。在 Vampirefall 这种混合品类(塔防 + Roguelike)中,移动系统还承担着”在塔之间快速穿梭”的功能性需求。

关键概念:

1.2 数学模型

🏃 冲刺速度曲线

冲刺不应是线性的位移,而应该具有爆发感。通常使用 对数衰减自定义贝塞尔曲线

Velocity(t) = DashSpeed * (1 - (t / DashDuration)^Power)

其中:
- DashSpeed: 初始爆发速度
- DashDuration: 冲刺总时长
- Power: 衰减指数 (通常 > 1,如 2 或 3,使速度快速下降)

🛡️ 无敌帧窗口

timeline
    title 冲刺过程时间轴
    0ms : 冲刺开始 (Start)
    0-50ms : 前摇 (Startup) - 有碰撞体积
    50-250ms : 无敌帧 (I-Frames) - 免疫伤害
    250-350ms : 后摇 (Recovery) - 速度衰减,无敌结束
    350ms : 冲刺结束 (End)

1.3 设计心理学

💡 “Kinesthetic Pleasure” (动觉快感)

移动本身应该是有趣的。


🛠️ 2. 实践应用 (Practical Implementation)

2.1 Vampirefall 适配设计

🏰 塔防环境下的移动

Vampirefall 的地图充满了塔基和怪物。

🔋 资源管理:体力 vs 充能

采用 充能点数 (Charges) 系统而非耐力条 (Stamina Bar)。

2.2 数据结构设计

[System.Serializable]
public class MovementConfig
{
    [Header("基础移动")]
    public float moveSpeed = 8.0f;
    public float acceleration = 50.0f; // 起步加速度
    public float deceleration = 60.0f; // 停止摩擦力
    
    [Header("冲刺参数")]
    public float dashDistance = 6.0f;
    public float dashDuration = 0.3f;
    public AnimationCurve dashSpeedCurve; // 速度变化曲线
    
    [Header("冷却与资源")]
    public int maxDashCharges = 2;
    public float dashCooldown = 1.2f; // 充能恢复时间
    public float dashInternalCooldown = 0.15f; // 连续冲刺最小间隔
    
    [Header("高级机制")]
    public float iframeDuration = 0.2f; // 无敌时间
    public bool canDashThroughEnemies = true;
    public LayerMask obstacleLayer; // 阻挡层(墙壁、塔基)
}

2.3 核心逻辑实现 (Unity)

🎮 状态机驱动的移动控制器

public class PlayerMovement : MonoBehaviour
{
    public MovementConfig config;
    private CharacterController cc;
    private Vector3 velocity;
    private float currentDashTime;
    private Vector3 dashDirection;
    
    private enum State { Normal, Dashing, Stunned }
    private State currentState;
    
    void Update()
    {
        switch (currentState)
        {
            case State.Normal:
                HandleNormalMovement();
                HandleDashInput();
                break;
            case State.Dashing:
                HandleDashMovement();
                break;
        }
    }
    
    void HandleNormalMovement()
    {
        Vector3 input = GetInputVector();
        
        // 简单的加减速处理
        if (input.magnitude > 0.1f)
        {
            // 加速向目标速度
            Vector3 targetVel = input * config.moveSpeed;
            velocity = Vector3.MoveTowards(velocity, targetVel, config.acceleration * Time.deltaTime);
        }
        else
        {
            // 减速至静止
            velocity = Vector3.MoveTowards(velocity, Vector3.zero, config.deceleration * Time.deltaTime);
        }
        
        cc.Move(velocity * Time.deltaTime);
        
        // 朝向移动方向
        if (velocity.sqrMagnitude > 0.1f)
            transform.forward = velocity.normalized;
    }
    
    void StartDash()
    {
        if (dashCharges <= 0) return;
        
        dashCharges--;
        currentState = State.Dashing;
        currentDashTime = 0f;
        
        // 确定冲刺方向:如果有输入则按输入方向,否则按角色朝向
        Vector3 input = GetInputVector();
        dashDirection = input.magnitude > 0.1f ? input.normalized : transform.forward;
        
        // 开启无敌
        StartCoroutine(IFrameRoutine());
        
        // 播放特效和音效
        PlayDashEffects();
    }
    
    void HandleDashMovement()
    {
        currentDashTime += Time.deltaTime;
        float progress = currentDashTime / config.dashDuration;
        
        if (progress >= 1f)
        {
            EndDash();
            return;
        }
        
        // 采样速度曲线
        float speedMultiplier = config.dashSpeedCurve.Evaluate(progress);
        float currentSpeed = (config.dashDistance / config.dashDuration) * speedMultiplier;
        
        cc.Move(dashDirection * currentSpeed * Time.deltaTime);
    }
}

🛡️ 碰撞处理与穿透

为了实现”穿过敌人但被墙挡住”,我们需要动态调整 Layer Collision Matrix 或 Collider 设置。

IEnumerator IFrameRoutine()
{
    // 1. 开启无敌状态(免疫伤害逻辑在 Health 组件中检查此标志)
    isInvincible = true;
    
    // 2. 忽略与敌人的物理碰撞(防止被卡住)
    Physics.IgnoreLayerCollision(LayerMask.NameToLayer("Player"), LayerMask.NameToLayer("Enemy"), true);
    
    yield return new WaitForSeconds(config.iframeDuration);
    
    // 3. 恢复碰撞
    Physics.IgnoreLayerCollision(LayerMask.NameToLayer("Player"), LayerMask.NameToLayer("Enemy"), false);
    isInvincible = false;
}

[!WARNING] 穿模风险: 如果冲刺结束时玩家正好重叠在敌人体内,物理引擎可能会产生巨大的排斥力将玩家弹飞。 解决方案:

  1. 保持软碰撞(Soft Collision),允许重叠但施加持续推力分离。
  2. 或者在冲刺结束检测重叠,如果重叠则向最近的空地微推一段距离。

🌟 3. 业界优秀案例 (Industry Best Practices)

3.1 Hades (Supergiant Games)

✅ 核心机制:Dash-Strike (冲刺攻击)

🎯 Vampirefall 借鉴点

3.2 Hollow Knight (Team Cherry)

✅ 核心机制:空中机动与下劈 (Pogo)

🎯 Vampirefall 借鉴点

3.3 Celeste (Maddy Thorson)

✅ 核心机制:辅助手感 (Game Feel Assists)

🎯 Vampirefall 借鉴点

// 简单的转角修正逻辑示意
void HandleCornerSliding(Vector3 moveDir)
{
    if (Physics.Raycast(transform.position, moveDir, out RaycastHit hit, 1f, obstacleLayer))
    {
        // 如果撞墙角度很小(擦边),则修正移动方向沿墙面滑动
        Vector3 slideDir = Vector3.ProjectOnPlane(moveDir, hit.normal).normalized;
        cc.Move(slideDir * speed * Time.deltaTime);
    }
}

🔗 4. 参考资料 (References)

📄 必读文章

  1. “The Art of the Dash”
    • 来源: Game Developer (Gamasutra)
    • 重点: 分析了 Hyper Light Drifter 和 Furi 的冲刺设计差异。
  2. “Celeste’s Player Movement”

📺 视频分析

  1. “Game Feel: Why Your Death Animation Sucks” (涉及移动手感)
    • 频道: Game Maker’s Toolkit
    • 链接: YouTube
  2. “How Hades Makes You Feel Like a God”
    • 频道: Adam Millard
    • 重点: 分析 Hades 的 Dash-Strike 和取消机制。

🛠️ 工具与资源

  1. Kinematic Character Controller (Unity Asset)
    • 专业的角色控制器插件,处理了所有物理边缘情况(虽然我们可能自己写简单的,但值得参考其文档)。
  2. DOTween
    • 用于实现平滑的冲刺相机跟随和视野拉伸效果。

📊 总结

🎯 Vampirefall 实施建议

  1. 手感优先: 必须实现转角修正,因为塔防地图障碍物极多,卡顿感是致命的。
  2. 视觉反馈: 冲刺必须有残影音效,明确传达无敌状态。
  3. 资源循环: 采用充能制,并与杀敌挂钩,鼓励进攻性走位。
  4. 技能联动: 冲刺不只是移动,更是触发器(如:冲刺路径留下毒雾、冲刺结束释放冲击波)。

文档版本: v1.0
最后更新: 2025-12-04
维护者: Vampirefall Tech Team