Unity Double Click event - c#

I am new here and I'm beginning my adventure with UNITY. I have problem with double click event. I'd like to buying or selling something in my shop. When I assign a button on unity (public Button button;) earlier it works. But when i try this change to button on Start and Update methods:
void Start () {
button = GameObject.Find(EventSystem.current.currentSelectedGameObject.name).GetComponent<Button>();
button.onClick.AddListener(ButtonListner);
}
void Update()
{
button = GameObject.Find(EventSystem.current.currentSelectedGameObject.name).GetComponent<Button>();
}
private void ButtonListner()
{
counter++;
if (counter == 1)
{
StartCoroutine("doubleClickEvent");
}
}
IEnumerator doubleClickEvent()
{
yield return new WaitForSeconds(clickTimer);
if (counter > 1)
{...}
the method doubleClickEvent() doesnt work unfortunately...
What shall I do? Regards ;)

First thing I noticed was: button = GameObject.Find(EventSystem.current.currentSelectedGameObject.name).GetComponent<Button>();
The EventSystem.current.currentSelectedGameObject property can be null at anytime especially in the first frame which means that using it in the Start function is not a good idea. Find the Button GameObject then get the Button component from it:
Button button;
void Start()
{
button = GameObject.Find("YourButtonName").GetComponent<Button>();
button.onClick.AddListener(ButtonListner);
}
Replace "YourButtonName" with the name of your Button GameObject.
You don't even need to do most of the stuff you did. You can get double click or click count with PointerEventData.clickCount from the OnPointerClick function. You must implement the IPointerClickHandler interface for this to work.
Simply attach to the Button GameObject:
public class ClickCountDetector : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
int clickCount = eventData.clickCount;
if (clickCount == 1)
OnSingleClick();
else if (clickCount == 2)
OnDoubleClick();
else if (clickCount > 2)
OnMultiClick();
}
void OnSingleClick()
{
Debug.Log("Single Clicked");
}
void OnDoubleClick()
{
Debug.Log("Double Clicked");
}
void OnMultiClick()
{
Debug.Log("MultiClick Clicked");
}
}

You can detect double click without a coroutine like the following:
// Choose the time you want between clicks to consider it a double click
float doubleClickTime = .2f, lastClickTime;
void Update()
{
// Checking left mouse button click, you could choose the input you want here
if (Input.GetMouseButtonDown(0))
{
float timeSinceLastClick = Time.time - lastClickTime;
if (timeSinceLastClick <= doubleClickTime)
Debug.Log("Double click");
else
Debug.Log("Normal click");
lastClickTime = Time.time;
}
}

Related

The object of type Text has been destroyed but you are still trying to access it - Unity

I have a game that has a main play scene and a game over scene. The Game over scene has a button that simply starts the main scene again.
Everything works fine for the first play, but when restarting through the game over screen and trying to add to my score I get the following error:
The object of type Text has been destroyed but you are still trying to access it.
I'm not sure why this is, I've tried logging out the referenced text object in the update and it looks fine.
I'm setting the highScore by dragging the text UI component to the script in the editor. I've also tried using GetComponent but run into the same issue.
Heres the relevant script:
public class ScoreHandler : MonoBehaviour
{
public Text highScore;
private int score = 0;
private int basicEnemyValue = 10;
private int speedyEnemnyValue = 15;
private int bossEnemyValue = 50;
private int startingHighScore;
public static event Action<int> OnScoreChange;
void OnEnable() {
BasicEnemy.OnBasicEnemyDestroyed += BasicEnemyDestroyed;
TankEnemy.OnTankEnemyDestroyed += TankEnemyDestroyed;
SpeedyEnemy.OnSpeedyEnemyDestroyed += SpeedyEnemyDestroyed;
}
void onDisable() {
BasicEnemy.OnBasicEnemyDestroyed -= BasicEnemyDestroyed;
TankEnemy.OnTankEnemyDestroyed -= TankEnemyDestroyed;
SpeedyEnemy.OnSpeedyEnemyDestroyed -= SpeedyEnemyDestroyed;
}
void Start () {
startingHighScore = PlayerPrefs.GetInt ("highscore", 0);
highScore.text = "Score: " + score;
}
void Update () {
Debug.Log(highScore); // Looks good here, has a ref to the correct component UnityEngine.Ui.Text
Debug.Log(highScore.text); // Same as above - has what's set in start
}
private void checkHighScore(int score) {
if (score > startingHighScore) {
PlayerPrefs.SetInt ("highscore", score);
}
}
// ***HERE IS THE PROBLEM FUNCTION***
private void updateScoreText() {
Debug.Log(highScore); // null
highScore.text = "Score: " + score;
checkHighScore(score);
OnScoreChange?.Invoke(score);
}
private void BasicEnemyDestroyed() {
score += basicEnemyValue;
updateScoreText();
}
private void TankEnemyDestroyed() {
score += bossEnemyValue;
updateScoreText();
}
private void SpeedyEnemyDestroyed() {
score += speedyEnemnyValue;
updateScoreText();
}
}
I'm a bit confused as everything looks fine in the update, but seems to loose the component when I call the function on it. I've tried to add dontDestoryOnLoad() to the UI, but that adds a number of other issues with other UI components not being defined.
onDisable -> OnDisable
It's case-sensitve, so your event handlers are never removed.
FYI there is a shortcut to insert these messages in Visual Studio: CtrlShiftM

