Implement Designs in Studio

Implementing your designs is the process of creating your wireframes in Studio using both built-in and custom UI elements with scripts that trigger your UI contextually. This exciting step of the tutorial is where you get to see all of your designs and hard work come together into a cohesive set of workflows that are complete and ready for player interaction.

Using the sample laser tag experience .rbxl file as a reference, this section of the user interface design curriculum shows you how to bring your UI planning to life, including guidance on:

  • Retrieving asset IDs from the UI asset library so that you can recreate the sample laser tag experience's UI components.
  • Emulating various devices directly in Studio to see how your UI displays on different screens and aspect ratios.
  • Creating ScreenGui, SurfaceGui, and BillboardGui objects to display your UI on players' screens, part surfaces, and within the 3D space, respectively.

After you review the techniques in this section, you can apply them to your own projects to make exciting UI components that help players navigate what they can do within your experiences.

Get Asset Library

Asset libraries are collections of assets you can add into your inventory for easy access and reuse. The asset library you will use for your project from the Creator Store includes nine 2D individual UI element assets, and the final versions of the objective, blaster selector, and player info components you are creating in this section of the tutorial.

MultiBlaster Icon
rbxassetid://14309094777
SingleBlaster Icon
rbxassetid://14309094641
Pink Team Icon
rbxassetid://14309678581
Trapezoid
rbxassetid://14304828203
Upside Down Trapezoid
rbxassetid://14304827304
Green Team Icon
rbxassetid://14309678701
Fade
rbxassetid://14304826876
Multi-directional Fade
rbxassetid://14304827147
Blast Button Icon - Default
rbxassetid://18308375067
Blast Button Icon - Pressed
rbxassetid://18308372597
Crosshair Icon
rbxassetid://14400935532
Hit Marker Icon
rbxassetid://14401148777
Hexagon
rbxassetid://14462567943
Selection Arrow Icon
rbxassetid://14309187282
Border Fade
rbxassetid://14309518632

You can add most of the library to your inventory within Studio by clicking the Add to Inventory link in the following component. Once assets are within your inventory, you can reuse them in any project on the platform.


To get the asset library from your inventory into your experience:

  1. In the menu bar, select the View tab.

  2. In the Show section, click Toolbox. The Toolbox window displays.

  3. In the Toolbox window, click the Inventory tab. The My Models sort displays.

  4. Click the dropdown menu, then select the My Packages sort.

  5. Click the Final Screen UI Components tile, then in the Explorer window, select Completed Components, then drag them into the StarterGui service. You can now enable any of the final components to reference their design.

Emulate Devices

Studio's Device Emulator allows you to test how players will see and interact with your UI on various devices. This tool is a vital part of the implementation process because the aspect ratio of your viewport in Studio doesn't necessarily reflect the aspect ratio of the screens players use to access your experience, and it's important that your UI is both legible and accessible on every device.

For example, if you don't test your UI on a range of screen sizes, players with large screens may not be able to read your text or decipher your icons, and players with small screens may not be able to see the 3D space because your UI elements take up too much room on the display.

To emulate your screen to the smallest screen size:

  1. In the menu bar, select the Test tab.

  2. In the Emulation section, click Device. The viewport changes to reflect the aspect ratio of an average laptop.

    Device button indicated in Test tab
  3. In the resolution dropdown, select Actual Resolution. This allows you to see the true resolution of your UI elements on the device you're emulating.

  4. In the device dropdown, select the device with the smallest screen size that players can use to access your experience. While the best option varies according to the devices your experience supports, the sample laser tag experience tests with an iPhone 4S to verify how the UI looks with limited screen space.

Create ScreenGui Objects

To display UI elements on every player's screen, you can create a ScreenGui object in the StarterGui service. ScreenGui objects are the primary containers for on-screen UI, and the StarterGui service copies its contents to each player's PlayerGui container as they enter an experience.

You can create multiple ScreenGui objects to organize and display groupings of UI elements contextually throughout gameplay. For example, the sample laser tag experience includes five separate ScreenGui objects that are initially disabled until players meet different conditions during the main user flow of the experience:

  • HUDGui - Displays key information about the experience's gameplay when players are active in a round, such as the objective and each team's total points.
  • PickABlasterGui - Displays all blaster choices when players start or rejoin a round.
  • ForceFieldGui - Displays a hexagonal grid when players are selecting a blaster and while they are temporarily invincible.
  • OutStateGui - Displays a dark border around the screen when players are tagged out.
  • RoundResultsGui - Displays a dark overlay on top of the screen with information on which team won the round.

After you create a ScreenGui object, you can create and customize its child GuiObjects according to each container's purpose. To demonstrate, in the immediate sections that follow, you will learn how to implement UI elements for the three categories of information players need to know to be successful in the sample laser tag experience. You can adjust any part of the process to meet the specifications of your own experience.

To create a ScreenGui object:

  1. In the Explorer window, hover over the StarterGui service, then click the icon. A contextual menu displays.

  2. Insert a ScreenGui.

  3. Rename the ScreenGui according to the context of its child UI elements.

  4. Repeat this process for each grouping of UI elements you need to display on every player's screen.

Objective UI

Following the visual hierarchy best practices from Wireframe Your Layouts, this section teaches you how to implement all on-screen UI elements relating to the experience's objective. This grouping of UI elements is near the top of the screen because the objective and each team's points have the most significance on how to win the game.

For example, the sample provides an objective UI component that players reference to know what they need to do to be successful in a round. As players tag out enemy team members and earn points, this component keeps track of each team's score against the overall goal within the header's prompt. For a high-level review of all of the client and server scripts that work together to track points, see Track Points in the Gameplay Scripting Curriculum.

