You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity第一人称恐怖游戏Player Controller快跑功能失效求助

Unity第一人称恐怖游戏快跑功能失效排查与修复

问题描述

正在开发一款基于Unity+C#的3D第一人称恐怖游戏,使用Character Controller组件实现玩家移动,功能需求如下:

  • W/A/S/D:前后左右移动
  • Left Control:切换站立/爬行状态
  • Left Shift:按住W时,若冷却计时器≤0则触发快跑;满足计时器≥冷却值、松开W或再次按Left Shift时停止快跑(站立/爬行均可触发)
  • Space:跳跃

当前其他功能正常,仅快跑(Fast()函数)失效,按下Left Shift无法触发快跑。原脚本如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Character_Movement_V2 : MonoBehaviour
{
    CharacterController characterController;

    //test
    public float showSpeed;

    public float gravity = -9.81f;
    public float gravityMultiplier = 3f;
    public float verticalVelocity;

    public float movspeed = 1f;

    public float isFastSpeed = 1f;
    public float CrawlSpeed = 1f;

    public bool isCrawling;
    public bool isFast;

    public float jumpHeight = 3f;
    public float jumpCooldown = 4f;
    public float jumpTimer;

    public float isFastTimer;
    public float isFastCooldown = 6f;

    // Start is called before the first frame update
    void Start()
    {
        characterController = GetComponent<CharacterController>();
        jumpTimer = jumpCooldown;
        isFastTimer = isFastCooldown;
    }

    // Update is called once per frame
    void Update()
    {
        MovePlayer();
        Fast();
        Crawl();
        JumpandGravity();
    }

    public void MovePlayer()
    {
        //Move Player

        //check
        Vector3 move = (movspeed * isFastSpeed * CrawlSpeed * (transform.right * Input.GetAxis("Horizontal") + transform.forward * Input.GetAxis("Vertical")) + transform.up * verticalVelocity);
        characterController.Move(move * Time.deltaTime);

        //test
        showSpeed = Mathf.Sqrt((move.x) * (move.x) + (move.z) * (move.z));
    }

    public void Fast()
    {
        if(Input.GetKey(KeyCode.W))
        {
            if (isFastTimer <= 0f && Input.GetKeyDown(KeyCode.LeftShift))
            {
                isFast = true;
            }
        }
        else
        {
            isFast = false;
        }
       
        if ((isFast && Input.GetKeyDown(KeyCode.LeftShift)) || (isFastTimer >= isFastCooldown))
        {
            isFast = false;
        }

        if (isFast)
        {
            isFastSpeed = 3f;
        }
        else
        {
            isFastSpeed = 1f;
        }

        //isFast Timer
        if(isFast && isFastTimer < isFastCooldown)
        {
            isFastTimer += Time.deltaTime;
        }
        if(!isFast && 0f < isFastTimer)
        {
            isFastTimer -= Time.deltaTime;
        }
    }

    public void Crawl()
    {
        if(!isCrawling && Input.GetKeyDown(KeyCode.LeftControl))
        {
            isCrawling = true;
        }
        else if(isCrawling && Input.GetKeyDown(KeyCode.LeftControl))
        {
            isCrawling = false;
        }

        if(isCrawling)
        {
            CrawlSpeed = 0.25f;
        }
        else
        {
            CrawlSpeed = 1f;
        }
    }

    public void JumpandGravity()
    {
       
        //Jump
        if (characterController.isGrounded)
        {
            if (Input.GetKeyDown(KeyCode.Space) && jumpTimer <= 0f)
            {
                verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity * gravityMultiplier);
                jumpTimer = jumpCooldown;
            }
            else
            {
                verticalVelocity = -1f;
            }
        }
        else
        {
            //Gravity
            verticalVelocity += gravity * gravityMultiplier * Time.deltaTime;
        }

        //Jump Cooldown
        if (jumpTimer > 0f)
        {
            jumpTimer -= Time.deltaTime;
        }
        if (jumpTimer < 0f)
        {
            jumpTimer = 0f;
        }
    }
}

问题原因分析

  1. 初始计时器值错误:Start()中把isFastTimer初始化为isFastCooldown(6f),但触发快跑的条件是isFastTimer <= 0f,导致游戏启动后无法满足触发条件,根本无法激活快跑。
  2. 逻辑顺序冲突:在Fast()函数中,先判断触发快跑的条件,紧接着就判断停止快跑的条件。由于初始isFastTimer等于isFastCooldown,就算成功触发isFast = true,后续的停止判断会立刻将isFast设为false,导致快跑状态被瞬间取消。
  3. 计时器边界处理不严谨:当isFastTimer刚好等于isFastCooldown时,停止逻辑会直接触发,但此时快跑状态可能刚激活,不符合需求逻辑。

修复后的代码及说明

修改Start()初始化

void Start()
{
    characterController = GetComponent<CharacterController>();
    jumpTimer = jumpCooldown;
    // 初始冷却完成,允许触发快跑
    isFastTimer = 0f;
}

修改Fast()函数

public void Fast()
{
    // 先处理停止快跑的条件:松开W、再次按LeftShift、计时器超过冷却值
    if (!Input.GetKey(KeyCode.W) || (isFast && Input.GetKeyDown(KeyCode.LeftShift)) || (isFastTimer >= isFastCooldown))
    {
        isFast = false;
    }
    // 再处理触发快跑的条件:按住W、冷却完成、按下LeftShift
    else if (isFastTimer <= 0f && Input.GetKeyDown(KeyCode.LeftShift))
    {
        isFast = true;
    }

    // 设置移动速度
    isFastSpeed = isFast ? 3f : 1f;

    // 计时器更新逻辑:限制数值范围,避免溢出或负数
    if (isFast)
    {
        isFastTimer = Mathf.Min(isFastTimer + Time.deltaTime, isFastCooldown);
    }
    else
    {
        isFastTimer = Mathf.Max(isFastTimer - Time.deltaTime, 0f);
    }
}

修改点说明

  1. 调整逻辑顺序:先处理停止快跑的条件,再处理触发条件,避免刚激活就被取消的问题。
  2. 修正初始计时器值:将isFastTimer初始化为0,确保游戏启动后可以直接触发快跑。
  3. 优化计时器更新:使用Mathf.Min和Mathf.Max限制计时器范围,避免数值溢出或低于0的情况,逻辑更严谨。
  4. 简化速度赋值:用三元运算符替代if-else,代码更简洁。

内容的提问来源于stack exchange,提问作者A.A.Knightmare

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 03:52:11