有没有一种方法可以通过单击按钮来启动方法,而无需统一使用Update()函数

时间:2019-01-08 05:58:01

标签: c# unity3d 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);
    }
}

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

2 个答案:

答案 0 :(得分:5)

目前只能在Update中使用的原因是

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

需要反复调用。否则,它将仅旋转一帧,而导致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;
    }
}

或仍在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;
}

请注意,Update解决方案仅用于您了解正在发生的事情。不能那样使用它,因为这样会效率不高,因为Update会被连续调用,如果还没有旋转,还会检查布尔值。这产生了不必要的开销。以下示例同样适用于所有:在Update上优先使用协程(在这种情况下!在其他情况下,使用更好,更有效一个Update方法而不是多个并发协同程序..但这是另一回事了。)

旋转一定时间

协程

// 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;
}

或在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;
}

切换旋转

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

协程

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;
    }
}

或作为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;
}

旋转直到释放

这是最“复杂”的部分,因为仅Button不能完成此操作(不存在“发布时”)。但是您可以使用IPointerXHandler接口来实现。

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

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

现在,您需要按钮的扩展名。它将像whilePressed这样的每帧重复调用Update事件,因此您只需要在Rotate中引用whilePressed方法而不是onClick

同样有两种选择将其实现为协同程序:

[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;
        }
    }
}

或者您也可以在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;
    }
}

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


再次:Update解决方案目前看起来更干净/更简单,但不像Coroutine解决方案那样有效(在此用例中)且易于控制(这可能是基于观点)。

答案 1 :(得分:0)

请搜索有关按键功能的文章。这将有助于您找到答案。如果我们需要在项目中连续执行某项操作,则使用更新,在该操作中,一次执行该操作会按

the Example for the Update issue

此示例还用于解决您的问题,并在按下特定按钮时使用此脚本