To exactly recreate the objective UI within the sample Laser Tag experience:

  1. Create a container for the entire component.

    1. Insert a Frame into the HUDGui ScreenGui object.

      1. In the Explorer window, navigate to the StarterGui service.
      2. Hover over its child HUDGui object, then click the ⊕ icon. A contextual menu displays.
      3. From the contextual menu, insert a Frame.
    2. Select the new Frame, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0 to set the frame's origin point in the top-middle of itself (50% from the left to the right of the frame, and 0% from the top to the bottom of the frame).
      2. Set BackgroundTransparency to 1 to make the frame's background completely transparent.
      3. Set Position to {0.5, 0},{0.03, 0} to set the frame near the top-middle of the screen (50% from the left to the right of the screen, and 3% from the top to the bottom of the screen so there is a little buffer).
      4. Set Size to {0.5, 0},{0.13, 0} so the frame's elements take up a large portion of the top of the screen to grab players' attention (50% horizontally, and 13% vertically).
      5. Set Name to Objective.
    3. (Optional) Insert a UIAspectRatioConstraint into Objective to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its UIAspectRatioConstraint.AspectRatio property to 7.

  2. Create a container for the objective's prompt objects.

    1. Insert a Frame into Objective.

    2. Select the new Frame, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0 to set the frame's origin point in the top-middle of itself (50% from the left to the right of the frame, and 0% from the top to the bottom of the frame).
      2. Set BackgroundTransparency to 1 to make the frame's background completely transparent.
      3. Set Position to {0.5, 0},{0, 0} to set the frame in the middle of the container (50% from the left to the right of the parent frame, and 0% from the top to the bottom of the parent frame).
      4. Set Size to {1, 0},{0.67, 0} so the selection UI components take up about more than half of the container from top to bottom (100% horizontally and 67% vertically of the parent frame).
      5. Set Name to ObjectiveDisplay.
  3. Create the title elements.

    1. Insert an ImageLabel into ObjectiveDisplay.

    2. Select the ImageLabel, then in the Properties window,

      1. Set AnchorPoint to 0.5, 1 to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set LayoutOrder to -1.
      4. Set Position to {0.5, 0},{0.34, 0} to set the label near the top-upper middle of the frame (50% from the left to the right of the parent frame, and 34% from the top to the bottom of the parent frame).
      5. Set Size to {0.46, 0},{0.34, 0} to widen the prompt area to almost half of the frame (46% horizontally and 34% vertically of the parent frame).
      6. Set Name to Header.
      7. Set Image to rbxassetid://14304828123 to display a trapezoid.
      8. Set ImageTransparency to 0.15 to make the header semi-transparent.
    3. (Optional) Insert a UIAspectRatioConstraint into the ImageLabel to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its UIAspectRatioConstraint.AspectRatio property to 13.781.

    4. Insert a TextLabel into Header to display a title.

    5. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set Position to {0.5, 0},{0.5, 0} to move the label to the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).
      4. Set Size to {0.62, 0},{0.55, 0} to widen the text space to more than half of the parent label (62% horizontally and 55% vertically of the parent label).
      5. Set Name to HeaderTextLabel.
      6. Set FontFace to Montserrat to fit the futuristic aesthetic.
      7. Set Weight to Medium to thicken the font.
      8. Set Text to OBJECTIVE.
      9. Enable TextScaled.
  4. Create the prompt elements.

    1. Insert an ImageLabel into ObjectiveDisplay.

    2. Select the ImageLabel, then in the Properties window,

      1. Set AnchorPoint to 0.5, 1 to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set Position to {0.5, 0},{1, 0} to move the label to the bottom-middle of its parent frame (50% from the left to the right of the parent frame, and 100% from the top to the bottom of the parent frame).
      4. Set Size to {0.89, 0},{0.66, 0} to widen the text space to almost the full width of the parent frame (89% horizontally and 66% vertically of the parent frame).
      5. Set Name to Body.
      6. Set Image to rbxassetid://14304827265 to display an upside-down trapezoid.
      7. Set ImageColor3 to 0, 0, 0 to tint the image black.
      8. Set ImageTransparency to 0.3 to make the header semi-transparent.
    3. (Optional) Insert a UIAspectRatioConstraint into the ImageLabel to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its UIAspectRatioConstraint.AspectRatio property to 13.781.

    4. Insert a TextLabel into Body to display a prompt.

    5. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set Position to {0.5, 0},{0.5, 0} to move the label to the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).
      4. Set Size to {0.85, 0},{0.39, 0} to widen the text space to more than half of the parent label (85% horizontally and 39% vertically of the parent label).
      5. Set Name to BodyTextLabel.
      6. Set FontFace to Montserrat to fit the futuristic aesthetic.
      7. Set Weight to Medium to thicken the font.
      8. Set TextColor3 to 255, 255, 255 to make the text white against the dark background.
      9. Set Text to Tag opposing players to score points! First team to %d points wins..
      10. Enable TextScaled.
  5. Create a container for the objective's team counters.

    1. Insert a Frame into Objective.

    2. Select the new Frame, then in the Properties window,

      1. Set AnchorPoint to 0.5, 1 to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the frame, and 100% from the top to the bottom of the frame).
      2. Set BackgroundTransparency to 1 to make the frame's background completely transparent.
      3. Set Position to {0.5, 0},{1, 0} to set the frame in the bottom-middle of the container (50% from the left to the right of the parent frame, and 100% from the top to the bottom of the parent frame).
      4. Set Size to {0.44, 0},{0.27, 0} so the selection UI components take up about less than half of the container from left to right (44% horizontally and 27% vertically of the parent frame).
      5. Set Name to TeamPointCounter.
  6. Create padding for the team counters.

    1. Insert a UIListLayout object into the frame from step 5.
    2. Select the UIListLayout object, then in the Properties window,
      1. Set Padding to 0.025, 0 to provide space between the future team counters.
      2. Set FillDirection to Horizontal so each team counter displays next to each other.
      3. Set HorizontalAlignment to Center so each team counter aligns to the middle of one another.
  7. Create the green team counter elements.

    1. Insert an ImageLabel into TeamPointCounter.

    2. Select the ImageLabel, then in the Properties window,

      1. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      2. Set Position to {0.5, 0},{1, 0} to move the label to the bottom-middle of its parent frame (50% from the left to the right of the parent frame, and 100% from the top to the bottom of the parent frame).
      3. Set Size to {0.5, 0},{1, 0} to widen the label to half width of the parent frame (50% horizontally and 100% vertically of the parent frame).
      4. Set Name to TeamACounter.
      5. Set Image to rbxassetid://14304826831 to display a directional fade.
      6. Set ImageColor3 to 88, 218, 171 to tint the image mint green.
    3. Configure a custom attribute to track that this label is for the green team.

      1. In the Properties window, navigate to the Attributes section, then click the plus icon. A pop-up dialog displays.
      2. In the Name field, input teamColor.
      3. In the Type dropdown menu, select BrickColor.
      4. Click the Save button.
      5. Set the new teamColor attribute to Mint.
    4. Insert a TextLabel into TeamACounter to display a prompt.

    5. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 1, 0.5 to set the new label's origin point in the right-middle of itself (100% from the left to the right of the label, and 50% from the top to the bottom of the label).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set Position to {0.95, 0},{0.5, 0} to move the label to the right of its parent label (95% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).
      4. Set Size to {0.85, 0},{0.39, 0} to widen the text space to more than half of the parent label (85% horizontally and 39% vertically of the parent label).
      5. Set FontFace to Montserrat to fit the futuristic aesthetic.
      6. Set Weight to Bold to thicken the font.
      7. Set TextColor3 to 255, 255, 255 to make the text white against the dark background.
      8. Set Text to -.
      9. Enable TextScaled.
      10. Set TextXAlignment to Right.
    6. Insert a UIStroke object into the TextLabel, then in the Properties window, set Color to 8, 78, 52 to outline the dash with a dark green stroke.

  8. Create the pink team counter elements.

    1. Duplicate TeamAICounter and its children.

    2. Select the duplicate TeamACounter, then in the Properties window,

      1. Set Name to TeamBCounter.
      2. Set Image to rbxassetid://14305849451 to display a directional fade in the opposite direction.
      3. Set ImageColor3 to 255, 170, 255 to tint the image carnation pink.
      4. Set the teamColor attribute to Carnation Pink.
    3. Select the duplicate TextLabel child of TeamBCounter, then in the Properties window,

      1. Set AnchorPoint to 0, 0.5 to set the new label's origin point in the left-middle of itself (0% from the left to the right of the label, and 50% from the top to the bottom of the label).
      2. Set Position to {0.05, 0},{0.5, 0} to move the label to the left of its parent label (5% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).
      3. Set TextXAlignment to Left.
    4. Select the duplicate UIStroke child of TeamBCounter, then in the Properties window, set Color to 158, 18, 94 to outline the dash with a dark pink stroke.

  9. Reference the following ReplicatedStorage scripts within the sample Laser Tag place file that programmatically update the objective prompt and track team points.

The following script requires a set of module scripts that work together to set up the main Heads Up Display (HUD), including setObjective and startSyncingTeamPoints. After a player joins a round and selects their blaster, this script ensures all HUD UI elements display appropriately for the player's state, device, and team status.


local Players = game:GetService("Players")
local setPlayerPortrait = require(script.setPlayerPortrait)
local setPlayerName = require(script.setPlayerName)
local startSyncingTeamColor = require(script.startSyncingTeamColor)
local setObjective = require(script.setObjective)
local setupTouchButtonAsync = require(script.setupTouchButtonAsync)
local startSyncingTeamPoints = require(script.startSyncingTeamPoints)
local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled)
local setupHitmarker = require(script.setupHitmarker)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("HUDGui")
setPlayerPortrait(gui)
setPlayerName(gui)
startSyncingTeamColor(gui)
setObjective(gui)
startSyncingTeamPoints(gui)
disableMouseWhileGuiEnabled(gui)
setupHitmarker(gui)
setupTouchButtonAsync(gui)

Blaster UI

Following the visual hierarchy best practices from Wireframe Your Layouts, this section teaches you how to implement all on-screen UI elements relating to the player's blaster. This grouping of UI elements takes up the majority of the screen space near the center of the screen because it acts as the focal point to draw players' attention to the action in 3D space, and it has the most significance for playing the game.

Crosshair

A crosshair is a UI element that informs players where they're going to make impact when they blast their weapon. This UI element is a vital gameplay requirement for first-person shooter experiences because players need to be able to accurately aim their blaster and tag out enemy team members.

Like most other experiences in the first-person shooter genre, the sample laser tag experience positions the crosshair in the center of the screen so players have something static to focus on while their avatar moves through the 3D space. In addition to reducing motion sickness, this placement allows the crosshair to be perceptible while also blending into the overall environment.

To exactly recreate the crosshair within the sample Laser Tag experience:

  1. Insert an ImageLabel into the HUDGui ScreenGui object.

    1. In the Explorer window, navigate to the StarterGui service.

    2. Hover over its child HUDGui object, then click the icon. A contextual menu displays.

    3. From the contextual menu, insert an ImageLabel.

  2. Select the new ImageLabel, then in the Properties window,

    1. Set Image to rbxassetid://14400935446.
    2. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of the label (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
    3. Set BackgroundTransparency to 1 to make the label's background completely transparent.
    4. Set Name to Crosshair.
    5. Set Position to {0.5,0},{0.5,0} to set the label in the middle of the screen.
    6. Set ScaleType to Fit so the image fits within its container and doesn't stretch on various screen sizes.
  3. (Optional) Insert a UIAspectRatioConstraint into Crosshair to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its UIAspectRatioConstraint.AspectRatio property to 0.895.

Hit Marker

A hit marker is a UI element that only displays when a blast makes impact with another player on the enemy team. Like the crosshair, this UI element is a vital gameplay requirement for first-person shooter experiences because it provides visual feedback of when players are successful in tagging out their opponents.

To exactly recreate the hit marker within the sample Laser Tag experience:

  1. Insert an ImageLabel into the Crosshair ImageLabel object.

    1. In the Explorer window, navigate to the StarterGui service.

    2. Hover over its child Crosshair object, then click the icon. A contextual menu displays.

    3. From the contextual menu, insert an ImageLabel.

  2. Select the new ImageLabel, then in the Properties window,

    1. Set Image to rbxassetid://14401148736 to display the rectangular hit marker icon.
    2. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of the label.
    3. Set BackgroundTransparency to 1 to make the label's background completely transparent.
    4. Set Position to {0.5,0},{0.5,0} to set the label in the middle of the screen.
    5. Set Name to Hitmarker.
    6. Set Size to {0.6, 0},{0.06, 0} to reduce the size of the rectangles around the middle of the crosshair.
    7. Set ImageTransparency to 1 to make the hit marker completely transparent. The scripts in the following step turn the transparency back to 0 every time a player's blast makes impact with another player on the enemy team.
  3. Reference the following ReplicatedStorage scripts within the sample Laser Tag place file that programmatically display the hit marker when a blast makes impact with a player on the enemy team.

The following script requires a set of module scripts that work together to set up the main Heads Up Display (HUD), including setupHitmarker. After a player joins a round and selects their blaster, this script ensures all HUD UI elements display appropriately for the player's state, device, and team status.


local Players = game:GetService("Players")
local setPlayerPortrait = require(script.setPlayerPortrait)
local setPlayerName = require(script.setPlayerName)
local startSyncingTeamColor = require(script.startSyncingTeamColor)
local setObjective = require(script.setObjective)
local setupTouchButtonAsync = require(script.setupTouchButtonAsync)
local startSyncingTeamPoints = require(script.startSyncingTeamPoints)
local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled)
local setupHitmarker = require(script.setupHitmarker)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("HUDGui")
setPlayerPortrait(gui)
setPlayerName(gui)
startSyncingTeamColor(gui)
setObjective(gui)
startSyncingTeamPoints(gui)
disableMouseWhileGuiEnabled(gui)
setupHitmarker(gui)
setupTouchButtonAsync(gui)

