beginner Utilities

Interaction System

Raycast-based interaction with prompt UI, hold-to-interact option, and IInteractable interface. Press E to interact with anything.

Unity 2022.3+ · 2.8 KB · InteractionSystem.cs

How to Use

1

Attach InteractionSystem to your player (or camera)

2

Set interact range and layer mask in inspector

3

Implement IInteractable on any object you want to interact with:

4

public class Door : MonoBehaviour, IInteractable {

5

public string InteractionPrompt => "Open Door";

6

public void Interact(GameObject interactor) { /* open */ }

7

}

8

Hook OnPromptShow/OnPromptHide to your UI text

9

Press E (configurable) to interact, or enable hold-to-interact

Source Code

InteractionSystem.cs
C#
using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// Interface for objects that can be interacted with.
/// Implement this on doors, NPCs, pickups, levers, etc.
/// </summary>
public interface IInteractable
{
    string InteractionPrompt { get; }
    void Interact(GameObject interactor);
}

/// <summary>
/// Raycast-based interaction system. Attach to the player or camera.
/// Detects IInteractable objects and shows a prompt.
/// </summary>
public class InteractionSystem : MonoBehaviour
{
    [Header("Raycast")]
    [SerializeField] private float interactRange = 3f;
    [SerializeField] private LayerMask interactLayer = ~0;
    [SerializeField] private Transform raycastOrigin;

    [Header("Input")]
    [SerializeField] private KeyCode interactKey = KeyCode.E;
    [SerializeField] private bool holdToInteract = false;
    [SerializeField] private float holdDuration = 1f;

    [Header("Events")]
    public UnityEvent<string> OnPromptShow;
    public UnityEvent OnPromptHide;
    public UnityEvent<float> OnHoldProgress;
    public UnityEvent<GameObject> OnInteracted;

    private IInteractable currentTarget;
    private GameObject currentTargetObject;
    private float holdTimer;
    private bool isPromptShowing;

    private void Start()
    {
        if (raycastOrigin == null)
        {
            Camera cam = Camera.main;
            if (cam != null) raycastOrigin = cam.transform;
            else raycastOrigin = transform;
        }
    }

    private void Update()
    {
        CheckForInteractable();
        HandleInput();
    }

    private void CheckForInteractable()
    {
        Ray ray = new Ray(raycastOrigin.position, raycastOrigin.forward);
        IInteractable found = null;
        GameObject foundObject = null;

        if (Physics.Raycast(ray, out RaycastHit hit, interactRange, interactLayer))
        {
            found = hit.collider.GetComponent<IInteractable>();
            if (found == null)
                found = hit.collider.GetComponentInParent<IInteractable>();

            if (found != null)
                foundObject = hit.collider.gameObject;
        }

        if (found != currentTarget)
        {
            currentTarget = found;
            currentTargetObject = foundObject;
            holdTimer = 0f;

            if (found != null)
            {
                OnPromptShow?.Invoke(found.InteractionPrompt);
                isPromptShowing = true;
            }
            else if (isPromptShowing)
            {
                OnPromptHide?.Invoke();
                isPromptShowing = false;
            }
        }
    }

    private void HandleInput()
    {
        if (currentTarget == null) return;

        if (holdToInteract)
        {
            if (Input.GetKey(interactKey))
            {
                holdTimer += Time.deltaTime;
                OnHoldProgress?.Invoke(holdTimer / holdDuration);

                if (holdTimer >= holdDuration)
                {
                    PerformInteraction();
                    holdTimer = 0f;
                }
            }
            else
            {
                holdTimer = 0f;
                OnHoldProgress?.Invoke(0f);
            }
        }
        else
        {
            if (Input.GetKeyDown(interactKey))
                PerformInteraction();
        }
    }

    private void PerformInteraction()
    {
        currentTarget.Interact(gameObject);
        OnInteracted?.Invoke(currentTargetObject);
    }
}