Files
Click-PointRPG/Assets/Scripts/ClickToMove.cs

315 lines
11 KiB
C#

using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
public class ClickToMoveInputSystem : MonoBehaviour
{
[Header("References")]
public Camera cam;
public LayerMask movementLayers; // ground
public LayerMask selectionLayers; // enemies, NPCs, interactables
[Header("Options")]
[SerializeField]
private bool useLegacyMouseFallback = false;
[SerializeField]
private bool enableDebugLogs = false;
Camera cachedCam;
Vector2 pointerScreenPos;
PlayerInteractionController cachedInteraction;
NavMeshMovementController cachedMovement;
void Awake()
{
CacheCameraReference();
CachePlayerReferences();
}
void CacheCameraReference()
{
if (cam != null)
{
cachedCam = cam;
}
else
{
cachedCam = Camera.main;
if (cachedCam == null)
Debug.LogError("ClickToMoveInputSystem: No camera found! Assign one in the inspector or ensure Camera.main exists.", gameObject);
}
}
void CachePlayerReferences()
{
if (Player.current == null)
{
cachedInteraction = null;
cachedMovement = null;
return;
}
cachedInteraction = Player.current.GetComponent<PlayerInteractionController>();
cachedMovement = Player.current.MovementController;
if (cachedMovement == null)
{
cachedMovement = Player.current.GetComponent<NavMeshMovementController>();
}
}
PlayerInteractionController GetInteraction()
{
if (Player.current == null) return null;
if (cachedInteraction == null || cachedInteraction.gameObject != Player.current.gameObject)
{
CachePlayerReferences();
}
return cachedInteraction;
}
NavMeshMovementController GetMovement()
{
if (Player.current == null) return null;
if (cachedMovement == null || cachedMovement.gameObject != Player.current.gameObject)
{
CachePlayerReferences();
}
return cachedMovement;
}
void Log(string message)
{
if (enableDebugLogs)
{
Debug.Log(message, gameObject);
}
}
// Hook this to PointerPos action (Value Vector2)
public void OnPointerPos(InputAction.CallbackContext ctx)
{
pointerScreenPos = ctx.ReadValue<Vector2>();
}
// Hook this to LeftClick action (Button)
public void OnLeftClick(InputAction.CallbackContext ctx)
{
if (!ctx.performed) return;
HandleLeftClick();
}
private void HandleLeftClick()
{
if (cachedCam == null) return;
Ray ray = cachedCam.ScreenPointToRay(pointerScreenPos);
if (Physics.Raycast(ray, out RaycastHit hit, 500f, selectionLayers))
{
// Try NPC first
var npc = hit.collider.GetComponentInParent<NPC>();
if (npc != null)
{
// Check if NPC has a Selectable component
var selectable = npc.GetComponent<Selectable>();
if (selectable != null && SelectionManager.Instance != null)
{
SelectionManager.Instance.Select(selectable);
}
Debug.Log($"Selected NPC: {npc.npcName} ({npc.npcType})");
return;
}
// Try building first
var building = hit.collider.GetComponentInParent<Building>();
if (building != null)
{
if (SelectionManager.Instance != null)
SelectionManager.Instance.Select(building);
Log($"Selected building: {building.buildingName}");
return;
}
// Try to find a Selectable (Character, NPC, etc.)
var selectable2 = hit.collider.GetComponentInParent<Selectable>();
if (selectable2 != null)
{
if (SelectionManager.Instance != null)
SelectionManager.Instance.Select(selectable2);
// Set player target if it's a character
var character = selectable2.GetComponent<Character>();
if (character != null && Player.current != null)
{
Player.current.SetTarget(character);
}
Log($"Selected: {selectable2.displayName}");
return;
}
// Check for interactive objects (gates, bridges, ledges without Selection Manager)
var interactiveObject = hit.collider.GetComponentInParent<IInteractiveObject>();
if (interactiveObject != null)
{
Log($"Selected interactive object: {interactiveObject.GetDisplayName()}");
return;
}
// Generic interactable (if needed)
var interactable = hit.collider.GetComponentInParent<Interactable>();
if (interactable != null)
{
interactable.OnSelect();
Log($"Interacted with: {interactable.name}");
return;
}
}
// Nothing selected, clear selection and target
if (SelectionManager.Instance != null)
SelectionManager.Instance.ClearSelection();
if (Player.current != null)
Player.current.SetTarget(null);
}
// Hook this to RightClick action (Button)
public void OnRightClick(InputAction.CallbackContext ctx)
{
if (!ctx.performed) return;
HandleRightClick();
}
private void HandleRightClick()
{
if (cachedCam == null || Player.current == null) return;
Ray ray = cachedCam.ScreenPointToRay(pointerScreenPos);
// First check if right-clicking on a building to interact with it
if (Physics.Raycast(ray, out RaycastHit hit, 500f, selectionLayers))
{
// Check for NPC interaction first
var npc = hit.collider.GetComponentInParent<NPC>();
if (npc != null)
{
var interaction = GetInteraction();
if (interaction != null)
{
interaction.SetPendingNPCInteraction(npc);
Log($"Set interaction target: {npc.npcName}");
}
else
{
// Fallback: move to NPC and look at them
var movement = GetMovement();
if (movement != null)
{
movement.MoveToAndLookAt(npc.transform.position, npc.transform.position);
Log($"Moving to NPC: {npc.npcName}");
}
}
return;
}
var building = hit.collider.GetComponentInParent<Building>();
if (building != null)
{
// If building can be entered and has an entry point, move to the door
if (building.canEnter && building.entryPoint != null)
{
var interaction = GetInteraction();
if (interaction != null)
{
interaction.SetPendingBuildingInteraction(building);
Log($"Set interaction target: {building.buildingName}");
}
else
{
// Fallback: move to entry point and look at it
var movement = GetMovement();
if (movement != null)
{
movement.MoveToAndLookAt(building.entryPoint.position, building.transform.position);
}
}
}
else
{
// Cannot enter building - move as close as possible via NavMesh and look at building
var movement = GetMovement();
if (movement != null)
{
// Move toward the hit point and set look target to building center
movement.MoveToAndLookAt(hit.point, building.transform.position);
Log($"Moving toward building: {building.buildingName}");
}
}
return;
}
// Check for other interactive objects (gates, bridges, ledges, etc.)
var interactiveObject = hit.collider.GetComponentInParent<IInteractiveObject>();
if (interactiveObject != null)
{
var interaction = GetInteraction();
if (interaction != null)
{
Vector3 interactionPoint = interactiveObject.GetInteractionPoint();
interaction.SetPendingInteraction(interactiveObject, interactionPoint);
Log($"Set interaction target: {interactiveObject.GetDisplayName()}");
}
else
{
// Fallback if no PlayerInteractionController - move to interaction point directly and look at it
var movement = GetMovement();
if (movement != null)
{
Vector3 interactionPoint = interactiveObject.GetInteractionPoint();
movement.MoveToAndLookAt(interactionPoint, interactionPoint);
Log($"Moving to interactive object: {interactiveObject.GetDisplayName()}");
}
}
return;
}
}
// If not an interactive object, check for movement destination
if (Physics.Raycast(ray, out RaycastHit groundHit, 500f, movementLayers))
{
var movement = GetMovement();
if (movement != null)
{
movement.MoveTo(groundHit.point);
Log($"Moving to point: {groundHit.point}");
}
else
{
// fallback: try to find NavMeshAgent on player root
var agent = Player.current.GetComponent<NavMeshAgent>();
if (agent != null) agent.SetDestination(groundHit.point);
}
}
}
// Optional fallback if PointerPos not wired
void Update()
{
if (!useLegacyMouseFallback) return;
if (cachedCam == null) return;
if (Mouse.current == null) return;
pointerScreenPos = Mouse.current.position.ReadValue();
if (Mouse.current.leftButton.wasPressedThisFrame)
{
HandleLeftClick();
}
if (Mouse.current.rightButton.wasPressedThisFrame)
{
HandleRightClick();
}
}
}