我有一个运动脚本,并且其中还包含耐力部分。我有一个耗尽耐力的功能,但是我只想在按下按钮时调用它。如何更改空白部分,以使其仅在调用其定义的函数时才触发
这是我的代码(我在谈论的空白在第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);
}
}
不确定我是否理解您想要的,但是我想从您的代码中可以知道:
您希望staminaregen
和staminadrain
被重复调用,但仅在按下某个按钮后才能被调用。
首先,此检查没有意义
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());
}
注:在智能手机上键入,但我希望这个想法能清楚明白
有几种方法可以解决此问题。 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.
}