Class TextModeCRTBeam
Switches the beam simulator on and off for every CRT picture at once and holds its shared clock. The simulator brings back the motion clarity of a CRT on displays at 120 Hz and above.
Inherited Members
Namespace: Scylla.TextMode.CRT
Assembly: ScyllaTextMode.CRT.dll
Syntax
public static class TextModeCRTBeam
Remarks
A real CRT lights only the part of the picture the electron beam is drawing at that moment, and the phosphor fades quickly afterwards. That is why moving objects look sharp on a CRT and smeared on an LCD, which holds every frame for the whole refresh. The simulator copies this on a fast display: the display refreshes several times for every simulated CRT frame, and each refresh shows only the band of the picture the beam would have reached.
The simulator counts display refreshes, not game frames. With vSync on, every Unity frame is one refresh, so the count advances by one per frame. A frame that takes longer advances it by the refreshes it spanned, which keeps the beam in step with the display; the skipped refreshes are counted in MissedRefreshes and show as flicker. The math lives in CRTBeam and the counting in CRTBeamClock.
All state is static and shared. The settings come from the TextMode configuration the first time a CRT picture asks for them. A setting that game code assigns keeps its value when the configuration is applied, and ApplySettings(CRTSettings) replaces all of them. Every setting returns to its default when a play session starts, so nothing carries over when domain reload is off.
The simulator depends on the display, not on a picture's look, so it is one switch for every TextMode view with a CRT and every camera effect. The in-world monitor never uses it.
Photosensitivity: the simulator makes the picture flicker at the CRT rate, 60 Hz by default, like a real CRT. It is off by default, and Reduce Motion in the TextMode configuration keeps it off.
It needs vSync on with one refresh per frame (QualitySettings.vSyncCount = 1), a
display at twice the CRT rate or more, and variable refresh rate off. Frames the game
misses move the beam on and show as flicker. When the game runs much faster than the
display refreshes, which means vSync is not in effect, the simulator waits and the picture
stays as it is without it; see OutpacesDisplay(double, double). In the Editor, the
Game view only follows vSync with VSync turned on in its menu. It only runs in Play mode.
The following example switches the simulator on for a 240 Hz display and reads its state.
QualitySettings.vSyncCount = 1;
TextModeCRTBeam.CRTRate = 60f;
TextModeCRTBeam.ScanDirection = CRTBeamScanDirection.TopToBottom;
TextModeCRTBeam.Enabled = true;
var status = TextModeCRTBeam.GetStatus();
if (status.Enabled && !status.Active)
{
Debug.Log("Beam simulator is not running: " + status.Reason);
}
Fields
DEFAULT_CRT_RATE
Default simulated CRT refresh rate in hertz, 60. It is also the value CRTRate falls back to when it is set to NaN.
Declaration
public const float DEFAULT_CRT_RATE = 60
Field Value
| Type | Description |
|---|---|
| float |
Properties
AntiRetention
Gets or sets whether an even number of refreshes per CRT frame drifts slowly, which keeps an LCD from retaining a pattern. Can be turned off on OLED displays.
Declaration
public static bool AntiRetention { get; set; }
Property Value
| Type | Description |
|---|---|
| bool |
CRTRate
Gets or sets the simulated CRT's refresh rate in hertz. The display needs at least twice this rate. The value is clamped to 24 through 240 Hz, and NaN gives DEFAULT_CRT_RATE.
Declaration
public static float CRTRate { get; set; }
Property Value
| Type | Description |
|---|---|
| float |
DisplayRate
Gets the display refresh rate in hertz the simulator counts with.
Declaration
public static double DisplayRate { get; }
Property Value
| Type | Description |
|---|---|
| double |
EffectiveGain
Gets the gain in use: Gain, or the default for the display rate when it is 0. The result is 0.5 below 200 Hz and 0.7 from 200 Hz upward; see DefaultGain(double).
Declaration
public static float EffectiveGain { get; }
Property Value
| Type | Description |
|---|---|
| float |
Enabled
Gets or sets whether the beam simulator is wanted. It only runs when the display is fast enough, vSync is in effect, Reduce Motion is off and the game is playing; see IsActive. A value set from code is kept: when the TextMode configuration is applied, every setting code has set since the last time it was applied keeps its value, and only the others are taken from the configuration. ApplySettings(CRTSettings) overrides all of them. False until code or the configuration turns it on.
Declaration
public static bool Enabled { get; set; }
Property Value
| Type | Description |
|---|---|
| bool |
Frames
Gets the display refreshes per CRT frame, or 0 when the display is too slow.
Declaration
public static float Frames { get; }
Property Value
| Type | Description |
|---|---|
| float |
Gain
Gets or sets the share of the picture's brightness the tube gives off on average, from 0 to 1. 0 picks 0.5 below 200 Hz and 0.7 above. Higher is brighter with more motion blur.
Declaration
public static float Gain { get; set; }
Property Value
| Type | Description |
|---|---|
| float |
IsActive
Gets whether the simulator runs this frame. It is false when no CRT ticked the simulator in the previous frame, because the status then no longer describes what is on screen.
Declaration
public static bool IsActive { get; }
Property Value
| Type | Description |
|---|---|
| bool |
Remarks
The value only changes when a CRT picture draws, so it stays false while no CRT view or camera effect is on screen, even when Enabled is true.
MissedRefreshes
Gets the number of refreshes that passed without a frame since the simulator last went from inactive to active. A count that keeps rising means the game does not keep up with the display.
Declaration
public static long MissedRefreshes { get; }
Property Value
| Type | Description |
|---|---|
| long |
RefreshRateOverride
Gets or sets a display refresh rate in hertz to use instead of the one the display reports, for displays that report it wrongly. 0 uses the reported rate. Values above 1000 are clamped to 1000; NaN and values of 0 or less give 0.
Declaration
public static float RefreshRateOverride { get; set; }
Property Value
| Type | Description |
|---|---|
| float |
ScanDirection
Gets or sets the direction in which the beam sweeps the tube. The default is TopToBottom.
Declaration
public static CRTBeamScanDirection ScanDirection { get; set; }
Property Value
| Type | Description |
|---|---|
| CRTBeamScanDirection |
SlowMotionDivisor
Gets or sets how many frames each simulated refresh lasts, for watching the sweep on a slow display. 1, the default, runs at the real rate. A development aid only.
Declaration
public static int SlowMotionDivisor { get; set; }
Property Value
| Type | Description |
|---|---|
| int |
Methods
ApplySettings(CRTSettings)
Copies the beam simulator's settings from a TextMode configuration. The configuration of the running TextMode module is applied automatically when the simulator first looks at it, without touching settings game code has set; call this to apply every setting, for example settings a player changed at runtime.
Declaration
public static void ApplySettings(CRTSettings settings)
Parameters
| Type | Name | Description |
|---|---|---|
| CRTSettings | settings | The CRT settings of a TextMode configuration. |
GetStatus()
Returns a snapshot of the simulator's state.
Declaration
public static CRTBeamStatus GetStatus()
Returns
| Type | Description |
|---|---|
| CRTBeamStatus | The current state. |
ResolveDisplayRate()
Returns the display refresh rate the simulator uses: RefreshRateOverride, or the rate the display reports.
Declaration
public static double ResolveDisplayRate()
Returns
| Type | Description |
|---|---|
| double | The refresh rate in hertz, 0 when it is not known. |