using System.Collections; using TMPro; using UnityEngine; using UnityEngine.UI; public class RobotBootSequence : MonoBehaviour { [Header("References")] public PlayerInputController InputController; public Transform CameraTransform; [Header("Timing")] public bool PlayOnStart = true; [Min(0.1f)] public float BootDuration = 2.4f; [Min(0.1f)] public float CharacterPerSecond = 40f; [Min(0f)] public float LinePause = 0.35f; [Min(0f)] public float DelayBeforeReveal = 0.4f; [Header("Motion")] public Vector2 StartYawPitch = new Vector2(-30f, -20f); public float RollWobble = 2.5f; public float WobbleFrequency = 16f; public AnimationCurve EaseCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); [Header("Boot Text")] public Color BootTextColor = new Color(0.62f, 1f, 0.7f, 1f); public string[] BootLines = { "UNIT SB-3954 | preparing startup . . .", "verification of OS-5 . . . 4 . . . 3 . . . 2 . . . 1", "system integrity: OK", "motor bus: OK", "vision pipeline: ONLINE", "SYSTEM OK" }; [Header("Optional Audio")] public AudioSource BootAudioSource; private bool m_IsPlaying; private struct BootUI { public Canvas Canvas; public RectTransform LeftPanel; public RectTransform RightPanel; public TextMeshProUGUI Text; } private void Awake() { if (InputController == null) { InputController = GetComponent(); } } private void Start() { if (PlayOnStart) { StartCoroutine(PlayBootSequence()); } } [ContextMenu("Play Boot Sequence")] public void PlayBootSequenceFromMenu() { if (!Application.isPlaying || m_IsPlaying) { return; } StartCoroutine(PlayBootSequence()); } public IEnumerator PlayBootSequence() { if (m_IsPlaying) { yield break; } m_IsPlaying = true; if (InputController != null) { InputController.SetInputEnabled(false); } if (BootAudioSource != null) { BootAudioSource.Play(); } if (CameraTransform == null) { m_IsPlaying = false; if (InputController != null) { InputController.SetInputEnabled(true); } yield break; } Quaternion gameplayRotation = CameraTransform.localRotation; Quaternion fromRotation = Quaternion.Euler(StartYawPitch.y, StartYawPitch.x, 0f) * gameplayRotation; CameraTransform.localRotation = fromRotation; BootUI bootUI = CreateBootUI(); yield return StartCoroutine(PlayBootText(bootUI.Text)); if (DelayBeforeReveal > 0f) { yield return new WaitForSeconds(DelayBeforeReveal); } float elapsed = 0f; while (elapsed < BootDuration) { elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / BootDuration); float eased = EaseCurve.Evaluate(t); float wobbleFade = 1f - eased; float roll = Mathf.Sin(Time.time * WobbleFrequency) * RollWobble * wobbleFade; Quaternion wobbleRotation = Quaternion.Euler(0f, 0f, roll); CameraTransform.localRotation = Quaternion.Slerp(fromRotation, gameplayRotation, eased) * wobbleRotation; RectTransform rootRect = bootUI.Canvas.GetComponent(); float halfWidth = rootRect.rect.width * 0.5f; float leftTarget = -(halfWidth + 24f); float rightTarget = halfWidth + 24f; bootUI.LeftPanel.anchoredPosition = new Vector2(Mathf.Lerp(0f, leftTarget, eased), 0f); bootUI.RightPanel.anchoredPosition = new Vector2(Mathf.Lerp(0f, rightTarget, eased), 0f); Color textColor = bootUI.Text.color; textColor.a = 1f - eased; bootUI.Text.color = textColor; yield return null; } CameraTransform.localRotation = gameplayRotation; if (bootUI.Canvas != null) { Destroy(bootUI.Canvas.gameObject); } if (InputController != null) { InputController.SetInputEnabled(true); } m_IsPlaying = false; } private IEnumerator PlayBootText(TextMeshProUGUI label) { if (label == null || BootLines == null || BootLines.Length == 0) { yield break; } label.text = string.Empty; float charDelay = CharacterPerSecond <= 0f ? 0f : 1f / CharacterPerSecond; for (int i = 0; i < BootLines.Length; i++) { string line = BootLines[i]; for (int c = 0; c < line.Length; c++) { label.text += line[c]; if (charDelay > 0f) { yield return new WaitForSeconds(charDelay); } } if (i < BootLines.Length - 1) { label.text += "\n"; } if (LinePause > 0f) { yield return new WaitForSeconds(LinePause); } } } private BootUI CreateBootUI() { BootUI ui = new BootUI(); GameObject canvasGO = new GameObject("RobotBootCanvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster)); Canvas canvas = canvasGO.GetComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.sortingOrder = 5000; CanvasScaler scaler = canvasGO.GetComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution = new Vector2(1920f, 1080f); scaler.matchWidthOrHeight = 0.5f; RectTransform root = canvasGO.GetComponent(); root.anchorMin = Vector2.zero; root.anchorMax = Vector2.one; root.offsetMin = Vector2.zero; root.offsetMax = Vector2.zero; RectTransform leftPanel = CreatePanel("LeftPanel", root, true); RectTransform rightPanel = CreatePanel("RightPanel", root, false); TextMeshProUGUI label = CreateBootLabel(root); ui.Canvas = canvas; ui.LeftPanel = leftPanel; ui.RightPanel = rightPanel; ui.Text = label; return ui; } private RectTransform CreatePanel(string panelName, RectTransform parent, bool isLeft) { GameObject panelGO = new GameObject(panelName, typeof(RectTransform), typeof(Image)); RectTransform rect = panelGO.GetComponent(); rect.SetParent(parent, false); rect.anchorMin = isLeft ? new Vector2(0f, 0f) : new Vector2(0.5f, 0f); rect.anchorMax = isLeft ? new Vector2(0.5f, 1f) : new Vector2(1f, 1f); rect.pivot = new Vector2(0.5f, 0.5f); rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero; rect.anchoredPosition = Vector2.zero; Image image = panelGO.GetComponent(); image.color = Color.black; return rect; } private TextMeshProUGUI CreateBootLabel(RectTransform parent) { GameObject textGO = new GameObject("BootText", typeof(RectTransform), typeof(TextMeshProUGUI)); RectTransform rect = textGO.GetComponent(); rect.SetParent(parent, false); rect.anchorMin = new Vector2(0.13f, 0.5f); rect.anchorMax = new Vector2(0.13f, 0.5f); rect.pivot = new Vector2(0f, 0.5f); rect.sizeDelta = new Vector2(980f, 380f); TextMeshProUGUI text = textGO.GetComponent(); text.text = string.Empty; text.fontSize = 40f; text.alignment = TextAlignmentOptions.Left; text.color = BootTextColor; text.textWrappingMode = TextWrappingModes.Normal; return text; } }