使void不执行,因此它的存在只是为了在调用时运行一个函数

问题描述 投票:0回答:2

我有一个运动脚本,并且其中还包含耐力部分。我有一个耗尽耐力的功能,但是我只想在按下按钮时调用它。如何更改空白部分,以使其仅在调用其定义的函数时才触发

这是我的代码(我在谈论的空白在第38行称为staminadrain()

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

public class CharacterMover : MonoBehaviour
{
    private bool tired;
    public float walkSpeed = 6f;
    public CharacterController _charCont;
    public float gravity = -9.8f;
    private Vector3 moveDirection = Vector3.zero;
    public float stam { get; set; }
    private float currentstam { get; set; }
    public Slider stam_bar;
    public float sprintspeedincrease = 1.5f;
    public float staminadrainvalue = 1.0f;
    public float staminaregenvalue = 1.0f;
    public float tiredpenalty = 0.1f;
    private bool walkspeedreduced;

    public void Start ()
    {
        _charCont = GetComponent<CharacterController>();
        stam = 100f;
        currentstam = stam;
        stam_bar.value = calculateStam();
        InvokeRepeating("staminadrain", 1.0f, 1f);
        InvokeRepeating("staminaregen", 1.0f, 0.5f);
    }

    void staminaregen()
    {
        currentstam += staminaregenvalue;
        stam_bar.value = calculateStam();
    }

    void staminadrain()
    {
        currentstam -= staminadrainvalue;
        stam_bar.value = calculateStam();
        if(currentstam <= 0)
        {
            tired = true;
        }
    }

    float calculateStam()
    {
        return currentstam / stam;
    }

    void Update()
    {
        if(tired == true)
        {
            walkSpeed = 0;
        }
        else
        {
            tired = false;
            walkSpeed = 6;
        }

        if ((Input.GetKeyDown(KeyCode.LeftShift)) && (0 <= currentstam))
        {
            walkSpeed *= sprintspeedincrease;
        }

        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            staminadrain();
        }

        if ((Input.GetKeyUp(KeyCode.LeftShift)) && (0 <= currentstam))
        {
            walkSpeed /= sprintspeedincrease;
            staminaregen();
        }

        moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
        moveDirection = transform.TransformDirection(moveDirection);
        moveDirection *= walkSpeed;

        moveDirection.y = gravity;
        _charCont.Move(moveDirection * Time.deltaTime);
    }
}


unity3d
2个回答
1
投票

不确定我是否理解您想要的,但是我想从您的代码中可以知道:

您希望staminaregenstaminadrain被重复调用,但仅在按下某个按钮后才能被调用。

首先,此检查没有意义

    if(tired == true)
    {
        walkSpeed = 0;
    }
    else
    {
        tired = false;
        walkSpeed = 6;
    }

所以,如果tired == true永远保持true?并且,如果tired == false保持false,并且walkspeed每帧固定为6,那么以后增加它不会有太大影响。我将删除它,并在下面的例程中执行它。


通常,不是在Update中轮询状态并对它们做出反应,而是在此处使用Coroutines并删除这些行

InvokeRepeating("staminadrain", 1.0f, 1f);
InvokeRepeating("staminaregen", 1.0f, 0.5f);

然后有两个例程,如

private IEnumerator StaminaRegen()
{
    yield return new WaitForSeconds (1);
    while(!Input.GetKeyUp(KeyCode.LeftShift))
    {
        yield return new WaitForSeconds(0.5f);

        currentstam += staminaregenvalue;
        stam_bar.value = calculateStam();

        // Maximum reached?
        if(currentStam >= 100)
        {
            currentStam = 100;
            yield break;
        }
    }
}

void StaminaDrain()
{
    while(Input.GetKey(KeyCode.LeftShift))
    {
        yield return new WaitForSeconds(1);

        currentstam -= staminadrainvalue;
        stam_bar.value = calculateStam();
        if(currentstam <= 0)
        {
            walkspeed = 0;
            yield break;
        }
    }
}

然后您可以通过按钮启动这些例程

if(Input.GetKeyDown(KeyCode.LeftShift))
{
    if(currentStamina > 0) walkSpeed *= sprintspeedincrease;

    StartCoroutine (StaminaDrain());
}

if(Input.GetKeyUp(KeyCode.LeftShift))
{
    StopAllCoroutines();

    if(currentStamina == 0)
    {
        walkSpeed = 6;
    }
    else
    {
        walkSpeed /= sprintspeedincrease;
    }

    StartCoroutine (StaminaRegen());
}

注:在智能手机上键入,但我希望这个想法能清楚明白


0
投票

有几种方法可以解决此问题。 InvokeRepeating(“ staminadrain”,1.0f,1f)仅仅意味着每秒将在框架上调用以下内容:

void staminadrain()
    {
        currentstam -= staminadrainvalue;
        stam_bar.value = calculateStam();
        if(currentstam <= 0)
        {
            tired = true;
        }
    }