Blaster Selector

A blaster selector is a UI component that players use to select their blaster type before joining or rejoining a round. The sample laser tag experience provides two types of blasters: one that produces several beams with a wide, horizontal spread, and another that produces a single beam. The type of blaster that players select influences their strategy during the round, making this UI component an essential workflow for the overall experience.

The following steps detail how to create several containers for the different UI element groupings, a header with a prompt, the navigation and select buttons, and a blaster button prefab. The scripting logic for the overall component populates different visual characteristics into the blaster button prefab according to Configuration instances that represent each blaster type.

This setup allows you to create additional Configuration instances for more blaster types that automatically display correctly within the blaster selector without needing to create individual buttons within StarterGui.PickABlasterGui.

To exactly recreate the blaster selector within the sample Laser Tag experience:

  1. Create a container for the entire component.

    1. Insert a Frame into the PickABlaster ScreenGui object.

      1. In the Explorer window, navigate to the StarterGui service.
      2. Hover over its child PickABlaster object, then click the icon. A contextual menu displays.
      3. From the contextual menu, insert a Frame.
    2. Select the new frame, then in the Properties window,

      1. Set AnchorPoint to 0.5, 1 to set the frame's origin point in the bottom-middle of itself (50% from the left to the right of the frame, and 100% from the top to the bottom of the frame).
      2. Set BackgroundTransparency to 1 to make the frame's background completely transparent.
      3. Set Position to {0.5, 0},{0.9, 0} to set the frame near the bottom-middle of the screen (50% from the left to the right of the screen, and 92.4% from the top to the bottom of the screen).
      4. Set Size to {0.8, 0},{0.25, 0} so the blaster selector's UI components take up a large portion of the screen to grab players' attention (80% horizontally, and 25% vertically).
      5. Set Name to Component.
    3. (Optional) Insert a UIAspectRatioConstraint into Component to ensure the frame and its children UI elements' aspect ratio remains the same no matter the player's screen size. The sample sets its UIAspectRatioConstraint.AspectRatio property to 5.

  2. Create a container to hold UI element groupings.

    1. Insert a Frame into Component.

    2. Select the new frame, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the frames's origin point in the middle of itself (50% from the left to the right of the frame, and 50% from the top to the bottom of the frame).
      2. Set BackgroundTransparency to 1 to make the frame's background completely transparent.
      3. Set Position to {0.5, 0},{0.375, 0} to set the frame near the top-middle of the container (50% from the left to the right of the parent frame, and 37.5% from the top to the bottom of the parent frame).
      4. Set Size to {1, 0},{0.75, 0} so the selection UI components take up 3/4th of the container (100% horizontally and 75% vertically of the parent frame).
      5. Set Name to SelectionFrame.
  3. Create a prompt for the blaster selector.

    1. Insert an ImageLabel into SelectionFrame.

    2. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 1 to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set LayoutOrder to -1.
      4. Set Position to {0.5, 0},{0.22, 0} to set the label near the top-upper middle of the frame (50% from the left to the right of the parent frame, and 22% from the top to the bottom of the parent frame).
      5. Set Size to {0.45, 0},{0.22, 0} to widen the prompt area to almost half of the frame (45% horizontally and 22% vertically of the parent frame).
      6. Set Name to Header.
      7. Set Image to rbxassetid://14304828123 to display a trapezoid.
      8. Set ImageTransparency to 0.15 to make the header semi-transparent.
    3. (Optional) Insert a UIAspectRatioConstraint into the label to ensure the label's aspect ratio remains the same no matter the player's screen size. The sample sets its UIAspectRatioConstraint.AspectRatio property to 13.78.

    4. Insert a TextLabel into Header to display a prompt.

    5. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set Position to {0.5, 0},{0.5, 0} to move the label to the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).
      4. Set Size to {0.6, 0},{0.55, 0} to widen the text space to more than half of the parent label (60% horizontally and 55% vertically of the parent label).
      5. Set Name to HeaderTextLabel.
      6. Set FontFace to Montserrat to fit the futuristic aesthetic.
      7. Set Weight to Medium to thicken the font.
      8. Set Text to PICK A BLASTER.
      9. Enable TextScaled.
  4. Create the container for your blaster button container and selection arrows.

    1. Insert an ImageLabel into SelectionFrame.

    2. Select the new label, then in the Properties window,

      1. Remove the default Image value.
      2. Set AnchorPoint to 0.5, 1 to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label).
      3. Set BackgroundColor to 0, 0, 0 to make the label black.
      4. Set BackgroundTransparency to 0.3 to reduce the opacity of the label by 30%, and match all black UI elements in the experience.
      5. Set Position to {0.5, 0},{1, 0} to set the label to the bottom-middle of the frame (50% from the left to the right of the parent frame, and 100% from the top to the bottom of the parent frame).
      6. Set Size to {1, 0},{0.77, 0} to widen the label area to the space below the prompt (100% horizontally and 77% vertically of the parent frame).
    3. Round the corners of the container.

      1. Insert a UICorner object into the label.
      2. Select the new corner object, then in the Properties window, set CornerRadius to 0.075, 0 to round the corners.
  5. Create the container for your blaster buttons.

    1. Insert a Frame into the label from step 4.

    2. Select the new frame, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the new frame's origin point in the middle of itself (50% from the left to the right of the frame, and 50% from the top to the bottom of the frame).
      2. Set BackgroundTransparency to 1 to make the frame's background completely transparent.
      3. Set Position to {0.5, 0},{0.5, 0} to set the frame in the middle of its parent label (50% from the left to the right of the parent frame, and 50% from the top to the bottom of the parent frame).
      4. Set Size to {0.85, 0},{0.77, 0} to widen the frame area to most of the label (85% horizontally and 77% vertically of the parent label).
      5. Set Name to Container.
  6. Create padding for all future blaster buttons.

    1. Insert a UIListLayout object into the frame from step 5.
    2. Select the new layout object, then in the Properties window,
      1. Set Padding to 0.035, 0 to provide space between all future buttons.
      2. Set FillDirection to Horizontal so each button displays next to each other.
      3. Set both HorizontalAlignment and VerticalAlignment to Center so each button aligns to the middle of one another.
  7. Create the left navigation button.

    1. Insert an ImageButton object into the ImageLabel from step 4.

    2. Select the new button, then in the Properties window,

      1. Remove the default Image value.
      2. Set AnchorPoint to 0, 0.5 to set the new button's origin point in the left-middle of itself (0% from the left to the right of the button, and 50% from the top to the bottom of the button).
      3. Set BackgroundTransparency to 0.15 to provide visual feedback on hover that the button is selectable.
      4. Set Position to {0.02, 0},{0.5, 0} to provide padding to the left of the button from its parent container (2% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).
      5. Set Size to {0.04, 0},{0.33, 0} to make the selection button much smaller than the blaster buttons (4% horizontally and 33% vertically of the parent frame).
      6. Set Name to NavigationButtonLeft.
    3. Round the corners of the button.

      1. Insert a UICorner object into the button.
      2. Select the new corner object, then in the Properties window, set CornerRadius to 0.1, 0 to round the corners.
    4. Insert an ImageLabel object into the button.

    5. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
      2. Set Position to {0.45, 0},{0.5, 0} to set the label near the middle of its parent button (45% from the left to the right of the parent button, and 50% from the top to the bottom of the parent button). This value isn't in the middle because an arrow doesn't visually look like it's in the middle of the button at {0.5, 0},{0.5, 0}.
      3. Set Size to {0.8, 0},{0.8, 0} to widen the label area to space below the prompt (80% horizontally and 80% vertically of the parent frame).
      4. Set BackgroundTransparency to 1 to make the image's background completely transparent.
      5. Set Image to rbxassetid://14309187238.
      6. Set ScaleType to Fit.
  8. Create the right navigation button.

    1. Duplicate NavigationButtonLeft.

    2. Select the duplicate button, then in the Properties window,

      1. Set AnchorPoint to 1, 0.5 to set the new button's origin point in the right-middle of itself (100% from the left to the right of the button, and 50% from the top to the bottom of the button).
      2. Set Position to {0.98, 0},{0.5, 0} to provide padding to the right of the button from its parent container (98% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).
      3. Set Name to NavigationButtonRight.
    3. Select its ImageLabel child object.

      1. Set Rotation to 180 to flip the image.
      2. Set Position to {0.55, 0},{0.5, 0} to set the label near the middle of its parent button (55% from the left to the right of the parent button, and 50% from the top to the bottom of the parent button). This value isn't in the middle because an arrow doesn't visually look like it's in the middle of the button at {0.5, 0},{0.5, 0}.
  9. Create the SELECT button.

    1. Insert an ImageButton into Component. Notice how this process keeps the select button separate from SelectionFrame so that you can add padding between the main part of the component from the select button.

    2. Select the new button, then in the Properties window,

      1. Remove the default Image value.
      2. Set AnchorPoint to 0.5, 1 to set the new button's origin point in the bottom-middle of itself (50% from the left to the right of the button, and 100% from the top to the bottom of the button).
      3. Set BackgroundTransparency to 0.15 to provide visual feedback on hover that the button is selectable.
      4. Set Position to {0.5, 0},{0.99, 0} to set the button near the bottom middle of its container (50% from the left to the right of the parent frame, and 99% from the top to the bottom of the parent frame).
      5. Set Size to {0.17, 0},{0.18, 0} to length the button underneath the blaster buttons (17% horizontally and 18% vertically of the parent frame).
      6. Set Name to SelectButton.
    3. Round the corners of the button.

      1. Insert a UICorner object into the button.
      2. Select the new corner object, then in the Properties window, set CornerRadius to 0.2, 0 to round the corners.
    4. Insert a TextLabel object into the button so you can display a call to action.

    5. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set Position to {0.5, 0},{0.5, 0} to move the label to the middle of the button (50% from the left to the right of the parent button, and 50% from the top to the bottom of the parent button).
      4. Set Size to {0.9, 0},{0.55, 0} to widen the text space to almost all of the width of the parent label (90% horizontally and 55% vertically of the parent label).
      5. Set Name to SelectTextLabel.
      6. Set FontFace to Montserrat to fit the futuristic aesthetic.
      7. Set Weight to Medium to thicken the font.
      8. Set Text to SELECT.
      9. Enable TextScaled.
  10. Create a blaster button prefab.

    1. In the ReplicatedStorage service, create a folder structure to organize your UI objects. The sample uses an Instances folder with a child Guis folder.
    2. Insert an ImageButton object into the Guis folder.
    3. Select the new button, then in the Properties window,
      1. Remove the default Image value.
      2. Set AnchorPoint to 0.5, 0.5 to set the new button's origin point in the middle of itself (50% from the left to the right of the button, and 50% from the top to the bottom of the button).
      3. Set BackgroundTransparency to 0.65 to provide visual feedback that the button isn't in focus. Scripts in step 12 provide programmatic visual feedback when the button is in focus.
      4. Set LayoutOrder to 2.
      5. Set Name to BlasterButtonPrefab.
      6. Set Size to {0.8, 0},{0.8, 0}.
      7. Set ImageTransparency to 1 to make the image completely transparent.
    4. Insert a UIAspectRatioConstraint into BlasterButtonPrefab to ensure the button's aspect ratio remains the same within the component no matter the player's screen size.
    5. Round the corners of the button.
      1. Insert a UICorner object into BlasterButtonPrefab.
      2. Select the UICorner, then in the Properties window, set CornerRadius to 0.05, 0 to round the corners.
    6. Insert an ImageLabel into BlasterButtonPrefab.
    7. Select the new label, then in the Properties window,
      1. Remove the default Image value.
      2. Set AnchorPoint to 0.5, 0.5 to set the new label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
      3. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      4. Set Position to {0.52, 0},{0.497, 0} to set the label near the middle of its parent button (52% from the left to the right of the parent button, and 49.7% from the top to the bottom of the parent button). This value isn't in the middle because the blaster doesn't visually look like it's in the middle of the button at {0.5, 0},{0.5, 0}.
      5. Set Size to {1.20, 0},{0.9, 0} to widen the label area outside of the button (120% horizontally and 90% vertically of the parent button).
      6. Set ScaleType to Fit.
  11. Reference the following ReplicatedStorage scripts within the sample Laser Tag place file that programmatically display buttons for each blaster, scale the buttons when a player selects a button that isn't in focus, and attach a player's blaster selection to their avatar.

