412 lines
13 KiB
C#
412 lines
13 KiB
C#
/// <summary>
|
|
/// SURGE FRAMEWORK
|
|
/// Author: Bob Berkebile
|
|
/// Email: bobb@pixelplacement.com
|
|
///
|
|
/// Simplify the act of selecting and interacting with things.
|
|
///
|
|
/// </summary>
|
|
|
|
#pragma warning disable 0649
|
|
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
namespace Pixelplacement
|
|
{
|
|
public class Chooser : MonoBehaviour
|
|
{
|
|
//Public Events:
|
|
public GameObjectEvent OnSelected;
|
|
public GameObjectEvent OnDeselected;
|
|
public GameObjectEvent OnPressed;
|
|
public GameObjectEvent OnReleased;
|
|
|
|
//Public Enums:
|
|
public enum Method { Raycast, RaycastAll };
|
|
|
|
//Public Variables:
|
|
public bool _cursorPropertiesFolded;
|
|
public bool _unityEventsFolded;
|
|
public Transform source;
|
|
public float raycastDistance = 3;
|
|
public LayerMask layermask = -1;
|
|
public KeyCode[] pressedInput;
|
|
public Transform cursor;
|
|
public float surfaceOffset;
|
|
public float idleDistance = 3f;
|
|
public float stabilityDelta = 0.0127f;
|
|
public float snapDelta = 1;
|
|
public float stableSpeed = 2;
|
|
public float unstableSpeed = 20;
|
|
public bool flipForward;
|
|
public bool matchSurfaceNormal = true;
|
|
public bool autoHide;
|
|
public bool cursorHidden;
|
|
public bool flipCastDirection;
|
|
public LineRenderer lineRenderer;
|
|
|
|
//Public Properties:
|
|
public Transform[] Current
|
|
{
|
|
get
|
|
{
|
|
return _current.ToArray();
|
|
}
|
|
}
|
|
|
|
public bool IsHitting
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
//Private Variables:
|
|
[SerializeField] Method _method;
|
|
[SerializeField] bool _debugView = false;
|
|
Transform _previousCursor;
|
|
List<Transform> _current = new List<Transform>();
|
|
List<Transform> _previous = new List<Transform>();
|
|
Transform _currentRaycast;
|
|
Transform _previousRaycast;
|
|
Vector3 _targetPosition;
|
|
bool _hidden;
|
|
|
|
//Init:
|
|
private void Reset()
|
|
{
|
|
source = transform;
|
|
pressedInput = new KeyCode[] { KeyCode.Mouse0 };
|
|
}
|
|
|
|
//Flow:
|
|
private void OnEnable()
|
|
{
|
|
if (source == null)
|
|
{
|
|
source = transform;
|
|
}
|
|
|
|
if (cursor != null)
|
|
{
|
|
cursor.position = source.position;
|
|
cursor.gameObject.SetActive(true);
|
|
}
|
|
if (lineRenderer != null)
|
|
{
|
|
lineRenderer.positionCount = 0;
|
|
lineRenderer.enabled = true;
|
|
}
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (cursor != null) cursor.gameObject.SetActive(false);
|
|
if (lineRenderer != null) lineRenderer.enabled = false;
|
|
}
|
|
|
|
//Gizmos:
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
if (Application.isPlaying) return;
|
|
|
|
Vector3 castDirection = source.forward;
|
|
if (flipCastDirection) castDirection *= -1;
|
|
Gizmos.color = Color.green;
|
|
Gizmos.DrawRay(source.position, castDirection * raycastDistance);
|
|
|
|
if (cursor != null)
|
|
{
|
|
Gizmos.color = Color.yellow;
|
|
Gizmos.DrawLine(source.position, cursor.position);
|
|
}
|
|
}
|
|
|
|
//Public Methods:
|
|
public void Pressed()
|
|
{
|
|
switch (_method)
|
|
{
|
|
case Method.Raycast:
|
|
if (_currentRaycast != null)
|
|
{
|
|
_currentRaycast.SendMessage("Pressed", SendMessageOptions.DontRequireReceiver);
|
|
if (OnPressed != null) OnPressed.Invoke(_currentRaycast.gameObject);
|
|
}
|
|
break;
|
|
|
|
case Method.RaycastAll:
|
|
if (_current.Count > 0)
|
|
{
|
|
foreach (var item in _current)
|
|
{
|
|
item.SendMessage("Pressed", SendMessageOptions.DontRequireReceiver);
|
|
if (OnPressed != null) OnPressed.Invoke(item.gameObject);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void Released()
|
|
{
|
|
switch (_method)
|
|
{
|
|
case Method.Raycast:
|
|
if (_currentRaycast != null)
|
|
{
|
|
_currentRaycast.SendMessage("Released", SendMessageOptions.DontRequireReceiver);
|
|
if (OnReleased != null) OnReleased.Invoke(_currentRaycast.gameObject);
|
|
}
|
|
break;
|
|
|
|
case Method.RaycastAll:
|
|
if (_current.Count > 0)
|
|
{
|
|
foreach (var item in _current)
|
|
{
|
|
item.SendMessage("Released", SendMessageOptions.DontRequireReceiver);
|
|
if (OnReleased != null) OnReleased.Invoke(item.gameObject);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Loops:
|
|
private void Update()
|
|
{
|
|
//cursor setup:
|
|
if (cursor != _previousCursor)
|
|
{
|
|
_previousCursor = cursor;
|
|
if (cursor == null) return;
|
|
|
|
foreach (var item in cursor.GetComponentsInChildren<Collider>())
|
|
{
|
|
Debug.Log("Cursor can not contain colliders. Disabling colliders on: " + item.name);
|
|
item.enabled = false;
|
|
}
|
|
}
|
|
|
|
//process input:
|
|
if (pressedInput != null)
|
|
{
|
|
foreach (var item in pressedInput)
|
|
{
|
|
/* if (Input.GetKeyDown(item))
|
|
{
|
|
Pressed();
|
|
}
|
|
|
|
if (Input.GetKeyUp(item))
|
|
{
|
|
Released();
|
|
} */
|
|
}
|
|
}
|
|
|
|
//clear out:
|
|
_current.Clear();
|
|
|
|
//raycast:
|
|
RaycastHit hit;
|
|
Vector3 castDirection = source.forward;
|
|
if (flipCastDirection) castDirection *= -1;
|
|
Physics.Raycast(source.position, castDirection, out hit, raycastDistance, layermask);
|
|
_currentRaycast = hit.transform;
|
|
IsHitting = hit.transform != null;
|
|
|
|
//cache:
|
|
if (_method == Method.Raycast && IsHitting)
|
|
{
|
|
_current.Clear();
|
|
_current.Add(hit.transform);
|
|
}
|
|
|
|
//debug info:
|
|
if (_debugView)
|
|
{
|
|
if (hit.transform != null)
|
|
{
|
|
Debug.DrawLine(source.position, hit.point, Color.green);
|
|
}
|
|
else
|
|
{
|
|
Debug.DrawRay(source.position, castDirection * raycastDistance, Color.red);
|
|
}
|
|
}
|
|
|
|
//cursor visibility:
|
|
if (cursor != null)
|
|
{
|
|
if (cursorHidden)
|
|
{
|
|
cursor.gameObject.SetActive(false);
|
|
}
|
|
else
|
|
{
|
|
if (autoHide)
|
|
{
|
|
cursor.gameObject.SetActive(IsHitting);
|
|
if (lineRenderer != null) lineRenderer.enabled = IsHitting;
|
|
}
|
|
else
|
|
{
|
|
cursor.gameObject.SetActive(true);
|
|
if (lineRenderer != null) lineRenderer.enabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//cursor management:
|
|
if (cursor != null)
|
|
{
|
|
if (hit.transform != null)
|
|
{
|
|
//get position:
|
|
_targetPosition = hit.point + hit.normal * surfaceOffset;
|
|
|
|
//get position speed:
|
|
float posSpeed = unstableSpeed;
|
|
float delta = Vector3.Distance(_targetPosition, cursor.position);
|
|
if (delta <= stabilityDelta)
|
|
{
|
|
posSpeed = stableSpeed;
|
|
}
|
|
|
|
if (delta >= snapDelta)
|
|
{
|
|
cursor.position = _targetPosition;
|
|
}
|
|
else
|
|
{
|
|
cursor.position = Vector3.Lerp(cursor.position, _targetPosition, Time.unscaledDeltaTime * posSpeed);
|
|
}
|
|
|
|
//set rotation:
|
|
if (matchSurfaceNormal)
|
|
{
|
|
cursor.rotation = Quaternion.LookRotation(hit.normal, source.up);
|
|
}
|
|
else
|
|
{
|
|
cursor.LookAt(source, Vector3.up);
|
|
}
|
|
|
|
//adjust:
|
|
if (flipForward)
|
|
{
|
|
cursor.Rotate(Vector3.up * 180);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//put out in front and face source (flip if needed):
|
|
Vector3 inFront = source.position + castDirection * idleDistance;
|
|
float delta = Vector3.Distance(inFront, cursor.position);
|
|
float posSpeed = unstableSpeed;
|
|
|
|
if (delta <= stabilityDelta)
|
|
{
|
|
posSpeed = stableSpeed;
|
|
}
|
|
|
|
if (delta >= snapDelta)
|
|
{
|
|
cursor.position = inFront;
|
|
}
|
|
else
|
|
{
|
|
cursor.position = Vector3.Lerp(cursor.position, inFront, Time.unscaledDeltaTime * posSpeed);
|
|
}
|
|
|
|
cursor.LookAt(source.position);
|
|
if (flipForward)
|
|
{
|
|
cursor.Rotate(Vector3.up * 180);
|
|
}
|
|
}
|
|
}
|
|
|
|
//handle raycast messages:
|
|
if (_method == Method.Raycast)
|
|
{
|
|
//select:
|
|
if (_previousRaycast == null && hit.transform != null)
|
|
{
|
|
hit.transform.SendMessage("Selected", SendMessageOptions.DontRequireReceiver);
|
|
if (OnSelected != null) OnSelected.Invoke(hit.transform.gameObject);
|
|
}
|
|
|
|
//updated select:
|
|
if (hit.transform != null && _previousRaycast != null && _previousRaycast != hit.transform)
|
|
{
|
|
_previousRaycast.SendMessage("Deselected", SendMessageOptions.DontRequireReceiver);
|
|
if (OnDeselected != null) OnDeselected.Invoke(_previousRaycast.gameObject);
|
|
hit.transform.SendMessage("Selected", SendMessageOptions.DontRequireReceiver);
|
|
if (OnSelected != null) OnSelected.Invoke(hit.transform.gameObject);
|
|
}
|
|
|
|
//deselect:
|
|
if (_previousRaycast != null && hit.transform == null)
|
|
{
|
|
_previousRaycast.SendMessage("Deselected", SendMessageOptions.DontRequireReceiver);
|
|
if (OnDeselected != null) OnDeselected.Invoke(_previousRaycast.gameObject);
|
|
}
|
|
|
|
//cache:
|
|
_previousRaycast = hit.transform;
|
|
}
|
|
|
|
//raycast all:
|
|
if (_method == Method.RaycastAll)
|
|
{
|
|
//catalog:
|
|
foreach (var item in Physics.RaycastAll(source.position, castDirection, raycastDistance, layermask))
|
|
{
|
|
_current.Add(item.transform);
|
|
}
|
|
|
|
//handle selects:
|
|
if (_current.Count > 0)
|
|
{
|
|
foreach (var item in _current)
|
|
{
|
|
if (_previous.Count == 0 || !_previous.Contains(item))
|
|
{
|
|
item.SendMessage("Selected", SendMessageOptions.DontRequireReceiver);
|
|
if (OnSelected != null) OnSelected.Invoke(item.gameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
//handle deselects:
|
|
if (_previous.Count > 0)
|
|
{
|
|
foreach (var item in _previous)
|
|
{
|
|
if (_current.Count == 0 || !_current.Contains(item))
|
|
{
|
|
item.SendMessage("Deselected", SendMessageOptions.DontRequireReceiver);
|
|
if (OnDeselected != null) OnDeselected.Invoke(item.gameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
//cache:
|
|
_previous.Clear();
|
|
_previous.AddRange(_current);
|
|
}
|
|
|
|
//line renderer:
|
|
if (cursor != null && cursor.gameObject.activeSelf && lineRenderer != null )
|
|
{
|
|
if (lineRenderer.positionCount != 2) lineRenderer.positionCount = 2;
|
|
lineRenderer.SetPosition(0, source.position);
|
|
lineRenderer.SetPosition(1, cursor.position);
|
|
}
|
|
}
|
|
}
|
|
} |