本文将深入解析一个包含多种高级移动机制的Unity 2D角色控制系统,涵盖基础移动、跳跃优化、闪避机制、墙壁碰撞检测等核心功能,结合动画状态管理与物理特效实现

核心移动

private void HandleMovement()
{
    // 状态检查:在闪避、招架或攻击动画期间禁止移动
    if (isDodging || isParrying || anim.GetBool("IsAttacking")) return;

    // 获取水平输入并计算移动速度(根据奔跑状态切换速度)
    dirX = Input.GetAxisRaw("Horizontal") * (isRunning ? runSpeed : walkSpeed);

    // 空中移动限制:空中时移动速度不超过行走速度
    if (!IsGrounded())
    {
        dirX = Mathf.Clamp(dirX, -walkSpeed, walkSpeed);
    }

    // 墙壁碰撞检测:当面向墙壁时禁止横向移动
    if (isTouchingWall && Mathf.Sign(dirX) == (sprite.flipX ? -1 : 1))
    {
        dirX = 0;
    }

    // 平滑速度过渡:在地面时快速加速,空中时较慢调整速度
    float currentSpeed = Mathf.MoveTowards(
        rigidbody2D.linearVelocity.x,  // 当前水平速度
        dirX,                          // 目标速度
        IsGrounded() ? 50f : 25f * Time.deltaTime  // 加速度系数(地面/空中不同)
    );

    // 应用最终速度到刚体,保持原有垂直速度不变
    rigidbody2D.linearVelocity = new Vector2(currentSpeed, rigidbody2D.linearVelocity.y);
}
  1. 状态检查:优先处理角色特殊状态,确保在这些状态下无法移动
  2. 速度计算:根据输入方向和状态(行走/奔跑)计算基础移动速度
  3. 空中限制:防止空中移动速度超过地面行走速度,增加游戏真实性
  4. 墙壁检测:基于角色朝向和墙壁位置,阻止穿墙现象
  5. 平滑过渡:使用MoveTowards实现速度渐变效果,地面加速更快,空中惯性更大
  6. 物理更新:最终将计算后的速度应用到刚体组件,保持物理系统的正确性

地面检测判断

private bool IsGrounded()
{
    // 创建地面检测区域的尺寸(横向保留90%碰撞体宽度,高度为0.1单位的薄层)
    Vector2 boxSize = new Vector2(coll.bounds.size.x * 0.9f, 0.1f);
    
    // 计算检测区域中心点:
    // X轴:保持与碰撞体中心对齐
    // Y轴:位于碰撞体底部下方(偏移量为检测距离的一半)
    Vector2 boxCenter = new Vector2(
        coll.bounds.center.x,
        coll.bounds.min.y - groundCheckDistance * 0.5f
    );

    // 执行盒型碰撞检测:
    // - 检测位置:计算得到的中心点
    // - 检测范围:预设的盒子尺寸
    // - 检测角度:0度(不旋转)
    // - 检测图层:仅限可跳跃地面层
    return Physics2D.OverlapBox(
        boxCenter,
        boxSize,
        0f,
        jumpableGround
    );
}

注释说明:

  1. 区域设计原理:
    • 横向保留90%宽度:避免两侧墙壁的误判
    • 极薄的高度(0.1单位):创建精确的地面接触平面
    • 下移检测位置:确保检测区域延伸到角色底部下方
  2. 参数细节:
    • groundCheckDistance:控制检测区域下移距离的参数
    • jumpableGround:专门的地面层级,过滤不可跳跃的表面
  3. 检测特性:
    • 使用OverlapBox相比射线检测:更稳定地处理平台边缘
    • 适合各种地面形状:斜坡、移动平台等复杂地形
    • 性能优化:仅检测指定图层,避免不必要的碰撞计算
  4. 返回值说明:
    • true:角色底部与可跳跃地面存在有效接触
    • false:角色处于空中或接触不可跳跃表面

攻击模块

private void HandleAttack()
{
    // 攻击条件检查:招架状态/冷却中/攻击动画播放期间禁止发起新攻击
    if (isParrying || Time.time < lastAttackTime + attackCooldown || anim.GetBool("IsAttacking")) return;

    // 鼠标左键攻击触发检测
    if (Input.GetMouseButtonDown(0))
    {
        isAttacking = true;                    // 设置攻击状态标志
        lastAttackTime = Time.time;            // 记录本次攻击时间戳
        anim.SetTrigger("Attacking");          // 触发攻击动画状态机
    }
}

