mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-25 04:54:07 +00:00
update rules
This commit is contained in:
parent
4c390fdd37
commit
d5522b2fef
8549 changed files with 11783 additions and 1888 deletions
349
.cursor/rules/roblox/en-us/players/avatar-context-menu.md
Normal file
349
.cursor/rules/roblox/en-us/players/avatar-context-menu.md
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
---
|
||||
title: Avatar Context Menu
|
||||
description: The Avatar Context Menu (ACM) allows users to interact and customize actions with others in their experience.
|
||||
---
|
||||
|
||||
The **Avatar Context Menu** (ACM) makes it easier for users to interact with each other. When the ACM is enabled in your experience, a user can walk up to another user's character and click on them to open a small menu with several default options. The player can send a connection request, begin a private chat, view the user's profile, or wave.
|
||||
|
||||
After [enabling](#enable-the-avatar-context-menu) the ACM in your experience, you can customize the ACM in the following ways:
|
||||
|
||||
- Programmatically [open and close](#open-and-close-the-acm) the ACM for specific users.
|
||||
- [Add](#add-menu-options) custom options and [remove](#remove-menu-options) existing options to the ACM.
|
||||
- [Customize the ACM appearance](#customize-menu-appearance) to create a unique user interface.
|
||||
|
||||
## Enable the Avatar Context Menu
|
||||
|
||||
The Avatar Context Menu must be enabled using the `Class.StarterGui:SetCore()` option "AvatarContextMenuEnabled" in a `Class.LocalScript`. The ACM is best used in experiences where there is no predefined behavior for clicking on other users.
|
||||
|
||||
Use the following code sample to enable the ACM in a `Class.LocalScript`:
|
||||
|
||||
```lua
|
||||
-- Run in a LocalScript, ideally within "StarterPlayerScripts"
|
||||
local StarterGui = game:GetService("StarterGui")
|
||||
StarterGui:SetCore("AvatarContextMenuEnabled", true)
|
||||
```
|
||||
|
||||
If you need to detect whether the ACM is enabled, you can use the following code to return a boolean on the current status of the ACM:
|
||||
|
||||
```lua
|
||||
---Returns a boolean indicating if the ACM is currently enabled.
|
||||
StarterGui:GetCore(AvatarContextMenuEnabled)
|
||||
```
|
||||
|
||||
## Open and close the ACM
|
||||
|
||||
Once enabled, you can open and close the ACM programmatically with `Class.StarterGui`.
|
||||
|
||||
To programmatically open the ACM, use the following code:
|
||||
|
||||
```lua
|
||||
-- Use StarterGui:SetCore where targetPlayer is a valid Player object
|
||||
StarterGui:SetCore("AvatarContextMenuTarget", targetPlayer)
|
||||
```
|
||||
|
||||
To programmatically close the ACM, use the following code:
|
||||
|
||||
```lua
|
||||
StarterGui:SetCore("AvatarContextMenuTarget", nil)
|
||||
```
|
||||
|
||||
## Menu options
|
||||
|
||||
You can [add](#add-menu-options) and [remove](#remove-menu-options) actions through scripting. By default, the menu has the following options:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Menu option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Connection</td>
|
||||
<td>Sends a connection request to the selected user.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Chat</td>
|
||||
<td>Opens a private conversation with the selected user in the in-experience chat.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>View</td>
|
||||
<td>Opens a window to inspect the selected user's appearance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wave</td>
|
||||
<td>Initiates a wave animation to the selected user. </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Once the ACM is open, the user can scroll and select other users on the character selection carousel. Characters are sorted based on distance to the selected character. The menu only updates when opened and the list of available users will repeat when scrolling.
|
||||
|
||||
<img src="../assets/avatar/context-menu/ACM-User-Selector.png" width="345" />
|
||||
|
||||
### Add menu options
|
||||
|
||||
Once enabled, experience-specific actions can be added to the ACM. For example, an experience might allow for trade requests, add-to-party options, or other special interactions.
|
||||
|
||||
The following example shows how to add a custom action to the Avatar Context Menu:
|
||||
|
||||
```lua
|
||||
local Players = game:GetService("Players")
|
||||
local StarterGui = game:GetService("StarterGui")
|
||||
local player = Players.LocalPlayer
|
||||
|
||||
-- Connect a function to a "BindableEvent"
|
||||
local bindableEvent = Instance.new("BindableEvent")
|
||||
local function onCustomACMAction(targetPlayer)
|
||||
-- At this point, you could call InvokeServer() on a RemoteFunction to alert the server of the selection
|
||||
print("ACM event selected by " .. player.Name .. " on " .. targetPlayer.Name)
|
||||
end
|
||||
bindableEvent.Event:Connect(onCustomACMAction)
|
||||
|
||||
-- Add the ACM option using SetCore() with "AddAvatarContextMenuOption"
|
||||
local options = {"Custom ACM Action", bindableEvent}
|
||||
StarterGui:SetCore("AddAvatarContextMenuOption", options)
|
||||
```
|
||||
|
||||
### Remove menu options
|
||||
|
||||
You can remove custom and the default Add Friend, Chat, View, and Wave options from the ACM by referencing the custom Action name or the default `Enum.AvatarContextMenuOption` enum.
|
||||
|
||||
Use the following code to remove a default and custom menu option:
|
||||
|
||||
```lua
|
||||
-- Remove the "Custom ACM Action" option
|
||||
StarterGui:SetCore("RemoveAvatarContextMenuOption", "Custom ACM Action")
|
||||
|
||||
-- Remove the default "Friend" option by referencing the AvatarContextMenuOption.Friend Enum
|
||||
StarterGui:SetCore("RemoveAvatarContextMenuOption", Enum.AvatarContextMenuOption.Friend)
|
||||
```
|
||||
|
||||
<Alert severity ='warning'>
|
||||
You can not make edits to the ACM while it is open. Your code should verify that an action is still relevant in case the action was removed from the menu. For example, if a marketplace area adds an option to send trade requests, you should verify both users are still in the marketplace.
|
||||
</Alert>
|
||||
|
||||
## Customize menu appearance
|
||||
|
||||
To change the appearance of the Avatar Context Menu, call `Class.StarterGui:SetCore()` with the option "AvatarContextMenuTheme", providing a table of parameters and values to adjust the menu appearance.
|
||||
|
||||
The ACM user interface includes the following sections:
|
||||
|
||||
<img src="../assets/avatar/context-menu/ACM-Sections-Labeled.png" width="404" />
|
||||
|
||||
A. Name Tag: The user name of the character being interacted with.
|
||||
|
||||
B. Button Frame: Contains all of the ACM buttons.
|
||||
|
||||
C. Buttons: Individual buttons for the default or custom ACM actions.
|
||||
|
||||
### Appearance parameters
|
||||
|
||||
These are the customization parameters with the ACM:
|
||||
|
||||
#### Background
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>BackgroundColor</td>
|
||||
<td>A `Datatype.Color3` for the overall background of the ACM (most useful when not using a background image).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BackgroundTransparency</td>
|
||||
<td>Transparency value (0–1) for the overall background of the ACM (most useful when not using a background image).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BackgroundImage</td>
|
||||
<td>A valid asset ID of an image for the ACM background.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BackgroundImageTransparency</td>
|
||||
<td>Transparency value (0–1) for the background image.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BackgroundImageScaleType</td>
|
||||
<td>A `Enum.ScaleType` enum for background image scaling.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BackgroundImageSliceCenter</td>
|
||||
<td>A `Datatype.Rect` specifying the center of a nine-slice image when BackgroundImageScaleType is set to `Enum.ScaleType.Slice`.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Name tag
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>NameTagColor</td>
|
||||
<td>A `Datatype.Color3` for the bar showing which player is being interacted with.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NameUnderlineColor</td>
|
||||
<td>A `Datatype.Color3` for the thin line between the name tag and action buttons.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Button frame
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>ButtonFrameColor</td>
|
||||
<td>A `Datatype.Color3` for the section (frame) containing the action buttons.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ButtonFrameTransparency </td>
|
||||
<td>Transparency value (0–1) for the button frame section.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Button
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>ButtonColor</td>
|
||||
<td>A `Datatype.Color3` for the background of the ACM action buttons.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ButtonTransparency</td>
|
||||
<td>Transparency value (0–1) for the background color of the action buttons.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ButtonHoverColor</td>
|
||||
<td>A `Datatype.Color3` for the "hover" state of the action buttons.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ButtonHoverTransparency</td>
|
||||
<td>Transparency value (0–1) for the "hover" color of the action buttons.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ButtonUnderlineColor</td>
|
||||
<td>A `Datatype.Color3` for the thin line which separates each action button.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ButtonImage</td>
|
||||
<td>A valid asset ID of an image for the background of buttons.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ButtonImageScaleType</td>
|
||||
<td>A `Enum.ScaleType` enum for button image scaling.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ButtonImageSliceCenter</td>
|
||||
<td>A `Datatype.Rect` specifying the center of a nine-slice image when ButtonImageScaleType is set to `Enum.ScaleType.Slice`.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Text
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Font</td>
|
||||
<td>A `Enum.Font` enum value for the name tag and button text.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TextColor</td>
|
||||
<td>A `Datatype.Color3` for all text within the ACM.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TextScale</td>
|
||||
<td>A float value to scale the default text sizes of each element.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Various image
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>LeaveMenuImage</td>
|
||||
<td>An asset ID of an image for the ACM close button.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ScrollLeftImage</td>
|
||||
<td>An asset ID of an image for the carousel "scroll left" button.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ScrollRightImage</td>
|
||||
<td>A valid asset ID of an image for the carousel "scroll right" button.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Selected character
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>SelectedCharacterIndicator</td>
|
||||
<td>The `Class.MeshPart` which floats above a character's head to indicate they are selected.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Size and position
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td>A `Datatype.UDim2` for the overall size of the ACM.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MinSize</td>
|
||||
<td>A `Datatype.Vector2` specifying the minimum size of the ACM.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MaxSize</td>
|
||||
<td>A `Datatype.Vector2` specifying the maximum size of the ACM.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AspectRatio</td>
|
||||
<td>A float value specifying the relative width and height of the ACM.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AnchorPoint</td>
|
||||
<td>The `Class.GuiObject.AnchorPoint|AnchorPoint` of the ACM.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OnScreenPosition</td>
|
||||
<td>A `Datatype.UDim2` specifying the on-screen position of the ACM (the position where it tweens to when opened).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OffScreenPosition</td>
|
||||
<td>A `Datatype.UDim2` specifying the off-screen position of the ACM (the position where it tweens from/to when opened/closed).</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Example customization
|
||||
|
||||
The following code sample customizes the ACM theme using some basic parameters:
|
||||
|
||||
```lua
|
||||
local StarterGui = game:GetService("StarterGui")
|
||||
|
||||
StarterGui:SetCore("AvatarContextMenuTheme", {
|
||||
BackgroundImage = "",
|
||||
BackgroundTransparency = 0.5,
|
||||
BackgroundColor = Color3.fromRGB(111, 145, 242),
|
||||
NameTagColor = Color3.fromRGB(0, 0, 200),
|
||||
NameUnderlineColor = Color3.fromRGB(213, 233, 255),
|
||||
ButtonFrameColor = Color3.fromRGB(15, 24, 65),
|
||||
ButtonFrameTransparency = 0.2,
|
||||
ButtonUnderlineColor = Color3.fromRGB(213, 233, 255),
|
||||
Font = Enum.Font.SciFi
|
||||
})
|
||||
```
|
||||
|
||||
<img src="../assets/avatar/context-menu/Custom-ACM.png" width="345" />
|
||||
210
.cursor/rules/roblox/en-us/players/avatar-editor.md
Normal file
210
.cursor/rules/roblox/en-us/players/avatar-editor.md
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
title: Avatar Editor Service
|
||||
description: The Avatar Editor Service allows access to a user's avatar and inventory and Marketplace.
|
||||
---
|
||||
|
||||
The Avatar Editor Service lets you access and make changes to a user's avatar within an experience. The Avatar Editor Service can also access a user's inventory and the [Marketplace](https://www.roblox.com/catalog) to save outfits and purchase avatar items to the user's account.
|
||||
|
||||
We recommend implementing the Avatar Editor Service with an in-game avatar editor for a complete character customization experience. See the [Simple Avatar Editor Demo](https://www.roblox.com/games/9376329300/Simple-Avatar-Editor-Demo) reference place for an example of this feature.
|
||||
|
||||
To begin using the Avatar Editor Service, you must first [request access](#request-access) to the user's inventory. After access is successfully granted, you can perform the following actions:
|
||||
|
||||
- [Read user's inventory](#read-user-inventory) to get a list of items owned by the user.
|
||||
- [Search the Marketplace](#search-the-marketplace), using a variety of properties to filter and sort.
|
||||
- [Equip avatar items and save outfits](#save-avatars-and-outfits) to the user's avatar.
|
||||
- [Prompt the user to purchase](#purchase-items) an Marketplace item.
|
||||
|
||||
## Request access
|
||||
|
||||
To begin accessing a user's inventory, you need to prompt the user to allow access through `Class.AvatarEditorService:PromptAllowInventoryReadAccess()|PromptAllowInventoryReadAccess()`. You need to perform this request once per session.
|
||||
|
||||
Use the following code sample to initiate the access prompt and listen for the user response:
|
||||
|
||||
```lua
|
||||
local AvatarEditorService = game:GetService("AvatarEditorService")
|
||||
|
||||
AvatarEditorService:PromptAllowInventoryReadAccess()
|
||||
|
||||
local result = AvatarEditorService.PromptAllowInventoryReadAccessCompleted:Wait()
|
||||
|
||||
if result == Enum.AvatarPromptResult.Success then
|
||||
-- Access granted!
|
||||
end
|
||||
```
|
||||
|
||||
The user receives the following prompt:
|
||||
|
||||
<img src="../assets/avatar/avatar-editor-service/Avatar-Editor-Access-Items.png" width="400" />
|
||||
|
||||
Once the user accepts the prompt, the `Class.AvatarEditorService` can begin accessing the user's inventory.
|
||||
|
||||
## Read user inventory
|
||||
|
||||
Once access is granted by the user, you can read their inventory with the `Class.AvatarEditorService:GetInventory()|GetInventory()` function, supplying an array of `Enum.AvatarAssetType|AvatarAssetTypes` to filter by. This function returns an `Class.InventoryPages` object containing the user owned items.
|
||||
|
||||
Use the following code sample to print a list of specific accessories in a user's inventory:
|
||||
|
||||
```lua
|
||||
local AvatarEditorService = game:GetService("AvatarEditorService")
|
||||
|
||||
AvatarEditorService:PromptAllowInventoryReadAccess()
|
||||
|
||||
local result = AvatarEditorService.PromptAllowInventoryReadAccessCompleted:Wait()
|
||||
|
||||
if result == Enum.AvatarPromptResult.Success then
|
||||
-- Access granted!
|
||||
local assetTypes = {
|
||||
Enum.AvatarAssetType.BackAccessory,
|
||||
Enum.AvatarAssetType.ShoulderAccessory,
|
||||
Enum.AvatarAssetType.WaistAccessory
|
||||
}
|
||||
local pagesObject = AvatarEditorService:GetInventory(assetTypes)
|
||||
local currentPage = pagesObject:GetCurrentPage()
|
||||
for _, item in currentPage do
|
||||
print(item)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Search the Marketplace
|
||||
|
||||
`Class.AvatarEditorService` includes functions and events which let you search the Roblox catalog. To search, supply your query with a `Datatype.CatalogSearchParams` object that includes one or more of the following properties:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>AssetTypes</td>
|
||||
<td>An array of `Enum.AvatarAssetType` such as Enum.AvatarAssetType.BackAccessory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BundleTypes</td>
|
||||
<td>An array of `Enum.BundleType` such as Enum.BundleType.BodyParts.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CategoryFilter</td>
|
||||
<td>A `Enum.CatalogCategoryFilter` describing the various catalog categories like "Featured" or "Community Creations". By default this is set to `Enum.CatalogCategoryFilter.None`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MaxPrice</td>
|
||||
<td>An integer describing the maximum price to filter.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MinPrice</td>
|
||||
<td>An integer describing the minimum price to filter. By default, MinPrice is <b>0</b>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SearchKeyword</td>
|
||||
<td>A string to query against item descriptions in the catalog.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SortType</td>
|
||||
<td>A `Enum.CatalogSortType` that describes how the results are ordered. By default this is set to `Enum.CatalogSortType.Relevance`.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IncludeOffSale</td>
|
||||
<td>A boolean describing whether the results of the search include off sale items. By default this is set to false.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CreatorId</td>
|
||||
<td>An integer to specify a given creator. You can use either a UserId or a GroupId.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CreatorName</td>
|
||||
<td>A string used to search by items created by a given creator. You can use either a User Name or a Group Name.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
The following code sample constructs a `Datatype.CatalogSearchParams` object for **Back** and **Shoulder** asset types, and passes that through a `Class.AvatarEditorService:SearchCatalog()|SearchCatalog()` call:
|
||||
|
||||
```lua
|
||||
local AvatarEditorService = game:GetService("AvatarEditorService")
|
||||
|
||||
local catalogSearchParams = CatalogSearchParams.new()
|
||||
local assetTypes = {
|
||||
Enum.AvatarAssetType.BackAccessory,
|
||||
Enum.AvatarAssetType.ShoulderAccessory
|
||||
}
|
||||
catalogSearchParams.AssetTypes = assetTypes
|
||||
|
||||
local pagesObject =
|
||||
--This function returns a CatalogPages object containing the results.
|
||||
AvatarEditorService:SearchCatalog(catalogSearchParams)
|
||||
local currentPage = pagesObject:GetCurrentPage()
|
||||
for _, item in currentPage do
|
||||
print(item)
|
||||
end
|
||||
```
|
||||
|
||||
## Save avatars and outfits
|
||||
|
||||
When used alongside an in-game avatar editor, `Class.AvatarEditorService` can save and update avatar items and outfits to the Roblox platform. Users don't receive catalog items they don't own when saving an avatar or outfit.
|
||||
|
||||
Any `Class.HumanoidDescription` can be saved to the user's current avatar with `Class.AvatarEditorService:PromptSaveAvatar()|PromptSaveAvatar()`. This may include:
|
||||
|
||||
- Pre-defined avatar configurations that you've built using existing catalog items.
|
||||
- Any configuration that the user has chosen through an in-game avatar editor.
|
||||
|
||||
<img src="../assets/avatar/avatar-editor-service/Avatar-Editor-Equip-Items.png" width="400" />
|
||||
|
||||
Since `Class.AvatarEditorService:PromptSaveAvatar()` does not yield, you can get the result by listening to the `Class.AvatarEditorService.PromptSaveAvatarCompleted` event.
|
||||
|
||||
The following code will save a current `Class.HumanoidDescription` using `Class.AvatarEditorService:PromptSaveAvatar()|PromptSaveAvatar()` and checks for a successful `Class.AvatarEditorService.PromptSaveAvatarCompleted` event:
|
||||
|
||||
```lua
|
||||
local AvatarEditorService = game:GetService("AvatarEditorService")
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local player = Players.LocalPlayer
|
||||
local character = player.Character or player.CharacterAdded:Wait()
|
||||
local humanoid = character:WaitForChild("Humanoid")
|
||||
|
||||
local currentDescription = humanoid:GetAppliedDescription()
|
||||
AvatarEditorService:PromptSaveAvatar(currentDescription, humanoid.RigType)
|
||||
|
||||
local result = AvatarEditorService.PromptSaveAvatarCompleted:Wait()
|
||||
if result == Enum.AvatarPromptResult.Success then
|
||||
-- Avatar saved!
|
||||
end
|
||||
```
|
||||
|
||||
To save any `Class.HumanoidDescription` as an outfit (without overwriting the user's current avatar), use `Class.AvatarEditorService:PromptCreateOutfit()`.
|
||||
|
||||
<img src="../assets/avatar/avatar-editor-service/Avatar-Editor-Save-Outfit.png" width="400" />
|
||||
|
||||
Once called, you can get the result of `Class.AvatarEditorService:PromptCreateOutfit()` by listening to the `Class.AvatarEditorService.PromptCreateOutfitCompleted` event.
|
||||
|
||||
The following code sample creates an outfit with `Class.AvatarEditorService:PromptCreateOutfit()` and listens for a successful `Class.AvatarEditorService.PromptCreateOutfitCompleted` event:
|
||||
|
||||
```lua
|
||||
local AvatarEditorService = game:GetService("AvatarEditorService")
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local player = Players.LocalPlayer
|
||||
local character = player.Character or player.CharacterAdded:Wait()
|
||||
local humanoid = character:WaitForChild("Humanoid")
|
||||
|
||||
local currentDescription = humanoid:GetAppliedDescription()
|
||||
AvatarEditorService:PromptCreateOutfit(currentDescription, humanoid.RigType)
|
||||
|
||||
local result = AvatarEditorService.PromptCreateOutfitCompleted:Wait()
|
||||
if result == Enum.AvatarPromptResult.Success then
|
||||
-- Outfit saved!
|
||||
end
|
||||
```
|
||||
|
||||
## Purchase items
|
||||
|
||||
When saving either an avatar or an outfit that uses catalog items, the user doesn't receive any items that they do not own. Before saving an avatar or outfit, check if the user owns the asset with `Class.MarketplaceService:PlayerOwnsAsset()` and provide them with an option to purchase the item with `Class.MarketplaceService:PromptPurchase()`.
|
||||
|
||||
If you don't wish to implement item purchases, you can instead allow users to favorite non-owned items with `Class.AvatarEditorService:PromptSetFavorite()`.
|
||||
|
||||
<Alert severity="info">
|
||||
Be aware that you can't earn Robux <a href="../marketplace/marketplace-fees-and-commissions.md#commissions">commissions</a> if the user decides to purchase those items outside of your experience.
|
||||
</Alert>
|
||||
67
.cursor/rules/roblox/en-us/players/avatar-inspect-menu.md
Normal file
67
.cursor/rules/roblox/en-us/players/avatar-inspect-menu.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
title: Avatar Inspect Menu
|
||||
description: The Avatar Inspect Menu allows users to view other users' avatars, try on items, make purchases.
|
||||
---
|
||||
|
||||
The **Avatar Inspect Menu** allows users to view another user's Roblox avatar character, try on items, and even make purchases within an experience. Roblox enables this menu by default and users in your experience can access this menu in three ways:
|
||||
|
||||
- Opening the experience's main menu and clicking the **View** button next to a user in the **Players** tab.
|
||||
- Clicking on a user's name in the player list.
|
||||
- Selecting the Inspect option in the [Avatar Context Menu](../players/avatar-context-menu.md), an opt-in feature which provides additional user-to-user social interaction.
|
||||
|
||||
You can customize a user's **Avatar Inspect Menu** in the following ways:
|
||||
|
||||
- Change the inspect target from the user's Roblox avatar to their [current in-experience appearance](#inspect-currently-equipped-items).
|
||||
- Change the inspect target to a [specific User ID](#inspect-specific-users), even if they are not in the experience.
|
||||
|
||||
For more specific catalog access, you can also use the [Avatar Editor Service](../players/avatar-editor.md) to access and make changes to a user's platform-wide avatar within an experience.
|
||||
|
||||
## Inspect currently equipped items
|
||||
|
||||
By default, the Inspect Menu shows the same information as the user's Roblox Avatar profile page. The avatar outfit may not match the user's current appearance, since you may have opted to equip different accessories or avatar items at that specific moment.
|
||||
|
||||
In the cases where the default Inspect Menu may not be accurate to the current character's outfit, you can inspect a character's current outfit with the following steps:
|
||||
|
||||
1. Disable the default profile-based Inspect Menu by setting `Class.GuiService:SetInspectMenuEnabled()` with a value of **false**.
|
||||
2. Get a current `Class.HumanoidDescription` from the target player character.
|
||||
3. Call `Class.GuiService:InspectPlayerFromHumanoidDescription()` to load the Inspect menu based off of the `Class.HumanoidDescription`.
|
||||
|
||||
Use the following code sample to inspect the currently equipped items of a specific player:
|
||||
|
||||
```lua
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local Players = game:GetService("Players")
|
||||
local player = Players.LocalPlayer
|
||||
|
||||
-- Make profile-based Inspect Menu inaccessible
|
||||
GuiService:SetInspectMenuEnabled(false)
|
||||
|
||||
local humanoid = player.Character and player.Character:FindFirstChildWhichIsA("Humanoid")
|
||||
if humanoid then
|
||||
-- Get current HumanoidDescription from a player character
|
||||
local humanoidDescription = humanoid:GetAppliedDescription()
|
||||
|
||||
-- Load the inspect menu from a humanoid description
|
||||
GuiService:InspectPlayerFromHumanoidDescription(humanoidDescription, player.Name)
|
||||
end
|
||||
```
|
||||
|
||||
## Inspect specific users
|
||||
|
||||
The Avatar Inspect Menu can inspect players who aren't in the current experience. You can use `Class.GuiService:InspectPlayerFromUserId()` to inspect a player from any `Class.Player.UserId`.
|
||||
|
||||
Use the following code sample to open the Avatar Inspect Menu based on a `Class.Player.UserId|UserId`:
|
||||
|
||||
```lua
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
-- Get user ID by username
|
||||
local success, userId = pcall(function()
|
||||
return Players:GetUserIdFromNameAsync("RobloxUser")
|
||||
end)
|
||||
|
||||
if success then
|
||||
GuiService:InspectPlayerFromUserId(userId)
|
||||
end
|
||||
```
|
||||
8
.cursor/rules/roblox/en-us/players/disable-ui.md
Normal file
8
.cursor/rules/roblox/en-us/players/disable-ui.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: Disable default UI
|
||||
description: Explains the process of disabling Roblox's default user interface elements.
|
||||
---
|
||||
|
||||
import DefaultUI from '../includes/ui/default-ui.md'
|
||||
|
||||
<DefaultUI components={props.components} />
|
||||
176
.cursor/rules/roblox/en-us/players/index.md
Normal file
176
.cursor/rules/roblox/en-us/players/index.md
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
---
|
||||
title: Users and players
|
||||
description: The Player instance contains information on the individual users within your experience.
|
||||
---
|
||||
|
||||
When a user joins an experience, Roblox represents them as a **Player** in the data model. The `Class.Player` object contains information about the user that's universal across experiences, such as their username, connection list, saved [avatar character](../characters/index.md#avatar-characters), and Roblox membership type, as well as properties, methods, and events that affects the user's lifecycle between joining and leaving your experience.
|
||||
|
||||
The `Class.Players` service contains all the `Class.Player` instances in an experience. Each `Class.Player` object represents a user in the experience, and it parents four important containers that you can use to customize a user's experience: `Class.Backpack`, `Class.StarterGear`, `Class.PlayerGui`, and `Class.PlayerScripts`.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
Client and server-side scripts can both connect to the `Class.Players.PlayerAdded` and `Class.Players.PlayerRemoved` events to perform actions in response to the lifecycle of a `Class.Player` object. They can also connect to the `Class.Player.CharacterAdded`, `Class.Player.CharacterRemoving`, and `Class.Humanoid.Died` events to perform gameplay-related actions for when the character spawns, despawns, and dies.
|
||||
|
||||
Use Scripts to access server-related services, such as a data store to retrieve and save data when a user joins or leaves. Use LocalScripts if the client needs to create and remove gameplay instances tied to the new user, such as a GUI display for the user's stats on a custom leaderboard.
|
||||
|
||||
### User joining
|
||||
|
||||
When a client connects to an experience, its associated `Class.Player` object clones to the `Class.Players` service. The `Class.Players.PlayerAdded` represents users joining the experience. Some example use-cases include loading user data, assigning teams, and changing a user's character's clothing. The `Class.Players.PlayerAdded` event passes the `Class.Player` object of the user who joins, which you can use when calling other functions, such as data store and `Class.RemoteEvent` objects.
|
||||
|
||||
#### Load user data on join
|
||||
|
||||
To load a user's data when they join an experience, use the `Class.Players.PlayerAdded` event in a `Class.Script`. The following example `Class.Script` listens to the event and attempts to retrieve a user's data using their user ID as the datastore key. After successfully retrieving the user data, you can use it to load the user's progress and stats.
|
||||
|
||||
```lua Script in ServerScriptService
|
||||
local DataStoreService = game:GetService("DataStoreService")
|
||||
local playerDataStore = DataStoreService:GetDataStore("PlayerData")
|
||||
|
||||
game:GetService("Players").PlayerAdded:Connect(function(player)
|
||||
local userId = player.UserId
|
||||
|
||||
-- Read data store key
|
||||
local getSuccess, currentData = pcall(function()
|
||||
return playerDataStore:GetAsync(userId)
|
||||
end)
|
||||
|
||||
if getSuccess then
|
||||
print(currentData)
|
||||
end
|
||||
|
||||
-- Do further actions with currentData
|
||||
end)
|
||||
```
|
||||
|
||||
<Alert severity = 'info'>
|
||||
The `Class.Instance.Name` of the `Class.Player` object is the name of the user. If you need a unique reference to a user, such as to save information about them in a [data store](../cloud-services/data-stores/index.md), use their `Class.Player.UserId` instead of their `Class.Player.Name` because users can't change their `UserId` even though they can change their Username and Display Name.
|
||||
</Alert>
|
||||
|
||||
### User leaving
|
||||
|
||||
When a client disconnects from an experience, the server destroys its associated `Class.Player` object from the `Class.Players` service. The `Class.Players.PlayerRemoving` event represents users leaving the experience. Some example use-cases include saving user data, removing their stats from a scoreboard, and destroying any of their models, such as their house. The `Class.Players.PlayerRemoving` event passes the `Class.Player` object of the user who leaves, which you can use when calling other functions, such as data store and `Class.RemoteEvent` objects.
|
||||
|
||||
Notice that the event is called `Class.Player.PlayerRemoving`, not `Class.Player.PlayerRemoved`, because "removed" would imply that the `Class.Player` object is already removed and is therefore inaccessible to scripts.
|
||||
|
||||
#### Save user data on leave
|
||||
|
||||
To save a user's data when they leave an experience, use the `Class.Players.PlayerRemoving` event in a `Class.Script`. The following example `Class.Script` listens to the event and attempts to save a user's data using their user ID as the data store key.
|
||||
|
||||
```lua title="Script in ServerScriptService"
|
||||
local DataStoreService = game:GetService("DataStoreService")
|
||||
local playerDataStore = DataStoreService:GetDataStore("PlayerData")
|
||||
|
||||
game:GetService("Players").PlayerRemoving:Connect(function(player)
|
||||
local userId = player.UserId
|
||||
|
||||
-- Get the player's data state in the game
|
||||
local currentData = getCurrentData(player)
|
||||
|
||||
-- Save to data store
|
||||
local setSuccess, errorMessage = pcall(function()
|
||||
playerDataStore:SetAsync(userId, currentData)
|
||||
end)
|
||||
|
||||
if not setSuccess then
|
||||
warn(errorMessage)
|
||||
end
|
||||
end)
|
||||
|
||||
```
|
||||
|
||||
### Character spawning
|
||||
|
||||
A user's `Class.Player.Character` model represents their avatar. By default, `Class.Player.CharacterAutoLoads` is true, and a user's character model automatically spawns when they join the experience. If `Class.Player.CharacterAutoLoads` is false, then you need to call `Class.Player:LoadCharacter()` to manually spawn the character.
|
||||
|
||||
When a user's `Class.Player.Character` spawns, Scripts and LocalScripts in `Class.StarterCharacterScripts` clone into the character model and the `Class.Player.CharacterAdded` event fires.
|
||||
The `Class.Player.CharacterAdded` event passes the new character model to any event listeners, which you can use to find the character's `Class.Humanoid` object and modify its behavior. For example, you can use `Class.Humanoid:ApplyDescription()` to change the outfit of the avatar and `Class.Humanoid.WalkSpeed` or `Class.Humanoid.JumpHeight` to modify the avatar's movement.
|
||||
|
||||
### Character despawning
|
||||
|
||||
When the player's `Class.Humanoid` dies, its body parts fall to the ground and the `Class.Humanoid.Died` event fires. The server automatically removes the character model and any scripts inside it after amount of time that the `Class.Players.Respawntime` property determines. You can use the `Class.Player.CharacterRemoving` event to reset other objects associated with the character, such as the network ownership of a vehicle they were driving.
|
||||
|
||||
#### Count player deaths
|
||||
|
||||
You can use the `Class.Humanoid.Died` event to handle scoring for a kill or create a custom ragdoll model. The following `Class.Script` connects to `Class.Player.CharacterAdded` to retrieve each user's character model, then connects to the character's `Class.Humanoid` object. When the humanoid's `Class.Humanoid.Died` event fires, the script increments the number of times the user's humanoid has died and outputs that number.
|
||||
|
||||
```lua title="Script in ServerScriptService"
|
||||
game:GetService("Players").PlayerAdded:Connect(function(player)
|
||||
local deaths = 0
|
||||
player.CharacterAdded:Connect(function(character)
|
||||
local humanoid = character:WaitForChild("Humanoid")
|
||||
humanoid.Died:Connect(function()
|
||||
deaths += 1
|
||||
print(player.Name .. " death count: " .. deaths)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
```
|
||||
|
||||
## Ban users
|
||||
|
||||
To ensure civility and fair play in your experiences, you can ban users who violate your experience rules and community guidelines. You can modify ban durations, ban messages, and even extend bans to potential alternate accounts. When using this feature, you must also follow guidelines for [banning](#ban-guidelines) and [messaging](#message-guidelines). You have several options for working with bans:
|
||||
|
||||
- Each experience page on the [Creator Hub](https://create.roblox.com/) has a [Bans dashboard](../production/bans.md).
|
||||
- For programmatic usage with the Engine API, see `Class.Players:BanAsync()`.
|
||||
- For Open Cloud, see [Bans and blocks](/cloud/features/bans-and-blocks).
|
||||
|
||||
### Ban guidelines
|
||||
|
||||
When implementing bans in your experience, adhere to the following guidelines:
|
||||
|
||||
- Experience rules must not contradict Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards) and [Terms of Use](https://en.help.roblox.com/hc/en-us/articles/115004647846-Roblox-Terms-of-Use).
|
||||
- For example, you can not create an experience rule that excludes someone because of their gender as this violates [Roblox's Discrimination, Slurs, and Hate Speech policy](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards#discrimination-slurs-and-hate-speech).
|
||||
- Creators must clearly state their experience rules somewhere accessible to all users.
|
||||
- Creators must apply their experience rules fairly and not arbitrarily target certain users.
|
||||
- Users can appeal to creators directly if they believe their ban was incorrect.
|
||||
- Roblox will not mediate these appeals, unless the user believes the creator's experience rules or enforcement of their rules violate the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards).
|
||||
- Roblox can moderate an experience if there is reason to believe that a creator's experience rules or enforcement of their rules violate the [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards).
|
||||
|
||||
### Message guidelines
|
||||
|
||||
When a user is banned, they receive an error modal displaying information such as the ban length and reason. In the text-filtered message, you can include additional information such as appeal or contact information as long as you meet Roblox's [Community Standards](https://en.help.roblox.com/hc/en-us/articles/203313410-Roblox-Community-Standards).
|
||||
|
||||
For example, in your ban messages, you are allowed to reference brand names and platforms:
|
||||
|
||||
- "Visit the Discord in my group/experience page"
|
||||
- "Message me on Twitter or X"
|
||||
|
||||
Mentions of personal information or direct links are not allowed in this message field. This includes posting a specific username or handle, or providing a direct link to a Discord server or X account.
|
||||
|
||||
## Containers
|
||||
|
||||
The `Class.Player` object stores several important containers:
|
||||
|
||||
- [Backpack](#backpack)
|
||||
- [StarterGear](#startergear)
|
||||
- [PlayerGui](#playergui)
|
||||
- [PlayerScripts](#playerscripts)
|
||||
|
||||
### Backpack
|
||||
|
||||
The `Class.Player.Backpack` container stores the user's inventory. The `Class.Tool` objects in a user's `Class.Backpack` display in their inventory at the bottom of their screen. If a user selects a `Class.Tool` from the inventory, they equip it, and it moves from the `Class.Player.Backpack` to the `Class.Player.Character`.
|
||||
|
||||
When a user's `Class.Player.Character` spawns, the contents of the `Class.StarterPack` service and their `Class.Player.StarterGear` clones to their `Class.Player.Backpack`. When the character dies, the client destroys their `Class.Backpack` and replaces it with a new one.
|
||||
|
||||
The `Class.Backpack` also stores and runs `Class.Script|Scripts` and `Class.LocalScript|LocalScripts` that the client and server can both access.
|
||||
|
||||
Roblox provides an interface for a player to access their `Class.Backpack` and inventory at the bottom of the screen. To disable the default Roblox backpack GUI and replace it with your own, call `Class.StarterGui:SetCoreGuiEnabled()` in a LocalScript. For more information, see `Class.StarterGui`.
|
||||
|
||||
### StarterGear
|
||||
|
||||
The `Class.StarterGear` container clones its contents to the user's `Class.Player.Backpack` when its character spawns. Additionally, if your place permits gear and a user owns gear, the `Class.Tool` objects of their gear clone to their `Class.Player.StarterGear` when they spawn.
|
||||
|
||||
Unlike `Class.StarterPack`, `Class.Player.StarterGear` isn't a service but rather a child of each `Class.Player` object, so its contents are user-specific. Each user can have different `Class.Tool` objects within their `Class.Player.StarterGear`. To use `Class.Player.StarterGear`, enable Gear in your experience's settings page under **Permissions**. On the permissions page, you can enable by gear by its type. To disable gear, deselect its type.
|
||||
|
||||
Always test experiences after adding Gear to them to check that users can't easily abuse them there. Gear includes `Class.Script` objects and allows the player to perform actions that you might not consider. For example, a navigational gear might allow the player to access a part of the map that you don't want them to. Weapons allow players with gear to damage other players, possibly without retribution or retaliation.
|
||||
|
||||
### PlayerGui
|
||||
|
||||
The `Class.PlayerGui` container stores objects that create the player's GUI. If a ScreenGui is a descendant of a `Class.PlayerGui`, then any `Class.GuiObject` inside the ScreenGui displays on the player's screen. Any `Class.LocalScript` runs when it clones to `Class.PlayerGui`. When the player's `Class.Player.Character` spawns for the first time, all of the contents of StarterGui automatically copy into the player's `Class.PlayerGui`.
|
||||
|
||||
If Players.CharacterAutoLoads is set to false, the character doesn't spawn, and `Class.StarterGui` contents don't copy over until `Class.Player:LoadCharacter()` is called. If `Class.StarterGui.ResetPlayerGuiOnSpawn` is set to true, every time the player's character respawns, all of the contents of that player's `Class.PlayerGui` are cleared and replaced with the contents of `Class.StarterGui`.
|
||||
|
||||
### PlayerScripts
|
||||
|
||||
When a user joins the experience, the contents in `Class.StarterPlayer.StarterPlayerScripts` container clone to `Class.PlayerScripts`. Any LocalScripts and ModuleScripts run when they clone.
|
||||
|
||||
Unlike the `Class.Backpack` and `Class.PlayerGui` containers, the `Class.PlayerScripts` container isn't accessible to the server, and a user's `Class.PlayerScripts` container doesn't reset when their character dies and respawns. Server-side `Class.Script` objects also don't run when parented to `Class.PlayerScripts`. The `Class.PlayerScripts` container is useful for scripts that aren't tied to a user's character life cycle, such as the general chat system or player input controls.
|
||||
160
.cursor/rules/roblox/en-us/players/leaderboards.md
Normal file
160
.cursor/rules/roblox/en-us/players/leaderboards.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
---
|
||||
title: In-experience leaderboards
|
||||
description: In-experience Leaderboards let you display user information to all users in the experience, such as top scores.
|
||||
---
|
||||
|
||||
Roblox has a built-in **leaderboard system** that lets you display user information like scores, currency, or the fastest time in a race.
|
||||
|
||||
<img alt="Leaderboard Screen" src="../assets/players/leaderboard/Leaderboard-On-Screen.jpg" width="100%" />
|
||||
|
||||
## Set up the leaderboard
|
||||
|
||||
To set up the leaderboard and add players when they enter the experience:
|
||||
|
||||
1. Create a new `Class.Script` within `Class.ServerScriptService` and name it `Leaderboard`.
|
||||
|
||||
<img alt="Leaderboard Insert Script" src="../assets/players/leaderboard/Leaderboards-Insert-Script.png" width="320" />
|
||||
|
||||
2. In the script, connect a function to the `Class.Players.PlayerAdded|PlayerAdded` event.
|
||||
|
||||
```lua
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local function leaderboardSetup(player)
|
||||
|
||||
end
|
||||
|
||||
Players.PlayerAdded:Connect(leaderboardSetup)
|
||||
```
|
||||
|
||||
3. Inside the connected function, create a new `Class.Folder` instance, name it `leaderstats`, and parent it to the player.
|
||||
|
||||
```lua
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local function leaderboardSetup(player)
|
||||
local leaderstats = Instance.new("Folder")
|
||||
leaderstats.Name = "leaderstats"
|
||||
leaderstats.Parent = player
|
||||
end
|
||||
|
||||
Players.PlayerAdded:Connect(leaderboardSetup)
|
||||
```
|
||||
|
||||
<Alert severity="warning">
|
||||
It's essential that the folder is named `leaderstats` with all lowercase letters. Roblox doesn't add the player to the leaderboard if you name it any other way.
|
||||
</Alert>
|
||||
|
||||
## Add stats
|
||||
|
||||
Leaderboards use **value type objects** to store and display player stats. This script will show a player's gold using an `Class.IntValue`, a placeholder for an integer.
|
||||
|
||||
In the `leaderboardSetup()` function, add lines 8 through 11:
|
||||
|
||||
```lua
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local function leaderboardSetup(player)
|
||||
local leaderstats = Instance.new("Folder")
|
||||
leaderstats.Name = "leaderstats"
|
||||
leaderstats.Parent = player
|
||||
|
||||
local gold = Instance.new("IntValue")
|
||||
gold.Name = "Gold"
|
||||
gold.Value = 0
|
||||
gold.Parent = leaderstats
|
||||
end
|
||||
|
||||
Players.PlayerAdded:Connect(leaderboardSetup)
|
||||
```
|
||||
|
||||
These lines accomplish the following:
|
||||
|
||||
1. An `Class.IntValue` instance is created.
|
||||
|
||||
2. The instance's `Class.Instance.Name|Name` is set to `"Gold"`. This is exactly how the stat will appear on the leaderboard.
|
||||
|
||||
<img alt="Stat name 'Gold' shown on leaderboard" src="../assets/players/leaderboard/Leaderboard-Stat-Name.png" width="540" />
|
||||
|
||||
3. The stat's initial `Class.IntValue.Value|Value` is set to `0`. This can be set to any value you wish, including a value stored in a [data store](../cloud-services/data-stores) if you're implementing persistent leaderboards.
|
||||
|
||||
<Alert severity="info">
|
||||
Note that the value of objects such as `Class.IntValue`, `Class.NumberValue`, and `Class.StringValue` must be set through their `Value` property, as in `gold.Value` on line 10.
|
||||
</Alert>
|
||||
|
||||
4. The instance is parented to the `leaderstats` folder which adds it to the leaderboard. When a player enters the experience, their name appears on the board.
|
||||
|
||||
<img alt="Multiple players shown on leaderboard" src="../assets/players/leaderboard/Leaderboards-Multiple-Players.png" width="540" />
|
||||
|
||||
## Update stats
|
||||
|
||||
To update a player's leaderboard stat, change the `Value` property of that stat within their `leaderstats` folder. For example, you can attach the following `Class.Script` to any pickup object to increase the `Gold` stat of the player collects it.
|
||||
|
||||
```lua
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local goldChunk = script.Parent
|
||||
|
||||
local function onPartTouch(otherPart)
|
||||
local partParent = otherPart.Parent
|
||||
local player = Players:GetPlayerFromCharacter(partParent)
|
||||
local leaderstats = player and player:FindFirstChild("leaderstats")
|
||||
local goldStat = leaderstats and leaderstats:FindFirstChild("Gold")
|
||||
|
||||
if goldStat then
|
||||
-- Destroy the pickup
|
||||
goldChunk:Destroy()
|
||||
|
||||
-- Update the player's leaderboard stat
|
||||
goldStat.Value += 10
|
||||
end
|
||||
end
|
||||
|
||||
goldChunk.Touched:Connect(onPartTouch)
|
||||
```
|
||||
|
||||
## Order stats
|
||||
|
||||
There are three ways to control the order of stats in a leaderboard:
|
||||
|
||||
- Add the stats in the order that you want them to appear.
|
||||
- Add a child `Class.BoolValue` named `IsPrimary` to the stat and set its value to `true` to place the stat first in the leaderboard.
|
||||
- Add a child `Class.NumberValue` named `Priority` to the stat and set its value to an integer. Higher priority values appear earlier in the leaderboard. Stats without a priority have a default priority of `0`.
|
||||
|
||||
This code sample shows how to add an `IsPrimary` value to a stat:
|
||||
|
||||
```lua
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local function leaderboardSetup(player)
|
||||
local leaderstats = Instance.new("Folder")
|
||||
leaderstats.Name = "leaderstats"
|
||||
leaderstats.Parent = player
|
||||
|
||||
local gold = Instance.new("IntValue")
|
||||
gold.Name = "Gold"
|
||||
gold.Value = 0
|
||||
gold.Parent = leaderstats
|
||||
|
||||
local isPrimary = Instance.new("BoolValue")
|
||||
isPrimary.Name = "IsPrimary"
|
||||
isPrimary.Value = true
|
||||
isPrimary.Parent = gold
|
||||
end
|
||||
|
||||
Players.PlayerAdded:Connect(leaderboardSetup)
|
||||
```
|
||||
|
||||
<Alert severity="info">
|
||||
`IsPrimary` takes precedence over any `Priority` values. If multiple stats have `IsPrimary` values set to `true`, their `Priority` values determine the leaderboard order.
|
||||
</Alert>
|
||||
|
||||
## Hide the leaderboard
|
||||
|
||||
To hide the leaderboard, such as on a menu screen or during a cutscene, place a `Class.LocalScript` within `Class.StarterGui` or `Class.StarterPlayerScripts` containing a call to `Class.StarterGui:SetCoreGuiEnabled()|StarterGui`.
|
||||
|
||||
```lua
|
||||
local StarterGui = game:GetService("StarterGui")
|
||||
|
||||
StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false)
|
||||
```
|
||||
151
.cursor/rules/roblox/en-us/players/loading-screens.md
Normal file
151
.cursor/rules/roblox/en-us/players/loading-screens.md
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
---
|
||||
title: Loading screens
|
||||
description: Explains the process of customizing the loading screen when users are connecting to your experience.
|
||||
---
|
||||
|
||||
Roblox displays a default loading screen when users are connecting to an experience, but you can personalize your experience with a custom loading screen that contains static or animated content.
|
||||
|
||||
<Alert severity="info">
|
||||
This article covers the loading screens that display when a user initially joins an experience. To customize loading screens that display when a user teleports between places, see [Teleport between places](../projects/teleport.md#create-custom-teleport-screens).
|
||||
</Alert>
|
||||
|
||||
## Implementation
|
||||
|
||||
To display a custom loading screen, you can either design a `Class.ScreenGui` instance directly within a `Class.LocalScript`, or you can reference a `Class.ScreenGui` object in your place hierarchy. Both options utilize `Class.ReplicatedFirst`, as this service that replicates instances to clients before anything else is replicated. This ensures that your loading screen is the first thing users see when they enter your experience.
|
||||
|
||||
### From script
|
||||
|
||||
To design and display a custom loading screen entirely from a `Class.LocalScript`:
|
||||
|
||||
1. In `Class.ReplicatedFirst`, create a `Class.LocalScript`.
|
||||
2. Paste in the following code sample to create and customize a `Class.ScreenGui` object. You can modify the following code with your own values to create your design.
|
||||
|
||||
```lua title="Script-Created Loading Screen"
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedFirst = game:GetService("ReplicatedFirst")
|
||||
|
||||
local player = Players.LocalPlayer
|
||||
local playerGui = player:WaitForChild("PlayerGui")
|
||||
|
||||
local loadingScreen = Instance.new("ScreenGui")
|
||||
loadingScreen.IgnoreGuiInset = true
|
||||
loadingScreen.Parent = playerGui
|
||||
|
||||
-- Replace ScreenGui values with your own
|
||||
local textLabel = Instance.new("TextLabel")
|
||||
textLabel.Size = UDim2.new(1, 0, 1, 0)
|
||||
textLabel.BackgroundColor3 = Color3.fromRGB(0, 20, 40)
|
||||
textLabel.Font = Enum.Font.DenkOne
|
||||
textLabel.TextColor3 = Color3.new(0.8, 0.8, 0.8)
|
||||
textLabel.Text = "Loading"
|
||||
textLabel.TextSize = 32
|
||||
textLabel.Parent = loadingScreen
|
||||
|
||||
-- Remove the default loading screen
|
||||
ReplicatedFirst:RemoveDefaultLoadingScreen()
|
||||
|
||||
task.wait(5) -- Force custom loading screen to appear for a minimum time
|
||||
|
||||
if not game:IsLoaded() then
|
||||
game.Loaded:Wait()
|
||||
end
|
||||
|
||||
loadingScreen:Destroy()
|
||||
```
|
||||
|
||||
### From existing GUI
|
||||
|
||||
Instead of creating a loading screen `Class.ScreenGui` entirely through a `Class.LocalScript`, you can also reference an existing `Class.ScreenGui` in your place hierarchy. Ensure that your experience includes the referenced `Class.ScreenGui` within `Class.ReplicatedFirst` and that the `Class.ScreenGui` includes UI elements like `Class.TextLabel|TextLabels` and `Class.ImageLabel|ImageLabels`.
|
||||
|
||||
To demonstrate this process, the following `Class.LocalScript` references a `Class.ScreenGui` named **LoadingScreen** within `Class.ReplicatedFirst`, then it removes the default loading screen so the only loading screen a player can see is your own custom loading screen.
|
||||
|
||||
<Alert severity="success">
|
||||
Remember that you can design your loading screen `Class.ScreenGui` within the `Class.StarterGui` container to preview it, then move it to `Class.ReplicatedFirst` once you're satisfied with the result.
|
||||
</Alert>
|
||||
|
||||
```lua title="Reference Existing Loading Screen"
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedFirst = game:GetService("ReplicatedFirst")
|
||||
|
||||
local player = Players.LocalPlayer
|
||||
local playerGui = player:WaitForChild("PlayerGui")
|
||||
|
||||
local loadingScreen = ReplicatedFirst:FindFirstChild("LoadingScreen")
|
||||
if loadingScreen then
|
||||
loadingScreen.IgnoreGuiInset = true
|
||||
loadingScreen.Parent = playerGui
|
||||
|
||||
-- Remove the default loading screen
|
||||
ReplicatedFirst:RemoveDefaultLoadingScreen()
|
||||
|
||||
task.wait(5) -- Force screen to appear for a minimum number of seconds
|
||||
|
||||
if not game:IsLoaded() then
|
||||
game.Loaded:Wait()
|
||||
end
|
||||
|
||||
loadingScreen:Destroy()
|
||||
end
|
||||
```
|
||||
|
||||
## Add animations
|
||||
|
||||
In addition to static custom loading screens, you can add animations to enhance the loading screen and indicate loading progress. The easiest way to do this is to create a UI element, such as a `Class.TextLabel` or `Class.ImageLabel`, then to animate it using `Class.TweenService`. For example, the following code sample creates a new `Class.ScreenGui` with a child `Class.ImageLabel`, removes the default loading screen, then `Class.TweenService` fades in and rotates the central elements until the experience loads.
|
||||
|
||||
<video controls width="90%" src="../assets/ui/misc/Loading-Animation.mp4">
|
||||
</video>
|
||||
|
||||
```lua title="Script-Created Loading Screen (Animated)"
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedFirst = game:GetService("ReplicatedFirst")
|
||||
local TweenService = game:GetService("TweenService")
|
||||
|
||||
local player = Players.LocalPlayer
|
||||
local playerGui = player:WaitForChild("PlayerGui")
|
||||
|
||||
local loadingScreen = Instance.new("ScreenGui")
|
||||
loadingScreen.IgnoreGuiInset = true
|
||||
loadingScreen.Parent = playerGui
|
||||
|
||||
local textLabel = Instance.new("TextLabel")
|
||||
textLabel.Size = UDim2.new(1, 0, 1, 0)
|
||||
textLabel.BackgroundColor3 = Color3.fromRGB(0, 20, 40)
|
||||
textLabel.Font = Enum.Font.DenkOne
|
||||
textLabel.TextColor3 = Color3.new(0.8, 0.8, 0.8)
|
||||
textLabel.Text = "Loading"
|
||||
textLabel.TextSize = 32
|
||||
textLabel.TextTransparency = 1
|
||||
textLabel.Parent = loadingScreen
|
||||
|
||||
local loadingRing = Instance.new("ImageLabel")
|
||||
loadingRing.Size = UDim2.new(0, 256, 0, 256)
|
||||
loadingRing.BackgroundTransparency = 1
|
||||
loadingRing.Image = "rbxassetid://4965945816"
|
||||
loadingRing.AnchorPoint = Vector2.new(0.5, 0.5)
|
||||
loadingRing.Position = UDim2.new(0.5, 0, 0.5, 0)
|
||||
loadingRing.ImageTransparency = 1
|
||||
loadingRing.Parent = loadingScreen
|
||||
|
||||
-- Remove the default loading screen
|
||||
ReplicatedFirst:RemoveDefaultLoadingScreen()
|
||||
|
||||
-- Initiate and start fade-in tweens
|
||||
local fadeTweenInfo = TweenInfo.new(1, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, 2)
|
||||
local fadeTween1 = TweenService:Create(textLabel, fadeTweenInfo, {TextTransparency = 0})
|
||||
local fadeTween2 = TweenService:Create(loadingRing, fadeTweenInfo, {ImageTransparency = 0})
|
||||
fadeTween1:Play()
|
||||
fadeTween2:Play()
|
||||
|
||||
-- Initiate and start rotation tween
|
||||
local tweenInfo = TweenInfo.new(4, Enum.EasingStyle.Linear, Enum.EasingDirection.In, -1)
|
||||
local tween = TweenService:Create(loadingRing, tweenInfo, {Rotation = 360})
|
||||
tween:Play()
|
||||
|
||||
task.wait(5) -- Force screen to appear for a minimum number of seconds
|
||||
|
||||
if not game:IsLoaded() then
|
||||
game.Loaded:Wait()
|
||||
end
|
||||
|
||||
loadingScreen:Destroy()
|
||||
```
|
||||
60
.cursor/rules/roblox/en-us/players/teams.md
Normal file
60
.cursor/rules/roblox/en-us/players/teams.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
title: Implement teams
|
||||
description: The Teams service allows you assign players to different teams within your experience.
|
||||
---
|
||||
|
||||
You can divide players in your experience into multiple teams using the `Class.Teams` service. By [configuring](#add-teams) the `Class.Teams` service and [assigning teams](#assign-players-to-teams), players are automatically differentiated by their [display name](../characters/name-health-display.md) color, as well as their name in the default player list.
|
||||
|
||||
## Add teams
|
||||
|
||||
By default, no teams are configured. To add teams:
|
||||
|
||||
1. In the [Explorer](../studio/explorer.md) hierarchy, hover over the `Class.Teams` object and click the **⊕** icon to show a list of objects.
|
||||
2. Insert a new `Class.Team` object under the `Class.Teams` service.
|
||||
|
||||
<img src="../assets/studio/explorer/Teams-Team.png" width="320" />
|
||||
|
||||
3. Select the new `Class.Team` object and, in the [Properties](../studio/properties.md) window, change the team `Class.Team.Name|Name` and assign a unique matching `Class.Team.TeamColor|TeamColor`. A team's name and color both appear in the experience's player list.
|
||||
|
||||
<img src="../assets/studio/properties/Team-Name-TeamColor.png" width="320" />
|
||||
|
||||
4. Repeat these steps to add more teams.
|
||||
|
||||
## Assign players to teams
|
||||
|
||||
By default, Roblox **auto-assigns** new players joining the experience to the team with the fewest members, and you can still use the following steps to assign players to a specific team.
|
||||
|
||||
1. Select each `Class.Team` object in the `Class.Teams` service to display its properties.
|
||||
|
||||
2. Uncheck the `Class.Team.AutoAssignable|AutoAssignable` checkbox.
|
||||
|
||||
<img src="../assets/studio/properties/Team-AutoAssignable-Disabled.png" width="320" />
|
||||
|
||||
3. Assign a player to a specific team by changing their `Class.Player.Team` property to the team name in the format of `Teams[TEAM_NAME]`, such as <Typography noWrap>`Teams["Blue Team"]`</Typography>.
|
||||
|
||||
## Team spawn locations
|
||||
|
||||
You can use `Class.SpawnLocation` objects to spawn players of different teams at specific locations when they join or respawn. By default, `Class.SpawnLocation|SpawnLocations` are **neutral** and any player can spawn upon them, so you need to lock each one to the team that can occupy it using the following steps:
|
||||
|
||||
1. Using the [Explorer](../studio/explorer.md), insert a new `Class.SpawnLocation` into the workspace.
|
||||
2. With the new `Class.SpawnLocation` selected, uncheck its `Class.SpawnLocation.Neutral|Neutral` checkbox in the [Properties](../studio/properties.md) window.
|
||||
3. Set the spawn's `Class.SpawnLocation.TeamColor|TeamColor` property to the `Class.Team.TeamColor|TeamColor` of an existing [team](#add-teams). For example, if the intended team's `Class.Team.TeamColor` is `Lapis`, set the spawn's `Class.SpawnLocation.TeamColor` to `Lapis` as well.
|
||||
|
||||
<Grid container spacing={3} alignItems="end">
|
||||
<Grid item>
|
||||
<figure>
|
||||
<img src="../assets/studio/properties/Team-Name-TeamColor.png" width="320" />
|
||||
<figcaption>`Class.Team.TeamColor`</figcaption>
|
||||
</figure>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<figure>
|
||||
<img src="../assets/studio/properties/SpawnLocation-TeamColor.png" width="320" />
|
||||
<figcaption>`Class.SpawnLocation.TeamColor`</figcaption>
|
||||
</figure>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Alert severity="warning">
|
||||
Note that the `Class.SpawnLocation.TeamColor` property is different from `Class.SpawnLocation.BrickColor` and `Class.SpawnLocation.Color` which represent the **visual** color of the spawn object and are not related to team functionality.
|
||||
</Alert>
|
||||
216
.cursor/rules/roblox/en-us/players/tools.md
Normal file
216
.cursor/rules/roblox/en-us/players/tools.md
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
---
|
||||
title: In-experience tools
|
||||
description: Create in-experience tools for your players.
|
||||
---
|
||||
|
||||
In-experience `Class.Tool|Tools` are interactive tools that players can equip in sessions, such as weapons, magic wands, flashlights, keys, and more. You can design tools and include them in a player's inventory or place them around the world as pickups.
|
||||
|
||||
## Create an in-experience tool
|
||||
|
||||
The `Class.Tool` object is the basis of any tool in Roblox, so you'll need to create one. It's easier to preview how a tool looks by designing it in the `Class.Workspace`, and later you can make it an [inventory](#player-inventory) item or an [earned/purchased](#earnedpurchased-tool) item.
|
||||
|
||||
1. In the [Explorer](../studio/explorer.md) hierarchy, hover over `Class.Workspace`, click the **⊕** button, and insert a `Class.Tool`.
|
||||
|
||||
<img src="../assets/studio/explorer/Workspace-Tool.png" width="320" alt="Tool object created in the workspace" />
|
||||
|
||||
2. With the new `Class.Tool` selected, access the [Properties](../studio/properties.md) window and customize how the tool will appear in the player's inventory:
|
||||
|
||||
- `Class.Tool.TextureId|TextureId` — Specifies the image asset ID for the tool in a player's inventory.
|
||||
- `Class.Tool.ToolTip|ToolTip` — The on-hover tooltip name for the tool in a player's inventory.
|
||||
|
||||
<img src="../assets/players/in-experience-tools/Tool-Inventory.jpg" width="800" />
|
||||
|
||||
3. Decide whether the tool will be a [physical tool in the 3D environment](#physical-tool) (most common) or a [non‑physical](#non-physical-tool) tool that simply occupies a player's inventory and responds to their input.
|
||||
|
||||
### Physical tool
|
||||
|
||||
After creating the `Class.Tool` instance for a physical tool, you'll need to [add parts/meshes](#add-partsmeshes) to compose its appearance in the 3D world. You'll also need to include a [handle](#set-the-handle) which player characters [grip](#adjust-the-grip-positionorientation).
|
||||
|
||||
#### Add parts/meshes
|
||||
|
||||
In-experience tools can consist of one or more `Class.Part|Parts` and/or `Class.MeshPart|MeshParts`. If you construct the tool of multiple pieces, remember to connect them all together using `Class.WeldConstraint|WeldConstraints` or other [mechanical constraints](../physics/mechanical-constraints.md) so that the tool stays intact as a unit while the player carries it around.
|
||||
|
||||
<img src="../assets/studio/general/Toolbar-Constraint-Pickers.png" width="800" alt="Constraint pickers indicated in Studio's toolbar" />
|
||||
|
||||
<Alert severity="warning">
|
||||
When constructing tools, make sure that no parts are `Class.BasePart.Anchored|Anchored`. If you anchor the tool in 3D space, player characters will get stuck in place when they pick up or equip the tool.
|
||||
|
||||
If you want to place collectible tools as pickups around the 3D world and anchor them in specific places, such as floating slightly above the ground, you can anchor them but you'll need to add a `Class.Script` to each tool which unanchors its parts upon collection. See [collectible tools](#collectible-tool) for details.
|
||||
</Alert>
|
||||
|
||||
#### Set the handle
|
||||
|
||||
To enable players to carry tools around, one part of the tool needs to be named `Handle`. This is where the tool will attach to the character model's `RightHand` part (R15) or <Typography noWrap>`Right Arm`</Typography> part (R6).
|
||||
|
||||
If the tool is composed of just one `Class.MeshPart`, simply name that part `Handle`. Otherwise, you can include a separate part named `Handle` as a **direct child** of the parent `Class.Tool` and then attach it to another part with a `Class.WeldConstraint` or [mechanical constraint](../physics/mechanical-constraints.md).
|
||||
|
||||
The following example shows a tool with three instances: the `Handle` mesh, a `Class.RopeConstraint`, and a lantern mesh which hangs from the rope.
|
||||
|
||||
<img src="../assets/players/in-experience-tools/Tool-Handle-Example.jpg" width="720" />
|
||||
|
||||
<Alert severity="warning">
|
||||
Make sure to include only **one** part named `Handle`. If you include multiple parts named `Handle`, one will be randomly chosen as the attachment point for the character's hand, potentially causing issues such as the character holding a sword's blade instead of its hilt.
|
||||
</Alert>
|
||||
|
||||
#### Adjust the grip position/orientation
|
||||
|
||||
The tool's `Class.Tool.Grip|Grip` property is a `Datatype.CFrame` which specifies how the tool is positioned and oriented relative to the character holding it.
|
||||
|
||||
<img src="../assets/studio/properties/Tool-Grip.png" width="320" />
|
||||
|
||||
Since the ideal grip position/orientation is different for every tool, you'll need to experiment with the values until your tool's grip looks correct. The following example shows possible incorrect and correct `Class.Tool.Grip|Grip` settings for the hanging lantern:
|
||||
|
||||
<GridContainer numColumns="2">
|
||||
<figure>
|
||||
<img src="../assets/players/in-experience-tools/Tool-Grip-Incorrect.jpg" />
|
||||
<figcaption>Lantern dragging on ground with `Class.Tool.Grip|Grip.Position` of <Typography noWrap>`(0, -2, 0)`</Typography> and `Class.Tool.Grip|Grip.Orientation` of <Typography noWrap>`(-90, 0, 0)`</Typography></figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<img src="../assets/players/in-experience-tools/Tool-Grip-Correct.jpg" />
|
||||
<figcaption>Lantern in front of character with `Class.Tool.Grip|Grip.Position` of <Typography noWrap>`(0, -1.5, 0)`</Typography> and `Class.Tool.Grip|Grip.Orientation` of <Typography noWrap>`(25, 0, 0)`</Typography></figcaption>
|
||||
</figure>
|
||||
</GridContainer>
|
||||
|
||||
### Non-physical tool
|
||||
|
||||
A non-physical `Class.Tool` simply occupies a player's [inventory](#player-inventory) and responds to their input. For example, a roleplaying experience might feature a series of magical spells that player's can "cast" through their inventory.
|
||||
|
||||
<img src="../assets/players/in-experience-tools/Tool-Inventory-Non-Physical.jpg" width="800" />
|
||||
|
||||
Non-physical tools don't require any 3D composition and do not utilize a `Handle` part, so you should **disable** the tool's `Class.Tool.RequiresHandle|RequiresHandle` property:
|
||||
|
||||
<img src="../assets/studio/properties/Tool-RequiresHandle.png" width="320" />
|
||||
|
||||
## Add tools to an experience
|
||||
|
||||
Once you finish [creating](#create-an-in-experience-tool) your in-experience tool, you'll need to place it in the correct area of the [Explorer](../studio/explorer.md) hierarchy. Where you place the tool in the hierarchy depends on its intended usage.
|
||||
|
||||
### Player inventory
|
||||
|
||||
If you want all players to start out with a tool in their inventory, put it inside the `Class.StarterPack` container. When any player spawns, the system copies the tool to their `Class.Backpack`. From there, the player can equip the tool by clicking its inventory icon or by pressing the associated hotkey such as <kbd>1</kbd> or <kbd>2</kbd>.
|
||||
|
||||
<img src="../assets/studio/explorer/StarterPack-Tools.png" width="320" />
|
||||
|
||||
Note that tools are copied to the player's inventory (`Class.Backpack`) in the order they were added to `Class.StarterPack`. For a more controlled ordering, you can add a `Class.LocalScript` to `Class.StarterPack` which temporarily moves the tools to `Class.ReplicatedStorage` then re‑parents them to `Class.Backpack` in the order specified in the `order` table.
|
||||
|
||||
```lua title="Reorder Inventory"
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
|
||||
local order = {"WaterSpell", "FireSpell", "AirSpell", "EarthSpell"}
|
||||
local backpack = script.Parent
|
||||
|
||||
for _, item in backpack:GetChildren() do
|
||||
if item:IsA("Tool") then
|
||||
item.Parent = ReplicatedStorage
|
||||
end
|
||||
end
|
||||
|
||||
-- Re-parent tools to backpack in the specified order
|
||||
for i = 1, #order do
|
||||
local tool = ReplicatedStorage:FindFirstChild(order[i])
|
||||
if tool then
|
||||
tool.Parent = backpack
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Collectible tool
|
||||
|
||||
If you want to allow players to collect physical tools as they explore the 3D world, place the tools in the `Class.Workspace` just like normal 3D objects. Collected tools will be automatically equipped and added to the player's inventory (you do not need to add extra collision detection for characters to grab the tool).
|
||||
|
||||
As noted in the [add parts/meshes](#add-partsmeshes) section, tool parts should not typically be `Class.BasePart.Anchored|Anchored`, since player characters will get stuck in place when they pick up or equip an anchored tool. However, if you want to anchor a collectible tool to a specific place in the 3D world, such as floating slightly above the ground, you can add the following `Class.Script` to the tool to unanchor its parts upon collection.
|
||||
|
||||
```lua title="Unanchor Tool on Equip/Collect"
|
||||
local tool = script.Parent
|
||||
|
||||
tool.Equipped:Connect(function()
|
||||
for _, part in tool:GetDescendants() do
|
||||
if part:IsA("BasePart") and part.Anchored then
|
||||
part.Anchored = false
|
||||
end
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
### Earned/purchased tool
|
||||
|
||||
If you want to set tools as awards for when players accomplish tasks, or offer tools for sale in an in‑experience store, put those tools inside `Class.ReplicatedStorage`. From there, you can `Class.Instance:Clone()|Clone()` a tool to the player's `Class.Backpack` at the proper time.
|
||||
|
||||
<img src="../assets/studio/explorer/ReplicatedStorage-Tools.png" width="320" />
|
||||
|
||||
```lua title="Clone Tool to Player's Inventory"
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
|
||||
local player = Players.LocalPlayer
|
||||
local backpack = player:WaitForChild("Backpack")
|
||||
|
||||
-- Create a clone of the tool
|
||||
local tool = ReplicatedStorage:FindFirstChild("WaterSpell")
|
||||
if tool then
|
||||
local clone = tool:Clone()
|
||||
clone.Parent = backpack -- Adds clone to the player's backpack
|
||||
end
|
||||
```
|
||||
|
||||
## Implement tool effects
|
||||
|
||||
After [adding tools to your experience](#add-tools-to-an-experience), you can implement scripts to enable players to use their tools and perform the desired actions. The following tool‑specific events indicate the state of the tool and the player's input with it.
|
||||
|
||||
- `Class.Tool.Equipped|Equipped()` — Fires when the player selects the tool from their backpack.
|
||||
- `Class.Tool.Unequipped|Unequipped()` — Fires when the player switches tools or drops the tool.
|
||||
- `Class.Tool.Activated|Activated()` — Fires when the player starts activating the tool (clicks, taps, or presses the back‑right trigger on a gamepad).
|
||||
- `Class.Tool.Deactivated|Deactivated()` — Fires when the player stops the activation input (releases the button or touch).
|
||||
|
||||
```lua title="Tool Events Template"
|
||||
local tool = script.Parent
|
||||
|
||||
local function onEquip()
|
||||
print(tool.Name, "tool equipped")
|
||||
end
|
||||
local function onUnequip()
|
||||
print(tool.Name, "tool unequipped")
|
||||
end
|
||||
local function onActivate()
|
||||
print(tool.Name, "tool activated")
|
||||
end
|
||||
local function onDeactivate()
|
||||
print(tool.Name, "tool deactivated")
|
||||
end
|
||||
|
||||
tool.Equipped:Connect(onEquip)
|
||||
tool.Unequipped:Connect(onUnequip)
|
||||
tool.Activated:Connect(onActivate)
|
||||
tool.Deactivated:Connect(onDeactivate)
|
||||
```
|
||||
|
||||
Tool events only fire in client‑side `Class.LocalScript|LocalScripts` because only the player's device knows when [input](../input/index.md) happens. As a result, most tools require both a `Class.LocalScript` and a server‑side `Class.Script` where each handles certain aspects of the tool's behavior and a `Class.RemoteEvent` passes information from the client to the server (see [remote events and callbacks](../scripting/events/remote.md) for more information).
|
||||
|
||||
As follows are example tools and their behaviors managed by either a `Class.LocalScript` or a `Class.Script`:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Tool</td>
|
||||
<td>`Class.LocalScript`</td>
|
||||
<td>`Class.Script`</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Creator's Wand**</td>
|
||||
<td>Detects where the player activates the tool (`Class.Tool.Activated|Activated()`).</td>
|
||||
<td>Creates a new part at the location within the 3D world where the player touched or clicked.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Invisibility Cloak**</td>
|
||||
<td>Detects when the player equips the tool (`Class.Tool.Equipped|Equipped()`) or unequips the tool (`Class.Tool.Unequipped|Unequipped()`).</td>
|
||||
<td>Makes the player invisible to all other players while the cloak is equipped and restores the player to full visibility when the cloak is unequipped.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Mega‑Bow**</td>
|
||||
<td>Detects how long the player activates the tool (time between `Class.Tool.Activated|Activated()` and `Class.Tool.Deactivated|Deactivated()`).</td>
|
||||
<td>Shoots a magical arrow with greater or lesser power, depending on the detected activation time.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
Loading…
Add table
Add a link
Reference in a new issue