Better Subtitles

#Kismet
#HUD
Modified @ 2022-04-27 13:12:25

An improved system from the Subtitles utilized in Nyakuza Metro!


LATEST UPDATE: Added a new node see bottom, modified the manager a little.

Old way to do subtitles in a succession:

With this system you can do the above with less stuff for a similar functionality:

A small but useful system that can capitalized on to make subtitles for cinematics, with a lot of properties to mess with as shown below:

Properties info:

LifeTime | Duration of the subtitles, will run OnExpired when reaching zero. Note: Setting this to 0 will be infinite till "Skip or Stop" are called.

FadeIn | Time to reach fade in, occurs at the beginning of the life time.

FadeOut | Time to fade out, occurs at the end of the life time.

Text | Text to render on subtitles

Position | BETWEEN 0 AND 1! This value is transformed to the screen current resolution of Max Height/Width! tl;dr X/Y = 0 is left/top, 0.5f is center, X/Y = 1.0f is right/bottom.

Scale | Size of the text.

Font | Custom font? Set it here! Keeping this None will use Cursed Casual aka default game font.

Alignment | Adjust the text based on the anchoring point from Position, like starting left/right or center and so on.

TextColor | Color of the text

BorderTextColor | Color of the border

BorderWidth | Width of the border

BorderQuality | The quality of the border, the less this is the more pixelated it gets, good to increase if the text is too big!

Shadow | Adds a shadow to the text

ShadowAlpha | How much opacity the shadow has? 0 values is pretty much Shadow set to false.

VerticalSize | Stretches the text veritcally

SS_SeqAct_ShutdownSubtitles.uc

Call this for an easier way to shutdown subtitles entirely without calling stop for ALL subtitle nodes.

SS_SeqAct_ShutdownSubtitles.uc

[RAW] [Download]

Class SS_SeqAct_ShutdownSubtitles extends SequenceAction;
 
event Activated()
{
    local Hat_PlayerController pc;
    local SS_HUDElement_BetterSubtitles bs;
    local Object o;
 
    o = Targets[0];
 
    pc = Hat_PlayerController(GetController(Actor(o)));
    bs = SS_HUDElement_BetterSubtitles(GetHUD(pc.MyHUD));
 
    if(pc == None || bs == None) return;
    if(InputLinks[0].bHasImpulse)
    {
        bs.KismetSubtitle = None;
        bs.CloseHUD(pc.MyHUD, bs.Class);
        ForceActivateOutput(0);
    }
}
 
function Hat_HUDElement GetHUD(HUD myHUD)
{
    local Hat_HUD h;
    h = Hat_HUD(myHUD);
    return h.GetHUD(class'SS_HUDElement_BetterSubtitles');
}
 
defaultproperties
{
    ObjName="Shutdown All Subtitles";
    ObjCategory="HUD";
 
    InputLinks(0)=(LinkDesc="In");
    OutputLinks(0)=(LinkDesc="Out");
 
    bCallHandler = false;
    bAutoActivateOutputLinks = false;
}
 
SS_HUDElement_BetterSubtitles.uc

[RAW] [Download]

Class SS_HUDElement_BetterSubtitles extends Hat_HUDElement;
 
var String Text;
var Font Font;
var Vector2D Position;
var float Scale, ShadowAlpha, BorderWidth, VerticalSize, BorderQuality;
var bool Shadow;
var TextAlign Alignment;
var Color TextColor, BorderColor;
 
var bool bFadeOut, bShuttingDown;
var float Opacity, FadeSpeed, LifeTime;
var SS_SeqAct_SubtitleManager KismetSubtitle;
 
function OnOpenHUD(HUD H, optional String command)
{
    Text = command;
}
 
