| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- using UnityEngine;
- using UnityEngine.UIElements;
- /// <summary>
- /// Adds a small UI Toolkit control panel for adjusting Time.timeScale at runtime.
- /// Buttons: Pause (0x), 0.5x, 1x (normal), 2x.
- /// Camera movement is independent of Time.timeScale (uses unscaledDeltaTime),
- /// so the simulation can be paused/slowed without affecting navigation.
- /// </summary>
- public class SimulationControlsUIPanel : MonoBehaviour
- {
- [Header("Speed Options")]
- [SerializeField] private float pausedScale = 0f;
- [SerializeField] private float halfScale = 0.5f;
- [SerializeField] private float normalScale = 1f;
- [SerializeField] private float doubleScale = 2f;
- private VisualElement root;
- private Button pauseButton;
- private Button halfButton;
- private Button normalButton;
- private Button doubleButton;
- private Label statusLabel;
- private float lastSpeedBeforePause = 1f;
- void Start()
- {
- InitializeUI();
- ApplySpeed(normalScale);
- }
- void OnDestroy()
- {
- // Always restore normal speed when leaving the scene/play mode
- Time.timeScale = 1f;
- }
- private void InitializeUI()
- {
- var uiDocument = FindAnyObjectByType<UIDocument>();
- if (uiDocument == null)
- {
- var docGO = new GameObject("UIDocument");
- uiDocument = docGO.AddComponent<UIDocument>();
- }
- root = uiDocument.rootVisualElement;
- // Container - bottom-center
- var panel = new VisualElement();
- panel.name = "SimulationControlsPanel";
- panel.style.position = Position.Absolute;
- panel.style.bottom = 20;
- panel.style.left = new StyleLength(new Length(50, LengthUnit.Percent));
- panel.style.translate = new StyleTranslate(new Translate(new Length(-50, LengthUnit.Percent), 0));
- panel.style.flexDirection = FlexDirection.Row;
- panel.style.alignItems = Align.Center;
- panel.style.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.85f);
- panel.style.borderBottomLeftRadius = 6;
- panel.style.borderBottomRightRadius = 6;
- panel.style.borderTopLeftRadius = 6;
- panel.style.borderTopRightRadius = 6;
- panel.style.paddingBottom = 6;
- panel.style.paddingTop = 6;
- panel.style.paddingLeft = 10;
- panel.style.paddingRight = 10;
- var title = new Label("Speed:");
- title.style.color = Color.white;
- title.style.fontSize = 13;
- title.style.unityFontStyleAndWeight = FontStyle.Bold;
- title.style.marginRight = 10;
- panel.Add(title);
- pauseButton = MakeButton("Pause", () => ApplySpeed(pausedScale));
- halfButton = MakeButton("0.5x", () => ApplySpeed(halfScale));
- normalButton = MakeButton("1x", () => ApplySpeed(normalScale));
- doubleButton = MakeButton("2x", () => ApplySpeed(doubleScale));
- panel.Add(pauseButton);
- panel.Add(halfButton);
- panel.Add(normalButton);
- panel.Add(doubleButton);
- statusLabel = new Label("1x");
- statusLabel.style.color = new Color(0.7f, 0.9f, 1f);
- statusLabel.style.fontSize = 12;
- statusLabel.style.marginLeft = 10;
- statusLabel.style.minWidth = 50;
- statusLabel.style.unityTextAlign = TextAnchor.MiddleLeft;
- panel.Add(statusLabel);
- root.Add(panel);
- }
- private Button MakeButton(string text, System.Action onClick)
- {
- var btn = new Button(onClick) { text = text };
- btn.style.width = 60;
- btn.style.height = 28;
- btn.style.marginLeft = 4;
- btn.style.marginRight = 4;
- btn.style.fontSize = 12;
- btn.style.color = Color.white;
- btn.style.backgroundColor = new Color(0.25f, 0.25f, 0.28f, 1f);
- btn.style.borderBottomLeftRadius = 4;
- btn.style.borderBottomRightRadius = 4;
- btn.style.borderTopLeftRadius = 4;
- btn.style.borderTopRightRadius = 4;
- return btn;
- }
- private void ApplySpeed(float scale)
- {
- if (scale > 0f)
- lastSpeedBeforePause = scale;
- Time.timeScale = scale;
- UpdateButtonHighlight(scale);
- if (statusLabel != null)
- statusLabel.text = scale <= 0f ? "PAUSED" : $"{scale}x";
- }
- private void UpdateButtonHighlight(float scale)
- {
- SetActive(pauseButton, Mathf.Approximately(scale, pausedScale));
- SetActive(halfButton, Mathf.Approximately(scale, halfScale));
- SetActive(normalButton, Mathf.Approximately(scale, normalScale));
- SetActive(doubleButton, Mathf.Approximately(scale, doubleScale));
- }
- private void SetActive(Button btn, bool active)
- {
- if (btn == null) return;
- btn.style.backgroundColor = active
- ? new Color(0.2f, 0.6f, 0.9f, 1f) // highlighted
- : new Color(0.25f, 0.25f, 0.28f, 1f);
- btn.style.unityFontStyleAndWeight = active ? FontStyle.Bold : FontStyle.Normal;
- }
- void Update()
- {
- // Keyboard shortcuts: Space = pause/resume, 1/2/3/4 = speed presets
- var kb = UnityEngine.InputSystem.Keyboard.current;
- if (kb == null) return;
- if (kb.spaceKey.wasPressedThisFrame)
- {
- if (Time.timeScale > 0f)
- ApplySpeed(0f);
- else
- ApplySpeed(lastSpeedBeforePause);
- }
- else if (kb.digit1Key.wasPressedThisFrame) ApplySpeed(pausedScale);
- else if (kb.digit2Key.wasPressedThisFrame) ApplySpeed(halfScale);
- else if (kb.digit3Key.wasPressedThisFrame) ApplySpeed(normalScale);
- else if (kb.digit4Key.wasPressedThisFrame) ApplySpeed(doubleScale);
- }
- }
|