265 lines
7.9 KiB
C#
265 lines
7.9 KiB
C#
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<PlayerInputController>();
|
|
}
|
|
}
|
|
|
|
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<RectTransform>();
|
|
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>();
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
canvas.sortingOrder = 5000;
|
|
|
|
CanvasScaler scaler = canvasGO.GetComponent<CanvasScaler>();
|
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|
scaler.referenceResolution = new Vector2(1920f, 1080f);
|
|
scaler.matchWidthOrHeight = 0.5f;
|
|
|
|
RectTransform root = canvasGO.GetComponent<RectTransform>();
|
|
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<RectTransform>();
|
|
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>();
|
|
image.color = Color.black;
|
|
|
|
return rect;
|
|
}
|
|
|
|
private TextMeshProUGUI CreateBootLabel(RectTransform parent)
|
|
{
|
|
GameObject textGO = new GameObject("BootText", typeof(RectTransform), typeof(TextMeshProUGUI));
|
|
RectTransform rect = textGO.GetComponent<RectTransform>();
|
|
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<TextMeshProUGUI>();
|
|
text.text = string.Empty;
|
|
text.fontSize = 40f;
|
|
text.alignment = TextAlignmentOptions.Left;
|
|
text.color = BootTextColor;
|
|
text.enableWordWrapping = true;
|
|
|
|
return text;
|
|
}
|
|
}
|