7 Commits

Author SHA1 Message Date
Pierre Ryssen
d0d5fccc37 feat (vision): remove the crt 2026-03-30 16:16:30 +02:00
Thibault Pouch
faf2f92521 feat: add editor-specific quit functionality to RetroMainMenuUI 2026-03-13 21:25:34 +01:00
Thibault Pouch
ffa489db0d fix : error in the menu and rename files 2026-03-13 21:23:42 +01:00
Thibault Pouch
53fd617abe feat: add RetroMainMenuUI script and associated meta file for main menu functionality 2026-03-13 19:28:19 +01:00
Thibault Pouch
64b2d63799 refactor: update text wrapping mode in RobotBootSequence and clean up CRTRendererFeature 2026-03-13 19:28:03 +01:00
Thibault Pouch
f42463176f feat: add robot boot sequence with input control management 2026-03-13 18:58:15 +01:00
Thibault Pouch
ab434be65f feat : add a cathodic efect on the player cam 2026-03-13 18:38:48 +01:00
13 changed files with 976 additions and 32 deletions

View File

@@ -4,6 +4,7 @@ using UnityEngine.InputSystem;
public class PlayerInputController : MonoBehaviour
{
public InputActionAsset InputActions;
public bool InputEnabled { get; private set; } = true;
private InputAction m_moveAction;
private InputAction m_lookAction;
@@ -44,6 +45,17 @@ public class PlayerInputController : MonoBehaviour
private void Update()
{
if (!InputEnabled)
{
MoveAmount = Vector2.zero;
LookAmount = Vector2.zero;
ShiftPressed = false;
JumpPressed = false;
ThrowPressed = false;
HeadInteractionPressed = false;
return;
}
MoveAmount = m_moveAction.ReadValue<Vector2>();
LookAmount = m_lookAction.ReadValue<Vector2>();
@@ -52,4 +64,9 @@ public class PlayerInputController : MonoBehaviour
ThrowPressed = m_throwAction.WasPressedThisFrame();
HeadInteractionPressed = m_headInteractAction.WasPressedThisFrame();
}
public void SetInputEnabled(bool enabled)
{
InputEnabled = enabled;
}
}

View File

@@ -16,9 +16,9 @@ public class PlayerLook : MonoBehaviour
private void Awake()
{
m_rigidbody = GetComponent<Rigidbody>();
input = GetComponent<PlayerInputController>();
headController = GetComponent<PlayerHeadController>();
m_rigidbody = GetComponent<Rigidbody>();
input = GetComponent<PlayerInputController>();
headController = GetComponent<PlayerHeadController>();
}
private void FixedUpdate()

View File