注释说明:

  1. 状态优先级管理:
    • 招架状态优先于攻击(isParrying)
    • 冷却时间机制确保攻击间隔(attackCooldown)
    • 动画状态检查防止动画叠加(IsAttacking)
  2. 输入检测策略:
    • 使用GetMouseButtonDown确保精确的单次点击响应
    • 限定主攻击按钮为鼠标左键(0号按钮)
  3. 动画同步机制:
    • 通过Animator触发器驱动攻击动画
    • 动画控制器应自动重置IsAttacking状态
    • 攻击动画需包含实际伤害判定帧
  4. 冷却系统实现:
    • 基于Unity引擎时间(Time.time)
    • 计算公式:当前时间 > 上次攻击时间 + 冷却时长
    • 支持通过attackCooldown参数灵活调整平衡性

墙壁检测

private void UpdateWallStatus()
{
    // 保存上一帧的墙壁接触状态用于状态变化检测
    bool wasTouchingWall = isTouchingWall;
    isTouchingWall = false;

    // 确定检测方向(根据角色朝向)
    Vector2 checkDirection = sprite.flipX ? Vector2.left : Vector2.right;
    // 计算射线总长度(碰撞体半宽 + 额外检测距离)
    float rayLength = coll.bounds.extents.x + wallCheckDistance;

    // 多射线平行检测系统(覆盖角色高度范围)
    for (int i = 0; i < wallRaysCount; i++)
    {
        // 计算每条射线的起点:
        // - 水平位置:碰撞体边缘(根据朝向)
        // - 垂直位置:沿角色高度等间距分布(中心对称)
        Vector2 origin = (Vector2)coll.bounds.center +
                        (checkDirection * coll.bounds.extents.x) +
                        Vector2.up * (wallRaySpacing * (i - (wallRaysCount - 1) / 2f));

        // 执行射线检测:
        RaycastHit2D hit = Physics2D.Raycast(
            origin,              // 射线起点
            checkDirection,      // 检测方向
            wallCheckDistance,   // 检测距离
            wallLayer            // 限定墙壁图层
        );

        // 命中处理:任意射线命中即视为接触墙壁
        if (hit.collider != null)
        {
            isTouchingWall = true;
            break;
        }
    }

    // 墙壁接触即时响应:当新接触墙壁且在空中时,立即停止横向移动
    if (!wasTouchingWall && isTouchingWall && !IsGrounded())
    {
        rigidbody2D.linearVelocity = new Vector2(0, rigidbody2D.linearVelocity.y);
    }
}

注释说明:

  1. 多射线检测设计:
    • wallRaysCount:控制射线数量,影响检测精度
    • wallRaySpacing:射线垂直间距,适应不同角色高度
    • 中心对称分布:确保上下方都有检测覆盖
  2. 方向计算逻辑:
    • sprite.flipX 决定角色左右朝向
    • checkDirection 动态跟随角色转向
    • rayLength 包含碰撞体自身宽度避免穿模
  3. 物理响应机制:
    • 仅在状态变化时(首次接触)重置速度
    • 保留垂直速度实现墙滑效果
    • 地面状态豁免(避免与地面碰撞冲突)
  4. 参数协同工作:
    • wallCheckDistance:控制检测灵敏度
    • wallLayer:过滤非墙壁物体
    • extents.x:自动适配不同尺寸碰撞体

闪避

private void HandleDodgeInput()
{
    // 闪避输入检测系统
    // 监听左右方向键的瞬时按下事件(A/D 或 左/右箭头)
    if (Input.GetKeyDown(KeyCode.A))
    {
        // 检测左方向双击操作
        CheckDoubleTap(KeyCode.A);
    }
    else if (Input.GetKeyDown(KeyCode.D))
    {
        // 检测右方向双击操作
        CheckDoubleTap(KeyCode.D);
    }
}

