Creating a Health Pickup

Throughout the Basic Scripting tutorials, you have scripted individual parts to create playable scenes. With the previous method, if you were to duplicate the parts you would then also have duplicate scripts. This would make updating the scripts tedious as changes would have to be done script by script.

In this tutorial, a different pattern will be used to create a number of health pickups, with only a single copy of the script which determines the health pickup behaviour. When the pickup is touched, it will restore the player's health, fade slightly and be disabled for a short period of time.

Setting Up

First up, you'll need a part or a model to use as a pickup. The Showdown Town example world includes plenty of health pickups spread all over the map - you can open it in Studio by clicking Edit as shown.

Example game page open with Edit button highlighted under the Menu icon

Each health pickup is a Union of two rectangular parts with a green PointLight inside. They're all stored in one folder in the Workspace called HealthPickups, which is where the script will look for them. If you add any more to the map, it's essential you ensure that they are also stored in this folder.

Restoring Health

To begin with, the script needs to restore a player's health. This pattern should be familiar to you from the Deadly Lava tutorial.

  1. In ServerScriptService, add a script called PickupManager.

  2. In this script, declare a constant called MAX_HEALTH with the value 100.

  3. Create a function called onTouchHealthPickup with parameters for the other part that touched the pickup and the pickup itself.


    1local MAX_HEALTH = 100
    2
    3local function onTouchHealthPickup(otherPart, healthPickup)
    4
    5end
    6
  4. In the function, get the character model from the parent of otherPart. Next, check to see if it has a Humanoid using FindFirstChildWhichIsA.

  5. If it has a humanoid, set their Health property to MAX_HEALTH.


    1local MAX_HEALTH = 100
    2
    3local function onTouchHealthPickup(otherPart, healthPickup)
    4 local character = otherPart.Parent
    5 local humanoid = character:FindFirstChildWhichIsA("Humanoid")
    6 if humanoid then
    7 humanoid.Health = MAX_HEALTH
    8 end
    9end
    10

Getting the Pickups Folder

The folder holding the health pickups may not have loaded into the game by the time the script runs. WaitForChild can be used to pause the script and get the HealthPickups folder when it loads.

When called on a folder, the GetChildren function will return an array of the folder's contents.

  1. Beneath MAX_HEALTH, declare a variable called healthPickupsFolder and use the WaitForChild function to get the HealthPickups folder from the Workspace.

  2. Create a variable named healthPickups to store the result of calling the GetChildren function on healthPickupsFolder.


    1local MAX_HEALTH = 100
    2
    3local healthPickupsFolder = workspace:WaitForChild("HealthPickups")
    4local healthPickups = healthPickupsFolder:GetChildren()
    5
    6local function onTouchHealthPickup(otherPart, healthPickup)
    7local character = otherPart.Parent
    8 local humanoid = character:FindFirstChildWhichIsA("Humanoid")
    9 if humanoid then
    10 humanoid.Health = MAX_HEALTH
    11 end
    12end
    13

Looping with ipairs

onTouchHealthPickup needs to be called for each health pickup in the array. To do this efficiently, a new kind of loop syntax will be used.

ipairs is a function that can be used with a for loop to go through each element of an array. You do not need to specify where the loop begins and ends. A for loop using ipairs is defined as follows:

for index, value in ipairs(array) do

  • Index: this is equivalent to the control variable in a regular for loop.
  • Value: this will be populated with each element in the array as the loop iterates. It's a good idea to name the value variable after what it will actually contain.
  • Array: the array you want to iterate over is passed to the ipairs function.

In the following code, you don't need the index for anything, so it can be left blank with _. Create a for loop using the ipairs function, passing healthPickups.


1local function onTouchHealthPickup(otherPart, healthPickup)
2 local character = otherPart.Parent
3 local humanoid = character:FindFirstChildWhichIsA("Humanoid")
4 if humanoid then
5 humanoid.Health = MAX_HEALTH
6 end
7end
8
9for _, healthPickup in ipairs(healthPickups) do
10
11end
12

A wrapper function will be needed to pass the health pickup to the onTouchHealthPickup function when connecting to the Touched event.

  1. In the for loop, connect the Touched event to an anonymous function with a parameter called otherPart.

  2. Call the onTouchHealthPickups function, passing both the otherPart parameter and the healthPickup.


    1for _, healthPickup in ipairs(healthPickups) do
    2 healthPickup.Touched:Connect(function(otherPart)
    3 onTouchHealthPickup(otherPart, healthPickup)
    4 end)
    5end
    6

Test your code now and you should find that the health pickup restores your health. You will need to damage your player first - try standing on the vent next to the spawn point.

Steaming vent in example world to the right of the spawn point

A health bar should appear in the top right which will disappear when the player is healed.

Pickup Cooldown

