added dialogue manager and radar
This commit is contained in:
278
Assets/Scripts/DialogueManager.cs
Normal file
278
Assets/Scripts/DialogueManager.cs
Normal file
@@ -0,0 +1,278 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton. Manages the dialogue UI box.
|
||||
/// Attach to any persistent GameObject in the scene (e.g. a "Managers" object),
|
||||
/// or drop it on the Player — it will survive fine either way.
|
||||
///
|
||||
/// Controls:
|
||||
/// E or Space or Enter — advance to next page / close
|
||||
/// Escape — close immediately
|
||||
///
|
||||
/// While dialogue is open:
|
||||
/// - Cursor is unlocked and visible
|
||||
/// - FirstPersonController mouse look is blocked (via IsOpen flag)
|
||||
/// </summary>
|
||||
public class DialogueManager : MonoBehaviour
|
||||
{
|
||||
// ── Singleton ─────────────────────────────────────────────────────
|
||||
public static DialogueManager Instance { get; private set; }
|
||||
|
||||
// ── Inspector ─────────────────────────────────────────────────────
|
||||
[Header("Box Layout")]
|
||||
[Tooltip("Height of the dialogue box as a fraction of screen height.")]
|
||||
public float boxHeightFraction = 0.22f;
|
||||
public float boxMargin = 24f;
|
||||
public float innerPad = 18f;
|
||||
|
||||
[Header("Colours — Cruelty Squad palette")]
|
||||
public Color colBoxBg = new Color(0.04f, 0.06f, 0.04f, 0.94f);
|
||||
public Color colBoxBorder = new Color(0.18f, 0.75f, 0.22f, 0.90f);
|
||||
public Color colNameBg = new Color(0.10f, 0.28f, 0.10f, 1.00f);
|
||||
public Color colNameText = new Color(0.25f, 1.00f, 0.30f, 1.00f);
|
||||
public Color colBodyText = new Color(0.78f, 0.90f, 0.78f, 1.00f);
|
||||
public Color colHintText = new Color(0.35f, 0.55f, 0.35f, 0.85f);
|
||||
public Color colPageDots = new Color(0.28f, 0.65f, 0.30f, 0.80f);
|
||||
|
||||
[Header("Text")]
|
||||
public int nameFontSize = 13;
|
||||
public int bodyFontSize = 14;
|
||||
public int hintFontSize = 10;
|
||||
|
||||
[Header("Typewriter Effect")]
|
||||
public bool useTypewriter = true;
|
||||
public float charsPerSecond = 40f;
|
||||
|
||||
// ── Public state ──────────────────────────────────────────────────
|
||||
public bool IsOpen { get; private set; }
|
||||
|
||||
// ── Private ───────────────────────────────────────────────────────
|
||||
private DialogueLine[] _lines;
|
||||
private int _lineIndex;
|
||||
private int _pageIndex;
|
||||
|
||||
// Typewriter
|
||||
private float _charTimer;
|
||||
private int _visibleChars;
|
||||
private bool _pageComplete;
|
||||
|
||||
private Texture2D _white;
|
||||
|
||||
// ── Current text shortcuts ─────────────────────────────────────────
|
||||
private string CurrentSpeaker => (_lines != null && _lineIndex < _lines.Length)
|
||||
? _lines[_lineIndex].speakerName : "";
|
||||
private string CurrentPage => (_lines != null && _lineIndex < _lines.Length
|
||||
&& _pageIndex < _lines[_lineIndex].pages.Length)
|
||||
? _lines[_lineIndex].pages[_pageIndex] : "";
|
||||
private int TotalPages => (_lines != null && _lineIndex < _lines.Length)
|
||||
? _lines[_lineIndex].pages.Length : 1;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
_white = Texture2D.whiteTexture;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
public void StartDialogue(DialogueLine[] lines)
|
||||
{
|
||||
if (lines == null || lines.Length == 0) return;
|
||||
|
||||
_lines = lines;
|
||||
_lineIndex = 0;
|
||||
_pageIndex = 0;
|
||||
IsOpen = true;
|
||||
|
||||
BeginPage();
|
||||
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
}
|
||||
|
||||
void BeginPage()
|
||||
{
|
||||
_charTimer = 0f;
|
||||
_visibleChars = useTypewriter ? 0 : int.MaxValue;
|
||||
_pageComplete = !useTypewriter;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
void Update()
|
||||
{
|
||||
if (!IsOpen) return;
|
||||
|
||||
// Advance typewriter
|
||||
if (useTypewriter && !_pageComplete)
|
||||
{
|
||||
_charTimer += Time.deltaTime;
|
||||
_visibleChars = Mathf.FloorToInt(_charTimer * charsPerSecond);
|
||||
if (_visibleChars >= CurrentPage.Length)
|
||||
{
|
||||
_visibleChars = CurrentPage.Length;
|
||||
_pageComplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance or close on E / Space / Return
|
||||
bool advance = Input.GetKeyDown(KeyCode.E)
|
||||
|| Input.GetKeyDown(KeyCode.Space)
|
||||
|| Input.GetKeyDown(KeyCode.Return);
|
||||
bool cancel = Input.GetKeyDown(KeyCode.Escape);
|
||||
|
||||
if (cancel)
|
||||
{
|
||||
CloseDialogue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (advance)
|
||||
{
|
||||
// If typewriter is still running, skip to end of page first
|
||||
if (useTypewriter && !_pageComplete)
|
||||
{
|
||||
_visibleChars = CurrentPage.Length;
|
||||
_pageComplete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try next page in current line
|
||||
_pageIndex++;
|
||||
if (_pageIndex < TotalPages)
|
||||
{
|
||||
BeginPage();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try next line
|
||||
_lineIndex++;
|
||||
_pageIndex = 0;
|
||||
if (_lineIndex < _lines.Length)
|
||||
{
|
||||
BeginPage();
|
||||
return;
|
||||
}
|
||||
|
||||
// All done
|
||||
CloseDialogue();
|
||||
}
|
||||
}
|
||||
|
||||
void CloseDialogue()
|
||||
{
|
||||
IsOpen = false;
|
||||
_lines = null;
|
||||
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
Cursor.visible = false;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
void OnGUI()
|
||||
{
|
||||
if (!IsOpen || _lines == null) return;
|
||||
|
||||
float sw = Screen.width;
|
||||
float sh = Screen.height;
|
||||
float bh = sh * boxHeightFraction;
|
||||
float bw = sw - boxMargin * 2f;
|
||||
float by = sh - bh - boxMargin;
|
||||
float bx = boxMargin;
|
||||
|
||||
// ── Outer border ─────────────────────────────────────────────
|
||||
float border = 2f;
|
||||
DrawTex(new Rect(bx - border, by - border, bw + border * 2f, bh + border * 2f), colBoxBorder);
|
||||
|
||||
// ── Main background ───────────────────────────────────────────
|
||||
DrawTex(new Rect(bx, by, bw, bh), colBoxBg);
|
||||
|
||||
// ── Left accent stripe ────────────────────────────────────────
|
||||
DrawTex(new Rect(bx, by, 4f, bh), colBoxBorder);
|
||||
|
||||
float cx = bx + innerPad + 6f; // content X (after the stripe + pad)
|
||||
float cy = by + innerPad;
|
||||
float cw = bw - innerPad * 2f - 6f;
|
||||
|
||||
// ── Speaker name bar ──────────────────────────────────────────
|
||||
string speaker = CurrentSpeaker;
|
||||
float nameH = 0f;
|
||||
if (!string.IsNullOrWhiteSpace(speaker))
|
||||
{
|
||||
GUIStyle nameStyle = new GUIStyle();
|
||||
nameStyle.fontSize = nameFontSize;
|
||||
nameStyle.fontStyle = FontStyle.Bold;
|
||||
nameStyle.normal.textColor = colNameText;
|
||||
nameStyle.alignment = TextAnchor.MiddleLeft;
|
||||
|
||||
Vector2 ns = nameStyle.CalcSize(new GUIContent(speaker));
|
||||
nameH = ns.y + 6f;
|
||||
float namePadX = 10f;
|
||||
|
||||
DrawTex(new Rect(cx - 2f, cy, ns.x + namePadX * 2f + 4f, nameH), colNameBg);
|
||||
GUI.Label(new Rect(cx + namePadX, cy + 3f, ns.x + 4f, ns.y), speaker, nameStyle);
|
||||
cy += nameH + 8f;
|
||||
}
|
||||
|
||||
// ── Body text (typewriter) ────────────────────────────────────
|
||||
string fullText = CurrentPage;
|
||||
string visibleText = useTypewriter
|
||||
? fullText.Substring(0, Mathf.Min(_visibleChars, fullText.Length))
|
||||
: fullText;
|
||||
|
||||
GUIStyle bodyStyle = new GUIStyle();
|
||||
bodyStyle.fontSize = bodyFontSize;
|
||||
bodyStyle.fontStyle = FontStyle.Normal;
|
||||
bodyStyle.normal.textColor = colBodyText;
|
||||
bodyStyle.wordWrap = true;
|
||||
bodyStyle.richText = true;
|
||||
|
||||
float bodyH = bh - innerPad * 2f - nameH - 8f - 20f; // leave room for hint
|
||||
GUI.Label(new Rect(cx, cy, cw, bodyH), visibleText, bodyStyle);
|
||||
|
||||
// ── Page indicator dots ───────────────────────────────────────
|
||||
int total = TotalPages;
|
||||
if (total > 1)
|
||||
{
|
||||
float dotR = 4f;
|
||||
float dotGap = dotR * 2f + 4f;
|
||||
float dotsW = total * dotGap;
|
||||
float dotX = bx + bw * 0.5f - dotsW * 0.5f;
|
||||
float dotY = by + bh - innerPad - dotR;
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
Color dc = (i == _pageIndex) ? colBoxBorder : colPageDots;
|
||||
DrawTex(new Rect(dotX + i * dotGap, dotY - dotR, dotR * 2f, dotR * 2f), dc);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Advance hint ──────────────────────────────────────────────
|
||||
string hintStr = _pageComplete
|
||||
? (_lineIndex >= _lines.Length - 1 && _pageIndex >= TotalPages - 1
|
||||
? "[ E ] Close"
|
||||
: "[ E ] Next")
|
||||
: "[ E ] Skip";
|
||||
|
||||
GUIStyle hintStyle = new GUIStyle();
|
||||
hintStyle.fontSize = hintFontSize;
|
||||
hintStyle.fontStyle = FontStyle.Bold;
|
||||
hintStyle.normal.textColor = colHintText;
|
||||
hintStyle.alignment = TextAnchor.LowerRight;
|
||||
|
||||
GUI.Label(new Rect(bx, by, bw - innerPad, bh - 6f), hintStr, hintStyle);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
void DrawTex(Rect r, Color c)
|
||||
{
|
||||
Color prev = GUI.color;
|
||||
GUI.color = c;
|
||||
GUI.DrawTexture(r, _white);
|
||||
GUI.color = prev;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user