The following script requires a set of scripts that work together to create the blaster selector. When a player joins the experience or respawns back into a round after their health reaches zero, this script activates all of the blaster selector's UI elements until the player makes their selection.


local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local GuiAttribute = require(ReplicatedStorage.GuiAttribute)
local setupBlasterButtons = require(script.setupBlasterButtons)
local connectResetSelectionOnEnabled = require(script.connectResetSelectionOnEnabled)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("PickABlasterGui")
setupBlasterButtons(gui)
connectResetSelectionOnEnabled(gui)
gui:SetAttribute(GuiAttribute.selectedIndex, 1)

Blast Button

A blast button is a UI component that players use to blast their blaster if they are accessing the experience through a mobile or tablet device. The sample laser tag experience uses a blaster button with an icon that depicts both a crosshair and a blast to communicate the button's function without text.

To exactly recreate the blast button within the sample Laser Tag experience:

  1. Insert an ImageButton into the HUDGui ScreenGui object.

    1. In the Explorer window, navigate to the StarterGui service.

    2. Hover over its child HUDGui object, then click the icon. A contextual menu displays.

    3. From the contextual menu, insert an ImageButton.

  2. In the viewport, move the button to where a player's thumb naturally rests so you can get a visual sense of what the button will look like on a player's device, then in the Properties window,

    1. Set Image to rbxassetid://18308375035 to display the blast button icon.
    2. Set PressedImage to rbxassetid://18308372558 to display an inverted version of the blast button icon when a player presses the button.
    3. Set BackgroundTransparency to 1 to make the label's background completely transparent.
    4. Set Name to BlastButton.
    5. Set ScaleType to Fit so the image fits within its container and doesn't stretch on various screen sizes.
    6. Set ImageTransparency to 0.3 to reduce the opacity of the label so that it matches all black UI elements in the experience.
  3. Insert a UIAspectRatioConstraint into BlastButton to ensure the button's aspect ratio remains the same no matter the player's screen size.

  4. Reference the following ReplicatedStorage scripts within the sample Laser Tag place file that programmatically display the blaster button when a player is using touch input on a device that accepts touch controls.

The following script requires a set of module scripts that work together to set up the main Heads Up Display (HUD), including setupTouchButtonAsync. After a player joins a round and selects their blaster, this script ensures all HUD UI elements display appropriately for the player's state, device, and team status.


local Players = game:GetService("Players")
local setPlayerPortrait = require(script.setPlayerPortrait)
local setPlayerName = require(script.setPlayerName)
local startSyncingTeamColor = require(script.startSyncingTeamColor)
local setObjective = require(script.setObjective)
local setupTouchButtonAsync = require(script.setupTouchButtonAsync)
local startSyncingTeamPoints = require(script.startSyncingTeamPoints)
local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled)
local setupHitmarker = require(script.setupHitmarker)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("HUDGui")
setPlayerPortrait(gui)
setPlayerName(gui)
startSyncingTeamColor(gui)
setObjective(gui)
startSyncingTeamPoints(gui)
disableMouseWhileGuiEnabled(gui)
setupHitmarker(gui)
setupTouchButtonAsync(gui)

Player UI

Following the visual hierarchy best practices from Wireframe Your Layouts, this section teaches you how to implement all on-screen UI elements relating to the state of the player. This grouping of UI elements is near the sides of the screen because players can comprehend this peripheral information without diverting their attention from the gameplay.

Player Indicator

A player indicator is a UI component that players reference to quickly decipher what team they belong to as soon as they spawn into their team's spawn zone. The sample laser tag experience provides two versions of the player indicator depending on if the player is on the green or pink team.

Green Team
Pink Team

Following the guidance from Select a Color Theme, both versions of the player indicator combine the team color with a unique, simple icon with minimal detail so that they remain legible on small screens. Providing two forms of visual feedback is important because it helps to keep the design accessible for players with colorblindness.