Detect one tap and long tap on touch

I am trying to implement two touch functions. One tap and long tap. I have implemented one tap which is pretty straightforward but for a long tap function, a timer must be set?
I would like the long press to be active only after 1 second of holding the tap and then the timer should get reset on release. How do I do this?
public float longTapTime;
void Update()
{
if ((Input.touchCount > 0) && (Input.GetTouch (0).phase == TouchPhase.Began)) {
//One tap
}
if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
{
longTapTime = 0;
}
}
using UnityEngine;
public class example : MonoBehaviour
{
[SerializeField]
private float long_tap_consider_duration = 1.0f;
private float time_helper;
private float duration;
private bool is_touched;
private void Start()
{
time_helper = 0;
duration = 0;
is_touched = false;
}
private void Update()
{
if (Input.touchCount > 0)
ProcessInput();
}
private void ProcessInput()
{
if (is_touched && ((Input.GetTouch(0).phase == TouchPhase.Ended) || (Input.GetTouch(0).phase == TouchPhase.Canceled)))
{
if (duration > long_tap_consider_duration)
OnLongTap();
else OnTap();
}
if (!is_touched && (Input.GetTouch(0).phase == TouchPhase.Began))
{
time_helper = Time.time;
is_touched = true;
}
duration = Time.time - time_helper;
}
//Callback function, when just a short tap occurs
private void OnTap()
{
Debug.Log("Short Tap");
}
//Callback function, when long tap occurs
private void OnLongTap()
{
Debug.Log("Long Tap");
}
}
actually, if you are using the new input system, this is a built in feature and does not need any code.
find your Input Actions ".inputActions"
find the button on the list of inputs registered
with that item selected, find the plus "+" next to the word interactions on the right hand side
add a "hold" interaction from the list and set desired hold time.
for more information on the input action asset system: https://www.youtube.com/watch?v=mvuXOyKz7k4

The object of type 'GameObject' has been destroyed but you are still trying to access it

So I am working on a runner game and it is almost done. The problem is that I am testing the Pause Panel and when player touches a zombie, pause panel shows up and I am restarting the game again by pressing the restart button. But when I touch the zombie again panel doesnt show up and gives me the error in the title. I am stuck and any help will be appreciated. This is the code and I referenced to the line that error sends me :
[SerializeField]
private GameObject pausePanel;
[SerializeField]
private Button RestartGameButton;
[SerializeField]
private Text ScoreText;
private int score;
void Start ()
{
pausePanel.SetActive(false);
ScoreText.text = score + "M";
StartCoroutine(CountScore());
}
IEnumerator CountScore()
{
yield return new WaitForSeconds(0.6f);
score++;
ScoreText.text = score + "M";
StartCoroutine(CountScore());
}
void OnEnable()
{
PlayerDeath.endgame += PlayerDiedEndTheGame;
}
void OnDisable()
{
PlayerDeath.endgame += PlayerDiedEndTheGame;
}
void PlayerDiedEndTheGame()
{
if (!PlayerPrefs.HasKey("Score"))
{
PlayerPrefs.SetInt("Score", 0);
}
else
{
int highscore = PlayerPrefs.GetInt("Score");
if(highscore < score)
{
PlayerPrefs.SetInt("Score", score);
}
}
pausePanel.SetActive(true); //this is the line that error sends me but I cant figure it out because I didnt try to destroy the panel in the first place.
RestartGameButton.onClick.RemoveAllListeners();
RestartGameButton.onClick.AddListener(() => RestartGame());
Time.timeScale = 0f;
}
public void PauseButton()
{
Time.timeScale = 0f;
pausePanel.SetActive(true);
RestartGameButton.onClick.RemoveAllListeners();
RestartGameButton.onClick.AddListener(() => ResumeGame());
}
public void GoToMenu()
{
Time.timeScale = 1f;
SceneManager.LoadScene("MainMenu");
}
public void ResumeGame()
{
Time.timeScale = 1f;
pausePanel.SetActive(false);
}
public void RestartGame()
{
Time.timeScale = 1f;
SceneManager.LoadScene("Gameplay");
}
I found the solution. It was a simple mistake on the OnDisable method. I deleted the += sign and changed it with -= sign because it was enabling the event when it was supposed to disable.

