Scavenger Hunt

The ScavengerHunt developer module gives players an inherently gamified way to explore your experience, organically introducing them to the entire place. Player progress is persistent, so scavenger hunts can continue across sessions.

Module Usage

Installation

To use the ScavengerHunt module in an experience:

  1. From the View tab, open the Toolbox and select the Creator Store tab.

    Toolbox toggle button in Studio
  2. Make sure the Models sorting is selected, then click the See All button for Categories.

  3. Locate and click the Dev Modules tile.

  4. Locate the Scavenger Hunt module and click it, or drag-and-drop it into the 3D view.

  5. In the Explorer window, move the entire ScavengerHunt model into ServerScriptService. Upon running the experience, the module will distribute itself to various services and begin running.

Using Tokens

The scavenger hunt module uses tokens as the items which players search for and collect. The module comes with one token model that you can position in the 3D world.

  1. Locate the Token1 mesh inside the Workspace folder of the module's main folder.

  2. Move Token1 into the top-level Workspace hierarchy and position it where desired.

  3. Give the token a unique name; this name is how the module tracks which tokens each player has collected.

  4. To add more tokens, duplicate an existing token and give it a unique name.

If you don't want to use the bundled mesh tokens, any Model or BasePart will work, as long as it meets the following criteria:

  • Object has a CollectionService tag of ScavengerHuntPart. If desired, the CollectionService tag name which the module utilizes can be changed by setting a different value for tokenTag in a configureServer call.

  • Object contains a child StringValue instance set to the "flavor text" to display when the token is collected.

    Model
    MeshPart

Using Regions

Regions differ slightly from tokens, as large areas that are marked as "collected" once the player enters them. Additionally, when a player leaves the region, the flavor text modal automatically dismisses and the region itself is removed from the workspace.

  1. Create an anchored part around the region, such as a block or sphere. The module will automatically disable the CanCollide property on runtime so players do not physically collide with the region.

  2. Give it a unique name. This name is how the module tracks which regions each player has entered.

  3. Using the Tags section of the part's properties, or Studio's Tag Editor, apply the tag ScavengerHuntPart to the part so that CollectionService detects it. If desired, the tag name which the module utilizes can be changed by setting a different value for tokenTag in a configureServer call.

  4. Include a child StringValue instance set to the "flavor text" to display when the region is entered.

Configuration

The module is preconfigured to work for most use cases, but it can be easily customized. For example, to change the token rotation speed and customize the modal info message:

  1. In StarterPlayerScripts, create a new LocalScript and rename it to ConfigureScavengerHunt.

  2. Paste the following code into the new script.

    LocalScript - ConfigureScavengerHunt

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
    ScavengerHunt.configureClient({
    infoModalText = "Welcome to my Scavenger Hunt!",
    completeModalText = "Thanks for playing my Scavenger Hunt!",
    tokenRotationSpeed = 60,
    })

Collection Events

Every time a player collects a token or enters a region, the collected event fires. You can listen to this event from a server-side Script and respond accordingly. The connected function receives the Player that collided with the token or entered the region and that token or region's name.

Similarly, when a player collects all tokens or enters all tagged regions, the allCollected event fires and the connected function receives the associated Player. This function is only fired once per player and it can be used to reward that player with a badge, access to a new area, in-experience currency, etc.

Script

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.collected:Connect(function(player, itemName)
print(player.DisplayName, itemName)
end)
ScavengerHunt.allCollected:Connect(function(player)
print(player.DisplayName .. " completed the hunt!")
end)

Custom GUI

This module exposes several options to customize its default GUI, but you can opt to display custom GUI elements instead.

When useCustomModals is set to true in the configureClient function, the showInfoModal event fires every time the player activates the token tracker. Similarly, the showCompleteModal event fires when the player has collected everything in the scavenger hunt. Both of these events can be listened to in a LocalScript.

LocalScript

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.showInfoModal:Connect(function()
-- Show a custom info modal
local infoModal = Players.LocalPlayer.PlayerGui.ScavengerInfoModal
infoModal.Enabled = true
end)
ScavengerHunt.showCompleteModal:Connect(function()
-- Show a custom complete modal
local completeModal = Players.LocalPlayer.PlayerGui.ScavengerCompleteModal
completeModal.Enabled = true
end)

GUI Visibility

By default, the scavenger hunt hides all ScreenGuis and CoreGuis (except for the player list) when the info modal or completion modal appears. If you want to override this auto-hiding behavior and programmatically decide which GUIs should remain visible, include the hideOtherGuis and showOtherGuis callbacks and respond with your own custom logic.

LocalScript

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StarterGui = game:GetService("StarterGui")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local hiddenInstances = {}
-- Create a screen GUI that will not be hidden
local specialGuiInstance = Instance.new("ScreenGui")
-- Draw the screen GUI above the scavenger hunt GUI
specialGuiInstance.DisplayOrder = 1
specialGuiInstance.Parent = playerGui
-- Add text label to the GUI
local specialLabel = Instance.new("TextLabel")
specialLabel.Size = UDim2.fromScale(1, 0.1)
specialLabel.Text = "Remains visible when displaying modals"
specialLabel.Font = Enum.Font.GothamMedium
specialLabel.TextSize = 24
specialLabel.Parent = specialGuiInstance
ScavengerHunt.hideOtherGuis(function()
-- Hide all developer-defined screen GUIs
local instances = playerGui:GetChildren()
for _, instance in instances do
if instance:IsA("ScreenGui") and not instance.Name == "ScavengerHunt" and instance.Enabled then
instance.Enabled = false
table.insert(hiddenInstances, instance)
end
end
-- Hide specific core GUIs
StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false)
end)
ScavengerHunt.showOtherGuis(function()
-- Show all developer-defined screen GUIs that were hidden
for _, instance in hiddenInstances do
instance.Enabled = true
end
hiddenInstances = {}
-- Show specific core GUIs that were hidden
StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, true)
end)

