首页 > 解决方案 > 统一冷却实施

问题描述

我正在尝试在我的 Unity 项目中实现冷却,虽然这段代码似乎有意义,但它不起作用。发布的代码是一个全方位的基本运动脚本。

我尝试用 a 做一些事情cooldown -=time.deltatime,但这似乎不起作用。我一直在尝试几种方法,但似乎都没有奏效。

编码:

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

public class MovementScript : MonoBehaviour
{
    public float cooldown = 0;
    public float actualcooldown = 3f;
    public bool isCooldown = false;

    // Start is called before the first frame update
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.R))
        {
            GetComponent<Renderer>().material.color = Color.red;
        }
        if (Input.GetKey(KeyCode.G))
        {
            GetComponent<Renderer>().material.color = Color.green;
        }
        if (Input.GetKey(KeyCode.B))
        {
            GetComponent<Renderer>().material.color = Color.blue;
        }
        if (Input.GetKey(KeyCode.D))
        {
            transform.Translate(6f * Time.deltaTime, 0, 0);
        }
        if (Input.GetKey(KeyCode.A))
        {
            transform.Translate(-6f * Time.deltaTime, 0, 0);
        }
        if (Input.GetKeyDown(KeyCode.Space) && cooldown <= 0) {
            transform.Translate(0f, 20f * Time.deltaTime, 0f);
            isCooldown = true;
            while (isCooldown == true)
            {
                coolDownhappening();
            }
        }
    }

    public void coolDownhappening()
    {
        cooldown = actualcooldown;
        cooldown -= Time.deltaTime;

        if (cooldown <= 0)
        {
            cooldown = 0;
        }
    }
}

标签: c#unity3d

解决方案


你做

 while (isCooldown == true)
 {
     coolDownhappening();
 }

但你永远不会改变isCoolddown任何地方!

同样正如评论中已经提到的,您根本不想while在该方法中使用Update,至少在这个用例中不是!这将在给定的冷却时间内冻结整个主线程 - 或者在你的情况下永远冻结!


您的代码中还有很多其他问题,所以让我们一步一步来:

  • Input.GetKey当他们给定键被按下时,每一帧都是真的。但是,这没有任何意义,而且只要按住按钮,重复将材质颜色设置为相同值只会导致不必要的开销。你宁愿做的是应用一次。

    → 宁可Input.GetKeyDown用于这些!

  • GetComponent是一个相当昂贵的电话。您不应重复使用GetComponent<Renderer>(),而应将引用存储一次并在以后重新使用

    // most efficient is always to already reference this via the Inspector
    [SerializeField] private Renderer _renderer;
    
    // alternatively get it on runtime
    private void Awake()
    {
        if(!_renderer) _rednerer = GetComponent<Renderer>();
    }
    

    然后稍后使用

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.R))
        {
            _renderer.material.color = Color.red;
        }
    
        ...
    }
    
  • 你的活动部分实际上很好。为了使它更具可读性,我实际上宁愿做类似的事情

    if (Input.GetKey(KeyCode.D))
    {
        transform.Translate(Vector3.right * 6f * Time.deltaTime);
    }
    else if (Input.GetKey(KeyCode.D))
    {
        transform.Translate(Vector3.left * 6f * Time.deltaTime);
    }
    

    还要注意else这里。当然取决于您的需求,但通常您希望相反的按钮独占。

  • 最后是真正的交易:您实际上希望在这里有一个带有冷却时间的跳跃方法。

    首先在这里你做了相反的事情:当键按下时,在框架中Input.GetKeyDown只被调用一次。所以你的对象“跳跃” 20 * 1/FPS,对于 60 FPS 来说总是大约0.33. 您可能更希望在多个帧上向上移动一定距离。达到一定高度后,激活冷却。

    正如评论中提到的,可以使用计时器来做到这一点,Update但这通常会使代码有点混乱。而是使用协程:

    private bool _canJump;
    
    private void Update()
    {
        ...
    
        // _canJump is cheaper to check so check it first
        if (_canJump && Input.GetKeyDown(KeyCode.Space)) 
        {
            StartCoroutine(JumpRoutine());
        }
    }
    
    private IEnumerator JumpRoutine()
    {
        // avoid concurrent routines
        if(!_canJump) yield break;
        // disable jumping
        _canJump = false;
    
        // Now it really depends on what you actually want to do 
        // and how your object should move now
        // you might e.g. want something like
        var jumpedHeight = 0f;
        while(jumpedHeight < 20f)
        {
            var jumpThisFrame = Vector3.up * 6f * Time.deltaTime;
            transform.Translate(jumpThisFrame);
    
            // important now! yield tells Unity to "pause" here,
            // render this frame, and continue from here int he next frame
            // without the yield statements this would again freeze your game until
            // the exit condition is fulfilled!
            yield return null;
        }
    
        // After reaching the target height this waits for 3 seconds but keeps on rendering meanwhile
        yield return new WaitForSeconds(actualcooldown);
    
        // after the cooldown allow next jump
        _canJump = true;
    }    
    

推荐阅读