有没有办法从按钮单击启动方法而不使用统一的Update()函数

Ima*_*oon 2 c# unity-game-engine vuforia

下面是我的C#脚本.我在项目中添加了一个带有On Click事件的按钮,并调用了Rotate()方法.但由于某种原因它不起作用

using System.Threading;
using UnityEngine;

public class Orbit : MonoBehaviour {

    public GameObject sun;
    public float speed;

    // Use this for initialization
    void Start () {

    }

    public void Update()
    {
        Rotate();
    }

    public void Rotate()
    {
        transform.RotateAround(sun.transform.position, Vector3.up, speed * 
        Time.deltaTime);
    }
}
Run Code Online (Sandbox Code Playgroud)

我在调用Rotate()方法时注释了Update()方法.我还为脚本创建了一个游戏对象.

der*_*ugo 5

它之所以在Update目前才有效的原因是

public void Rotate()
{
    transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
Run Code Online (Sandbox Code Playgroud)

需要被重复调用.否则它只会旋转一帧,并且Time.deltaTime只会产生很小的量.但组件的onClick事件Button只被触发一次.它类似于例如Input.GetKeyDown当键下降时仅调用一次.Button组件中没有实现它来处理持续按下按钮.


根据我的理解,你想要的是在点击按钮后旋转对象

  • 开始永远旋转
  • 持续一段时间
  • 直到你再次按下按钮
  • 直到它被释放( - >实现一个持续触发按钮,见下文)

Button单独的组件只能执行前三个:

永远旋转

使用Coroutine

private bool isRotating;

public void Rotate()
{
    // if aready rotating do nothing
    if(isRotating) return;

    // start the rotation
    StartCoroutine(RotateRoutine());

    isRotating = true;
}

private IEnumerator RotateRoutine()
{
    // whuut?!
    // Don't worry coroutines work a bit different
    // the yield return handles that .. never forget it though ;)
    while(true)
    {
         // rotate a bit
         transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);

        // leave here, render the frame and continue in the next frame
        yield return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

或者仍然在做 Update

private bool isRotating = false;

private void Update()
{
    // if not rotating do nothing
    if(!isRotating) return;

    // rotate a bit
    transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}

public void Rotate()
{
    // enable the rotation
    isRotating = true;
}
Run Code Online (Sandbox Code Playgroud)

请注意,Update解决方案仅供您了解所发生的情况.它不应该像那样使用,因为它不是那么有效,因为它被Update连续调用并且如果还没有旋转则检查bool.这会产生不必要的开销 这同样适用于以下所有示例:优先使用Coroutines Update(在这种情况下!在其他情况下,使用一种Update方法而不是多个并发协同程序,它实际上更好,更有效..但这是另一个故事.)

旋转一段时间

作为Coroutine

// adjust in the inspector
// how long should rotation carry on (in seconds)?
public float duration = 1;

private bool isAlreadyRotating;

public void Rotate()
{
    // if aready rotating do nothing
    if(isAlreadyRotating) return;

    // start a rottaion
    StartCoroutine(RotateRoutine());
}

private IEnumerator RotateRoutine()
{
    // set the flag to prevent multiple callse
    isAlreadyRotating = true;

    float timePassed = 0.0f;
    while(timePassed < duration)
    {
         // rotate a small amount
         transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);

         // add the time passed since last frame
         timePassed += Time.deltaTime;

         // leave here,  render the frame and continue in the next frame
         yield return null;
    }

    // reset the flag so another rotation might be started again
    isAlreadyRotating = false;
}
Run Code Online (Sandbox Code Playgroud)

或者在 Update

public float duration;

private bool isRotating;
private float timer;

private void Update()
{
    // if not rotating do nothing
    if(!isRotating) return;

    // reduce the timer by passed time since last frame
    timer -= Time.deltaTime;

    // rotate a small amount
    transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);

    // if the timer is not 0 return
    if(timer > 0) return;

    // stop rottaing
    isRotating = false;
}

public void Rotate()
{
    // if already rotating do nothing
    if(isRotating) return;

    // start rotating
    isRotating = true;

    // enable timer
    timer = duration;
}
Run Code Online (Sandbox Code Playgroud)

切换旋转