To exactly recreate the player indicator component within the sample Laser Tag experience:

  1. Insert a Frame into the HUDGui ScreenGui object.

    1. In the Explorer window, navigate to the StarterGui service.
    2. Hover over its child HUDGui object, then click the ⊕ icon. A contextual menu displays.
    3. From the contextual menu, insert a Frame.
  2. Select the new Frame, then in the Properties window,

    1. Set AnchorPoint to 0, 1 to set the frame's origin point in the bottom-middle of itself (0% from the left to the right of the frame, and 100% from the top to the bottom of the frame).

    2. Set BackgroundTransparency to 1 to make the label's background completely transparent.

    3. Set Name to PlayerDisplay.

    4. Set Position to {0.02, 0},{0.97, 0} to set the frame near the bottom-left of the screen.

    5. Set Size to {0.23, 0},{0.08, 0} to both shorten and widen the frame.

    6. Enable ClipsDescendants to trim child GuiObjects that extend beyond the frame.

  3. Create the polygonal shape.

    1. Insert an ImageLabel into PlayerDisplay.

    2. Select the new label, then in the Properties window,

      1. Set Image to rbxassetid://14304828123 to display the trapezoid icon.
      2. Set AnchorPoint to 1, 1 to set the label's origin point in the bottom-right of itself (100% from the left to the right of the label, and 100% from the top to the bottom of the label).
      3. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      4. Set Name to Block.
      5. Set Position to {1,0},{1,0} to set the label to the right side of the frame.
      6. Set Size to {1.858, 0},{0.581, 0} to widen the label outside of the frame, and shorten it to a little over half the length of the frame.
      7. Set ImageTransparency to 0.15 to make the label slightly transparent.
      8. Set ScaleType to Fit so the image fits within its container and doesn't stretch on various screen sizes.
      1. Insert a UIAspectRatioConstraint into Block to ensure the label and its children UI elements' aspect ratio remains the same no matter the player's screen size.
      2. Select the new constraint, then in the Properties window, set AspectRatio to 13.78.
  4. Create the box for the player's portrait.

    1. Insert an ImageLabel into PlayerDisplay.

    2. Select the new label, then in the Properties window,

      1. Remove the placeholder assetID within the Image property. The scripts in step 7 programmatically insert the player's portrait into the image label.
      2. Set AnchorPoint to 0, 1 to set the label's origin point in the bottom-left of itself (0% from the left to the right of the label, and 100% from the top to the bottom of the label).
      3. Set BackgroundColor3 to 0, 0, 0 to set the label's background color to black.
      4. Set BackgroundTransparency to 0.3 to reduce the opacity of the label by 30%, and match all black UI elements in the experience.
      5. Set Name to PlayerPortrait.
      6. Set Position to {0.11, 0},{1, 0} to set the label to the left side of the polygonal shape.
      7. Set Size to {0.23, 0},{1, 0} to shrink the label.
      8. Set ImageTransparency to 0.15 to make the label slightly transparent.
      9. Set ScaleType to Fit so the image fits within its container and doesn't stretch on various screen sizes.
      1. Insert a UIAspectRatioConstraint into PlayerPortrait to ensure the label and its children UI elements' aspect ratio remains the same no matter the player's screen size.
      2. Insert a UICorner into PlayerPortrait, then in the Properties window, set CornerRadius to 0.05, 0 to slightly round the corners.
  5. Create the text label for the player's name.

    1. Insert a TextLabel object into PlayerDisplay.

    2. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0, 0.5 to set the new button's origin point in the left-middle of itself (0% from the left to the right of the button, and 50% from the top to the bottom of the button).
      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
      3. Set Name to PlayerNameTextLabel.
      4. Set Position to {0.35, 0},{0.72, 0} to set the label to the right side of its container (35% from the left to the right of the parent label, and 72% from the top to the bottom of the parent label).
      5. Set Size to {0.52, 0},{0.3, 0} so the text can take up most of the polygonal shape area (52% horizontally and 30% vertically of the parent frame).
      6. Set FontFace to Montserrat to fit the futuristic aesthetic.
      7. Set Weight to Bold to thicken the font.
      8. Remove the placeholder text within the Text property. The scripts in step 7 programmatically insert the player's name into the text label.
      9. Enable TextScaled.
      10. Set TextXAlignment to Left.
  6. Create the team icons and colors that display to the left of the player's portrait.

    1. Insert a Folder into PlayerDisplay, then rename it TeamIcons.

    2. Create the green team icon and color.

      1. Insert an ImageLabel into TeamIcons.
      2. Select the new label, then in the Properties window,
        1. Set AnchorPoint to 0, 1 to set the label's origin point in the bottom-left of itself (0% from the left to the right of the label, and 100% from the top to the bottom of the label).
        2. Set BackgroundColor3 to 88, 218, 171 to set the label's background color to mint green.
        3. Set Name to TeamAIcon.
        4. Set Position to {0, 0},{1, 0} to set the label to the left side of the frame.
        5. Set Size to {0.135, 0},{0.58, 0} to shrink the label to the left of the player portrait.
        6. Set ImageTransparency to 1 to make the label transparent.
      1. Configure a custom attribute to track that this label is for the green team. This step is very important for the scripts in step 7.
        1. In the Properties window, navigate to the Attributes section, then click the plus icon. A pop-up dialog displays.
        2. In the Name field, input teamColor.
        3. In the Type dropdown menu, select BrickColor.
        4. Click the Save button.
        5. Set the new teamColor attribute to Mint.
      2. Insert a UIAspectRatioConstraint into TeamAIcon to ensure the label and its children UI elements' aspect ratio remains the same no matter the player's screen size.
      3. Create the icon.
        1. Insert an ImageLabel into TeamAIcon.
        2. Select the new label, then in the Properties window,
          1. Set Image to rbxassetid://14309678670 to display the green team icon.
          2. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
          3. Set BackgroundTransparency to 1 to make the label's background completely transparent.
          4. Set Name to Icon.
          5. Set Position to {0.5, 0},{0.5, 0} to set the label to the middle of its parent label.
          6. Set Size to {0.7, 0},{0.6, 0} to shrink the label.
          7. Set ScaleType to Fit so the image fits within its container and doesn't stretch on various screen sizes.
    3. Create the pink team icon and color.

      1. Duplicate TeamAIcon and its children.
      2. Select the duplicate TeamAIcon, then in the Properties window,
        1. Set BackgroundColor3 to 255, 170, 255 to set the label's background color to carnation pink.
        2. Set Name to TeamBIcon.
        3. Set the teamColor attribute to Carnation Pink.
        4. Select the duplicate Icon child of TeamBIcon, then in the Properties window, set Image to rbxassetid://14309678549 to display the pink team icon.
  7. Reference the following ReplicatedStorage scripts within the sample Laser Tag place file that programmatically display the player indicator with the appropriate team color and icon while a player is active in a round.

The following script requires a set of module scripts that work together to set up the main Heads Up Display (HUD), including startSyncingTeamColor, setPlayerName, and setPlayerPortrait. After a player joins a round and selects their blaster, this script ensures all HUD UI elements display appropriately for the player's state, device, and team status.


local Players = game:GetService("Players")
local setPlayerPortrait = require(script.setPlayerPortrait)
local setPlayerName = require(script.setPlayerName)
local startSyncingTeamColor = require(script.startSyncingTeamColor)
local setObjective = require(script.setObjective)
local setupTouchButtonAsync = require(script.setupTouchButtonAsync)
local startSyncingTeamPoints = require(script.startSyncingTeamPoints)
local disableMouseWhileGuiEnabled = require(script.disableMouseWhileGuiEnabled)
local setupHitmarker = require(script.setupHitmarker)
local localPlayer = Players.LocalPlayer
local gui = localPlayer.PlayerGui:WaitForChild("HUDGui")
setPlayerPortrait(gui)
setPlayerName(gui)
startSyncingTeamColor(gui)
setObjective(gui)
startSyncingTeamPoints(gui)
disableMouseWhileGuiEnabled(gui)
setupHitmarker(gui)
setupTouchButtonAsync(gui)

Force Field Screen

A force field screen is a UI element that overlays the viewport to inform players they're safe from enemy team fire while joining or rejoining a round. Following the aesthetic guidelines for icons from Choose an Art Style, the sample laser tag experience utilizes a semi-transparent hexagonal pattern to symbolize a force field. This design decision not only reinforces the overall futuristic art style for all UI in the experience, but it also communicates the player's state without any text or additional guidance.

To exactly recreate the force field screen within the sample Laser Tag experience:

  1. Insert an ImageLabel into the ForceFieldGui ScreenGui object.

    1. In the Explorer window, navigate to the StarterGui service.

    2. Hover over its child ForceFieldGui object, then click the icon. A contextual menu displays.

    3. From the contextual menu, insert an ImageLabel.

  2. Select the new label, then in the Properties window,

    1. Set Image to rbxassetid://14462567888.

    2. Set BackgroundTransparency to 0.8 to make the force field translucent.

    3. Set Size to {1, 0},{1, 0} to make the image fill the entire screen (100% horizontally and 100% vertically of the parent ScreenGui).

    4. Set ScaleType to Tile to make the hexagon tile across the entire screen.

    5. Set TileSize to {0, 104},{0, 180}.

  3. Insert a UIGradient object into the label.

  4. Select the new gradient object, then in the Properties window,

    1. Set Color to a color sequence that starts blue, turns white, then turns blue again.

      1. Set Color to 120, 192, 250 to apply a light blue hue to all of the hexagons.

      2. Click the Color property, then click the button. A color sequence pop-up displays.

        Each triangle on the bottom axis of the color sequence is a keypoint that determines the color value of the property at that point of the image from left to right.

      3. Click and drag on the color sequence until you reach a Time value of 0.05, then click the small square next to Color to open the Colors pop-up window.

      4. Select a bright white, then close the pop-up window.

      5. Click and drag on the color sequence until you reach a Time value of 0.95, then open the Colors pop-up window again, and select the same color white as before.

    2. Set Rotation to 225 to make the blue part of your color sequence display in the top-left and bottom-right corners.

    3. Set Transparency to a number sequence that makes the force field look like it's shimmering.

      1. Click the Transparency property, then click the button. A number sequence pop-up displays. Each square at the start and end of the number sequence is a keypoint that determines the transparency value of the property at that point of the image from left to right.

      2. Set the following time and value properties throughout the number sequence:

      • Time = 0, Value = 0.25
      • Time = .101, Value = 0.875
      • Time = .183, Value = 0
      • Time = .3, Value = 1
      • Time = .7, Value = 1
      • Time = 1, Value = 0.9
  5. Duplicate the ImageLabel from step 2.

  6. Select the UIGradient object within the duplicate label, then in the Properties window,

    1. Set Rotation to -45 to flip the image so that it nearly mirrors each other along the Y axis.

    2. Modify Transparency to make the shimmer look more organic.

      1. Click the Transparency property, then click the button. A number sequence pop-up displays.
      2. Select the third keyframe, then click the Delete button.
  7. Reference the following ReplicatedStorage scripts within the sample Laser Tag place file that programmatically display the force field screen while a player joins or rejoins a round.

The following ReplicatedStorage.ForceFieldClientVisuals client script substitutes the default ForceField visual with StarterGui.ForceFieldGui. When players load into an experience and spawn on a SpawnLocation with a Duration property that is greater than 0, the default behavior in every experience is to provide their avatar with a protective blue orb that momentarily prevents them from losing health.