注释说明:

  1. 输入响应策略:
    • 使用GetKeyDown确保精确捕获按键按下瞬间
    • 分离处理左右方向输入(避免同时触发双向操作)
    • 基于键位设计:A/D 对应左右闪避,符合多数键盘控制方案
  2. 双击检测机制:
    CheckDoubleTap方法应包含时间阈值判断(例如:0.3秒内两次按下)
    • 方向键参数传递确保区分左右闪避动作
    • 状态机处理:dodgeCooldown、dodgeDirection等参数
  3. 条件分支设计:
    • else if 结构防止同时响应多个方向输入
    • 优先级:优先检测最后按下的有效方向
    • 与移动系统协同:闪避方向应与当前输入方向一致
  4. 可扩展性考虑:
    • 新增闪避方向只需添加新分支(例如:上闪避加入W键检测)
    • 可通过KeyCode参数化设计支持键位重映射
    • 与游戏手柄输入系统兼容(可替换为JoystickButton检测)
    该实现通过分离输入检测与核心逻辑,使闪避系统具备以下特性:
    • 明确的输入方向性
    • 防止误触发的条件过滤
    • 可配置的双击灵敏度
    • 平滑集成到角色状态机中

双击判断

private void CheckDoubleTap(KeyCode currentKey)
{
    // 双击判定逻辑:满足所有条件时触发闪避
    if (Time.time - lastKeyPressTime < doubleTapThreshold && // 时间间隔在有效范围内
        currentKey == lastKeyPressed &&                     // 连续按下相同方向键
        !isDodging &&                                        // 非闪避状态中
        Time.time >= lastDodgeTime + dodgeCooldown &&       // 冷却时间已结束
        IsGrounded())                                       // 地面状态验证
    {
        StartCoroutine(PerformDodge(currentKey));           // 启动闪避协程
    }

    // 更新按键记录(无论是否触发都需更新)
    lastKeyPressed = currentKey;    // 记录最后一次按下的方向键
    lastKeyPressTime = Time.time;   // 记录最后一次按键时间戳
}

注释说明:

  1. 核心判定条件:
    • 时间窗口:doubleTapThreshold 控制两次按键最大间隔(典型值0.2-0.3秒)
    • 方向一致性:确保两次输入为同方向操作
    • 状态验证:过滤空中闪避/冷却中/重复触发等情况
  2. 参数协同机制:
    lastDodgeTime 记录上次闪避结束时间
    dodgeCooldown 控制闪避技能冷却时间
    IsGrounded() 确保仅地面允许闪避
  3. 执行流程:
    • 满足条件时调用异步闪避协程(处理位移/动画/无敌帧)
    • 无论是否触发都更新按键历史,为下次检测做准备
  4. 防误触设计:
    • 时间戳比对防止长按触发
    • 状态标志位防止动作叠加
    • 地面检测防止空中滥用
    该实现通过分层验证确保精确输入识别,同时具备以下特性:
    • 自然的手感响应(符合玩家双击直觉)
    • 防止按键抖动造成的误触发
    • 可灵活调整的平衡参数(阈值/冷却时间)
    • 与角色状态机的深度集成

闪避与特效

private IEnumerator PerformDodge(KeyCode directionKey)
{
    // 闪避状态初始化
    isDodging = true;                          // 设置闪避状态标志
    lastDodgeTime = Time.time;                 // 记录闪避起始时间(用于冷却计算)
    anim.SetTrigger("Dodge");                  // 触发闪避动画状态机

    // 特效轨迹起点计算(降低到角色脚部位置)
    Vector3 startPos = transform.position + new Vector3(0, -0.3f, 0);

    // 运动参数设置
    float dodgeDirection = directionKey == KeyCode.A ? -1f : 1f;  // 方向转换(A键左,D键右)
    rigidbody2D.linearVelocity = new Vector2(
        dodgeDirection * dodgeSpeed,           // 设置高速水平移动
        rigidbody2D.linearVelocity.y           // 保持原有垂直速度(支持空中闪避)
    );

    // 闪避动作持续时间控制
    yield return new WaitForSeconds(dodgeDuration);

    // 残影特效生成(仅在完整完成闪避时触发)
    if (isDodging)
    {
        Vector3 endPos = transform.position + new Vector3(0, -0.3f, 0);
        GameObject lineObj = Instantiate(
            lineEffectPrefab, 
            Vector3.zero,                     // 世界坐标原点(通过SetPositions精确定位)
            Quaternion.identity
        );
        
        LineRenderer line = lineObj.GetComponent<LineRenderer>();
        line.SetPositions(new Vector3[] {
            endPos + Vector3.right * 0.2f,    // 终点右侧偏移(增强视觉效果)
            startPos + Vector3.left * 0.2f    // 起点左侧偏移(形成动态拖尾)
        });

        StartCoroutine(FadeLine(line));        // 启动渐隐效果协程
    }

    // 状态重置
    rigidbody2D.linearVelocity = new Vector2(0, rigidbody2D.linearVelocity.y);  // 停止水平运动
    isDodging = false;                         // 清除闪避状态
}

