Forum rules - please read before posting.

hold button to interact

Hey, in my game I use :

  • Control method: direct
  • Input: keyboard
  • Interaction: context sensitive

    So I use the space bar for interaction. Sometimes I would like to have hotspots where the player has to hold the spacebar for a few seconds before the interaction is successful. (And i would show some kind of loading bar ui to show the interaction progress) Is there a way to make this "holding the button to interact"without writing a whole custom interaction system? Or would is be easy to write a custom script for it?

Comments

  • edited June 2023

    Best way is probably to use a custom script that simulates the pressing of the InteractionA input once another button has been held down.

    If you've already got your InteractionA input mapped to space, rename this input instead to CustomInteraction, and try adding the following to your scene:

    using UnityEngine;
    using UnityEngine.UI;
    using AC;
    
    public class HoldToInteract : MonoBehaviour
    {
    
        public float holdDuration = 2f;
        public string tagName = "HoldToInteract";
        public string inputName = "CustomInteraction";
        public Slider uiSlider;
        private bool isOverHoldHotspot;
        private float timer;
    
        private void OnEnable ()
        {
            EventManager.OnHotspotSelect += OnSelectHotspot;
            EventManager.OnHotspotDeselect += OnDeselectHotspot;
        }
    
        private void OnDisable ()
        {
            EventManager.OnHotspotSelect -= OnSelectHotspot;
            EventManager.OnHotspotDeselect -= OnDeselectHotspot;
        }
    
        private void Update ()
        {
            if (isOverHoldHotspot)
            {
                if (Input.GetButton (inputName) && timer < holdDuration)
                {
                    timer += Time.deltaTime;
                    if (timer >= holdDuration)
                    {
                        KickStarter.playerInput.SimulateInputButton ("InteractionA");
                    }
                }
            }
            else
            {
                if (Input.GetButtonDown (inputName))
                {
                    KickStarter.playerInput.SimulateInputButton ("InteractionA");
                }
            }
    
            if (uiSlider)
            {
                uiSlider.value = GetProgress ();
            }
        }
    
        private void OnSelectHotspot (Hotspot hotspot)
        {
            isOverHoldHotspot = hotspot.CompareTag (tagName);
            timer = 0f;
        }
    
        private void OnDeselectHotspot (Hotspot hotspot)
        {
            isOverHoldHotspot = false;
        }
    
        public float GetProgress ()
        {
            if (isOverHoldHotspot)
            {
                return Mathf.Clamp01 (timer / holdDuration);
            }
            return 0f;
        }
    
    }
    

    Tag any Hotspot you want to hold the button down for as "HoldToInteract" (or whatever you change the Inspector field to), and that should then cause their interactions to run only when the input is held down for a given time.

    If you also assign a UI Slider component, then its value will be updated with the hold's progress - though this is more of an example.

    I do wonder though: perhaps another way to go about this would be to create a QTE as part of a regular Hotspot's interaction - whereby the rest of the Interaction's Actions are only run if a "Hold Key" QTE is succesfully completed.

  • Thanks, this is script is very helpful!

  • Hello.
    Sorry for my english.

    This script doesn't work correctly for me.
    Unity 2020.3.30f1
    AC v1.75.3

    CustomInteraction just duplicates a single click on InteractionA.
    Nothing changes. Both buttons work for tagged hotspots. And both buttons work the same way.

    Is there a way to solve a similar problem now? Or am I doing something wrong?

    Thank you.

  • Just a typo in the script - I've corrected it above.

  • Thank you very much. How it works.
    Is there any way to make it work correctly with the mouse and keyboard input method. Now Interaction running also with a single mouse click, not only with holding input button.
    I'm trying to make it work only with the left mouse button. For tagged hotspots - holding left mouse button. For untagged - sungle click.

    Now I did it using QTE, but I had some problems and decided to try this method as well.

  • Uncheck Mouse clicks have default functionality? in the Settings Manager, and then map your "CustomInteraction" input to "mouse 0" in the Input Manager.

  • edited June 2023

    Thanks for the quick response.
    If I uncheck this option, does it mean that I have to create a new InteractionA input in the manager in order to preserve the functionality of the left mouse button for all other untagged hotspots?

    Because if I do this, then a single click (as well as holding the left mouse button) still works with tagged hotspots.

  • After much thought, I haven't found a better solution other than to dynamically enable and disable "Mouse clicks have default functionality?" in settings.

    Now it looks like this.

    using UnityEngine;
    using UnityEngine.UI;
    using AC;
    using UnityEditor;
    public class HoldToInteract : MonoBehaviour
    {
    
        public float holdDuration = 2f;
        public string tagName = "HoldToInteract";
        public string inputName = "CustomInteraction";
        public string menuName = "myMenuWithSlider";
        private bool isOverHoldHotspot;
        private float timer;
        GVar myVariable1; //for menu slider
    
    
        private void OnEnable()
        {
            EventManager.OnHotspotSelect += OnSelectHotspot;
            EventManager.OnHotspotDeselect += OnDeselectHotspot;
        }
    
        private void OnDisable()
        {
            EventManager.OnHotspotSelect -= OnSelectHotspot;
            EventManager.OnHotspotDeselect -= OnDeselectHotspot;
        }
    
        private void Update()
        {
            if (isOverHoldHotspot)
            {
                if (Input.GetButton(inputName) && timer < holdDuration)
                {
                    myVariable1.FloatValue = GetProgress();
                    timer += Time.deltaTime;
                    if (timer >= holdDuration)
                    {
                        KickStarter.playerInput.SimulateInputButton("InteractionA");
                    }
                }
            }
            else
            {
                if (Input.GetButtonDown(inputName))
                {
                    KickStarter.playerInput.SimulateInputButton("InteractionA");
                }
            }
        }
    
        private void OnSelectHotspot(Hotspot hotspot)
        {
            isOverHoldHotspot = hotspot.CompareTag(tagName);
            if (isOverHoldHotspot)
            {
                myVariable1 = AC.GlobalVariables.GetVariable(87);
                myVariable1.FloatValue = 0f;
                PlayerMenus.GetMenuWithName(menuName).TurnOn();
                KickStarter.settingsManager.defaultMouseClicks = false;
            }
            timer = 0f;
        }
    
        private void OnDeselectHotspot(Hotspot hotspot)
        {
    
            PlayerMenus.GetMenuWithName(menuName).TurnOff();
            KickStarter.settingsManager.defaultMouseClicks = true;
            isOverHoldHotspot = false;
    
        }
    
        public float GetProgress()
        {
            if (isOverHoldHotspot)
            {
                return Mathf.Clamp01(timer / holdDuration);
            }
    
            return 0f;      
        }
    
    }
    

    I also haven't found any other way to use the slider from the menu other than through the variable that represents the slider.
    Now it seems to be working, but I'm not quite sure that I did everything right. I will be grateful for a cursory analysis. Thank you.

  • Because if I do this, then a single click (as well as holding the left mouse button) still works with tagged hotspots.

    The script itself simulates the pressing of the InteractionA input - you shouldn't need to define it separately.

  • edited June 2023

    The script itself simulates the pressing of the InteractionA input - you shouldn't need to define it separately.

    The problem is that if I don't define InteractionA input separately , then a single left-click funcrionlity disappears and no longer works with untagged hotspots.

    To return a single click for untagged hotspots, I have to define interactionA input.
    But in this case, as I wrote above, the tagged hotspot's Interactions running also with a single mouse click, not only with holding input button.

  • The problem is that if I don't define InteractionA input separately , then a single left-click funcrionlity disappears and no longer works with untagged hotspots.

    Have you mapped "mouse 0" to the CustomInteraction input?

  • Have you mapped "mouse 0" to the CustomInteraction input?

    Yes. And the interaction performed while holding the input button works correctly for tagged hotspots.

    But if I uncheck Mouse clicks have default functionality? in the Settings Manager, then a single left-click functionality disappears and no longer works with untagged hotspots. ( if I don't define InteractionA input separately).

  • Are you using any other Input-related packages, scripts or assets? I'm afraid I can't recreate the issue - it's working on my end.

    However, since the script above was written, it's now possible to override inputs at the moment they're requested - try this alternative which makes use of that.

    using UnityEngine;
    using UnityEngine.UI;
    using AC;
    
    public class HoldToInteract : MonoBehaviour
    {
    
        public float holdDuration = 2f;
        public string tagName = "HoldToInteract";
        public string inputName = "CustomInteraction";
        public Slider uiSlider;
        private bool isOverHoldHotspot;
        private float timer;
    
        private void OnEnable ()
        {
            EventManager.OnHotspotSelect += OnSelectHotspot;
            EventManager.OnHotspotDeselect += OnDeselectHotspot;
        }
    
        private void OnDisable ()
        {
            EventManager.OnHotspotSelect -= OnSelectHotspot;
            EventManager.OnHotspotDeselect -= OnDeselectHotspot;
        }
    
        private void Start ()
        {
            KickStarter.playerInput.InputGetButtonDownDelegate = GetButtonDown;
        }
    
        private bool GetButtonDown (string _inputName)
        {
            if (_inputName == "InteractionA")
            {
                if (isOverHoldHotspot)
                {
                    return (timer >= holdDuration);
                }
                _inputName = inputName;
            }
    
            try
            {
                return Input.GetButtonDown (_inputName);
            }
            catch {}
            return false;
        }
    
        private void Update ()
        {
            if (isOverHoldHotspot)
            {
                if (!Input.GetButton (inputName))
                {
                    timer = 0f;
                }
                else
                {
                    timer += Time.deltaTime;
                }
            }
    
            if (uiSlider)
            {
                uiSlider.value = GetProgress ();
            }
        }
    
        private void OnSelectHotspot (Hotspot hotspot)
        {
            isOverHoldHotspot = hotspot.CompareTag (tagName);
            timer = 0f;
        }
    
        private void OnDeselectHotspot (Hotspot hotspot)
        {
            isOverHoldHotspot = false;
        }
    
        public float GetProgress ()
        {
            if (isOverHoldHotspot)
            {
                return Mathf.Clamp01 (timer / holdDuration);
            }
            return 0f;
        }
    
    }
    
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.