This script starts by listening to when the ForceField is added to a character, disables the default first-person force field visuals, then enables the ForceFieldGui ScreenGui object. Note that this does not impact third-person visuals when players look at other players respawning back into the experience.

First-person force field visuals include a futuristic hexagonal grid on the perimeter of the screen.
First-person force field visuals
Third-person force field visuals include a blue sparkling orb around the player spawning into the experience.
Third-person force field visuals

local Players = game:GetService("Players")
local localPlayer = Players.LocalPlayer
local function onCharacterAddedAsync(character: Model)
local forceField = character:WaitForChild("ForceField", 3)
if not forceField then
-- If the player spawns at a spawn point with ForceField disabled
return
end
forceField.Visible = false
localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = true
forceField.Destroying:Wait()
localPlayer.PlayerGui.ForceFieldGui.Enabled = false
end
if localPlayer.Character then
onCharacterAddedAsync(localPlayer.Character)
end
localPlayer.CharacterAdded:Connect(onCharacterAddedAsync)

Respawn Screen

A respawn screen is a UI element that dims the viewport to inform players that they have been tagged out, and that the server is in the process of respawning them back to their spawn zone. This UI element is important because it gives players time to process that they've been tagged out, and strategize their next move before they rejoin the active round.

For more information on custom respawning behavior in the sample laser tag experience, see Respawn Characters from the Gameplay Scripting curriculum.

To exactly recreate the respawn screen within the sample Laser Tag experience:

  1. Create the center information banner.

    1. Insert an ImageLabel into the OutStateGui ScreenGui object.

      1. In the Explorer window, navigate to the StarterGui service.

      2. Hover over its child OutStateGui object, then click the icon. A contextual menu displays.

      3. From the contextual menu, insert an ImageLabel.

    2. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the new button's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).

      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.

      3. Set Position to {0.5, 0},{0.5, 0} to set the label in the middle of its container (50% from the left to the right of the parent ScreenGui, and 50% from the top to the bottom of the parent ScreenGui).

      4. Set Size to {0.48, 0},{0.06, 0} to widen the label (48% horizontally and 6% vertically of the parent ScreenGui).

      5. Set Name to Block.

      6. Set Image to rbxassetid://14304827265 to make the image a trapezoid.

      7. Set ImageColor to 0,0,0 to make the trapezoid black.

      8. Set ImageTransparency to 0.3 to reduce the opacity of the label by 30%, and match all black UI elements in the experience.

    3. Insert a UIAspectRatioConstraint into Block to ensure the label and its children UI elements' aspect ratio remains the same no matter the player's screen size.

    4. Select the new constraint, then in the Properties window, set AspectRatio to 13.78.

    5. Insert a TextLabel into Block for the informational text.

    6. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).

      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.

      3. Set Position to {0.5, 0},{0.5, 0} to set the label in the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).

      4. Set Size to {.85, 0},{0.55, 0} so the text can take up most of the trapezoid area (85% horizontally and 55% vertically of the parent label).

      5. Set Name to BodyTextLabel.

      6. Set FontFace to Montserrat to fit the futuristic aesthetic.

      7. Set Weight to Bold to thicken the font.

      8. Set Text to Respawning….

      9. Set TextColor3 to 255, 255, 255 to make the text white.

      10. Enable TextScaled.

  2. Create the header.

    1. Insert an ImageLabel into Block.

    2. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 1 to set the label's origin point in the bottom-middle of itself (50% from the left to the right of the label, and 100% from the top to the bottom of the label).

      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.

      3. Set Position to {0.5, 0},{0, 0} to set the label at the top-middle of its parent label (50% from the left to the right of the parent label, and 0% from the top to the bottom of the parent label).

      4. Set Size to {0.46, 0},{0.56, 0} to widen the label (46% horizontally and 56% vertically of the parent label).

      5. Set Name to Header.

      6. Set Image to rbxassetid://14304826985 to make the image a multi-directional fade.

      7. Set ImageColor to 245, 46, 46 to make the fade red to signify that the player is temporarily inactive while they're tagged out of the round.

    3. Insert a TextLabel into Header for the informational text.

    4. Select the new label, then in the Properties window,

      1. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).

      2. Set BackgroundTransparency to 1 to make the label's background completely transparent.

      3. Set Position to {0.5, 0},{0.5, 0} to set the label in the middle of its parent label (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).

      4. Set Size to {.85, 0},{0.55, 0} so the text can take up most of the fade area (85% horizontally and 55% vertically of the parent label).

      5. Set Name to HeaderTextLabel.

      6. Set FontFace to Montserrat to fit the futuristic aesthetic.

      7. Set Weight to Black to thicken the font.

      8. Set Text to TAGGED - YOU'RE OUT!

      9. Set TextColor3 to 255, 255, 255 to make the text white.

      10. Enable TextScaled.

  3. Create the fade around the borders of the screen.

    1. Insert an ImageLabel into OutStateGui.
    2. Select the new label, then in the Properties window,
      1. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
      2. Set BackgroundColor3 to 0,0,0 to set the label's background color to black.
      3. Set BackgroundTransparency to 0.5 to make the label's background halfway transparent.
      4. Set Position to {0.5, 0},{0.5, 0} to set the label at the middle of its container (50% from the left to the right of the parent ScreenGui, and 50% from the top to the bottom of the parent ScreenGui).
      5. Set Size to {1, 0},{1, 0} to widen the label to the whole screen (100% horizontally and 100% vertically of the parent ScreenGui).
      6. Set ZIndex to -1 to display the fade behind the other UI elements.
      7. Set Name to Header.
      8. Set Image to rbxassetid://14309518613 to make the image a border fade.
      9. Set ImageTransparency to 0.1 to make the fade slightly translucent.
  4. Reference the following ReplicatedStorage script within the sample Laser Tag place file that programmatically displays the respawn screen when a player's health reaches zero, and they're in the process of respawning back to their team's spawn zone.

The following ReplicatedStorage.PlayerStateHandler client script contains functions that trigger different types of behavior according to the playerState attribute. All event responses are logically grouped together in this script because they require similar behavior of enabling or disabling player controls, camera movement, and which UI layer is visible.

When a player's health reaches zero, their playerState becomes TaggedOut, which triggers the onTaggedOut() function. onTaggedOut() immediate triggers the following behavior:

  • The player can't move in the arena.
  • The player can't move their camera.
  • The player can't use their blaster.
  • The StarterGui.OutStateGui becomes exclusively enabled.

When the player respawns, their playerState becomes SelectingBlaster, which triggers the onSelectingBlaster() function. onSelectingBlaster() then exclusively enables the StarterGui.PickABlasterGui, which automatically disables the respawn screen. For more information on these conditions, see Handle Client State from the Gameplay Scripting curriculum.


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local PlayerState = require(ReplicatedStorage.PlayerState)
local PlayerAttribute = require(ReplicatedStorage.PlayerAttribute)
local BlasterState = require(ReplicatedStorage.Blaster.BlasterState)
local togglePlayerMovement = require(script.togglePlayerMovement)
local togglePlayerCamera = require(script.togglePlayerCamera)
local scheduleDestroyForceField = require(ReplicatedStorage.scheduleDestroyForceField)
local localPlayer = Players.LocalPlayer
local playerGui = localPlayer.PlayerGui
local guiLayers = {
playerGui:WaitForChild("HUDGui"),
playerGui:WaitForChild("OutStateGui"),
playerGui:WaitForChild("PickABlasterGui"),
}
-- Disable all UI Layers except the given exception
local function setGuiExclusivelyEnabled(enabledGui: ScreenGui?)
-- guiLayers contains a list of the guis that should be set exclusively.
for _, screenGui in guiLayers do
screenGui.Enabled = screenGui == enabledGui
end
end
local function onSelectingBlaster()
-- Enable the camera so players can look around while selecting a blaster
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
-- Disable blaster while selecting a blaster
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
local function onPlaying()
-- Enable player movement after picking a blaster
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
-- Enable blaster while playing
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
-- Schedule the destroy force field logic when the player begins playing
scheduleDestroyForceField()
end
local function onTaggedOut()
-- Disable controls while tagged out
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- Disable blaster while tagged out
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
local function onInLobby()
-- Enable controls while in the lobby
togglePlayerMovement(true)
togglePlayerCamera(true)
-- Hide all HUD while in the lobby
setGuiExclusivelyEnabled(nil)
-- Disable blaster while in the lobby
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
local function onPlayerStateChanged(newPlayerState: string)
if newPlayerState == PlayerState.SelectingBlaster then
onSelectingBlaster()
elseif newPlayerState == PlayerState.Playing then
onPlaying()
elseif newPlayerState == PlayerState.TaggedOut then
onTaggedOut()
elseif newPlayerState == PlayerState.InLobby then
onInLobby()
else
warn(`Invalid player state ({newPlayerState})`)
end
end
-- Handle the initial player state if set
local initialPlayerState = localPlayer:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(initialPlayerState)
local function updateFromPlayerState()
onPlayerStateChanged(localPlayer:GetAttribute(PlayerAttribute.playerState))
end
-- Handle future player state updates
localPlayer:GetAttributeChangedSignal(PlayerAttribute.playerState):Connect(updateFromPlayerState)
-- Make sure changes are still applied after respawning
localPlayer.CharacterAdded:Connect(updateFromPlayerState)

Create SurfaceGui Objects

To display UI on a part's surface in the 3D space that responds to scripting logic for each individual player, you can parent a SurfaceGui object to the part that you want to display your UI within the ReplicatedStorage service. This technique ensures your UI and its scripting logic are available to both the server and each player's client.