这与之前的非常相似,但这次是通过再次单击停止旋转而不是计时器.(你甚至可以将两者结合起来,但要小心isRotating正确地重置旗帜;))

作为Coroutine

private bool isRotating;

public void ToggleRotation()
{
    // if rotating stop the routine otherwise start one
    if(isRotating)
    {
        StopCoroutine(RotateRoutine());
        isRotating = false;
    }
    else
    {
        StartCoroutine(RotateRoutine());
        isRotating = true;
    }
}

private IEnumerator RotateRoutine()
{
    // whuut?!
    // Don't worry coroutines work a bit different
    // the yield return handles that .. never forget it though ;)
    while(true)
    {
        // rotate a bit
        transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);

        // leave here, render the frame and continue in the next frame
        yield return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

或者作为 Update

private bool isRotating;

private void Update()
{
    // if not rotating do nothing
    if(!isRottaing) return;

    // rotate a bit
    transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}

public void ToggleRotation()
{
    // toggle the flag
    isRotating = !isRotating;
}
Run Code Online (Sandbox Code Playgroud)

旋转直到释放

这是最"复杂"的部分,因为Button单凭这一部分无法做到这一点(没有"发布").但您可以使用IPointerXHandler接口实现此功能.

好消息:您可以保留原始脚本

public void Rotate()
{
    transform.RotateAround(sun.transform.position, Vector3.up, speed * 
    Time.deltaTime);
}
Run Code Online (Sandbox Code Playgroud)

现在您需要按钮的扩展名.它会在whilePressed每一帧重复调用事件,Update因此您只需要引用您的Rotate方法whilePressed而不是onClick.

再次有两个选项可以将它作为Coroutine实现:

[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
    // reference the same way as in onClick
    public UnityEvent whilePressed;       

    private Button button;
    private bool isPressed;

    private void Awake()
    {
        button = GetComponent<Button>();

        if(!button)
        {
            Debug.LogError("Oh no no Button component on this object :O",this);
        }
    }

    // Handle pointer down
    public void OnPointerDown()
    {
        // skip if the button is not interactable
        if(!button.enabled || !button.interactable) return;

        // skip if already rotating
        if(isPressed) return;

        StartCoroutine(PressedRoutine());
        isPressed= true;

    }

    // Handle pointer up
    public void OnPointerUp()
    {
        isPressed= false;
    }

    // Handle pointer exit
    public void OnPointerExit()
    {
        isPressed= false;
    }

    private IEnumerator RotateRoutine()
    {
        // repeatedly call whilePressed until button isPressed turns false
        while(isPressed)
        {
            // break the routine if button was disabled meanwhile
            if(!button.enabled || !button.interactable)
            {
                isPressed = false;
                yield break;
            }

            // call whatever is referenced in whilePressed;
            whilePressed.Invoke();

            // leave here, render the frame and continue in the next frame
            yield return null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者你可以做同样的Update再次以及

[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
    public UnityEvent whilePressed;

    private bool isPressed;
    private Button button;

    private void Awake()
    {
        button = GetComponent<Button>();

        if(!button)
        {
            Debug.LogError("Oh no no Button component on this object :O",this);
        }
    }


    private void Update()
    {
        // if button is not interactable do nothing
        if(!button.enabled || !button.interactable) return;

        // if not rotating do nothing
        if(!isPressed) return;

        // call whatever is referenced in whilePressed;
        whilePressed.Invoke();
    }

    // Handle pointer down
    public void OnPointerDown()
    {
        // enable pressed
        isPressed= true;
    }

    // Handle pointer up
    public void OnPointerUp()
    {
        // disable pressed
        isPressed= false;
    }

    // Handle pointer exit
    public void OnPointerExit()
    {
        // disable pressed
        isPressed= false;
    }
}
Run Code Online (Sandbox Code Playgroud)

将此组件放在组件旁边Button.您不必引用任何内容,onClick只需将其留空即可.而是引用一些东西onPressed.保留Button组件,因为它也处理我们的UI样式(如悬停更改颜色/精灵等)


再说一次:Update解决方案现在可能看起来更干净/更简单,但效率不高(在这个用例中)并且易于控制(这可能是基于意见的)作为Coroutine解决方案.