代码解析:

  1. 状态管理机制
    isDodging 标志位控制角色状态机,阻止其他动作(如攻击/跳跃)的触发
    lastDodgeTime 记录用于冷却时间计算,通过 dodgeCooldown 参数控制闪避频率
  2. 运动控制
    • 通过修改 linearVelocity 实现高速位移,保持物理系统交互性
    dodgeSpeed 参数控制移动速度,dodgeDuration 控制位移时间(移动距离 = speed × duration)
  3. 视觉效果实现
    • 轨迹特效:在闪避路径生成带有偏移的线条,Vector3(0, -0.3f, 0) 将锚点下移到脚部
    • 动态拖尾:通过起点/终点的左右偏移(±0.2f)创建斜线效果,增强速度感
    • 渐隐处理:FadeLine 协程应实现透明度渐变和对象销毁,避免内存泄漏
  4. 安全防护设计
    yield return 后二次检查 isDodging 状态,防止被外部系统中断导致的异常特效
    • 强制重置水平速度为0,确保闪避结束后立即停止,避免惯性滑动
    该实现通过协程精确控制闪避流程,整合了运动、动画与特效系统,建议配合以下参数范围使用:
    dodgeSpeed: 15-25(根据角色尺寸调整)
    dodgeDuration: 0.3-0.5秒(兼顾响应速度与表现力)
    doubleTapThreshold: 0.2-0.3秒(符合人体操作习惯)

跳跃

private void HandleJump()
{
    // 跳跃输入处理系统
    // 状态检查:当按下跳跃键且角色着地时触发跳跃
    if (Input.GetButton("Jump") && IsGrounded())
    {
        anim.SetTrigger("Jump");  // 触发跳跃动画状态机
        rigidbody2D.linearVelocity = new Vector2(
            rigidbody2D.linearVelocity.x,  // 保持原有水平速度
            jumpForce                      // 应用垂直跳跃力
        );
    }
}
  1. 输入检测机制
    • 使用 Input.GetButton("Jump") 检测跳跃键(默认空格键)
    • 支持按键缓冲:允许在着地前几帧预输入跳跃指令
    • 与 GetButtonDown 的区别:持续按压也会触发(根据项目需求选择)
  2. 跳跃条件验证
    IsGrounded() 确保地面接触状态
    • 防止空中连跳的二段跳限制
    • 可与 coyote time 机制配合使用(离地后短暂时间仍允许跳跃)
  3. 动画系统集成
    • 通过 Animator Trigger 驱动跳跃动画
    • 动画控制器应包含落地检测自动切换回 idle 状态
  4. 物理实现方式
    • 直接设置垂直速度(替代 AddForce 实现更可控的跳跃高度)
    • 保留水平速度:允许空中移动影响跳跃轨迹
    • jumpForce 参数建议范围:12-15(根据重力比例调整)

格挡系统

private void HandleParry()
{
    // 招架输入检测系统
    // 条件:鼠标右键按下 + 冷却时间结束
    if (Input.GetMouseButtonDown(1) && Time.time > lastParryTime + parryCooldown)
    {
        StartCoroutine(ParryAction());  // 启动招架协程
    }
}

private System.Collections.IEnumerator ParryAction()
{
    // 招架状态初始化
    isParrying = true;                  // 设置全局招架标志
    lastParryTime = Time.time;          // 记录本次招架时间戳
    parryCollider.enabled = true;       // 激活招架判定区域
    anim.SetTrigger("Parry");           // 触发招架动画

    // 维持招架状态持续时间
    yield return new WaitForSeconds(parryDuration);

    // 状态重置
    isParrying = false;                 // 清除招架状态
    parryCollider.enabled = false;      // 关闭招架判定区域

    Debug.Log("Parry cooldown started"); // 调试信息(发布时可移除)
}
  1. 核心机制
    • 鼠标右键(1号按钮)触发瞬间招架
    • 冷却系统通过lastParryTime + parryCooldown实现
    • 独立碰撞体parryCollider处理招架判定区域
  2. 参数协同
    parryDuration:招架有效帧持续时间(通常0.2-0.5秒)
    parryCooldown:招架硬直时间(平衡性关键参数)
    • 动画时长应与parryDuration保持一致
  3. 状态管理
    isParrying标志影响角色其他行为(如移动/攻击)
    • 碰撞体精准控制判定时机,避免持续激活
    • 协程实现精准的时序控制