问题
I am new to unity and building a game, I use 2 buttons (IncreaseButton, DecreaseButton) the problem I have is that the button callback functions are only called once, when the user clicks on the button but not when the button is held down. How can I make the button to be called repeatedly when held down?
Code
public void IncreaseBPM()
{
if (speed < 12)
{
speed += 0.05f;
bpmText.GetComponent<BeatTextControl> ().beats += 1;
PlayerPrefs.SetFloat ("savedBPM", speed);
}
}
public void DecreaseBPM()
{
if (speed > 1.5)
{
speed -= 0.05f;
bpmText.GetComponent<BeatTextControl> ().beats -= 1;
PlayerPrefs.SetFloat ("savedBPM", speed);
}
}
回答1:
okay so firstly you create 2 booleans:
bool increase = false;
bool decrease = false;
then you set them inside the functions
public void WhenButtonClicked()
{ increase = true; }
public void WhenOtherButtonClicked()
{ decrease = true; }
Then in the update function inside the same file you check:
Void update(){
If(increase){ IncreaseBPM(); }
Else if(decrease){ DecreaseBPM(); }
}
About when the button gets released, i found a simple answer: http://answers.unity3d.com/questions/843319/46-gui-button-onrelease.html
Add an Event Trigger component to your button (In the event menu). From there you can add a listener for OnPointerUp. Treat it just the same as OnClick.
And create 2 other functions that set increase and decrease to false !
回答2:
Unity's Button component does not have this functionality built in. You have to roll out your own with the Image component as a Button. Implement IPointerDownHandler and IPointerUpHandler interface then override the OnPointerDown and OnPointerUp functions.
When OnPointerDown is called, use struct to store the which object/volume Image is clicked and the current click pointerId in a List.
You can then check which Image is clicked in the Update function.
If the OnPointerUp is called, you have to check which pointerId is released then check if that pointerId exist in the List and remove it if it does.
I have gone ahead to do this. Below is the new script in your question:
public class ButtonTest : MonoBehaviour
{
// Use this for initialization
void Start()
{
//Register to Button events
ButtonDownRelease.OnButtonActionChanged += ButtonActionChange;
Debug.Log("Registered!");
}
// Update is called once per frame
void Update()
{
}
//Un-Register to Button events
void OnDisable()
{
ButtonDownRelease.OnButtonActionChanged -= ButtonActionChange;
}
//Will be called when there is a Button action
void ButtonActionChange(ButtonAction buttonAction)
{
//Check for held down
if (buttonAction == ButtonAction.DecreaseButtonDown)
{
Debug.Log("Decrease Button held Down!");
DecreaseBPM();
}
if (buttonAction == ButtonAction.IncreaseButtonDown)
{
Debug.Log("Increase Button held Down!");
IncreaseBPM();
}
//Check for release
if (buttonAction == ButtonAction.DecreaseButtonUp)
{
Debug.Log("Decrease Button Released!");
}
if (buttonAction == ButtonAction.IncreaseButtonUp)
{
Debug.Log("Increase Button Released!");
}
}
private void IncreaseBPM()
{
if (TempoController.GetComponent<Pendulum>().speed < 12)
{
TempoController.GetComponent<Pendulum>().speed += 0.05f;
}
}
private void DecreaseBPM()
{
if (TempoController.GetComponent<Pendulum>().speed > 1.5)
{
TempoController.GetComponent<Pendulum>().speed -= 0.05f;
}
}
}
Read carefully.
Create a new script called ButtonDownRelease then put the code below into it. Attach the ButtonDownRelease script to the Canvas The Canvas which is the parent of your Images/Buttons UI GameObjects. Create two Images (Increase and Decrease). Create two tags called increase and decrease then put both Images in the right tag.
Note:
You can still use Button instead of the Image to make this work if you don't want to use the Image component. Simply select each Button and change the tags to increase and decrease, then select the Text component that is under each Button and change their tags increase and decrease too. You must change the Button's Text tags too if you want to use the Button component with this script. Also, you will have to attach ButtonDownRelease to each Button not to the Canvas like you have to with Image component.
Your ButtonDownRelease script:
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System.Collections.Generic;
public class ButtonDownRelease : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
List<ButtonInfo> buttonInfo = new List<ButtonInfo>();
public delegate void ButtonActionChange(ButtonAction buttonAction);
public static event ButtonActionChange OnButtonActionChanged;
// Update is called once per frame
void Update()
{
//Send Held Button Down events
for (int i = 0; i < buttonInfo.Count; i++)
{
if (buttonInfo[i].buttonPresed == ButtonAction.DecreaseButtonDown)
{
dispatchEvent(ButtonAction.DecreaseButtonDown);
}
else if (buttonInfo[i].buttonPresed == ButtonAction.IncreaseButtonDown)
{
dispatchEvent(ButtonAction.IncreaseButtonDown);
}
}
}
void dispatchEvent(ButtonAction btAction)
{
if (OnButtonActionChanged != null)
{
OnButtonActionChanged(btAction);
}
}
public void OnPointerDown(PointerEventData eventData)
{
//Debug.Log("Button Down!");
ButtonInfo bInfo = new ButtonInfo();
bInfo.uniqueId = eventData.pointerId;
if (eventData.pointerCurrentRaycast.gameObject.CompareTag("increase"))
{
bInfo.buttonPresed = ButtonAction.IncreaseButtonDown;
addButton(bInfo);
}
else if (eventData.pointerCurrentRaycast.gameObject.CompareTag("decrease"))
{
bInfo.buttonPresed = ButtonAction.DecreaseButtonDown;
addButton(bInfo);
}
}
public void OnPointerUp(PointerEventData eventData)
{
//Debug.Log("Button Down!" + eventData.pointerCurrentRaycast);
removeButton(eventData.pointerId);
}
void addButton(ButtonInfo bInfo)
{
buttonInfo.Add(bInfo);
}
void removeButton(int unqID)
{
for (int i = 0; i < buttonInfo.Count; i++)
{
if (unqID == buttonInfo[i].uniqueId)
{
//Send Release Button Up events
if (buttonInfo[i].buttonPresed == ButtonAction.DecreaseButtonDown)
{
dispatchEvent(ButtonAction.DecreaseButtonUp);
}
else if (buttonInfo[i].buttonPresed == ButtonAction.IncreaseButtonDown)
{
dispatchEvent(ButtonAction.IncreaseButtonUp);
}
buttonInfo.RemoveAt(i);
}
}
}
public struct ButtonInfo
{
public int uniqueId;
public ButtonAction buttonPresed;
}
}
public enum ButtonAction
{
None,
IncreaseButtonDown, IncreaseButtonUp,
DecreaseButtonDown, DecreaseButtonUp
}
Finally, you will run into problems if you only use boolean variables to do this. This needs to be done with pointerId like the script provided in this answer in order to avoid bugs on mobile devices. A good example of this bug is when you click on an Image then release the finger on another GameObject, your boolean logic would break because OnPointerUp won't be called. Also using multi-touch on mobile devices will cause problems too.
来源:https://stackoverflow.com/questions/39195387/run-code-when-button-is-held-down