[如果您希望仅在按下按钮时消耗耐力,则可以添加一行代码,如果未按下该按钮,则这行代码会提前返回(假定在输入管理器中将“ Sprint”配置为“左移”):

        if(!Input.GetButton("Sprint"))
            return;

这使您的代码:

void staminadrain()
    {
        if(!Input.GetButton("Sprint"))
            return;
        currentstam -= staminadrainvalue;
        stam_bar.value = calculateStam();
        if(currentstam <= 0)
        {
            tired = true;
        }
    }

但是,使用InvokeRepeating似乎是一个错误的决定,因为您已经在使用Update。使用DerHugo提到的协程可能更有用。这是当按下sprint按钮时,如何使用协同程序使我的角色消耗耐力的方法:

bool sprinting;

IEnumerator DrainStamina()
{
    //If you don't have the stamina to sprint, exit the coroutine early
    if(currentstam <= 0)
        yield break;
    while(Input.GetButton("Sprint"))
    {
        sprinting = true;
        //Calculate the fraction of stamina lost since last frame
        currentstam -= staminadrainvalue / Time.deltaTime; 
        stam_bar.value = calculateStam();
        if(currentstam <= 0)
        {
            currentstam = 0;
            tired = true;
            //Exit the coroutine early if stamina is at or below 0 while you are still holding down sprint
            sprinting = false;
            yield break;
        }
        yield return null;
    }
}

现在,在您的更新代码中,每当您按下sprint按钮时,就启动DrainStamina Coroutine:

void Update()
{
    if(Input.GetButtomDown("Sprint")
        StartCoroutine(DrainStamina());
}

所有人都在说,我将对您的代码进行很多更改。这是我对您要尝试做的事情的总结:

  • 如果您走路,您的移动速度为6单位/秒。我认为不需要在RigidBody上使用CharacterController,尤其是在模拟重力的情况下。

  • 如果要冲刺,您的移动速度是步行速度的1.5倍。

  • 您有一个累人的惩罚,但是您没有使用它。我假设如果您累了,您的移动速度就很累Penaltyx您的步行速度。这是我的编码方式:

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

public class CharacterMover : MonoBehaviour
{
    public float walkSpeed = 6f;
    //You'll need to add the rigidBody to your gameObject, and configure your gravity/weight/etc there.
    public RigidBody rb;
    public float maxStamina = 100f;
    //SluggishRecoveryThreshold is the stamina level at which you return to normal walk speed and not sluggish
    public float sluggishRecoveryThreshold = 30f;
    public float sprintMult = 1.5f;
    public float tiredMult = 0.9f;
    public float staminaDrainRate = 1.0f;
    public float staminaRegenRate = 1.0f;
    public Slider stam_bar; 

    MoveState moveState;
    float stamina;

    enum MoveState
    {
        Walking,
        Sprinting,
        Sluggish
    }

    void Awake()
    {
        rb = GetComponent<RigidBody>();
    }

    public void Start ()
    {
        stamina = maxStamina;
        moveState = MoveState.Walking;
        stam_bar.value = stamina / maxStamina;
    }

    void Update()
    {        
        //On the first frame that you push the sprint button, start the sprint coroutine
        if(Input.GetButtonDown("Sprint"))
            StartCoroutine(Sprint());

        //If you're not currently sprinting, regen stamina
        if(moveState != MoveState.Sprinting)
        {
            stamina += staminaRegenRate/ Time.deltaTime; 
            if(stamina > maxStamina)
                stamina = maxStamina;
        }

        //If you're currently sluggish, check to see if you can stop being sluggish
        if(moveState == MoveState.Sluggish)
        {
            if(stamina > sluggishRecoveryThreshold)
               moveState = MoveState.Walking;
        }

        stam_bar.value = stamina / maxStamina;

        //Determine the move speed multiplier based on the current move state.
        float moveMult = 1f;
        if(moveState == MoveState.Sprinting)
            moveMult = sprintMult;
        if(moveState == MoveState.Sluggish)
            moveMult = tiredMult;

        //Determine the move direction based on axis input
        moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
        moveDirection = transform.TransformDirection(moveDirection);
        Vectory3 moveVector = moveDirection * walkSpeed * moveMult;

        //move the character by changing rigidbody velocity, but do not affect the y vector to allow gravity to continue to act.
        Vector3 rbVelocity = rb.velocity;
        rbVelocity.x = moveVector.x;
        rbVelcoity.z = moveVector.z;
        rb.velocity = rbVelocity;
    }

    IEnumerator Sprinting()
    {
        //If you don't have the stamina to sprint, exit the coroutine early
        if(stamina <= 0)
            yield break;
        while(Input.GetButton("Sprint"))
        {
            moveState = MoveState.Sprinting;
            //Calculate the fraction of stamina lost since last frame
            stamina -= staminaDrainRate / Time.deltaTime; 

            if(currentstam <= 0)
            {
                stamina = 0;
                //Exit the coroutine early and mark character as sluggish
                moveState = MoveState.Sluggish;
                yield break;
            }
            yield return null;
        }
    }
    //Disclaimer: This wall all written in browser and not tested. Please let me know if you find any syntax errors.
}
© www.soinside.com 2019 - 2024. All rights reserved.