Physics Based Pressure Pads
Pressure pads are a great tool for puzzle oriented game design. Let’s take a brief look at one way to set up physics based pressure pads for a 3D game in Unity.
The orthographic profile image shows the main object having a box collider that sits low and thin on the base of the structure. This trigger collider will be used to call some function when the white pad is compressed enough to touch it.
The pad is a child object of the main pressure pad parent, and holds a small collection of components. For starters, there is a hard surface collider and a rigidbody with constrained movement and rotation on all but the Y axis.
There is also a Spring Joint on the pad, with the Base object assigned as the Connected Body. The Base object must also have a rigidbody, and because it is stationary, the rigidbody is set to isKinematic. The Anchor and Connected Anchor positions on the Y axis are inverted, which will make the pad push upwards. The Spring value of 100 seems to do ok for this example.
In play mode, you can see the spring joint in action when I move the pad manually.
Dropping this heavy box on the pad will keep it compressed.
You can see where the pad is contacting the small trigger collider at the base of the object.
Scripting
The Pressure Pad script checks for the Pad tag in both on trigger enter and exit. When the pad is pressed down, the isLocked bool becomes false, and then that value gets passed through when the onPressurePadInteraction event is invoked. When the Pad exits the collider, the bool flips back to true and the event is raised.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class PressurePad : MonoBehaviour
{
public static event Action<bool> onPressurePadInteraction;
private bool _isLocked = true;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Pad"))
{
_isLocked = false;
onPressurePadInteraction?.Invoke(_isLocked);
}
}
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Pad"))
{
_isLocked = true;
onPressurePadInteraction?.Invoke(_isLocked);
}
}
}This Door class now subscribes to the onPressurePadInteraction event, and uses the isLocked bool to determine it’s locking and unlocking behavior.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Door : MonoBehaviour, iInteractable
{
[SerializeField] private bool _isOpen = false;
private bool _isLocked = true;
private Animator _animatior;
[SerializeField] private GameObject _lockMessageUI;
private WaitForSeconds _waitForSeconds;
private bool _isAnimating = false;
[SerializeField] GameObject _accessLight;
[SerializeField] private MeshRenderer _mr;
[SerializeField] private Material _emissiveMat;
private const string _emissiveColor = "_EmissiveColor";
private Color _lockedColor = Color.red;
private Color _unlockedColor = Color.green;
private void Start()
{
_animatior = GetComponent<Animator>();
_waitForSeconds = new WaitForSeconds(5.1f);
if (_animatior == null)
Debug.LogWarning("The Animator on the Door " + gameObject.name + " is NULL");
Terminal.onTerminalInteraction += Terminal_onTerminalInteraction;
PressurePad.onPressurePadInteraction += PressurePad_onPressurePadInteraction;
_mr = _accessLight.GetComponent<MeshRenderer>();
//get material
if (_mr != null)
{
_emissiveMat = _mr.material;
}
else
Debug.LogWarning("The Mesh Renderer on the Door Access object is NULL");
}
private void PressurePad_onPressurePadInteraction(bool isLocked)
{
if (isLocked)
{
Debug.Log("Doors are Locked");
LockDoor();
}
else
{
Debug.Log("Doors are Unlocked");
UnlockDoor();
}
}
private void OnDisable()
{
Terminal.onTerminalInteraction -= Terminal_onTerminalInteraction;
PressurePad.onPressurePadInteraction -= PressurePad_onPressurePadInteraction;
}
private void Terminal_onTerminalInteraction()
{
UnlockDoor();
}
public void Interact()
{
if (_isLocked)
{
//show lock message to disable after a time and fade out
if (_lockMessageUI != null)
{
_lockMessageUI.SetActive(true);
if (!_isAnimating)
{
_isAnimating = true;
StartCoroutine(DeactivateUIRoutine());
}
}
}
else
{
_isOpen = !_isOpen;
_animatior.SetBool("IsOpen", _isOpen);
}
}
private IEnumerator DeactivateUIRoutine()
{
yield return _waitForSeconds;
if (_lockMessageUI != null)
{
_lockMessageUI.SetActive(false);
}
_isAnimating = false;
}
private void UnlockDoor()
{
_isLocked = false;
_emissiveMat.color = _unlockedColor;
_emissiveMat.SetColor(_emissiveColor, _unlockedColor);
}
private void LockDoor()
{
_isLocked = true;
_emissiveMat.color = _lockedColor;
_emissiveMat.SetColor(_emissiveColor, _lockedColor);
}
}Here you can see the Debug message show that the doors have unlocked.
After pushing the box off the pressure pad, the doors have locked again.
I hope you enjoyed this introduction to physics based pressure plates in Unity, and thanks for reading!