SurfaceGui objects contain all GuiObjects that display on a part's surface in the 3D space. The sample laser tag experience only includes one instance of a SurfaceGui object: the cooldown meter that displays over each player's blaster. This object needs scripting logic for every player because it actively responds to each individual player's input, and provides visual feedback of when they can blast their blaster again.

To create a SurfaceGui object:

  1. In the Explorer window, hover over the ReplicatedStorage service, then click the icon. A contextual menu displays.

  2. From the contextual menu, insert a Part object.

  3. Insert a ScreenGui object into the part.

  4. Rename the SurfaceGui according to the context of its child UI elements.

  5. Repeat this process for every UI element you need to display on a part's surface in the 3D space.

Cooldown Meter

A cooldown meter is a UI component that informs players how long they have to wait before they're able to blast their blaster again. This slight pause prevents players from being able to blast as quickly as they can click or press a button, which is unrealistic for laser tag gameplay.

To exactly recreate the cooldown meter within the sample Laser Tag experience:

  1. Create a part to hold your SurfaceGui object.

    1. In the Explorer window, hover over the Workspace, then click the ⊕ icon. A contextual menu displays.
    2. From the contextual menu, insert a block part. This is a temporary location for the part so that you can visualize the changes in each step of the process.
  2. Position and orient the part around the position of where a player's character would hold their blaster, then in the Properties window,

    1. Set Transparency to 1 to make the part completely transparent.

    2. Set Name to CooldownBarPrefab.

    3. Set Size to 0.169, 0.027, 2.537 to scale the part to a size about the length of the blaster.

    4. Disable CanCollide and CanQuery.

  3. Insert a SurfaceGui into CooldownBarPrefab.

  4. Select the new SurfaceGui, then in the Properties window,

    1. Set Face to Top so the UI displays facing upward.

    2. Set LightInfluence and MaxDistance to 0.

    3. Set PixelsPerStud to 200.

  5. Create the black bar.

    1. Insert an ImageLabel into the SurfaceGui.

    2. Select the new label, then in the Properties window,

      1. Remove the default Image value.
      2. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
      3. Set BackgroundColor3 to 0,0,0 to set the label's background color to black.
      4. Set BackgroundTransparency to 0.4 to make the label's background semi-transparent.
      5. Set Position to {0.5, 0},{0.5, 0} to set the label at the middle of its container (50% from the left to the right of the parent SurfaceGui, and 50% from the top to the bottom of the parent SurfaceGui).
      6. Set Size to {1, 0},{1, 0} to widen the label to the whole part (100% horizontally and 100% vertically of the parent SurfaceGui).
      7. Set Name to Container.
  6. Round the corners of the container.

    1. Insert a UICorner object into Container.

    2. Select the UICorner, then in the Properties window, set CornerRadius to 0.15, 0 to slightly round the corners.

  7. Create the red bar.

    1. Insert an ImageLabel into Container.
    2. Select the new label, then in the Properties window,
      1. Remove the default Image value.
      2. Set AnchorPoint to 1, 0.5 to set the label's origin point in the right-middle of itself (100% from the left to the right of the label, and 50% from the top to the bottom of the label).
      3. Set BackgroundColor3 to 172, 13, 13 to set the label's background color to a dark red.
      4. Set BackgroundTransparency to 0.2 to make the label's background slightly transparent.
      5. Set Name to Bar.
      6. Set Position to {1, 0},{0.5, 0} to set the label at the right-middle of its container (100% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).
      7. Set Size to {0, 0},{1, 0} to lengthen the label to the top of its parent label (0% horizontally and 100% vertically of the parent label). This step is also beneficial for the tweening behavior that occurs in the scripts in step 8.
  8. Round the corners of the label.

    1. Insert a UICorner object into Bar.
    2. Select the UICorner, then in the Properties window, set CornerRadius to 0.15, 0 to slightly round the corners.
  9. Move CooldownBarPrefab to ReplicatedStorage.

    1. Create a folder structure to organize your UI objects. The sample uses an Instances folder with a child Guis folder.

    2. Move CooldownBarPrefab into Guis.

  10. Reference the following ReplicatedStorage scripts within the sample Laser Tag place file that programmatically attach the cooldown meter to the player's blaster, then animate the red bar after a player blasts their blaster.

The following ReplicatedStorage.FirstPersonBlasterVisuals client script handles all visual logic for the player's first-person blaster. It requires a set of module scripts that work together to set up blaster visuals that feel more realistic for laser tag gameplay, including FirstPersonBlasterVisuals.addCooldownBar and FirstPersonBlasterVisuals.runCooldownBarEffect.


local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local BlastData = require(ReplicatedStorage.Blaster.BlastData)
local PlayerAttribute = require(ReplicatedStorage.PlayerAttribute)
local PlayerState = require(ReplicatedStorage.PlayerState)
local getBlasterConfig = require(ReplicatedStorage.Blaster.getBlasterConfig)
local runBlastVisuals = require(script.runBlastVisuals)
local setupAnimations = require(script.setupAnimations)
local addCooldownBar = require(script.addCooldownBar)
local runCooldownBarEffect = require(script.runCooldownBarEffect)
local laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEvent
local RIG_OFFSET_FROM_CAMERA = CFrame.new(2, -2, -3) * CFrame.Angles(math.rad(0.25), math.rad(95.25), 0)
local localPlayer = Players.LocalPlayer
local currentCamera = Workspace.CurrentCamera
local rigModel = nil
local cooldownBar = nil
local animations = {}
local function addFirstPersonVisuals()
local blasterConfig = getBlasterConfig()
-- Add the first person rig
rigModel = blasterConfig.RigModel:Clone()
rigModel.Parent = Workspace
-- Add the cooldownBar
cooldownBar = addCooldownBar(rigModel.PrimaryPart.CooldownBarAttachment)
animations = setupAnimations(blasterConfig, rigModel)
end
local function removeFirstPersonVisuals()
for _, animation in animations do
animation:Stop()
animation:Destroy()
animation = nil
end
if rigModel then
-- This also destroys the cooldown bar since it is parented to the rig
rigModel:Destroy()
rigModel = nil
end
end
-- Run first person visual effects when a blast occurs
laserBlastedBindableEvent.Event:Connect(function(blastData: BlastData.Type)
runBlastVisuals(rigModel.PrimaryPart.TipAttachment, blastData, animations.blastAnimation)
runCooldownBarEffect(cooldownBar)
end)
-- Bind the rig to the camera if it exists
RunService.RenderStepped:Connect(function()
if rigModel then
-- Update to rig's CFrame relative to the camera's position and RIG_OFFSET_FROM_CAMERA
rigModel:PivotTo(currentCamera.CFrame * RIG_OFFSET_FROM_CAMERA)
end
end)
-- Handles changing visuals when the blasterType changes while playing
localPlayer:GetAttributeChangedSignal(PlayerAttribute.blasterType):Connect(function()
local playerState = localPlayer:GetAttribute(PlayerAttribute.playerState)
if playerState == PlayerState.Playing then
removeFirstPersonVisuals()
addFirstPersonVisuals()
end
end)
-- Handles changing visuals when the playerState changes
localPlayer:GetAttributeChangedSignal(PlayerAttribute.playerState):Connect(function()
local newPlayerState = localPlayer:GetAttribute(PlayerAttribute.playerState)
-- Remove the visuals when the player is selecting a blaster or is in the lobby
if newPlayerState == PlayerState.SelectingBlaster or newPlayerState == PlayerState.InLobby then
removeFirstPersonVisuals()
-- Add the visuals back when the player finishes selecting the blaster.
elseif newPlayerState == PlayerState.Playing then
addFirstPersonVisuals()
end
end)

Create BillboardGui Objects

In order to display UI elements within the 3D space that respond to scripting logic and always face each player's camera regardless of their viewing angle, such as player names or map markers, you can create a BillboardGui object as a child of a BasePart or Attachment that exists in the 3D space.

The sample laser tag experience includes two separate BillboardGui objects within the ReplicatedStorage service:

  • OtherPlayerIndicatorGuiPrefab - Displays a pink or green circle above each player's head when they are active in a round.
  • TaggedOutIndicatorGuiPrefab - Displays above a player's head when they are tagged out of the round.

After you create a BillboardGui object, you can create and customize its child GuiObjects according to each container's purpose. To demonstrate, in the immediate sections that follow, you will learn how to implement UI elements for both indicator types within the sample laser tag experience. You can adjust any part of the process to meet the specifications of your own experience.

To create a BillboardGui object:

  1. In the Explorer window, hover over a BasePart or Attachment, then click the icon. A contextual menu displays.
  2. From the contextual menu, insert a BillboardGui object.
  3. Rename the BillboardGui according to the context of its child UI elements.
  4. Repeat this process for every UI element you need to contextually display above players' heads.

Team Indicator

A team indicator is a UI element that informs players which team other players in the round belong to so that they can easily differentiate between their allies and enemy team members. This information is important because the gameplay of a first-person shooter experience requires players to make quick strategic decisions while they're in combat zones so that they don't get tagged out and lose the match.

