【问题标题】:Is there a way to start a method from button click without using Update() function in unity有没有办法在不统一使用 Update() 函数的情况下从按钮单击开始方法
【发布时间】:2019-01-08 05:58:00
【问题描述】:

下面是我的 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() 方法。我还为脚本创建了一个游戏对象。

【问题讨论】:

  • 你需要将Rotate方法绑定到一个按钮。编辑:我看到你已经做到了。有什么错误吗?您是如何设置 OnClick 事件的?
  • 没有错误。我创建了一个按钮,并在该按钮上添加了一个事件触发组件,并将游戏对象添加到 On Click() 方法并调用 Rotate() 方法。
  • 我不完全确定它为什么不起作用。我建议您在 Rotate 方法中执行 Debug.Log("Working") 以查看是否调用了该函数。
  • 添加按钮事件时似乎没有调用该函数。如果没有按钮事件,它会被调用。感谢您的帮助@bolkay
  • 标题真的很混乱,因为它让我觉得你在谈论Input.GetButton("XY")Update 与您对 Button 组件的 onClick 调用无关

标签: c# unity3d vuforia


【解决方案1】:

目前只在Update有效的原因是

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

需要重复调​​用。否则它只会旋转一帧,Time.deltaTime 的原因只有很小的一部分。但是Button 组件的onClick 事件只触发一次。它类似于例如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在这种情况下!在其他情况下,使用一个@实际上更好,更有效987654337@ 方法而不是多个并发 Coroutines .. 但这是另一回事。)

旋转一定时间

作为协程

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

现在您需要按钮的扩展名。它会像Update一样在每一帧重复调用whilePressed事件,所以你只需要在whilePressed中引用你的Rotate方法而不是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 解决方案高效(在此用例中)和易于控制(这可能基于意见)。

【讨论】:

  • 非常感谢。这对我有帮助。 :)
  • @ImaniAbayakoon 很高兴为您提供帮助!如果这解决了您的问题,请随时将答案标记为已接受:)
【解决方案2】:

请搜索有关按键功能的文章。它会帮助你找到你的答案。如果我们需要在项目中连续执行某些操作,则使用更新,当我们执行一次时使用按下的键

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

【讨论】:

  • 您的方法CheckInput 无论如何都必须在Update 或另一个重复调用的方法中调用,以便跟踪每一帧中的关键事件!然而,问题是关于UI.ButtononClick 事件,而不是使用Input。无论如何,主要问题实际上是OP的方法Rotate需要重复调​​用才能正常工作。
  • 好的伙计,我已经搜索并为您找到了一个非常好的视频,关于通过不同功能移动游戏对象。按键始终在更新方法中起作用,但移动游戏对象有不同的方法在下面的视频中解释。我制作了我的游戏并使用了 start 方法,但我调用了 update 方法,因为它总是逐帧工作。在更新方法中我们可以应用不同的功能链接youtube.com/watch?v=tNtOcDryKv4
  • 不幸的是,这不是 OP 所要求的。正如已经说过的,他想知道如何使用Rotate 方法而不在Update 中调用它,而是通过UI.Button 控制旋转......它与调用任何Input.GetKeyDown 或@987654334 无关@方法
  • 实际插入代码本身可能比屏幕截图更有用。
  • 截图帮助我节省时间并专注于其他关键问题,但感谢您的关注
猜你喜欢
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 2021-08-01
  • 2017-01-02
  • 1970-01-01
  • 2021-04-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多