function bool Render(HUD H)
{
    local float x, y, s;
 
    if(!Super.Render(H)) return false;
 
    H.Canvas.SetDrawColor(TextColor.R, TextColor.G, TextColor.B, TextColor.A * Opacity);
 
    H.Canvas.Font = (Font != None ? Font : Class'Hat_FontInfo'.static.GetDefaultFont("abcdefghijkmnlopqrstuvwxyzABCDEFGHIJKMNLOPQRSTUVWXYZ"));
 
    x = H.Canvas.ClipX * Position.X;
    y = H.Canvas.ClipY * Position.Y;
    s = FMin(H.Canvas.ClipY,H.Canvas.ClipX)/1080.0f * Scale;
 
    DrawBorderedText(H.Canvas, Text, x, y, s, Shadow, Alignment, ShadowAlpha, BorderWidth, BorderColor, VerticalSize, BorderQuality);
 
    return true;
}
 
function bool Tick(HUD H, float d)
{
    if(!Super.Tick(H,d)) return false;
 
    if(Lifetime > 0)
    {
        LifeTime = FMax(LifeTime - d, 0.0f);
        if(LifeTime <= 0 && KismetSubtitle != None)
            KismetSubtitle.PrepareToDestroy();
    }
 
    if(FadeSpeed > 0)
        Opacity = FClamp(Opacity + (bFadeOut ? -1.0f : 1.0f) * (1.0f/FadeSpeed) * d, 0.0f, 1.0f);
    else
        Opacity = bFadeOut ? 0 : 1;
 
    if(bShuttingDown && bFadeOut && Opacity <= 0)
    {
        if(KismetSubtitle != None)
            SetTimer(H, 0.01f, false, 'OnExpiring', KismetSubtitle);
        CloseHUD(H, Class);
    }
    return true;
}
 
defaultproperties
{
    Font = None;
    Position = (X = 0.5f, Y = 0.8f);
    Scale = 1.0f;
    Shadow = false;
    ShadowAlpha = 0.5f;
    Alignment = TextAlign_Center;
    VerticalSize = -1;
    BorderWidth = 4;
    BorderQuality = 1;
    TextColor = (R = 255, G = 255, B = 255, A = 255);
}
 
SS_SeqAct_SubtitleManager.uc

[RAW] [Download]

class SS_SeqAct_SubtitleManager extends SequenceAction;
 
var(Time) float LifeTime; // Duration of the subtitles, will run OnExpired when reaching zero. Note: Setting this to 0 will be infinite till "Skip or Stop" are called.
var(Time) float FadeIn; // Time to reach fade in, occurs at the beginning of the life time.
var(Time) float FadeOut; // Time to fade out, occurs at the end of the life time.
var(MainSettings) String Text; // Text to render on subtitles
var(MainSettings) Vector2D Position; // BETWEEN 0 AND 1! This value is transformed to the screen current resolution of Max Height/Width! tl;dr X/Y = 0 is left/top, 0.5f is center, X/Y = 1.0f is right/bottom.
var(MainSettings) float Scale; // Size of the text.
var(MainSettings) Font Font; // Custom font? Set it here! Keeping this None will use Cursed Casual aka default game font.
var(MainSettings) TextAlign Alignment; // Adjust the text based on the anchoring point from Position, like starting left/right or center and so on.
var(MainSettings) Color TextColor; // Color of the text
var(BorderSettings) Color BorderTextColor; // Color of the border
var(BorderSettings) float BorderWidth; // Width of the border
var(BorderSettings) float BorderQuality; // The quality of the border, the less this is the more pixelated it gets, good to increase if the text is too big!
var(OtherSettings) bool Shadow; // Adds a shadow to the text
var(OtherSettings) float ShadowAlpha; // How much opacity the shadow has? 0 values is pretty much Shadow set to false.
var(OtherSettings) float VerticalSize; // Stretches the text veritcally
 
var SS_HUDElement_BetterSubtitles SubtitlesHUD;
 