API Reference

Functions

configureClient

configureClient(config: table)

Overrides default client-side configuration options through the following keys/values in the config table. This function can only be called from a LocalScript.

KeyDescriptionDefault
autoDismissTimeTime in seconds before the modal automatically dismisses itself or navigates to the next page if there is one. Set to 0 to disable.20
closeModalGamepadGamepad button used to close modals (Enum.KeyCode).ButtonA
closeModalKeyboardKeyboard key used to close modals (Enum.KeyCode).E
completeModalTextText to show on the modal that appears after clicking the token tracker when the scavenger hunt is complete."Thanks for participating!"
infoModalTextText to show on the modal that appears after clicking the token tracker."Find all the tokens to complete the hunt"
tokenRotationSpeedSpeed at which the tokens rotate, in degrees per second. Set to 0 to prevent rotation.20
nextArrowImageImage used to indicate there are more modal pages to show after the current modal page."rbxassetid://8167172095"
openTokenTrackerGamepadGamepad button used to show the modals that appear after activating the token tracker (Enum.KeyCode).ButtonY
openTokenTrackerKeyboardKeyboard key used to show the modals that appear after activating the token tracker (Enum.KeyCode).Y
openTokenTrackerGamepadButtonImageImage for the gamepad button that is used to activate the token tracker."rbxassetid://8025860488"
regionIconIcon to display next to the token tracker when entering regions."rbxassetid://8073794624"
tokenIconIcon to display next to the token tracker when collecting tokens."rbxassetid://8073794477"
tokenTrackerPositionSmallDevicePosition of the token tracker UI on small devices such as phones (UDim2).(1, 0, 0, 84)
tokenTrackerPositionLargeDevicePosition of the token tracker UI on larger devices like tablets and PC (UDim2).(1, 0, 1, -16)
useRegionsInstead of tokens, use regions.false
LocalScript

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.configureClient({
infoModalText = "Welcome to my Scavenger Hunt!",
completeModalText = "Thanks for playing my Scavenger Hunt!",
tokenRotationSpeed = 60,
navigationBeam = {
lightEmission = 1
},
modal = {
textSize = 14
},
})

configureServer

configureServer(config: table)

Overrides default server-side configuration options through the following keys/values in the config table. This function can only be called from a Script.

KeyDescriptionDefault
tokenTagTag used by CollectionService to find all the tokens or regions used in the scavenger hunt."ScavengerHuntPart"
datastoreNameName of the DataStore used by the scavenger hunt to store each player's collection progress."ScavengerHuntTokens"
resetOnPlayerRemovingIf true, resets the user's progress when they leave the experience; convenient for not saving progress while testing the scavenger hunt.false
Script

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.configureServer({
tokenTag = "GreenGem",
})

disable

disable()

Hides all UI for the scavenger hunt, disconnects all input event listeners, and prevents players from collecting tokens or interacting with regions. This function can only be called from a Script.

Script

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.disable()

enable

enable()

Shows all UI for the scavenger hunt, connects all input event listeners, and allows players to collect tokens and interact with regions. This function can only be called from a Script.

Script

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.enable()

Events

collected

Fires when a player collides with a token or enters a region. The connected function will receive the Player that collided with the token or entered the region and the name of the token that was collided into or the region that was entered. This event can only be connected in a Script.

Parameters
player: PlayerUser who collided with a token or entered a region.
itemName: string Name of the token that was collided into or the region that was entered.
totalCollected: numberTotal number of tokens collected by the user represented by player.
Script

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.collected:Connect(function(player, itemName, totalCollected)
print(player.DisplayName, itemName, totalCollected)
end)

allCollected

Fires when a player collects all tokens or enters all regions in the scavenger hunt. The connected function will receive the Player that collected all tokens, and it is only ever fired once per player. This event can only be connected in a Script.

Parameters
player: PlayerPlayer who collected all tokens or entered all regions.
Script

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.allCollected:Connect(function(player)
print(player.DisplayName .. " completed the hunt!")
end)

showInfoModal

Fires when the player clicks on the token tracker when the useCustomModals configuration option is set to true. This event can only be connected in a LocalScript.

LocalScript

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.showInfoModal:Connect(function()
local infoModal = Players.LocalPlayer.PlayerGui.InfoModal
infoModal.Enabled = true
end)

showCompleteModal

Fires when the player clicks on the token tracker when the useCustomModals configuration option is set to true and the player has collected all tokens in the scavenger hunt. This event can only be connected in a LocalScript.

LocalScript

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScavengerHunt = require(ReplicatedStorage:WaitForChild("ScavengerHunt"))
ScavengerHunt.showCompleteModal:Connect(function()
local completeModal = Players.LocalPlayer.PlayerGui.CompleteModal
completeModal.Enabled = true
end)

Callbacks

hideOtherGuis

hideOtherGuis(callback: function)

This callback runs immediately before a modal is displayed, letting you disable entire ScreenGuis or elements within them before the modal is shown. See GUI Visibility for details and sample code.

showOtherGuis

showOtherGuis(callback: function)

This callback runs immediately after a modal has been dismissed, letting you enable entire ScreenGuis or elements within them. See GUI Visibility for details and sample code.