feat (interaction): Add PressurePlateButton and WallInteractButton classes with interaction logic
This commit is contained in:
8
Assets/Code/Scripts/Interaction.meta
Normal file
8
Assets/Code/Scripts/Interaction.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 303f7f8e61ca68143b9597e9d31f0e02
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
76
Assets/Code/Scripts/Interaction/PressurePlateButton.cs
Normal file
76
Assets/Code/Scripts/Interaction/PressurePlateButton.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class PressurePlateButton : MonoBehaviour
|
||||
{
|
||||
[Header("Detection")]
|
||||
[Tooltip("Layers that can activate the plate. Keep default to allow everything.")]
|
||||
[SerializeField] private LayerMask detectionMask = ~0;
|
||||
|
||||
[Tooltip("If true, rigidbody objects can activate the plate.")]
|
||||
[SerializeField] private bool allowRigidbodies = true;
|
||||
|
||||
[Tooltip("If true, the Player can activate the plate (tag Player or PlayerMovement component).")]
|
||||
[SerializeField] private bool allowPlayer = true;
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent OnPressed;
|
||||
public UnityEvent OnReleased;
|
||||
|
||||
private readonly HashSet<Collider> m_validCollidersOnPlate = new HashSet<Collider>();
|
||||
private bool m_isPressed;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
Collider col = GetComponent<Collider>();
|
||||
col.isTrigger = true;
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (!IsValidActivator(other))
|
||||
return;
|
||||
|
||||
m_validCollidersOnPlate.Add(other);
|
||||
|
||||
if (!m_isPressed)
|
||||
{
|
||||
m_isPressed = true;
|
||||
OnPressed?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (!m_validCollidersOnPlate.Remove(other))
|
||||
return;
|
||||
|
||||
if (m_validCollidersOnPlate.Count == 0 && m_isPressed)
|
||||
{
|
||||
m_isPressed = false;
|
||||
OnReleased?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsValidActivator(Collider other)
|
||||
{
|
||||
if (((1 << other.gameObject.layer) & detectionMask) == 0)
|
||||
return false;
|
||||
|
||||
if (allowPlayer)
|
||||
{
|
||||
if (other.CompareTag("Player"))
|
||||
return true;
|
||||
|
||||
if (other.GetComponentInParent<PlayerMovement>() != null)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (allowRigidbodies && other.attachedRigidbody != null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69374de63bb12844d97acb7a794b7b40
|
||||
148
Assets/Code/Scripts/Interaction/TestBlock.cs
Normal file
148
Assets/Code/Scripts/Interaction/TestBlock.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class TestBlock : MonoBehaviour
|
||||
{
|
||||
[Header("Visual")]
|
||||
[SerializeField] private Renderer targetRenderer;
|
||||
[SerializeField] private Color offColor = Color.red;
|
||||
[SerializeField] private Color onColor = Color.green;
|
||||
|
||||
[Header("Material")]
|
||||
[SerializeField] private string colorPropertyName = "_BaseColor";
|
||||
|
||||
[Header("Optional Auto Wiring")]
|
||||
[Tooltip("If assigned, block turns ON while plate is pressed and OFF when released.")]
|
||||
[SerializeField] private PressurePlateButton[] pressurePlates;
|
||||
|
||||
[Tooltip("If assigned, block toggles every time the wall button is interacted with.")]
|
||||
[SerializeField] private WallInteractButton[] wallButtons;
|
||||
|
||||
private MaterialPropertyBlock m_propertyBlock;
|
||||
private bool m_isOn;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (targetRenderer == null)
|
||||
{
|
||||
targetRenderer = GetComponentInChildren<Renderer>();
|
||||
}
|
||||
|
||||
m_propertyBlock = new MaterialPropertyBlock();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
SetOff();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
RegisterEvents();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
UnregisterEvents();
|
||||
}
|
||||
|
||||
public void SetOn()
|
||||
{
|
||||
m_isOn = true;
|
||||
Debug.Log($"{gameObject.name} turned ON");
|
||||
ApplyColor(onColor);
|
||||
}
|
||||
|
||||
public void SetOff()
|
||||
{
|
||||
m_isOn = false;
|
||||
Debug.Log($"{gameObject.name} turned OFF");
|
||||
ApplyColor(offColor);
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
if (m_isOn)
|
||||
SetOff();
|
||||
else
|
||||
SetOn();
|
||||
}
|
||||
|
||||
// Generic interaction entry point for UnityEvents from any interactable.
|
||||
public void TriggerInteraction()
|
||||
{
|
||||
Toggle();
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
SetOn();
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
SetOff();
|
||||
}
|
||||
|
||||
private void ApplyColor(Color color)
|
||||
{
|
||||
if (targetRenderer == null)
|
||||
return;
|
||||
|
||||
targetRenderer.GetPropertyBlock(m_propertyBlock);
|
||||
m_propertyBlock.SetColor(colorPropertyName, color);
|
||||
targetRenderer.SetPropertyBlock(m_propertyBlock);
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
if (pressurePlates != null)
|
||||
{
|
||||
for (int i = 0; i < pressurePlates.Length; i++)
|
||||
{
|
||||
if (pressurePlates[i] == null)
|
||||
continue;
|
||||
|
||||
pressurePlates[i].OnPressed.AddListener(SetOn);
|
||||
pressurePlates[i].OnReleased.AddListener(SetOff);
|
||||
}
|
||||
}
|
||||
|
||||
if (wallButtons != null)
|
||||
{
|
||||
for (int i = 0; i < wallButtons.Length; i++)
|
||||
{
|
||||
if (wallButtons[i] == null)
|
||||
continue;
|
||||
|
||||
wallButtons[i].OnInteract.AddListener(Toggle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterEvents()
|
||||
{
|
||||
if (pressurePlates != null)
|
||||
{
|
||||
for (int i = 0; i < pressurePlates.Length; i++)
|
||||
{
|
||||
if (pressurePlates[i] == null)
|
||||
continue;
|
||||
|
||||
pressurePlates[i].OnPressed.RemoveListener(SetOn);
|
||||
pressurePlates[i].OnReleased.RemoveListener(SetOff);
|
||||
}
|
||||
}
|
||||
|
||||
if (wallButtons != null)
|
||||
{
|
||||
for (int i = 0; i < wallButtons.Length; i++)
|
||||
{
|
||||
if (wallButtons[i] == null)
|
||||
continue;
|
||||
|
||||
wallButtons[i].OnInteract.RemoveListener(Toggle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Scripts/Interaction/TestBlock.cs.meta
Normal file
2
Assets/Code/Scripts/Interaction/TestBlock.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b93699191d7b58146b2c38540cbed40f
|
||||
109
Assets/Code/Scripts/Interaction/WallInteractButton.cs
Normal file
109
Assets/Code/Scripts/Interaction/WallInteractButton.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class WallInteractButton : MonoBehaviour
|
||||
{
|
||||
[Header("Interaction")]
|
||||
[Tooltip("Optional Input Action. If empty, fallback key will be used.")]
|
||||
[SerializeField] private InputActionReference interactAction;
|
||||
|
||||
[Tooltip("Used only if no Input Action is assigned.")]
|
||||
[SerializeField] private Key fallbackKey = Key.E;
|
||||
|
||||
[SerializeField] private bool oneShot = false;
|
||||
|
||||
[Header("Prompt")]
|
||||
[SerializeField] private TMP_Text promptText;
|
||||
[SerializeField] private string promptMessage = "Press E to interact";
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent OnInteract;
|
||||
|
||||
private bool m_playerInRange;
|
||||
private bool m_hasInteracted;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
Collider col = GetComponent<Collider>();
|
||||
col.isTrigger = true;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (interactAction != null)
|
||||
{
|
||||
interactAction.action.Enable();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (interactAction != null)
|
||||
{
|
||||
interactAction.action.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
UpdatePrompt(false);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!m_playerInRange)
|
||||
return;
|
||||
|
||||
if (oneShot && m_hasInteracted)
|
||||
return;
|
||||
|
||||
if (WasInteractPressed())
|
||||
{
|
||||
m_hasInteracted = true;
|
||||
OnInteract?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (!IsPlayer(other))
|
||||
return;
|
||||
|
||||
m_playerInRange = true;
|
||||
UpdatePrompt(true);
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (!IsPlayer(other))
|
||||
return;
|
||||
|
||||
m_playerInRange = false;
|
||||
UpdatePrompt(false);
|
||||
}
|
||||
|
||||
private bool WasInteractPressed()
|
||||
{
|
||||
if (interactAction != null)
|
||||
return interactAction.action.WasPressedThisFrame() || interactAction.action.WasPerformedThisFrame();
|
||||
|
||||
return Keyboard.current != null && Keyboard.current[fallbackKey].wasPressedThisFrame;
|
||||
}
|
||||
|
||||
private bool IsPlayer(Collider other)
|
||||
{
|
||||
return other.CompareTag("Player") || other.GetComponentInParent<PlayerMovement>() != null;
|
||||
}
|
||||
|
||||
private void UpdatePrompt(bool visible)
|
||||
{
|
||||
if (promptText == null)
|
||||
return;
|
||||
|
||||
promptText.text = promptMessage;
|
||||
promptText.gameObject.SetActive(visible);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96a3f628ff18ea34788167cc398180ca
|
||||
Reference in New Issue
Block a user