Currently, the pickup will indefinitely heal any player who touches it. It would be more effective in a game if it could only be picked up once, with a short cooldown before it can be used again.

First, you need to record whether or not the pickup is in the cooldown period. The pattern below should be familiar from Fading Trap - this time, the debounce will be achieved by setting an attribute on the health pickup.

  1. In the for loop, set a new attribute called "Enabled" to true.

  2. Wrap the code inside onTouchHealthPickup in an if statement with the condition healthPickup:GetAttribute("Enabled").


    1local function onTouchHealthPickup(otherPart, healthPickup)
    2 if healthPickup:GetAttribute("Enabled") then
    3 local character = otherPart.Parent
    4 local humanoid = character:FindFirstChildWhichIsA("Humanoid")
    5 if humanoid then
    6 humanoid.Health = MAX_HEALTH
    7 end
    8 end
    9end
    10
    11for _, healthPickup in ipairs(healthPickups) do
    12 healthPickup:SetAttribute("Enabled", true)
    13 healthPickup.Touched:Connect(function(otherPart)
    14 onTouchHealthPickup(otherPart, healthPickup)
    15 end)
    16end
    17

Disabling the Pickup

The pickup should provide visual feedback that it is disabled - a common way to indicate this is to make it slightly transparent.

  1. Declare three constants at the top of the script (feel free to adjust each value to your liking):

    • ENABLED_TRANSPARENCY = 0.4
    • DISABLED_TRANSPARENCY = 0.9
    • COOLDOWN = 10

    1local MAX_HEALTH = 100
    2local ENABLED_TRANSPARENCY = 0.4
    3local DISABLED_TRANSPARENCY = 0.9
    4local COOLDOWN = 10
    5
    6local healthPickupsFolder = workspace:WaitForChild("HealthPickups")
    7
  2. In the if statement in onTouchHealthPickup, set the Transparency of the pickup to DISABLED_TRANSPARENCY, and the value of the Enabled attribute to false.


    1local function onTouchHealthPickup(otherPart, healthPickup)
    2 if healthPickup:GetAttribute("Enabled") then
    3 local character = otherPart.Parent
    4 local humanoid = character:FindFirstChildWhichIsA("Humanoid")
    5 if humanoid then
    6 humanoid.Health = MAX_HEALTH
    7 healthPickup.Transparency = DISABLED_TRANSPARENCY
    8 healthPickup:SetAttribute("Enabled", false)
    9 end
    10 end
    11end
    12
  3. Call the task.wait function, passing COOLDOWN as the amount to wait.

  4. Set Transparency back to ENABLED_TRANSPARENCY and Enabled back to true.


    1local function onTouchHealthPickup(otherPart, healthPickup)
    2 if healthPickup:GetAttribute("Enabled") then
    3 local character = otherPart.Parent
    4 local humanoid = character:FindFirstChildWhichIsA("Humanoid")
    5 if humanoid then
    6 humanoid.Health = MAX_HEALTH
    7 healthPickup.Transparency = DISABLED_TRANSPARENCY
    8 healthPickup:SetAttribute("Enabled", false)
    9 task.wait(COOLDOWN)
    10 healthPickup.Transparency = ENABLED_TRANSPARENCY
    11 healthPickup:SetAttribute("Enabled", true)
    12 end
    13 end
    14end
    15

Test your pickup again: you should find that when you touch the pickup it restores your health, goes transparent, then comes back ready to be used again.

If you want to make the feedback more impactful for the player when the pickup is collected, try cutting the brightness of the PointLight in the pickup when you change the transparency.

Try using these health pickups in your own projects, or change the appearance and effect to give a different kind of power-up to your players.

Final Code


1local MAX_HEALTH = 100
2local ENABLED_TRANSPARENCY = 0.4
3local DISABLED_TRANSPARENCY = 0.9
4local COOLDOWN = 10
5
6local healthPickupsFolder = workspace:WaitForChild("HealthPickups")
7local healthPickups = healthPickupsFolder:GetChildren()
8
9local function onTouchHealthPickup(otherPart, healthPickup)
10 if healthPickup:GetAttribute("Enabled") then
11 local character = otherPart.Parent
12 local humanoid = character:FindFirstChildWhichIsA("Humanoid")
13 if humanoid then
14 humanoid.Health = MAX_HEALTH
15 healthPickup.Transparency = DISABLED_TRANSPARENCY
16 healthPickup:SetAttribute("Enabled", false)
17 task.wait(COOLDOWN)
18 healthPickup.Transparency = ENABLED_TRANSPARENCY
19 healthPickup:SetAttribute("Enabled", true)
20 end
21 end
22end
23
24for _, healthPickup in ipairs(healthPickups) do
25 healthPickup:SetAttribute("Enabled", true)
26 healthPickup.Touched:Connect(function(otherPart)
27 onTouchHealthPickup(otherPart, healthPickup)
28 end)
29end
30