final function Print(coerce string msg)
{
    local WorldInfo wi;
 
    wi = class'WorldInfo'.static.GetWorldInfo();
    if (wi == None) return;
 
    msg = "[" $ Class $ "]" @ msg;
 
    if(wi.GetALocalPlayerController() != None)
        wi.GetALocalPlayerController().TeamMessage(None, msg, 'Event', 6);
    else
        wi.Game.Broadcast(wi, msg);
}
 
event Activated()
{
    local Hat_PlayerController pc;
    local SS_HUDElement_BetterSubtitles bs;
    local Object o;
 
    if(Text ~= "") return;
 
    o = Targets[0];
 
    pc = Hat_PlayerController(GetController(Actor(o)));
    bs = SS_HUDElement_BetterSubtitles(OpenHUD(pc.MyHUD, Text));
 
    if(pc == None || bs == None) return;
    if(InputLinks[0].bHasImpulse)
    {
        bs.Position = Position;
        bs.Scale = Scale;
        bs.Font = Font;
        bs.Alignment = Alignment;
        bs.TextColor = TextColor;
        bs.BorderColor = BorderTextColor;
        bs.BorderWidth = BorderWidth;
        bs.BorderQuality = BorderQuality;
        bs.Shadow = Shadow;
        bs.ShadowAlpha = ShadowAlpha;
        bs.VerticalSize = VerticalSize;
        bs.FadeSpeed = FadeIn;
        bs.KismetSubtitle = self;
        if(Lifetime > 0) bs.LifeTime = FMax(LifeTime - FadeOut, 0.01f);
 
        SubtitlesHUD = bs;
        ForceActivateOutput(0);
    }
    else if(InputLinks[1].bHasImpulse)
    {
        PrepareToDestroy();
    }
    else if(InputLinks[2].bHasImpulse)
    {
        if(SubtitlesHUD != None)
            SubtitlesHUD.CloseHUD(pc.MyHUD, SubtitlesHUD.Class);
        ForceActivateOutput(2);
    }
}
 
function Hat_HUDElement OpenHUD(HUD myHUD, optional coerce string Command)
{
    local Hat_HUD h;
    h = Hat_HUD(myHUD);
    return h.OpenHUD(class'SS_HUDElement_BetterSubtitles', Command);
}
 
function OnExpiring()
{
    ForceActivateOutput(1);
}
 
function PrepareToDestroy()
{
    SubtitlesHUD.LifeTime = 0; // If it was running and cancelled, early, set this back to 0 to avoid jank garbage
    SubtitlesHUD.bFadeOut = true;
    SubtitlesHUD.bShuttingDown = true;
    SubtitlesHUD.FadeSpeed = FadeOut;
    SubtitlesHUD = None;
}
 
event CheckForErrors(out Array<string> ErrorMessages)
{
    if (Text ~= "") ErrorMessages.AddItem("No text was inputted, add some text!");
    if (Scale <= 0) ErrorMessages.AddItem("Scale set to 0 or less won't be able to render!");
    Super.CheckForErrors(ErrorMessages);
}
 
defaultproperties
{
    ObjName="Play Subtitles";
    ObjCategory="HUD";
 
    InputLinks(0)=(LinkDesc="In");
    InputLinks(1)=(LinkDesc="Skip");
    InputLinks(2)=(LinkDesc="Stop");
    OutputLinks(0)=(LinkDesc="Out");
    OutputLinks(1)=(LinkDesc="Expired");
    OutputLinks(2)=(LinkDesc="Stopped");
 
    bCallHandler = false;
 
    Font = None;
    Position = (X = 0.5f, Y = 0.8f);
    Scale = 1.0f;
    Shadow = false;
    ShadowAlpha = 0.5f;
    Alignment = TextAlign_Center;
    VerticalSize = -1;
    BorderWidth = 4;
    BorderQuality = 1;
    TextColor = (R = 255, G = 255, B = 255, A = 255);
 
    LifeTime = 10;
 
 
    bAutoActivateOutputLinks = false;
}