using System.Collections.Generic; using UnityEngine; // Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved. namespace SciFiShipController { /// /// Component to be used with the SSCDoorAnimator component to trigger when doors are locked or unlocked. /// Place it on a gameobject with a single trigger collider. /// Either the gameobject of this component, or the item that enters the trigger area must have a rigidbody /// attached, otherwise the OnTriggerEnter/Exit/Stay will not be called. /// The rigidbody can be set to isKinematic. /// WARNING: Beware of using multiple colliders on the same gameobject as this component. This can lead /// to unwanted behaviour like getting Enter/Exit/Stay events when you least expect it. /// For example, a box non-trigger collider on this gameobject way cause a OnTriggerEnter event /// to fire when say a sphere trigger collider has been configured but an object with a trigger collider /// has entered the space of the box collider. /// The reason this occurs is that Unity creates a compound collider for all colliders on the same gameobject. /// The solution is to move the trigger collider (and this component) to a child gameobject. /// public class SSCDoorProximity : MonoBehaviour { #region Public Variables public SSCDoorAnimator sscDoorAnimator; [Tooltip("Array of zero-based door indexes from the SSCDoorAnimator to control")] public int[] doorIndexes; [Tooltip("Should the door(s) be unlocked when an object enters the collider area?")] public bool isUnlockDoorsOnEntry = true; [Tooltip("Should the door(s) be openned when an object enters the collider area? Will have no effect on locked doors.")] public bool isOpenDoorsOnEntry = true; [Tooltip("Should the door(s) be closed when an object exits the collider area? Will have no effect on locked doors.")] public bool isCloseDoorsOnExit = true; [Tooltip("Should the door(s) be locked when an object exits the collider area?")] public bool isLockDoorsOnExit = true; [Tooltip("Array of Unity Tags for objects that affect this collider area. If none are provided, all objects can affect this area. NOTE: All tags MUST exist.")] public string[] tags = new string[] {}; #endregion #region Private Variables private Collider proximityCollider = null; private bool isInitialised = false; private int numTags = 0; #endregion #region Initialisation Methods // Start is called before the first frame update void Start() { if (sscDoorAnimator != null) { // Find the first trigger collider Collider[] colliders = GetComponents(); foreach (Collider collider in colliders) { if (collider.isTrigger) { proximityCollider = collider; break; } } if (proximityCollider != null) { ValidateTags(); isInitialised = true; } #if UNITY_EDITOR else { Debug.LogWarning("[ERROR] SSCDoorProximity could not find a (trigger) collider component. Did you attach one to this gameobject?"); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("[ERROR] SSCDoorProximity could not find SSCDoorAnimator component for " + gameObject.name); } #endif } /// /// Removes any empty or null tags. NOTE: May increase GC so don't use each frame. /// private void ValidateTags() { numTags = tags == null ? 0 : tags.Length; if (numTags > 0) { List tagList = new List(tags); for (int tgIdx = numTags - 1; tgIdx >= 0; tgIdx--) { // Remove invalid tag if (string.IsNullOrEmpty(tagList[tgIdx])) { tagList.RemoveAt(tgIdx); } } // If there were invalid entries, update the array if (tagList.Count != numTags) { tags = tagList.ToArray(); numTags = tags == null ? 0 : tags.Length; } } } #endregion #region Private Methods /// /// Does the gameobject have a tag that matches the array configured by the user. /// Will return true if a match is found OR there are no tags configured. /// /// /// private bool IsObjectTagMatched(GameObject objectGameObject) { if (!isInitialised) { return false; } if (objectGameObject == null) { return false; } else if (numTags < 1) { return true; } else { bool isMatch = false; for (int tgIdx = 0; tgIdx < numTags; tgIdx++) { if (objectGameObject.CompareTag(tags[tgIdx])) { isMatch = true; break; } } return isMatch; } } #endregion #region Event Methods private void OnTriggerEnter(Collider other) { if (isInitialised) { int numDoors = GetNumberOfDoors(); if (numDoors > 0 && IsObjectTagMatched(other.gameObject)) { //Debug.Log("[DEBUG] trigger enter " + proximityCollider.name + " other: " + other.name + " other tag: " + other.gameObject.tag + " T:" + Time.time); for (int dIdx = 0; dIdx < numDoors; dIdx++) { int doorNumber = doorIndexes[dIdx]; if (isUnlockDoorsOnEntry) { sscDoorAnimator.UnlockDoor(doorNumber); } if (isOpenDoorsOnEntry) { sscDoorAnimator.OpenDoors(doorNumber); } } } } //Debug.Log("[DEBUG] trigger enter " + proximityCollider.name + " other: " + other.name + " T:" + Time.time); } private void OnTriggerStay(Collider other) { if (isInitialised) { //Debug.Log("[DEBUG] trigger stay " + proximityCollider.name + " other: " + other.name + " T:" + Time.time); } } private void OnTriggerExit(Collider other) { if (isInitialised) { int numDoors = GetNumberOfDoors(); if (numDoors > 0 && IsObjectTagMatched(other.gameObject)) { //Debug.Log("[DEBUG] trigger exit " + proximityCollider.name + " other: " + other.name + " T:" + Time.time); for (int dIdx = 0; dIdx < numDoors; dIdx++) { int doorNumber = doorIndexes[dIdx]; if (isCloseDoorsOnExit) { sscDoorAnimator.CloseDoors(doorNumber); } if (isLockDoorsOnExit) { sscDoorAnimator.LockDoor(doorNumber); } } } } //Debug.Log("[DEBUG] trigger exit " + proximityCollider.name + " other: " + other.name + " T:" + Time.time); } #endregion #region Public API Methods /// /// Get the number of doors or sets of doors affect by this proximity component /// /// public int GetNumberOfDoors() { return doorIndexes == null ? 0 : doorIndexes.Length; } #endregion } }