NewsPanelUI

Overview

Ticker-style news marquee that scrolls localized lines horizontally and periodically performs a vertical slide-up to swap in the next line. It uses two synchronized TextMeshProUGUI elements (A & B) to create a seamless loop, supports pause via an Atom, and reloads content on language changes. fileciteturn25file0

Data Source & Localization

  • Loads a NewsData JSON from Resources/News/News (or from a serialized _newsJson when present).
  • Builds per-line I2 keys NEWS_000, NEWS_001, … and pulls strings via LocalizationManager.GetTranslation(key).
  • If a line contains {region_name}, the placeholder is replaced with a random Region’s localized name from GameManager.GetAllRegions().
  • Subscribes to LocalizationManager.OnLocalizeEvent and restarts the marquee on language change. fileciteturn25file0

Serialized Fields

State / Control

  • BoolVariable isPaused — when true, all movement halts.

Scroll Settings

  • float scrollSpeed = 100f — pixels/second (left for marquee, up for slide).
  • float repeatSpacing = 64f — gap between A’s end and B’s start in the loop.
  • float loopDuration = 30f — time to recycle A/B before finishing the cycle.
  • float endDelay = 1f — pause after the final horizontal snap, before slide-up.
  • float startDelay = 1f — pause after slide-up, before the next marquee.

UI

  • RectTransform maskRect — viewport with RectMask2D.
  • TextMeshProUGUI newsText (A), newsSecondText (B).

Padding

  • float startPadding = 6f — starting offset at the right edge.
  • float endPadding = 10f — right bound for the finishing snap. fileciteturn25file0

Public API

public void ResizeRectAndPlay();
// Measures the current text, positions both labels, and starts the marquee.

Internally this calls PrepareHorizontalFor(...) then starts ScrollNews(...). fileciteturn25file0

How It Works

Horizontal marquee (A/B loop)

  • Both labels show the same string and are placed just outside the right edge (maskRect.width + startPadding).
  • The script measures width using TMP_Text.GetPreferredValues(...) to fit each label’s RectTransform.
  • While loopDuration has not elapsed, both labels move left at scrollSpeed, and when one fully exits on the left it’s moved to the right of its sibling by width + repeatSpacing.
  • After the loop time expires, the script finishes cleanly by snapping whichever label is near the right bound so its right edge lands at maskRect.width - endPadding, then hands off to slide-up. fileciteturn25file0

Vertical slide-up transition

  • Designates the currently visible label as top and the other as bottom (placed just below the viewport).
  • Removes one instance of the current line from the rotation and picks a new localized line.
  • Only the bottom label’s text is changed; both move up until the bottom reaches the resting Y.
  • Marks the new line as pending and, after startDelay, restarts the marquee with the fresh content. fileciteturn25file0

Notes & Quirks

  • The constant rightStopMargin (10 px) is used to detect when a label is close enough to the right bound to snap precisely.
  • RemoveOneInstance(...) currently removes from the localized key list using the visible text string, which won’t match and thus won’t remove anything — likely a small bug worth adjusting (compare keys, not text).
  • The serialized static string newsDataPath = "News\\" is unused at runtime; loading uses the hardcoded "News/News" path. Consider de‑staticizing or removing. fileciteturn25file0

Integration Checklist

  • Place the component under a RectMask2D viewport and wire A and B TMP labels.
  • Provide Resources/News/News.json with a NewsData payload (items: string[]).
  • Add I2 terms NEWS_000, NEWS_001, … that mirror the JSON order.
  • Optionally include {region_name} in lines to inject random region names.
  • Bind isPaused to your global pause atom so the marquee respects game state. fileciteturn25file0