added dialogue manager and radar
This commit is contained in:
135
Assets/Scripts/DialogueNPC.cs
Normal file
135
Assets/Scripts/DialogueNPC.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Attach this to any world object (NPC, terminal, sign, corpse, etc.)
|
||||
/// to make it interactable. The player presses E when looking at it
|
||||
/// from within interactRange to start the dialogue.
|
||||
///
|
||||
/// The component draws a small world-space "[E]" prompt via OnGUI
|
||||
/// when the player is close enough and looking at the object.
|
||||
/// </summary>
|
||||
public class DialogueNPC : MonoBehaviour
|
||||
{
|
||||
[Header("Dialogue")]
|
||||
public DialogueLine[] lines = new DialogueLine[]
|
||||
{
|
||||
new DialogueLine { speakerName = "STRANGER", pages = new[] { "Hey." } }
|
||||
};
|
||||
|
||||
[Header("Interaction")]
|
||||
[Tooltip("Maximum distance from which the player can interact.")]
|
||||
public float interactRange = 3.5f;
|
||||
[Tooltip("Maximum angle (degrees) between player forward and direction to this object.")]
|
||||
public float interactAngle = 45f;
|
||||
[Tooltip("Layer mask used for the line-of-sight raycast.")]
|
||||
public LayerMask occlusionMask = ~0;
|
||||
|
||||
[Header("Prompt Style")]
|
||||
public string promptText = "[E] Talk";
|
||||
public Color colPrompt = new Color(0.20f, 0.95f, 0.40f, 1f);
|
||||
public Color colPromptBg = new Color(0f, 0f, 0f, 0.70f);
|
||||
|
||||
// ── Private ──────────────────────────────────────────────────────
|
||||
private Transform _playerTransform;
|
||||
private Camera _playerCamera;
|
||||
private bool _promptVisible;
|
||||
private Texture2D _white;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
void Start()
|
||||
{
|
||||
_white = Texture2D.whiteTexture;
|
||||
|
||||
// Find the player by component
|
||||
var player = FindObjectOfType<FirstPersonController>();
|
||||
if (player != null)
|
||||
{
|
||||
_playerTransform = player.transform;
|
||||
_playerCamera = player.GetComponentInChildren<Camera>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[DialogueNPC] '{name}': Could not find FirstPersonController in scene.");
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_playerTransform == null || DialogueManager.Instance == null) return;
|
||||
if (DialogueManager.Instance.IsOpen) { _promptVisible = false; return; }
|
||||
|
||||
_promptVisible = CanInteract();
|
||||
|
||||
if (_promptVisible && Input.GetKeyDown(KeyCode.E))
|
||||
DialogueManager.Instance.StartDialogue(lines);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
bool CanInteract()
|
||||
{
|
||||
if (_playerTransform == null || _playerCamera == null) return false;
|
||||
|
||||
// Distance check
|
||||
float dist = Vector3.Distance(_playerTransform.position, transform.position);
|
||||
if (dist > interactRange) return false;
|
||||
|
||||
// Angle check — is the player roughly facing this object?
|
||||
Vector3 dir = (transform.position - _playerCamera.transform.position).normalized;
|
||||
float dot = Vector3.Dot(_playerCamera.transform.forward, dir);
|
||||
if (dot < Mathf.Cos(interactAngle * Mathf.Deg2Rad)) return false;
|
||||
|
||||
// Line-of-sight (optional — fire a ray toward us)
|
||||
if (Physics.Raycast(_playerCamera.transform.position, dir, out RaycastHit hit, interactRange, occlusionMask))
|
||||
{
|
||||
// Allow if the hit object is us or a child of us
|
||||
if (!hit.transform.IsChildOf(transform) && hit.transform != transform)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
void OnGUI()
|
||||
{
|
||||
if (!_promptVisible || _playerCamera == null) return;
|
||||
|
||||
// Project to screen
|
||||
Vector3 worldPos = transform.position + Vector3.up * 0.5f; // slightly above pivot
|
||||
Vector3 screenPos = _playerCamera.WorldToScreenPoint(worldPos);
|
||||
|
||||
if (screenPos.z <= 0f) return; // behind camera
|
||||
|
||||
// Flip Y (GUI vs screen coords)
|
||||
float sx = screenPos.x;
|
||||
float sy = Screen.height - screenPos.y;
|
||||
|
||||
GUIStyle style = new GUIStyle();
|
||||
style.fontSize = 13;
|
||||
style.fontStyle = FontStyle.Bold;
|
||||
style.normal.textColor = colPrompt;
|
||||
style.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
Vector2 size = style.CalcSize(new GUIContent(promptText));
|
||||
float padX = 8f;
|
||||
float padY = 4f;
|
||||
float bgW = size.x + padX * 2f;
|
||||
float bgH = size.y + padY * 2f;
|
||||
Rect bgRect = new Rect(sx - bgW * 0.5f, sy - bgH * 0.5f, bgW, bgH);
|
||||
Rect txRect = new Rect(sx - size.x * 0.5f, sy - size.y * 0.5f, size.x, size.y);
|
||||
|
||||
// Background
|
||||
Color prev = GUI.color;
|
||||
GUI.color = colPromptBg;
|
||||
GUI.DrawTexture(bgRect, _white);
|
||||
GUI.color = prev;
|
||||
|
||||
GUI.Label(txRect, promptText, style);
|
||||
}
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = new Color(0f, 1f, 0.4f, 0.25f);
|
||||
Gizmos.DrawWireSphere(transform.position, interactRange);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user