@@ -0,0 +1,264 @@
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.textWrappingMode = TextWrappingModes.Normal;
return text;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ef6855cd57b4f94b47f410d47e89ff1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 97a4ae8015df4732ac9524441048a765
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,123 @@
// using UnityEngine;
// using UnityEngine.Rendering;
// using UnityEngine.Rendering.RenderGraphModule;
// using UnityEngine.Rendering.RenderGraphModule.Util;
// using UnityEngine.Rendering.Universal;
// public class CRTRendererFeature : ScriptableRendererFeature
// {
// [System.Serializable]
// public class CRTSettings
// {
// public bool EffectEnabled = true;
// public RenderPassEvent PassEvent = RenderPassEvent.AfterRenderingPostProcessing;
// public Shader CRTShader;
// [Range(0f, 1f)] public float Intensity = 0.65f;
// [Range(0f, 2f)] public float ScanlineDensity = 1.2f;
// [Range(0f, 1f)] public float ScanlineStrength = 0.18f;
// [Range(0f, 0.2f)] public float Curvature = 0.04f;
// [Range(0f, 1f)] public float VignetteStrength = 0.28f;
// [Range(0f, 0.05f)] public float ChromaticAberration = 0.004f;
// [Range(0f, 0.2f)] public float NoiseStrength = 0.03f;
// [Range(0f, 0.1f)] public float FlickerStrength = 0.015f;
// }
// class CRTPass : ScriptableRenderPass
// {
// private Material m_Material;
// private CRTSettings m_Settings;
// public void Setup(Material material, CRTSettings settings)
// {
// m_Material = material;
// m_Settings = settings;
// renderPassEvent = settings.PassEvent;
// requiresIntermediateTexture = true;
// }
// public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
// {
// if (m_Material == null || m_Settings == null || !m_Settings.EffectEnabled)
// {
// return;
// }
// UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
// if (resourceData.isActiveTargetBackBuffer)
// {
// return;
// }
// m_Material.SetFloat("_Intensity", m_Settings.Intensity);
// m_Material.SetFloat("_ScanlineDensity", m_Settings.ScanlineDensity);
// m_Material.SetFloat("_ScanlineStrength", m_Settings.ScanlineStrength);
// m_Material.SetFloat("_Curvature", m_Settings.Curvature);
// m_Material.SetFloat("_VignetteStrength", m_Settings.VignetteStrength);
// m_Material.SetFloat("_ChromaticAberration", m_Settings.ChromaticAberration);
// m_Material.SetFloat("_NoiseStrength", m_Settings.NoiseStrength);
// m_Material.SetFloat("_FlickerStrength", m_Settings.FlickerStrength);
// TextureHandle source = resourceData.activeColorTexture;
// TextureDesc destinationDesc = renderGraph.GetTextureDesc(source);
// destinationDesc.name = "CameraColor-CRT";
// destinationDesc.clearBuffer = false;
// TextureHandle destination = renderGraph.CreateTexture(destinationDesc);
// RenderGraphUtils.BlitMaterialParameters blitParams = new(source, destination, m_Material, 0);
// renderGraph.AddBlitPass(blitParams, "CRT Effect");
// resourceData.cameraColor = destination;
// }
// public void Dispose()
// {
// // RenderGraph path does not allocate persistent RTHandles in this pass.
// }
// }
// public CRTSettings Settings = new();
// private CRTPass m_Pass;
// private Material m_Material;
// public override void Create()
// {
// if (Settings.CRTShader == null)
// {
// Settings.CRTShader = Shader.Find("Hidden/HeadlessHazard/CRT");
// }
// if (Settings.CRTShader != null)
// {
// m_Material = CoreUtils.CreateEngineMaterial(Settings.CRTShader);
// }
// m_Pass ??= new CRTPass();
// }
// public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
// {
// if (m_Material == null || !Settings.EffectEnabled)
// {
// return;
// }
// if (renderingData.cameraData.cameraType != CameraType.Game)
// {
// return;
// }
// m_Pass.Setup(m_Material, Settings);
// renderer.EnqueuePass(m_Pass);
// }
// protected override void Dispose(bool disposing)
// {
// m_Pass?.Dispose();
// m_Pass = null;
// CoreUtils.Destroy(m_Material);
// m_Material = null;
// }
// }

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f2de7a6cfbd47c8bc740d43bb991205
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e88664529cd503644b2b92f055895969
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,379 @@
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem.UI;
#endif
public class RetroMainMenuUI : MonoBehaviour
{
private Canvas m_MenuCanvas;
private bool m_MenuActive;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
{
if (Object.FindFirstObjectByType<RetroMainMenuUI>() != null)
{
return;
}
GameObject root = new("RetroMainMenuUI");
root.AddComponent<RetroMainMenuUI>();
}
private void Awake()
{
m_MenuActive = true;
Time.timeScale = 0f;
ApplyMenuCursorState();
BuildMenu();
EnsureEventSystem();
UnityEngine.SceneManagement.SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDestroy()
{
UnityEngine.SceneManagement.SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(UnityEngine.SceneManagement.Scene scene, UnityEngine.SceneManagement.LoadSceneMode mode)
{
// Check again when the scene finishes loading to remove any baked-in duplicate EventSystems
EnsureEventSystem();
}
private void LateUpdate()
{
if (!m_MenuActive)
{
return;
}
// Some gameplay scripts lock the cursor during Start/Update.
// Force menu cursor state while the menu is active.
ApplyMenuCursorState();
}
private void BuildMenu()
{
Color bgColor = HexToColor("001e26");
Color panelColor = HexToColor("517567");
Color titleColor = HexToColor("f3d58d");
Color TextNormalColor = HexToColor("eb9843");
Color textWarningColor = HexToColor("c12204");
Color shadowColor = HexToColor("520805");
GameObject canvasObject = new("MainMenuCanvas");
Canvas canvas = canvasObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 10000;
m_MenuCanvas = canvas;
CanvasScaler scaler = canvasObject.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920f, 1080f);
scaler.matchWidthOrHeight = 0.5f;
canvasObject.AddComponent<GraphicRaycaster>();
// Background
GameObject background = CreateImage("Background", canvasObject.transform, bgColor);
StretchToFull(background.GetComponent<RectTransform>());
// Decorative horizontal lines (scanline aesthetic)
CreateLine("TopLine", background.transform, new Rect(0, -60, 0, 4), panelColor, AnchorPreset.TopStretch);
CreateLine("BotLine", background.transform, new Rect(0, 60, 0, 4), panelColor, AnchorPreset.BottomStretch);
// --- LEFT PANEL ---
GameObject leftPanel = new GameObject("LeftPanel", typeof(RectTransform));
leftPanel.transform.SetParent(canvasObject.transform, false);
RectTransform leftRect = leftPanel.GetComponent<RectTransform>();
leftRect.anchorMin = new Vector2(0.08f, 0.1f);
leftRect.anchorMax = new Vector2(0.45f, 0.9f);
leftRect.offsetMin = Vector2.zero;
leftRect.offsetMax = Vector2.zero;
// Title
TextMeshProUGUI titleText = CreateTMP("Title", leftPanel.transform, "HEADLESS HAZARD", titleColor, 72, TextAlignmentOptions.BottomLeft);
RectTransform titleRect = titleText.GetComponent<RectTransform>();
titleRect.anchorMin = new Vector2(0f, 0.85f);
titleRect.anchorMax = new Vector2(1f, 1f);
titleRect.offsetMin = Vector2.zero;
titleRect.offsetMax = Vector2.zero;
titleText.fontStyle = FontStyles.Bold;
// Title Shadow
TextMeshProUGUI titleShadow = CreateTMP("TitleShadow", leftPanel.transform, "HEADLESS HAZARD", shadowColor, 72, TextAlignmentOptions.BottomLeft);
RectTransform shadowRect = titleShadow.GetComponent<RectTransform>();
shadowRect.anchorMin = new Vector2(0f, 0.85f);
shadowRect.anchorMax = new Vector2(1f, 1f);
shadowRect.offsetMin = new Vector2(4f, -4f); // apply drop shadow offset
shadowRect.offsetMax = new Vector2(4f, -4f);
titleShadow.fontStyle = FontStyles.Bold;
titleShadow.transform.SetSiblingIndex(0); // push behind title
// Subtitle / Decorative Status
TextMeshProUGUI subText = CreateTMP("Subtitle", leftPanel.transform, "SYSTEM_BOOT // OS.ACTIVE_ ", panelColor, 20, TextAlignmentOptions.TopLeft);
RectTransform subRect = subText.GetComponent<RectTransform>();
subRect.anchorMin = new Vector2(0f, 0.80f);
subRect.anchorMax = new Vector2(1f, 0.85f);
subRect.offsetMin = Vector2.zero;
subRect.offsetMax = Vector2.zero;
// Button Group
GameObject buttonGroup = new("ButtonGroup", typeof(RectTransform), typeof(VerticalLayoutGroup));
buttonGroup.transform.SetParent(leftPanel.transform, false);
RectTransform groupRect = buttonGroup.GetComponent<RectTransform>();
groupRect.anchorMin = new Vector2(0f, 0f);
groupRect.anchorMax = new Vector2(1f, 0.65f);
groupRect.offsetMin = Vector2.zero;
groupRect.offsetMax = Vector2.zero;
VerticalLayoutGroup layout = buttonGroup.GetComponent<VerticalLayoutGroup>();
layout.childAlignment = TextAnchor.UpperLeft;
layout.spacing = 16f;
layout.childControlWidth = true;
layout.childControlHeight = false;
CreateTextButton(buttonGroup.transform, "> INITIALIZE_PLAY", TextNormalColor, titleColor, () =>
{
Debug.Log("Play clicked.");
OnPlayClicked();
});
CreateTextButton(buttonGroup.transform, "> CONFIGURE_PARAMS", TextNormalColor, titleColor, () =>
{
Debug.Log("Options clicked.");
});
CreateTextButton(buttonGroup.transform, "> TERMINATE_PROCESS", TextNormalColor, textWarningColor, () =>
{
Debug.Log("Quit clicked.");
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
});
// --- RIGHT PANEL (Level Info) ---
GameObject rightPanel = new GameObject("RightPanel", typeof(RectTransform));
rightPanel.transform.SetParent(canvasObject.transform, false);
RectTransform rightRect = rightPanel.GetComponent<RectTransform>();
rightRect.anchorMin = new Vector2(0.55f, 0.4f);
rightRect.anchorMax = new Vector2(0.92f, 0.82f);
rightRect.offsetMin = Vector2.zero;
rightRect.offsetMax = Vector2.zero;
// Right side Border lines
CreateLine("R_Top", rightPanel.transform, new Rect(0, 0, 0, 2), panelColor, AnchorPreset.TopStretch);
CreateLine("R_Bot", rightPanel.transform, new Rect(0, 0, 0, 2), panelColor, AnchorPreset.BottomStretch);
CreateLine("R_Left", rightPanel.transform, new Rect(0, 0, 2, 0), panelColor, AnchorPreset.LeftStretch);
CreateLine("R_Right", rightPanel.transform, new Rect(0, 0, 2, 0), panelColor, AnchorPreset.RightStretch);
// Right Panel Headers
TextMeshProUGUI headerText = CreateTMP("LevelHeader", rightPanel.transform, "CURRENT_SECTOR", panelColor, 24, TextAlignmentOptions.TopLeft);
headerText.GetComponent<RectTransform>().anchorMin = new Vector2(0f, 1f);
headerText.GetComponent<RectTransform>().anchorMax = new Vector2(1f, 1f);
headerText.GetComponent<RectTransform>().anchoredPosition = new Vector2(20f, -20f);
// Big Level Text
TextMeshProUGUI levelText = CreateTMP("LevelNumber", rightPanel.transform, "LEVEL 01", textWarningColor, 140, TextAlignmentOptions.Center);
StretchToFull(levelText.GetComponent<RectTransform>());
levelText.fontStyle = FontStyles.Bold;
// Decorative status
TextMeshProUGUI statusText = CreateTMP("LevelStatus", rightPanel.transform, "[ STATUS: OPTIMAL ]", panelColor, 24, TextAlignmentOptions.BottomRight);
statusText.GetComponent<RectTransform>().anchorMin = new Vector2(0f, 0f);
statusText.GetComponent<RectTransform>().anchorMax = new Vector2(1f, 0f);
statusText.GetComponent<RectTransform>().anchoredPosition = new Vector2(-20f, 20f);
}
private void OnPlayClicked()
{
m_MenuActive = false;
Time.timeScale = 1f;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
if (m_MenuCanvas != null)
{
Destroy(m_MenuCanvas.gameObject);
}
Destroy(gameObject);
}
private static GameObject CreateTextButton(
Transform parent,
string label,
Color normalColor,
Color highlightColor,
UnityEngine.Events.UnityAction clickAction)
{
GameObject buttonObject = new(label, typeof(RectTransform), typeof(TextMeshProUGUI), typeof(Button));
buttonObject.transform.SetParent(parent, false);
RectTransform rect = buttonObject.GetComponent<RectTransform>();
rect.sizeDelta = new Vector2(0f, 60f); // Height 60, width auto-controlled by LayoutGroup
TextMeshProUGUI text = buttonObject.GetComponent<TextMeshProUGUI>();
text.text = label;
text.fontSize = 38;
text.alignment = TextAlignmentOptions.Left;
text.color = Color.white; // Button tint applies on top of white
text.textWrappingMode = TextWrappingModes.NoWrap;
Button button = buttonObject.GetComponent<Button>();
button.targetGraphic = text;
button.transition = Selectable.Transition.ColorTint;
ColorBlock colors = button.colors;
colors.normalColor = normalColor;
colors.highlightedColor = highlightColor;
colors.pressedColor = highlightColor;
colors.selectedColor = highlightColor;
colors.disabledColor = Color.gray;
colors.colorMultiplier = 1f;
colors.fadeDuration = 0.1f;
button.colors = colors;
button.onClick.AddListener(clickAction);
return buttonObject;
}
private static TextMeshProUGUI CreateTMP(string name, Transform parent, string textStr, Color color, float size, TextAlignmentOptions align)
{
GameObject go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI));
go.transform.SetParent(parent, false);
TextMeshProUGUI tmp = go.GetComponent<TextMeshProUGUI>();
tmp.text = textStr;
tmp.color = color;
tmp.fontSize = size;
tmp.alignment = align;
tmp.textWrappingMode = TextWrappingModes.NoWrap;
return tmp;
}
enum AnchorPreset { TopStretch, BottomStretch, LeftStretch, RightStretch }
private static GameObject CreateLine(string name, Transform parent, Rect details, Color color, AnchorPreset preset)
{
GameObject line = CreateImage(name, parent, color);
RectTransform rect = line.GetComponent<RectTransform>();
switch (preset)
{
case AnchorPreset.TopStretch:
rect.anchorMin = new Vector2(0, 1);
rect.anchorMax = new Vector2(1, 1);
rect.sizeDelta = new Vector2(details.width, details.height);
rect.anchoredPosition = new Vector2(details.x, details.y);
break;
case AnchorPreset.BottomStretch:
rect.anchorMin = new Vector2(0, 0);
rect.anchorMax = new Vector2(1, 0);
rect.sizeDelta = new Vector2(details.width, details.height);
rect.anchoredPosition = new Vector2(details.x, details.y);
break;
case AnchorPreset.LeftStretch:
rect.anchorMin = new Vector2(0, 0);
rect.anchorMax = new Vector2(0, 1);
rect.sizeDelta = new Vector2(details.width, details.height);
rect.anchoredPosition = new Vector2(details.x, details.y);
break;
case AnchorPreset.RightStretch:
rect.anchorMin = new Vector2(1, 0);
rect.anchorMax = new Vector2(1, 1);
rect.sizeDelta = new Vector2(details.width, details.height);
rect.anchoredPosition = new Vector2(details.x, details.y);
break;
}
return line;
}
private static GameObject CreateImage(string name, Transform parent, Color color)
{
GameObject imageObject = new(name, typeof(RectTransform), typeof(Image));
imageObject.transform.SetParent(parent, false);
Image image = imageObject.GetComponent<Image>();
image.color = color;
return imageObject;
}
private static void StretchToFull(RectTransform rect)
{
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
}
private static Color HexToColor(string hex)
{
if (ColorUtility.TryParseHtmlString("#" + hex, out Color color))
{
return color;
}
return Color.magenta;
}
private static void ApplyMenuCursorState()
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
private static void EnsureEventSystem()
{
EventSystem[] allEventSystems = Object.FindObjectsByType<EventSystem>(FindObjectsInactive.Include, FindObjectsSortMode.None);
EventSystem eventSystem;
if (allEventSystems.Length == 0)
{
GameObject eventSystemObject = new("EventSystem", typeof(EventSystem));
eventSystem = eventSystemObject.GetComponent<EventSystem>();
}
else
{
eventSystem = allEventSystems[0];
for (int i = 1; i < allEventSystems.Length; i++)
{
if (allEventSystems[i] != null)
{
Destroy(allEventSystems[i].gameObject);
}
}
}
EnsureCompatibleInputModule(eventSystem.gameObject);
}
private static void EnsureCompatibleInputModule(GameObject eventSystemObject)
{
#if ENABLE_INPUT_SYSTEM
StandaloneInputModule standaloneModule = eventSystemObject.GetComponent<StandaloneInputModule>();
if (standaloneModule != null)
{
Destroy(standaloneModule);
}
if (eventSystemObject.GetComponent<InputSystemUIInputModule>() == null)
{
eventSystemObject.AddComponent<InputSystemUIInputModule>();
}
#else
if (eventSystemObject.GetComponent<StandaloneInputModule>() == null)
{
eventSystemObject.AddComponent<StandaloneInputModule>();
}
#endif
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 297533e46238b814989fcd5d46cf8927

View File

@@ -0,0 +1,97 @@
Shader "Hidden/HeadlessHazard/CRT"
{
Properties
{
_Intensity ("Intensity", Range(0,1)) = 0.65
_ScanlineDensity ("Scanline Density", Range(0,2)) = 1.2
_ScanlineStrength ("Scanline Strength", Range(0,1)) = 0.18
_Curvature ("Curvature", Range(0,0.2)) = 0.04
_VignetteStrength ("Vignette Strength", Range(0,1)) = 0.28
_ChromaticAberration ("Chromatic Aberration", Range(0,0.05)) = 0.004
_NoiseStrength ("Noise Strength", Range(0,0.2)) = 0.03
_FlickerStrength ("Flicker Strength", Range(0,0.1)) = 0.015
}
SubShader
{
Tags { "RenderPipeline" = "UniversalPipeline" }
Pass
{
Name "CRT"
ZWrite Off
ZTest Always
Cull Off
Blend One Zero
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
#pragma target 3.5
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
float _Intensity;
float _ScanlineDensity;
float _ScanlineStrength;
float _Curvature;
float _VignetteStrength;
float _ChromaticAberration;
float _NoiseStrength;
float _FlickerStrength;
float Random01(float2 seed)
{
return frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);
}
float2 DistortUV(float2 uv, float curvature)
{
float2 center = uv * 2.0 - 1.0;
float radius2 = dot(center, center);
center *= 1.0 + (radius2 * curvature);
return center * 0.5 + 0.5;
}
half4 Frag(Varyings input) : SV_Target
{
float2 uv = input.texcoord;
float2 curvedUV = DistortUV(uv, _Curvature);
if (curvedUV.x < 0.0 || curvedUV.x > 1.0 || curvedUV.y < 0.0 || curvedUV.y > 1.0)
{
return half4(0.0, 0.0, 0.0, 1.0);
}
float2 fromCenter = curvedUV - 0.5;
float2 aberrationOffset = fromCenter * _ChromaticAberration;
half red = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, curvedUV + aberrationOffset).r;
half green = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, curvedUV).g;
half blue = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, curvedUV - aberrationOffset).b;
half3 color = half3(red, green, blue);
float scanlineWave = sin((curvedUV.y * _ScreenParams.y * 0.5 * _ScanlineDensity) + (_Time.y * 18.0));
float scanlineMask = lerp(1.0, saturate(0.7 + 0.3 * scanlineWave), _ScanlineStrength);
color *= scanlineMask;
float noise = Random01(curvedUV * _ScreenParams.xy + _Time.yy * 37.0) - 0.5;
color += noise * _NoiseStrength;
float flicker = 1.0 - (_FlickerStrength * (0.5 + 0.5 * sin(_Time.y * 32.0)));
color *= flicker;
float2 vignetteUV = curvedUV * (1.0 - curvedUV.yx);
float vignette = saturate(pow(vignetteUV.x * vignetteUV.y * 18.0, 0.2));
color *= lerp(1.0 - _VignetteStrength, 1.0, vignette);
half3 baseColor = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv).rgb;
half3 finalColor = lerp(baseColor, color, _Intensity);
return half4(finalColor, 1.0);
}
ENDHLSL
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0a9f7eb85c2f4f9f8ec82c8565f4e8b1
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
preprocessorOverride: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,5 +1,30 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-4377071725885749089
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4f2de7a6cfbd47c8bc740d43bb991205, type: 3}
m_Name: CRTRendererFeature
m_EditorClassIdentifier: Assembly-CSharp::CRTRendererFeature
m_Active: 1
Settings:
EffectEnabled: 1
PassEvent: 600
CRTShader: {fileID: 4800000, guid: 0a9f7eb85c2f4f9f8ec82c8565f4e8b1, type: 3}
Intensity: 0.65
ScanlineDensity: 1.2
ScanlineStrength: 0.18
Curvature: 0.04
VignetteStrength: 0.28
ChromaticAberration: 0.004
NoiseStrength: 0.03
FlickerStrength: 0.015
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -13,32 +38,28 @@ MonoBehaviour:
m_Name: PC_Renderer
m_EditorClassIdentifier:
debugShaders:
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7,
type: 3}
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}
hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959,
type: 3}
probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, type: 3}
probeVolumeResources:
probeVolumeDebugShader: {fileID: 4800000, guid: e5c6678ed2aaa91408dd3df699057aae,
type: 3}
probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 03cfc4915c15d504a9ed85ecc404e607,
type: 3}
probeVolumeOffsetDebugShader: {fileID: 4800000, guid: 53a11f4ebaebf4049b3638ef78dc9664,
type: 3}
probeVolumeSamplingDebugShader: {fileID: 4800000, guid: 8f96cd657dc40064aa21efcc7e50a2e7,
type: 3}
probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 57d7c4c16e2765b47a4d2069b311bffe,
type: 3}
probeSamplingDebugTexture: {fileID: 2800000, guid: 24ec0e140fb444a44ab96ee80844e18e,
type: 3}
probeVolumeBlendStatesCS: {fileID: 7200000, guid: b9a23f869c4fd45f19c5ada54dd82176,
type: 3}
probeVolumeDebugShader: {fileID: 4800000, guid: e5c6678ed2aaa91408dd3df699057aae, type: 3}
probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 03cfc4915c15d504a9ed85ecc404e607, type: 3}
probeVolumeOffsetDebugShader: {fileID: 4800000, guid: 53a11f4ebaebf4049b3638ef78dc9664, type: 3}
probeVolumeSamplingDebugShader: {fileID: 4800000, guid: 8f96cd657dc40064aa21efcc7e50a2e7, type: 3}
probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 57d7c4c16e2765b47a4d2069b311bffe, type: 3}
probeSamplingDebugTexture: {fileID: 2800000, guid: 24ec0e140fb444a44ab96ee80844e18e, type: 3}
probeVolumeBlendStatesCS: {fileID: 7200000, guid: b9a23f869c4fd45f19c5ada54dd82176, type: 3}
m_RendererFeatures:
- {fileID: 7833122117494664109}
m_RendererFeatureMap: ad6b866f10d7b46c
- {fileID: -4377071725885749089}
m_RendererFeatureMap: ad6b866f10d7b46c9f882cbe748441c3
m_UseNativeRenderPass: 1
xrSystemData: {fileID: 0}
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
m_AssetVersion: 2
m_AssetVersion: 3
m_PrepassLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_OpaqueLayerMask:
serializedVersion: 2
m_Bits: 4294967295
@@ -56,6 +77,8 @@ MonoBehaviour:
m_RenderingMode: 2
m_DepthPrimingMode: 0
m_CopyDepthMode: 0
m_DepthAttachmentFormat: 0
m_DepthTextureFormat: 0
m_AccurateGbufferNormals: 0
m_IntermediateTextureMode: 0
--- !u!114 &7833122117494664109
@@ -84,12 +107,3 @@ MonoBehaviour:
BlurQuality: 0
Falloff: 100
SampleCount: -1
m_BlueNoise256Textures:
- {fileID: 2800000, guid: 36f118343fc974119bee3d09e2111500, type: 3}
- {fileID: 2800000, guid: 4b7b083e6b6734e8bb2838b0b50a0bc8, type: 3}
- {fileID: 2800000, guid: c06cc21c692f94f5fb5206247191eeee, type: 3}
- {fileID: 2800000, guid: cb76dd40fa7654f9587f6a344f125c9a, type: 3}
- {fileID: 2800000, guid: e32226222ff144b24bf3a5a451de54bc, type: 3}
- {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3}
- {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3}
m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3}