A UI Razor component that displays a sequence of tutorial tips. It shows an image, text, a progress bar, and advances tips when the player presses the use key or when a timer expires, then marks that the local player has seen tips and deletes the panel.
@using System
@using System.Collections.Generic
@using Sandbox
@using Sandbox.UI
@namespace BrickJam.UI
@inherits Panel
<root>
<div class="container">
<div class="image" style="background-image: url(@(Tip.Image));" />
<span class="text">@Tip.Text</span>
<div class="footer">
<span>Press [E] for the next tip.</span>
<span class="count">@(tipIndex + 1) of @tips.Count</span>
</div>
<div class="bar"><div class="fill" style="width: @($"{Progress * 100f}%")"></div></div>
</div>
</root>
@code {
private const float TipTime = 15f;
private const string TipAction = "use";
private int tipIndex;
private TimeSince tipTime;
private readonly List<(string Image, string Text)> tips = new()
{
("ui/tutorial/1.png", "Loot as much stuff as possible while avoiding the level's enemy!"),
("ui/tutorial/2.png", "Avoid tripping by staying away from obstacles on the floor."),
("ui/tutorial/3.png", "Look for an exit to the store or next level."),
("ui/tutorial/4.png", "Sell your stuff at the shop by dropping everything behind the counter."),
("ui/tutorial/5.png", "Invite a few friends over and have fun!")
};
private (string Image, string Text) Tip => tips[Math.Clamp( tipIndex, 0, tips.Count - 1 )];
private float Progress => ((float)tipTime / TipTime).Clamp( 0f, 1f );
private void NextTip()
{
tipIndex++;
if ( tipIndex >= tips.Count )
{
if ( Player.Local is { } player )
{
player.SeenTips = true;
Player.DataChanged = true;
}
Delete();
return;
}
tipTime = 0;
}
public override void Tick()
{
if ( Input.Pressed( TipAction ) || tipTime >= TipTime )
NextTip();
}
protected override int BuildHash() => HashCode.Combine( tipIndex, (int)((float)tipTime * 4f) );
}
<style>
TutorialTips {
position: absolute;
top: 0; right: 10px;
height: 100%;
align-items: center;
pointer-events: none;
.container {
width: 600px;
flex-direction: column;
background-color: rgba(0,0,0,0.5);
font-size: 32px;
color: white;
text-shadow: 4px 4px 0px black;
padding: 12px;
.image {
width: 100%;
aspect-ratio: 1.3333;
background-size: 100%;
background-position: center;
background-repeat: no-repeat;
border: 2px solid white;
margin-bottom: 10px;
}
.text { padding-bottom: 10px; }
.footer {
flex-direction: row;
justify-content: space-between;
color: rgba(180,180,180,1);
font-size: 22px;
}
.bar {
margin-top: 10px;
height: 8px;
background-color: rgba(255,255,255,0.15);
.fill { height: 8px; background-color: rgba(255,220,80,1); }
}
}
}
</style>