using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Rendering.PostProcessing;
public class UltimateExamineSystem : MonoBehaviour
{
[Header("GENERAL")]
public float detectableDistance = 5.0f; //max. distance for item detection
public MonoBehaviour playerMovementScript; //script used for player movement
public MonoBehaviour cameraControllerScript; //script used for camera movement
[SerializeField] PostProcessVolume _volume = null;
[SerializeField] AudioSource audioSource = null;
public bool lightEnabled = false; //liight will be used (true), light wont be used (false)
[Space(10)]
[Header("UI")]
[SerializeField] Canvas yourCanvas = null; //You canvas which will be disabled/enabled when needed
[SerializeField] Color cursorDotHighlighted = Color.red;
[Space(10)]
[Header("EXAMINE SETTINGS")]
[SerializeField] float zoomSmoothness = 15f; //controls how smooth the zoom is
public float rotationSpeed = 15; //controlls examine rotation speed
public float zoomSpeed = 1.5f; //controls zoom speed
public float defaultZoomDistance = 1; //distance from the camera, to which objects will initially go to when picked up
public Vector2 zoomMinMaxDefault = new Vector2(0.9f, 2.0f); //minimum and maximum zoom distance from defaultZoomDistance. (for zooming)
[HideInInspector] public bool examineState = false; //can be used to check if an item is being examined
float collisionCheckDistance = 0.4f; //distance used to check to disnctance - feel free to change
Transform detectedItem; //stores item which is being examined
Transform cam;
LayerMask layerMask;
Color cursorDotDefaultColor;
bool detectionState = false;
bool detectionStateChange = false;
Vector3 initialItemPosition; //position item was in when picked up
Vector3 initialItemRotation; //rotation item was in when picked up
float zoomDistance; //stores dynamic zoom distance which changes due to player input (mouse scrolling)
Rigidbody[] rigidbodies; //stores rigid bodies of the examined item
GameObject placeHolder;
Vector3 zoomMinMax;
bool hasInterestPoints;
Light examineLight = null;
Image cursorDot;
Canvas detectCanvas;
Canvas ExamineCanvas;
Canvas interestPointCanvas;
Text _itemName;
Text _itemInfo;
Text _interestPointInfo;
GameObject interestPointGroup;
void Start()
{
cam = GetComponentInChildren<Camera>().transform;
layerMask = LayerMask.GetMask("Examinable");
zoomMinMax = zoomMinMaxDefault;
examineLight = cam.GetComponentInChildren<Light>();
//Initialize UI variables
cursorDot = GameObject.Find("UESCursorDot").GetComponent<Image>();
detectCanvas = GameObject.Find("UESDetectCanvas").GetComponent<Canvas>();
ExamineCanvas = GameObject.Find("UESExamineCanvas").GetComponent<Canvas>();
interestPointCanvas = GameObject.Find("UESInterestPointInfoCanvas").GetComponent<Canvas>();
_itemName = GameObject.Find("UESItemNameText").GetComponent<Text>();
_itemInfo = GameObject.Find("UESItemInfoText").GetComponent<Text>();
_interestPointInfo = GameObject.Find("UESInterestPointInfoText").GetComponent<Text>();
interestPointGroup = GameObject.Find("UESInterestPointGroup");
cursorDotDefaultColor = cursorDot.color;
}
void Update()
{
/*if (ExamineState) //do this when entering examine state
{
Debug.Log("test1");
playerMovementScript.enabled = false;
cameraControllerScript.enabled = false;
LockCursor(false);
}
else { //do this when exiting examine state
playerMovementScript.enabled = true;
cameraControllerScript.enabled = true;
LockCursor(true);
}*/
if (Input.GetKeyDown(KeyCode.E) && DetectionState && !ExamineState) //do this when entering examine state
{
ExamineStateEnter();
}
else if(!ExamineState) //do this when NOT examining an item
{
DetectItem(); //look for examinable items
}
else Examine(); //start examining
}
void Examine() {
Debug.Log("Examining...");
if (Input.GetKeyDown(KeyCode.E)) //Stop examining
{
ExamineStateLeave();
}
else {
if (Input.GetKey(KeyCode.Mouse1)) //rotate the object if input detected
{
float rotX = Input.GetAxis("Mouse X") * rotationSpeed;
float rotY = Input.GetAxis("Mouse Y") * rotationSpeed;
detectedItem.Rotate(cam.up, -rotX, Space.World);
detectedItem.Rotate(cam.right, rotY, Space.World);
}
if (hasInterestPoints) DetectInterestPoints(); //look for interest points
//Check for collisions when zooming an item
float temp = zoomDistance;
float input = Input.GetAxis("Mouse ScrollWheel");
RaycastHit hit;
if (Physics.Raycast(cam.position, detectedItem.position - cam.position, out hit, zoomDistance + input + collisionCheckDistance) && !hit.transform.IsChildOf(detectedItem)) //if new position would collide do this
{
zoomDistance = Vector3.Distance(cam.position + cam.forward, hit.point); //sets zoom distance to the point where the collision was detected
}
else { //if new position wouldn't collide, freely change according to player input
zoomDistance += input;
}
zoomDistance = Mathf.Clamp(zoomDistance, zoomMinMax.x, zoomMinMax.y); //clamp current zoom distance to min/max limits
detectedItem.position = Vector3.Lerp(detectedItem.position, cam.transform.position + cam.forward * zoomDistance, zoomSmoothness * Time.deltaTime); //smoothly zoom by moving an objects closers/farther from the camera
//handle dynamic depth of field
_volume.profile.TryGetSettings<DepthOfField>(out var DOF);
DOF.focusDistance.value = Vector3.Distance(cam.position, detectedItem.position);
}
}
void ExamineStateEnter() { //do this when entering examine state
Debug.Log("ExamineStateEnter");
ExamineState = true;
//disable player and camera movement and unlock the cursor
playerMovementScript.enabled = false;
cameraControllerScript.enabled = false;
LockCursor(false);
//sore initial position and rotation, set zoomDistance to default one, enable post processing volume (Depth of field)
initialItemPosition = detectedItem.position;
initialItemRotation = new Vector3(detectedItem.eulerAngles.x, detectedItem.eulerAngles.y, detectedItem.eulerAngles.z);
zoomDistance = defaultZoomDistance;
cam.GetComponent<PostProcessVolume>().enabled = true;
ItemInfo itemInfo = detectedItem.GetComponent<ItemInfo>(); //sore iteminfo object reference
//disable detected item's colliders, reveal and remember interest points if there are any
itemInfo.DisableColliders();
itemInfo.RevealInterestPoints();
if (itemInfo.interestPoints.Length == 0) hasInterestPoints = false;
else hasInterestPoints = true;
//Override default zoom limits if there are any
Vector2 OverrideLimits = itemInfo.GetZoomLimits();
if (OverrideLimits != Vector2.zero)
{
zoomMinMax = OverrideLimits;
}
else
{
zoomMinMax = zoomMinMaxDefault;
}
//enable examine canvas and set info text
ExamineCanvas.enabled = true;
_itemInfo.text = itemInfo.GetInfo();
if (itemInfo.GetAudioInfo() && audioSource && !audioSource.isPlaying) //plays a sound clip if audiosource and audio clip are assigned and nothing is already playing
{
audioSource.PlayOneShot(itemInfo.GetAudioInfo());
}
if(examineLight && lightEnabled) examineLight.enabled = true; //turns on the light
rigidbodies = detectedItem.GetComponentsInChildren<Rigidbody>(); //Find rigid bodies in the item
foreach (Rigidbody rb in rigidbodies) { //disable rigidbodies
rb.isKinematic = true;
}
placeHolder = new GameObject("collider placeholder");
placeHolder.transform.position = detectedItem.position;
placeHolder.transform.rotation = detectedItem.rotation;
placeHolder.transform.localScale = detectedItem.localScale;
Collider col1 = detectedItem.GetComponent<Collider>();
if (col1 is BoxCollider)
{
placeHolder.AddComponent<BoxCollider>();
BoxCollider col = placeHolder.GetComponent<BoxCollider>();
col.center = ((BoxCollider)col1).center;
col.size = ((BoxCollider)col1).size;
}
else if (col1 is MeshCollider)
{
placeHolder.AddComponent<MeshCollider>();
MeshCollider col = placeHolder.GetComponent<MeshCollider>();
col.convex = ((MeshCollider)col1).convex;
col.sharedMesh = ((MeshCollider)col1).sharedMesh;
}
else if (col1 is SphereCollider)
{
placeHolder.AddComponent<SphereCollider>();
SphereCollider col = placeHolder.GetComponent<SphereCollider>();
col.center = ((SphereCollider)col1).center;
col.radius = ((SphereCollider)col1).radius;
}
}
void ExamineStateLeave() { //do this when leaving examine state
Debug.Log("ExamineStateLeave");
ExamineState = false;
//reset position and rotation
detectedItem.position = initialItemPosition;
detectedItem.eulerAngles = initialItemRotation;
//hide interest points
detectedItem.GetComponent<ItemInfo>().HideInterestPoints();
//disable canvases used for examining
interestPointCanvas.enabled = false;
ExamineCanvas.enabled = false;
//disable post processing volume(depth of field), re-enable colliders
cam.GetComponent<PostProcessVolume>().enabled = false;
detectedItem.GetComponent<ItemInfo>().EnableColliders();
//re-enable camera, player movement and lock the cursor
playerMovementScript.enabled = true;
cameraControllerScript.enabled = true;
LockCursor(true);
if(examineLight) examineLight.enabled = false; //turn off the light
foreach (Rigidbody rb in rigidbodies) //enable rigidbodies
{
rb.isKinematic = false;
}
Destroy(placeHolder);
}
void DetectInterestPoints() { //look for interest points
RaycastHit hit;
Camera camera = cam.GetComponent<Camera>();
if (Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit, detectableDistance)) //cast a ray from camera to mouse position
{
if (hit.transform.CompareTag("InterestPoint")) //if interest point transform has been hit
{
HandleInterestPointUI(hit.transform.gameObject, camera, true); //enable interest point UI
}
else
{
HandleInterestPointUI(null, null, false); //disable interest point UI
}
}
else {
HandleInterestPointUI(null, null, false); //disable interest point UI
}
}
void HandleInterestPointUI(GameObject item, Camera camera, bool detected) { //enable/disable interest point UI
if (detected) //enable canvas, set position, set interest point UI text
{
interestPointCanvas.enabled = true;
interestPointGroup.transform.position = camera.WorldToScreenPoint(item.transform.position) + (new Vector3(0, 60, 0));
_interestPointInfo.text = item.GetComponent<InterestPoint>().GetInfo();
}
else { //disable canvases
interestPointCanvas.enabled = false;
detectCanvas.enabled = false;
}
}
void DetectItem() { //look for examinable items
Debug.Log("Attempting to detect item...");
RaycastHit hit;
if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, detectableDistance, layerMask)) //if examinable item has been hit
{
Debug.Log($"Detected object: {hit.collider.gameObject.name}", hit.collider.gameObject);
if (detectionStateChange) HandleDetectionUI(hit.transform.gameObject, true); //enable detection UI (item name...)
DetectionState = true;
}
else
{
if (detectionStateChange) HandleDetectionUI(null, false); //disable detection UI
DetectionState = false;
}
}
void HandleDetectionUI(GameObject item, bool detected) { //disable/enable detectuin UI
Debug.Log("HandleDetectionUI");
if (detected && detectionStateChange) //if examinable object has been detected
{
detectedItem = item.transform; //store detected item reference
cursorDot.color = cursorDotHighlighted; //set cursor highlight color
detectCanvas.enabled = true;
try //set item name UI text to UI text
{
_itemName.text = item.GetComponent<ItemInfo>().GetName();
}
catch { //if item object doesnt have ItemInfo script attached
_itemName.text = "Unknown item";
Debug.LogWarning(item.name + " does not have ItemInfo component!");
}
}
else if(detectionStateChange){ //disable UI, set normal cursor color
cursorDot.color = cursorDotDefaultColor;
detectCanvas.enabled = false;
}
}
public bool DetectionState //keeps track of boolean value changes for optimal performance
{
get { return detectionState; }
set
{
if (value == detectionState)
return;
detectionState = value;
if (detectionState) //do this if new bool value equals to true
{
detectionStateChange = true;
}
else { //do this if new bool value equals to false
detectionStateChange = false;
}
}
}
public bool ExamineState //keeps track of boolean value changes for optimal performance
{
get { return examineState; }
set
{
if (value == examineState)
return;
examineState = value;
if (examineState) //do this if new bool value equals to true (enables Ui elements which we dont want in examine state)
{
cursorDot.enabled = false;
detectCanvas.enabled = false;
if(yourCanvas) yourCanvas.enabled = false;
}
else //do this if new bool value equals to false (enables Ui elements which were disabled)
{
cursorDot.enabled = true;
detectCanvas.enabled = true;
if (yourCanvas) yourCanvas.enabled = true;
}
}
}
public void LockCursor(bool locked) { //lock, hide / unlock, show cursor
if (locked)
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
else {
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
}
}