Add cinematic startup credits
This commit is contained in:
@@ -20,10 +20,10 @@ public:
|
||||
TSubclassOf<UAgrarianDemoNoticeWidget> NoticeWidgetClass;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo", meta = (ClampMin = "1.0"))
|
||||
float NoticeDurationSeconds = 9.0f;
|
||||
float NoticeDurationSeconds = 24.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo")
|
||||
FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.E - Build 2026.05.17"));
|
||||
FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.H - Build 2026.05.17"));
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo")
|
||||
FText DemoNotice = FText::FromString(TEXT("Beta prototype build - not for public distribution"));
|
||||
|
||||
@@ -2,9 +2,43 @@
|
||||
|
||||
#include "AgrarianDemoNoticeWidget.h"
|
||||
|
||||
#include "Engine/World.h"
|
||||
#include "Rendering/DrawElements.h"
|
||||
#include "Styling/CoreStyle.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct FAgrarianCreditCard
|
||||
{
|
||||
const TCHAR* Name;
|
||||
const TCHAR* Role;
|
||||
FLinearColor AccentColor;
|
||||
int32 IllustrationIndex;
|
||||
float HoldSeconds;
|
||||
};
|
||||
|
||||
const FAgrarianCreditCard CreditCards[] = {
|
||||
{ TEXT("Nathan Slaven"), TEXT("Lead Developer"), FLinearColor(0.70f, 0.90f, 0.46f, 1.0f), 0, 2.05f },
|
||||
{ TEXT("Hunter Slaven"), TEXT("Junior Developer"), FLinearColor(0.36f, 0.72f, 0.96f, 1.0f), 1, 2.05f },
|
||||
{ TEXT("Lisa Reiley"), TEXT("Quality Control"), FLinearColor(0.95f, 0.72f, 0.42f, 1.0f), 2, 2.05f },
|
||||
{ TEXT("River Slaven"), TEXT("Alpha Game Tester"), FLinearColor(0.56f, 0.82f, 0.66f, 1.0f), 3, 2.05f },
|
||||
{ TEXT("Fisher Slaven"), TEXT("Beta Game Tester"), FLinearColor(0.56f, 0.62f, 0.98f, 1.0f), 4, 2.05f },
|
||||
{ TEXT("Funding"), TEXT("Cherished individuals, Pacificao seed funding, and Lina Family Investment Funds"), FLinearColor(0.92f, 0.84f, 0.54f, 1.0f), 5, 3.0f },
|
||||
};
|
||||
|
||||
float SmoothStep(float Value)
|
||||
{
|
||||
const float ClampedValue = FMath::Clamp(Value, 0.0f, 1.0f);
|
||||
return ClampedValue * ClampedValue * (3.0f - (2.0f * ClampedValue));
|
||||
}
|
||||
}
|
||||
|
||||
void UAgrarianDemoNoticeWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
CreditsStartTimeSeconds = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f;
|
||||
}
|
||||
|
||||
int32 UAgrarianDemoNoticeWidget::NativePaint(
|
||||
const FPaintArgs& Args,
|
||||
const FGeometry& AllottedGeometry,
|
||||
@@ -17,7 +51,7 @@ int32 UAgrarianDemoNoticeWidget::NativePaint(
|
||||
LayerId = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
|
||||
|
||||
const FVector2D Size = AllottedGeometry.GetLocalSize();
|
||||
const float PanelWidth = FMath::Min(860.0f, Size.X - 96.0f);
|
||||
const float PanelWidth = FMath::Min(860.0f, FMath::Max(320.0f, Size.X - 96.0f));
|
||||
const float PanelHeight = 210.0f;
|
||||
const FVector2D PanelPosition((Size.X - PanelWidth) * 0.5f, 42.0f);
|
||||
|
||||
@@ -46,6 +80,8 @@ int32 UAgrarianDemoNoticeWidget::NativePaint(
|
||||
DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, DemoNotice, PanelPosition.Y + 126.0f, NoticeFont, FLinearColor(0.78f, 0.82f, 0.75f, 1.0f));
|
||||
DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, CopyrightNotice, PanelPosition.Y + 158.0f, NoticeFont, FLinearColor(0.66f, 0.70f, 0.64f, 1.0f));
|
||||
|
||||
DrawCinematicCredits(OutDrawElements, LayerId, AllottedGeometry);
|
||||
|
||||
return LayerId;
|
||||
}
|
||||
|
||||
@@ -72,3 +108,208 @@ void UAgrarianDemoNoticeWidget::DrawCenteredText(
|
||||
ESlateDrawEffect::None,
|
||||
Color);
|
||||
}
|
||||
|
||||
void UAgrarianDemoNoticeWidget::DrawTextAt(
|
||||
FSlateWindowElementList& OutDrawElements,
|
||||
int32& LayerId,
|
||||
const FGeometry& AllottedGeometry,
|
||||
const FText& Text,
|
||||
const FVector2D& Position,
|
||||
float Width,
|
||||
const FSlateFontInfo& Font,
|
||||
const FLinearColor& Color) const
|
||||
{
|
||||
FSlateDrawElement::MakeText(
|
||||
OutDrawElements,
|
||||
++LayerId,
|
||||
AllottedGeometry.ToPaintGeometry(FVector2f(FMath::Max(96.0f, Width), Font.Size + 18.0f), FSlateLayoutTransform(FVector2f(Position))),
|
||||
Text,
|
||||
Font,
|
||||
ESlateDrawEffect::None,
|
||||
Color);
|
||||
}
|
||||
|
||||
void UAgrarianDemoNoticeWidget::DrawCinematicCredits(
|
||||
FSlateWindowElementList& OutDrawElements,
|
||||
int32& LayerId,
|
||||
const FGeometry& AllottedGeometry) const
|
||||
{
|
||||
const UWorld* World = GetWorld();
|
||||
if (!World)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const FVector2D Size = AllottedGeometry.GetLocalSize();
|
||||
const float Elapsed = World->GetTimeSeconds() - CreditsStartTimeSeconds;
|
||||
const float IntroDelay = 0.55f;
|
||||
const float SlamSeconds = 0.28f;
|
||||
const float ExitSeconds = 0.48f;
|
||||
const float GapSeconds = 0.16f;
|
||||
|
||||
float Cursor = IntroDelay;
|
||||
int32 ActiveIndex = INDEX_NONE;
|
||||
float LocalTime = 0.0f;
|
||||
for (int32 Index = 0; Index < UE_ARRAY_COUNT(CreditCards); ++Index)
|
||||
{
|
||||
const float CardDuration = SlamSeconds + CreditCards[Index].HoldSeconds + ExitSeconds + GapSeconds;
|
||||
if (Elapsed >= Cursor && Elapsed < Cursor + CardDuration)
|
||||
{
|
||||
ActiveIndex = Index;
|
||||
LocalTime = Elapsed - Cursor;
|
||||
break;
|
||||
}
|
||||
Cursor += CardDuration;
|
||||
}
|
||||
|
||||
if (ActiveIndex == INDEX_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const FAgrarianCreditCard& Card = CreditCards[ActiveIndex];
|
||||
const bool bEntering = LocalTime < SlamSeconds;
|
||||
const bool bExiting = LocalTime > SlamSeconds + Card.HoldSeconds;
|
||||
const float EnterAlpha = bEntering ? SmoothStep(LocalTime / SlamSeconds) : 1.0f;
|
||||
const float ExitAlpha = bExiting ? 1.0f - SmoothStep((LocalTime - SlamSeconds - Card.HoldSeconds) / ExitSeconds) : 1.0f;
|
||||
const float Alpha = FMath::Clamp(EnterAlpha * ExitAlpha, 0.0f, 1.0f);
|
||||
const float ImpactOvershoot = bEntering ? FMath::Sin((LocalTime / SlamSeconds) * PI) * 28.0f : 0.0f;
|
||||
|
||||
const float PanelWidth = FMath::Min(1120.0f, FMath::Max(420.0f, Size.X - 112.0f));
|
||||
const float PanelHeight = FMath::Min(330.0f, FMath::Max(220.0f, Size.Y - 340.0f));
|
||||
const FVector2D TargetPosition((Size.X - PanelWidth) * 0.5f, FMath::Max(275.0f, (Size.Y - PanelHeight) * 0.5f + 72.0f));
|
||||
const float EnterOffset = bEntering ? FMath::Lerp(-Size.X * 0.85f, ImpactOvershoot, SmoothStep(LocalTime / SlamSeconds)) : 0.0f;
|
||||
const float ExitOffset = bExiting ? FMath::Lerp(0.0f, Size.X * 0.9f, SmoothStep((LocalTime - SlamSeconds - Card.HoldSeconds) / ExitSeconds)) : 0.0f;
|
||||
const FVector2D PanelPosition(TargetPosition.X + EnterOffset + ExitOffset, TargetPosition.Y);
|
||||
|
||||
FSlateDrawElement::MakeBox(
|
||||
OutDrawElements,
|
||||
++LayerId,
|
||||
AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f::ZeroVector)),
|
||||
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
|
||||
ESlateDrawEffect::None,
|
||||
FLinearColor(0.0f, 0.0f, 0.0f, 0.34f * Alpha));
|
||||
|
||||
FSlateDrawElement::MakeBox(
|
||||
OutDrawElements,
|
||||
++LayerId,
|
||||
AllottedGeometry.ToPaintGeometry(FVector2f(PanelWidth, PanelHeight), FSlateLayoutTransform(FVector2f(PanelPosition))),
|
||||
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
|
||||
ESlateDrawEffect::None,
|
||||
FLinearColor(0.015f, 0.018f, 0.014f, 0.92f * Alpha));
|
||||
|
||||
FSlateDrawElement::MakeBox(
|
||||
OutDrawElements,
|
||||
++LayerId,
|
||||
AllottedGeometry.ToPaintGeometry(FVector2f(PanelWidth, 5.0f), FSlateLayoutTransform(FVector2f(PanelPosition))),
|
||||
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
|
||||
ESlateDrawEffect::None,
|
||||
FLinearColor(Card.AccentColor.R, Card.AccentColor.G, Card.AccentColor.B, Alpha));
|
||||
|
||||
const FVector2D ArtSize(FMath::Min(275.0f, PanelWidth * 0.32f), PanelHeight - 64.0f);
|
||||
const FVector2D ArtPosition(PanelPosition.X + 32.0f, PanelPosition.Y + 32.0f);
|
||||
DrawCreditIllustration(OutDrawElements, LayerId, AllottedGeometry, ArtPosition, ArtSize, Card.IllustrationIndex, Card.AccentColor, Alpha);
|
||||
|
||||
const float TextX = ArtPosition.X + ArtSize.X + 42.0f;
|
||||
const float TextWidth = PanelPosition.X + PanelWidth - TextX - 36.0f;
|
||||
const FSlateFontInfo NameFont = FCoreStyle::GetDefaultFontStyle("Bold", ActiveIndex == 5 ? 46 : 56);
|
||||
const FSlateFontInfo RoleFont = FCoreStyle::GetDefaultFontStyle("Regular", ActiveIndex == 5 ? 22 : 28);
|
||||
const FSlateFontInfo LabelFont = FCoreStyle::GetDefaultFontStyle("Regular", 16);
|
||||
const float NameY = PanelPosition.Y + (ActiveIndex == 5 ? 76.0f : 92.0f);
|
||||
|
||||
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(Card.Name), FVector2D(TextX, NameY), TextWidth, NameFont, FLinearColor(0.94f, 0.98f, 0.88f, Alpha));
|
||||
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(Card.Role), FVector2D(TextX + 4.0f, NameY + 76.0f), TextWidth, RoleFont, FLinearColor(Card.AccentColor.R, Card.AccentColor.G, Card.AccentColor.B, Alpha));
|
||||
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Agrarian startup credits")), FVector2D(TextX + 6.0f, PanelPosition.Y + PanelHeight - 58.0f), TextWidth, LabelFont, FLinearColor(0.64f, 0.70f, 0.60f, 0.82f * Alpha));
|
||||
}
|
||||
|
||||
void UAgrarianDemoNoticeWidget::DrawCreditIllustration(
|
||||
FSlateWindowElementList& OutDrawElements,
|
||||
int32& LayerId,
|
||||
const FGeometry& AllottedGeometry,
|
||||
const FVector2D& Position,
|
||||
const FVector2D& Size,
|
||||
int32 IllustrationIndex,
|
||||
const FLinearColor& AccentColor,
|
||||
float Alpha) const
|
||||
{
|
||||
FSlateDrawElement::MakeBox(
|
||||
OutDrawElements,
|
||||
++LayerId,
|
||||
AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f(Position))),
|
||||
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
|
||||
ESlateDrawEffect::None,
|
||||
FLinearColor(0.07f, 0.08f, 0.065f, 0.96f * Alpha));
|
||||
|
||||
const FVector2D Center = Position + (Size * 0.5f);
|
||||
const FLinearColor SoftAccent(AccentColor.R, AccentColor.G, AccentColor.B, 0.28f * Alpha);
|
||||
const FLinearColor StrongAccent(AccentColor.R, AccentColor.G, AccentColor.B, Alpha);
|
||||
|
||||
auto DrawBox = [&](const FVector2D& BoxPosition, const FVector2D& BoxSize, const FLinearColor& Color)
|
||||
{
|
||||
FSlateDrawElement::MakeBox(
|
||||
OutDrawElements,
|
||||
++LayerId,
|
||||
AllottedGeometry.ToPaintGeometry(FVector2f(BoxSize), FSlateLayoutTransform(FVector2f(BoxPosition))),
|
||||
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
|
||||
ESlateDrawEffect::None,
|
||||
Color);
|
||||
};
|
||||
|
||||
auto DrawLine = [&](const TArray<FVector2f>& Points, const FLinearColor& Color, float Thickness)
|
||||
{
|
||||
FSlateDrawElement::MakeLines(
|
||||
OutDrawElements,
|
||||
++LayerId,
|
||||
AllottedGeometry.ToPaintGeometry(),
|
||||
Points,
|
||||
ESlateDrawEffect::None,
|
||||
Color,
|
||||
true,
|
||||
Thickness);
|
||||
};
|
||||
|
||||
DrawBox(Position + FVector2D(16.0f, 16.0f), FVector2D(Size.X - 32.0f, Size.Y - 32.0f), FLinearColor(0.0f, 0.0f, 0.0f, 0.22f * Alpha));
|
||||
|
||||
if (IllustrationIndex == 0)
|
||||
{
|
||||
DrawBox(Center + FVector2D(-72.0f, -46.0f), FVector2D(144.0f, 82.0f), SoftAccent);
|
||||
DrawBox(Center + FVector2D(-62.0f, -36.0f), FVector2D(124.0f, 62.0f), FLinearColor(0.02f, 0.03f, 0.025f, Alpha));
|
||||
DrawLine({ FVector2f(Center.X - 42.0f, Center.Y - 8.0f), FVector2f(Center.X - 8.0f, Center.Y - 28.0f), FVector2f(Center.X + 42.0f, Center.Y + 18.0f) }, StrongAccent, 4.0f);
|
||||
DrawBox(Center + FVector2D(-34.0f, 48.0f), FVector2D(68.0f, 10.0f), StrongAccent);
|
||||
}
|
||||
else if (IllustrationIndex == 1)
|
||||
{
|
||||
DrawLine({ FVector2f(Center.X, Center.Y + 58.0f), FVector2f(Center.X, Center.Y - 48.0f) }, StrongAccent, 5.0f);
|
||||
DrawLine({ FVector2f(Center.X, Center.Y - 8.0f), FVector2f(Center.X - 48.0f, Center.Y - 42.0f), FVector2f(Center.X - 74.0f, Center.Y - 20.0f) }, StrongAccent, 5.0f);
|
||||
DrawLine({ FVector2f(Center.X, Center.Y + 2.0f), FVector2f(Center.X + 52.0f, Center.Y - 34.0f), FVector2f(Center.X + 80.0f, Center.Y - 10.0f) }, StrongAccent, 5.0f);
|
||||
DrawBox(Center + FVector2D(-84.0f, 64.0f), FVector2D(168.0f, 8.0f), SoftAccent);
|
||||
}
|
||||
else if (IllustrationIndex == 2)
|
||||
{
|
||||
DrawBox(Center + FVector2D(-58.0f, -74.0f), FVector2D(116.0f, 148.0f), FLinearColor(0.93f, 0.88f, 0.72f, 0.22f * Alpha));
|
||||
DrawBox(Center + FVector2D(-34.0f, -88.0f), FVector2D(68.0f, 20.0f), StrongAccent);
|
||||
DrawLine({ FVector2f(Center.X - 32.0f, Center.Y - 26.0f), FVector2f(Center.X - 12.0f, Center.Y - 6.0f), FVector2f(Center.X + 42.0f, Center.Y - 48.0f) }, StrongAccent, 5.0f);
|
||||
DrawBox(Center + FVector2D(-34.0f, 28.0f), FVector2D(68.0f, 5.0f), StrongAccent);
|
||||
}
|
||||
else if (IllustrationIndex == 3)
|
||||
{
|
||||
DrawBox(Center + FVector2D(-72.0f, -28.0f), FVector2D(144.0f, 56.0f), SoftAccent);
|
||||
DrawLine({ FVector2f(Center.X - 70.0f, Center.Y), FVector2f(Center.X - 18.0f, Center.Y - 58.0f), FVector2f(Center.X + 32.0f, Center.Y + 44.0f), FVector2f(Center.X + 78.0f, Center.Y - 18.0f) }, StrongAccent, 5.0f);
|
||||
DrawBox(Center + FVector2D(-16.0f, -16.0f), FVector2D(32.0f, 32.0f), FLinearColor(0.92f, 0.96f, 0.86f, 0.80f * Alpha));
|
||||
}
|
||||
else if (IllustrationIndex == 4)
|
||||
{
|
||||
DrawBox(Center + FVector2D(-64.0f, -58.0f), FVector2D(128.0f, 116.0f), SoftAccent);
|
||||
DrawLine({ FVector2f(Center.X - 44.0f, Center.Y - 8.0f), FVector2f(Center.X - 8.0f, Center.Y + 28.0f), FVector2f(Center.X + 54.0f, Center.Y - 36.0f) }, StrongAccent, 5.0f);
|
||||
DrawBox(Center + FVector2D(-74.0f, 74.0f), FVector2D(148.0f, 8.0f), StrongAccent);
|
||||
DrawBox(Center + FVector2D(-74.0f, -82.0f), FVector2D(148.0f, 8.0f), StrongAccent);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawLine({ FVector2f(Center.X, Center.Y + 72.0f), FVector2f(Center.X, Center.Y - 36.0f) }, StrongAccent, 5.0f);
|
||||
DrawLine({ FVector2f(Center.X, Center.Y - 12.0f), FVector2f(Center.X - 52.0f, Center.Y - 50.0f), FVector2f(Center.X - 80.0f, Center.Y - 28.0f) }, StrongAccent, 5.0f);
|
||||
DrawLine({ FVector2f(Center.X, Center.Y - 6.0f), FVector2f(Center.X + 52.0f, Center.Y - 54.0f), FVector2f(Center.X + 84.0f, Center.Y - 28.0f) }, StrongAccent, 5.0f);
|
||||
DrawBox(Center + FVector2D(-92.0f, 78.0f), FVector2D(184.0f, 8.0f), SoftAccent);
|
||||
DrawBox(Center + FVector2D(-48.0f, 56.0f), FVector2D(96.0f, 8.0f), StrongAccent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ class AGRARIANGAME_API UAgrarianDemoNoticeWidget : public UUserWidget
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo")
|
||||
FText Motto = FText::FromString(TEXT("What survives after you are gone?"));
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo")
|
||||
FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.E - Build 2026.05.17"));
|
||||
FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.H - Build 2026.05.17"));
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo")
|
||||
FText DemoNotice = FText::FromString(TEXT("Beta prototype build - not for public distribution"));
|
||||
@@ -35,6 +37,8 @@ protected:
|
||||
bool bParentEnabled) const override;
|
||||
|
||||
private:
|
||||
float CreditsStartTimeSeconds = 0.0f;
|
||||
|
||||
void DrawCenteredText(
|
||||
FSlateWindowElementList& OutDrawElements,
|
||||
int32& LayerId,
|
||||
@@ -43,4 +47,29 @@ private:
|
||||
float Y,
|
||||
const FSlateFontInfo& Font,
|
||||
const FLinearColor& Color) const;
|
||||
|
||||
void DrawTextAt(
|
||||
FSlateWindowElementList& OutDrawElements,
|
||||
int32& LayerId,
|
||||
const FGeometry& AllottedGeometry,
|
||||
const FText& Text,
|
||||
const FVector2D& Position,
|
||||
float Width,
|
||||
const FSlateFontInfo& Font,
|
||||
const FLinearColor& Color) const;
|
||||
|
||||
void DrawCinematicCredits(
|
||||
FSlateWindowElementList& OutDrawElements,
|
||||
int32& LayerId,
|
||||
const FGeometry& AllottedGeometry) const;
|
||||
|
||||
void DrawCreditIllustration(
|
||||
FSlateWindowElementList& OutDrawElements,
|
||||
int32& LayerId,
|
||||
const FGeometry& AllottedGeometry,
|
||||
const FVector2D& Position,
|
||||
const FVector2D& Size,
|
||||
int32 IllustrationIndex,
|
||||
const FLinearColor& AccentColor,
|
||||
float Alpha) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user