Converting mouse position functionality to work with touchscreen

I'm developing in Unity, C#, and I have a bit of code that checks for player activity based on mouse position and it's working well but I'm needing to also check player activity on a touchscreen (not a mobile phone touchscreen, but a touchscreen attached to a pc). How should I modify the code that I have below to also work with touch?
private void Start()
{
InvokeRepeating("LastMousePosition", 0, _checkMousePositionTimingInterval);
}
private void Update()
{
_currentMousePosition = Input.mousePosition;
}
void LastMousePosition()
{
_prevMousePosition = Input.mousePosition;
}
void CheckPlayerIdle()
{
if (_currentMousePosition != _prevMousePosition)
UserActive = true;
else if (_currentMousePosition == _prevMousePosition)
UserActive = false;
}
Well for touch you start your inactivity timer if you don't get any touch inputs and wait for x duration.
for eg:
private void Update()
{
if(Input.touchCount == 0)
{
if(!checkingForInactivity)
{
checkingForInactivity = true;
myRoutine = StartCoroutine(CheckForInactivity());
}
}
else
{
if(checkingForInactivity) StopCoroutine(myRoutine);
}
}
Ienumrable CheckForInactivity()
{
yield new waitForSecond(3.0f);
//user is inactive
}
}

How to give information from a button?

I have four buttons like:
I also have a gameobject that attaches my script to it
Under the script I have one function, that should print the text of the button, when I use it in onClick() buttons.
Inspector
How to call one function on different buttons and print out the text of those buttons, that the user clicked?
Instead of passing the string to the callback function, it would make more sense to pass the Button instance itself. If you do this, you will be able to access more data from the Button if needed in the future.
public Button[] Btn;
void OnEnable()
{
//Register Button Events
for (int i = 0; i < Btn.Length; i++)
{
int btIndex = i;
Btn[i].onClick.AddListener(() => buttonCallBack(Btn[btIndex]));
}
}
private void buttonCallBack(Button buttonPressed)
{
Debug.Log(buttonPressed.GetComponentInChildren<Text>().text);
if (buttonPressed == Btn[0])
{
//Your code for button 1
}
if (buttonPressed == Btn[1])
{
//Your code for button 2
}
if (buttonPressed == Btn[2])
{
//Your code for button 3
}
if (buttonPressed == Btn[3])
{
//Your code for button 4
}
}
void OnDisable()
{
//Un-Register Button Events
for (int i = 0; i < Btn.Length; i++)
{
Btn[i].onClick.RemoveAllListeners();
}
}
If you still want to pass the Button text only:
public Button[] Btn;
void OnEnable()
{
//Register Button Events
for (int i = 0; i < Btn.Length; i++)
{
int btIndex = i;
string btText = Btn[btIndex].GetComponentInChildren<Text>().text;
Btn[i].onClick.AddListener(() => buttonCallBack(btText));
}
}
private void buttonCallBack(string buttonText)
{
Debug.Log(buttonText);
}
void OnDisable()
{
//Un-Register Button Events
for (int i = 0; i < Btn.Length; i++)
{
Btn[i].onClick.RemoveAllListeners();
}
}
Remember to assign your Buttons to the Btn variable slot in the Editor or you will get the null exception error.
Rather taking all the buttons in the list. Make a script Button.cs and give it to each UI buttons
In that script make one public method
public void OnButtonClickListener(int index) {
// Do your code here for button click using its index
}
now you just have to assign callbacks of each UI buttons to this public method and pass the argument of button's index.
I hope this will help you...

Categories