Interaction System
Raycast-based interaction with prompt UI, hold-to-interact option, and IInteractable interface. Press E to interact with anything.
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
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);
}
}