Files
ShopGame/Assets/Scripts/RayCastSystem.cs
2025-12-17 17:34:04 +00:00

152 lines
4.9 KiB
C#

using UnityEngine;
using TMPro;
public class RayCastSystem : MonoBehaviour
{
[Header("Layer Settings")]
public LayerMask interactableLayers;
[Header("Highlight Settings")]
public Color highlightColor = new Color(1f, 1f, 0f, 0.35f);
public float padding = 1.02f;
private Camera mainCamera;
private GameObject currentSelection;
private GameObject outlineObject;
private MeshFilter outlineFilter;
private MeshRenderer outlineRenderer;
private Rigidbody currentPickedRB = null;
[Header("UI Elements")]
public TMP_Text itemCountText;
void Start()
{
mainCamera = Camera.main;
CreateOutlineObject();
if(itemCountText) itemCountText.gameObject.SetActive(false);
}
void CreateOutlineObject()
{
outlineObject = new GameObject("SelectionHighlight");
outlineFilter = outlineObject.AddComponent<MeshFilter>();
outlineRenderer = outlineObject.AddComponent<MeshRenderer>();
Material ghostMat = new Material(Shader.Find("Standard"));
// Optimize Material: Enable GPU Instancing
ghostMat.enableInstancing = true;
ghostMat.SetFloat("_Mode", 3); // Transparent
ghostMat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
ghostMat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
ghostMat.SetInt("_ZWrite", 0);
ghostMat.DisableKeyword("_ALPHATEST_ON");
ghostMat.EnableKeyword("_ALPHABLEND_ON");
ghostMat.DisableKeyword("_ALPHAPREMULTIPLY_ON");
ghostMat.renderQueue = 3000;
ghostMat.color = highlightColor;
ghostMat.EnableKeyword("_EMISSION");
ghostMat.SetColor("_EmissionColor", highlightColor * 0.5f);
outlineRenderer.material = ghostMat;
outlineObject.SetActive(false);
// OPTIMIZATION: Do not parent to the player.
// Keeping it in the root ensures Scale calculations are always 1:1 with the target.
outlineObject.transform.SetParent(null);
}
void Update()
{
RaycastHit hit;
// Perform Raycast
if (Physics.Raycast(mainCamera.transform.position, mainCamera.transform.forward, out hit, 100f, interactableLayers))
{
GameObject hitObject = hit.collider.gameObject;
// Only run expensive logic if the selection ACTUALLY changed
if (currentSelection != hitObject)
{
currentSelection = hitObject;
UpdateOutline(currentSelection);
UpdateUI(currentSelection); // Separated UI logic for cleanliness
}
// Keep outline snapped
if (currentSelection != null && outlineObject.activeSelf)
{
FollowTarget(currentSelection);
}
}
else
{
if (currentSelection != null) ClearSelection();
}
}
void UpdateUI(GameObject target)
{
if (itemCountText == null) return;
Shelf shelf = target.GetComponent<Shelf>();
if (shelf != null)
{
// PERFORMANCE FIX:
// Instead of getting a whole dictionary, we just ask for the count directly.
// You need to add 'public int ItemCount' or similar to your Shelf script.
itemCountText.text = $"Items: {shelf.GetTotalItemCount()}";
itemCountText.gameObject.SetActive(true);
}
else
{
itemCountText.gameObject.SetActive(false);
}
}
void UpdateOutline(GameObject target)
{
if(currentPickedRB != null) {
outlineObject.SetActive(false);
return;
}
MeshFilter targetMeshFilter = target.GetComponent<MeshFilter>();
if (targetMeshFilter == null) targetMeshFilter = target.GetComponentInChildren<MeshFilter>();
if (targetMeshFilter == null)
{
outlineObject.SetActive(false);
return;
}
// Lightweight: Just swapping the reference, not creating new meshes
outlineFilter.sharedMesh = targetMeshFilter.sharedMesh;
FollowTarget(target);
outlineObject.SetActive(true);
}
void FollowTarget(GameObject target)
{
outlineObject.transform.position = target.transform.position;
outlineObject.transform.rotation = target.transform.rotation;
// This math is safer now that outlineObject is not a child of the Player
outlineObject.transform.localScale = target.transform.localScale * padding;
}
void ClearSelection()
{
currentSelection = null;
outlineObject.SetActive(false);
if (itemCountText) itemCountText.gameObject.SetActive(false);
}
public void SetCurrentPickedRB(Rigidbody rb)
{
currentPickedRB = rb;
if (currentPickedRB != null) ClearSelection();
}
}