To exactly recreate the team indicator within the sample Laser Tag experience:

  1. Insert a BillboardGui object into a temporary rig.

    1. In the menu bar, navigate to the Avatar tab, then click on Rig Builder.​

    2. ​Select from the available options. The sample uses a R15 rig type, a feminine body shape, and a Rthro avatar. The rig displays both in the 3D viewport and in the Explorer window under the name Rig.

    3. In the Explorer window, navigate to the rig's child Head mesh, then click the icon. A contextual menu displays.

    4. From the contextual menu, insert a BillboardGui.

  2. Select the new BillboardGui, then in the Properties window,

    1. Set LightInfluence to 0 to prevent environmental light from affecting the color of the indicator.

    2. Set Name to OtherPlayerIndicatorPrefab.

    3. Set Size to {0, 10},{0, 10} to make the label significantly smaller.

    4. Set StudsOffsetWorldSpace to 0, 4, 0 to position it above the rig's head.

  3. Insert a Frame object into OtherPlayerIndicatorPrefab.

  4. Select the new frame, then in the Properties window,

    1. Set AnchorPoint to 0.5, 0.5 to set the frame's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).

    2. Set BackgroundColor3 to 255, 3, 0 to set the frame's background color to red as a placeholder color.

    3. Set Position to {0.5, 0},{0.5, 0} to set the frame to the middle of its container (50% from the left to the right of the parent BillboardGui, and 50% from the top to the bottom of the parent BillboardGui).

    4. Set Size to {1, -2},{1, -2} to shorten the frame to the surface area of the BillboardGui.

  5. Insert a UICorner object into Frame to completely round the corners.

  6. Insert a UIStroke object into Frame to outline the circle of the indicator.

  7. Move OtherPlayerIndicatorPrefab to ReplicatedStorage.

  8. Reference the following ReplicatedStorage script within the sample Laser Tag 1A place file that programmatically displays the team indicator for every player in an active round unless they are on the enemy team and occluded.

The following ReplicatedStorage.OtherPlayerIndicatorGuiSetup script runs when players spawn into the arena for an active round. It attaches the team indicator by calling the addIndicatorToCharacter() function, which locates the Head object of each player character participating in the round. If they don't already have a team indicator, the script then clones and adds the otherPlayerIndicatorPrefab UI to the character's Head, and sets the team indicator color to their team color.

If other players are on the same team, the team indicator always displays, even if they hide behind objects in the 3D space; if other players are on the enemy team, the team indicator only displays if there isn't an object in the 3D space to occlude them.


local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local localPlayer = Players.LocalPlayer
local otherPlayerIndicatorPrefab = ReplicatedStorage.Instances.Guis.OtherPlayerIndicatorPrefab
local characterSpawnConnectionsByPlayer: { [Player]: RBXScriptConnection } = {}
local playerAddedConnection: RBXScriptConnection?
local function removeIndicatorFromPlayer(player: Player)
if not player.Character then
return
end
local head = player.Character:WaitForChild("Head", 3)
if not head then
return
end
local gui = head:FindFirstChild(otherPlayerIndicatorPrefab.Name)
if gui then
gui:Destroy()
end
end
local function addIndicatorToCharacter(otherCharacter: Model?)
local otherPlayer = Players:GetPlayerFromCharacter(otherCharacter)
if not otherPlayer then
return
end
task.spawn(function()
local otherHead = otherCharacter:WaitForChild("Head", 3)
if not otherHead then
return
end
-- Only add indicators to players participating in the round
if not otherPlayer.Team then
return
end
-- Avoid adding duplicate indicators, creating a new one only if it doesn't exist
local gui = otherHead:FindFirstChild(otherPlayerIndicatorPrefab.Name)
if not gui then
gui = otherPlayerIndicatorPrefab:Clone()
gui.Frame.BackgroundColor3 = otherPlayer.TeamColor.Color
gui.Parent = otherHead
end
-- The indicator is always on top only if the player is friendly
local isFriendly = otherPlayer.Team == localPlayer.Team
gui.AlwaysOnTop = isFriendly
end)
end
local function addIndicatorWhenCharacterSpawns(player: Player)
if characterSpawnConnectionsByPlayer[player] then
return
end
local connection = player.CharacterAdded:Connect(addIndicatorToCharacter)
characterSpawnConnectionsByPlayer[player] = connection
end
local function stopSyncingIndicators()
for _, connection in characterSpawnConnectionsByPlayer do
connection:Disconnect()
end
table.clear(characterSpawnConnectionsByPlayer)
if playerAddedConnection then
playerAddedConnection:Disconnect()
playerAddedConnection = nil
end
for _, player in Players:GetPlayers() do
removeIndicatorFromPlayer(player)
end
end
local function addIndicatorToPlayer(player: Player)
if player == localPlayer then
return
end
addIndicatorToCharacter(player.Character)
addIndicatorWhenCharacterSpawns(player)
end
local function startSyncingIndicators()
for _, player in Players:GetPlayers() do
addIndicatorToPlayer(player)
end
if not playerAddedConnection then
playerAddedConnection = Players.PlayerAdded:Connect(addIndicatorToPlayer)
end
end
local function onLocalTeamChanged()
local localTeam = localPlayer.Team
if localTeam then
startSyncingIndicators()
else
stopSyncingIndicators()
end
end
localPlayer:GetPropertyChangedSignal("Team"):Connect(onLocalTeamChanged)
onLocalTeamChanged()

Tagged Out Indicator

A tagged out indicator is a UI element that informs players when other players are no longer active in the round and are in the process of respawning back to their spawn zone. This information is important because the gameplay of a first-person shooter experience requires players to move onto their next target as soon as they tag out a player so that they don't become vulnerable in the arena by playing in the same location for too long.

To exactly recreate the tagged out indicator within the sample Laser Tag experience:

  1. Insert a BillboardGui object into a temporary rig so that you can visualize the changes in each step of the process.

    1. In the menu bar, navigate to the Avatar tab, then click on Rig Builder.​

    2. ​Select from the available options. The sample uses a R15 rig type, a masculine body shape, and a Rthro avatar. The rig displays both in the 3D viewport and in the Explorer window under the name Rig.

    3. In the Explorer window, navigate to the rig's child Head mesh, then click the icon. A contextual menu displays.

    4. From the contextual menu, insert a BillboardGui.

  2. Select the new BillboardGui, then in the Properties window,

    1. Set LightInfluence to 0 to prevent environmental light from affecting the color of the indicator.

    2. Set Name to TaggedOutIndicatorGuiPrefab.

    3. Set Size to {3, 0},{0.5, 0} to widen the space for a label.

    4. Set StudsOffset to 0, 3.25, 0 to position it above a player's head.

  3. Insert an ImageLabel object into TaggedOutIndicatorGuiPrefab.

  4. Select the new label, then in the Properties window,

    1. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).
    2. Set BackgroundTransparency to 1 to make the label's background completely transparent.
    3. Set Name to Frame.
    4. Set Position to {0.5, 0},{0.5, 0} to set the label to the middle of its container (50% from the left to the right of the parent BillboardGui, and 50% from the top to the bottom of the parent BillboardGui).
    5. Set Size to {1, 0},{1, 0} to widen the label to the whole BillboardGui (100% horizontally and 100% vertically of the parent BillboardGui).
    6. Set Image to rbxassetid://14304826985 to make the image a multi-directional fade.
    7. Set ImageColor to 245, 46, 46 to tint the label red.
  5. Insert a TextLabel object into Frame.

  6. Select the new label, then in the Properties window,

    1. Set AnchorPoint to 0.5, 0.5 to set the label's origin point in the middle of itself (50% from the left to the right of the label, and 50% from the top to the bottom of the label).

    2. Set BackgroundTransparency to 1 to make the label's background completely transparent.

    3. Set Name to BodyTextLabel.

    4. Set Position to {0.5, 0},{0.5, 0} to set the label to the middle of its container (50% from the left to the right of the parent label, and 50% from the top to the bottom of the parent label).

    5. Set Size to {0.85, 0},{0.7, 0} so the text can take up most of the fade area (85% horizontally and 70% vertically of the parent image label).

    6. Set FontFace to Montserrat to fit the futuristic aesthetic.

    7. Set Weight to Bold to thicken the font.

    8. Set Text to TAGGED.

    9. Set TextColor3 to 255, 255, 255 to make the text white.

    10. Enable TextScaled.

  7. Move TaggedOutIndicatorGuiPrefab to ReplicatedStorage.

  8. Reference the following ServerScriptService scripts within the sample Laser Tag 1A place file that programmatically display the tagged out indicator while a player is respawning back to their team's spawn zone.

The following ServerScriptService.SetupHumanoid server script runs as soon as a player loads the experience. It ensures that whenever a player's character is added to the data model, setupHumanoidAsync is called with their Humanoid.


local Players = game:GetService("Players")
local setupHumanoidAsync = require(script.setupHumanoidAsync)
local function onCharacterAdded(player: Player, character: Model)
local humanoid = character:WaitForChild("Humanoid")
setupHumanoidAsync(player, humanoid)
end
local function onPlayerAdded(player: Player)
-- Call onCharacterAdded if the player already has a character
if player.Character then
onCharacterAdded(player, player.Character)
end
-- Call onCharacterAdded for all future character spawns for this player
player.CharacterAdded:Connect(function(character: Model)
onCharacterAdded(player, character)
end)
end
-- Call onPlayerAdded for any players already in the game
for _, player in Players:GetPlayers() do
onPlayerAdded(player)
end
-- Call onPlayerAdded for all future players
Players.PlayerAdded:Connect(onPlayerAdded)

Congratulations on completing the User Interface Design Curriculum! Now that you have experience creating an art style, wireframing your layouts, and implementing your designs in Studio from start to finish, you can extend your project with new UI and functionality, or follow additional tutorial curricula, such as the Gameplay Scripting Curriculum that teaches you about the general organization and key implementation details of the sample laser tag experience. Happy creating!