Search

Core Game Dev - The Haunted Keep MMORPG

Core Game Dev: How To Make A Health Pickup

Do you want a way to give your Core players a Health Boost but you're not sure how?  Check out this tutorial on how to make a Health Pickup in Core that increases your health when you consume it.

How To Create A Health Pickup In Core

In this example, I will show you how to make a Health Pickup item that restores 10 Hit Points when you touch it.

We will use the finished product as a basis for expansion in future articles where we will talk more about adding more functions and optimizing for network use.

Let's get started!

The Requirements:

Consumed on touch regardless of how many HP you have.

Cannot go over MaxHP

Plays 1 video effect

Plays 1 audio effect

Restores 10 Hit points

We're going to need:

  • A trigger to detect when the Character has touched the Health Pickup.
  • A script to take action when the trigger is touched.
  • A sound effect to play
  • A video effect to play
  • A 3D mesh to represent the Health Pickup in the game

 

Getting Started: Laying Out A Framework

Create a new project.

Create a new Group called Health Pickup.

Make the group Networked.

Inside the Health Pickup Group make the following:

Create a Trigger and rename it Health Trigger.

Create a blank script called Health Pickup

Create two Groups:

  • FX (to hold the audio and video FX for the health pickup)
  • Health Geo (to hold the geometry of the pickup item)

Your Health Pickup Group hierarchy should look like this:

core_game_engine_health_pickup_hierarchy.jpg

 

Next, let's start filling in the groups.

Move the Health Pickup script so it is under Health Trigger.

In the FX Group:

  • Add in an Audio Effect of your choice to be played when the Health Item is picked up.  Call it Health Audio Effect.  For this example, I used Magic Sparkle Heart Whoosh 01.
  • Add in a Video Effect of your choice to be played when the Health Item is Picked up.  Call it Health Video Effect.  For this example, I used Heart Volume FX.

Add a 3D Object of your choice to the Health Geo Group.  This will be the item displayed in the game as the Health Pickup.  For this example, I used Heart - Polished.

Remember to drag these directly to the hierarchy so they are all created at the origin point (0,0,0). 

The hierarchy should now look like this:

core_game_engine_health_pickup_hierarchy2.jpg


The workspace should look something like this:

core game engine health pickup geometry and effects

 

Interacting With The Trigger

Our script needs to know when a Character has touched the bounding box of the Health Trigger.

Open up the Health Pickup script and enter the following:

local TRIGGER = script.parent

function GetHealthPickup(trigger, other)
    if other:IsA("Player") then
        print(trigger.name .. ": Begin Trigger Overlap with " .. other.name)
    end
end

TRIGGER.beginOverlapEvent:Connect(GetHealthPickup)

Let's take a look at that line by line.

local TRIGGER = script.parent

Here we are defining a Constant called TRIGGER and setting it to be the parent of this script.  I like to make scripts as children to the switches that control them.  If more than one switch can control a script, then I will make them at the sibling level.  Let's keep this example simple for learning's sake.

Next, we define a function to be called whenever the Trigger's bounding box is touched.

The function receives the trigger that is being touched and the object that is touching it.

function GetHealthPickup(trigger, other)
...
end


The function checks to make sure the object that touched the Trigger is a Player and not another Core Object.

If the object is a Player, then a message is sent to the Event Log.

     if other:IsA("Player") then
        print(trigger.name .. ": Begin Trigger Overlap with " .. other.name)
    end

In Lua, functions must be defined before they can be called. 

On the last line, we have the only piece of code that will run every time the Trigger bounding box is touched.  We use that event to call the OnBeginOverlap() function.

TRIGGER.beginOverlapEvent:Connect(GetHealthPickup)

Now that we know what all the sections are - let's go ahead and comment our code for future reference.
 
-- CONSTANTS
-- get the Trigger ID from this script's parent
local TRIGGER = script.parent

-- function called whenever an object overlaps the Trigger's bounding box
function GetHealthPickup(trigger, other)
    -- check to make sure this is triggered by a player and not another Core Object
    if other:IsA("Player") then
        -- send a message to the Event Log for troubleshooting purposes
        print(trigger.name .. ": Begin Trigger Overlap with " .. other.name)
    end
end

-- whenever an object overlaps the Trigger bounding box, call the OnBeginOverlap() function
TRIGGER.beginOverlapEvent:Connect(GetHealthPickup)
 

Ok great!

Now we have a working script and it is commented so the next time we work on it we won't forget what we were doing.

 

Let's Test Our Trigger

Since the Health Pickup has been created at the center of the map, it might overlap the Spawn Point for your Character, so move the Spawn Point off to the side while creating this object.

By creating it at the center of the map, it will be easier to manipulate and make into a Template later.

Open the Event Viewer (Window> Event Viewer), and run the game in Preview Mode.

When you touch the Health Trigger bounding box you should see a message in the Event Viewer.

If things went wrong, check the Event Viewer for details, or make sure the script is a child of the Health Trigger.

Now that we have been detected crossing the trigger, let's give ourselves some hit points.

 

Adventurer, Heal Thyself!

We want to heal ourselves for 10 points, so we need another constant to hold the Healing Amount.

At the top of the script, find this line:

local TRIGGER = script.parent

..and insert immediately after it:

-- set the amount to heal
local HEALING_AMOUNT = 10

Let's add a line after the Event Log message that actually adds to HP and sends us a message with the new hit points.

Find this line:

 -- send a message to the Event Log for troubleshooting purposes
print(trigger.name .. ": Begin Trigger Overlap with " .. other.name)

...and insert this immediately after it:

-- heal the player       
other.hitPoints = other.hitPoints + HEALING_AMOUNT
print("Player Hit Points: " .. other.hitPoints)
 
Your function should now look like this:

function GetHealthPickup(trigger, other)
    -- check to make sure this is triggered by a player and not another Core Object
    if other:IsA("Player") then
        -- send a message to the Event Log for troubleshooting purposes
        print(trigger.name .. ": Begin Trigger Overlap with " .. other.name)
 
        -- heal the player
        other.hitPoints = other.hitPoints + HEALING_AMOUNT
        print("Player Hit Points: " .. other.hitPoints)
    end
end

 

Run the Preview game and touch the Health Item, your Hit Points should be going up by 10 each time.

 

Put A Cap On Over-healing

We don't want the Health Pickup to take our health over MaxHitPoints, so let's make a modification that prevents Hit Points from exceeding the maximum.

After this line:

        other.hitPoints = other.hitPoints + HEALING_AMOUNT

Insert this:

        -- if the player's hitpoints are now greater than max, make them equal to max
        if other.hitPoints > other.maxHitPoints then
            other.hitPoints = other.maxHitPoints
        end

 

Save the script and test in Preview mode.

Now you should get the message that your health is at 100 when you touch the Health Pickup.  This is happening because you are not hurt and are already at Max Hit Points.

Well, everything seems to be working so far - except that pesky Health Pickup is just sitting there, giving us health every time we touch it, but we want it to be consumed the first time a player touches it.

 

Destroying Yourself

We want to object to destroy itself after it has been consumed so that it can only be used once.  We can do that with the Destroy command.

First, we need to create a reference to the Trigger's Parent, then we need to destroy the parent (from the game world).

It is possible to trigger the Destroy more than once, due to timing issues, so we'll check to see if it has already been done before calling the Destroy command.

At the top of the script, find this line:

local TRIGGER = script.parent
 
And insert this line below it:
-- get the ID of this object - the parent of the trigger
local HEALTH_PICKUP = script.parent.parent

Now that we have this Object's ID, we can destroy it when consumed.

Find this section:

        -- if the player's hitpoints are now greater than max, make them equal to max
        if other.hitPoints > other.maxHitPoints then
            other.hitPoints = other.maxHitPoints
        end

And insert this line below it:

        -- destroy the Health Pickup so it is consumed
        if Object.IsValid(HEALTH_PICKUP) then
            HEALTH_PICKUP:Destroy()
        end


Now save the script and run it in Preview mode.

You should now heal 10 points (up to your max hit points) if you are below max, and the Health Pickup will disappear when consumed.

 

Adding Effects

Now, it would be nice to have some feedback when the Player consumes the Health Pickup.

Drag the Health Audio Effect and Health Video Effect to the ADD CUSTOM Property button on the script Properties to provide access to the script.

At the top of the script, in the section where the CONSTANTS are defined, find the following line:

-- get the ID of this object - the parent of the trigger
local HEALTH_PICKUP = script.parent.parent
 

...and insert right the following:

-- Custom
local HEALTH_AUDIO_EFFECT = script:GetCustomProperty("HealthAudioEffect"):WaitForObject()
local HEALTH_VIDEO_EFFECT = script:GetCustomProperty("HealthVideoEffect"):WaitForObject()

This code can be copied from the Custom Properties panel after you drag the Effects to the Custom Property button.  This will give us the IDs of the two Effects we want to play.

Next, locate this section:

        -- if the player's hitpoints are now greater than max, make them equal to max
        if other.hitPoints > other.maxHitPoints then
            other.hitPoints = other.maxHitPoints
        end

...and insert this BEFORE the HEALTH_PICKUP:Destroy() line:

        HEALTH_VIDEO_EFFECT:Play()
        HEALTH_AUDIO_EFFECT:Play()
        Task.Wait(2)

Turn off Autoplay on the Health Video Effect so it doesn't start after spawning and is only turned on through scripting.

Now, when you run the Preview mode to test, you will see the visual Effect, hear the Audio Effect, and there will be a 2-second pause to give the effects a chance to play out.

The script should look like this:


-- CONSTANTS
-- get the Trigger ID from this script's parent
local TRIGGER = script.parent

-- set the amount to heal
local HEALING_AMOUNT = 10

-- get the ID of this object - the parent of the trigger
local HEALTH_PICKUP = script.parent.parent

-- get the ID of the audio effect
local HEALTH_AUDIO_EFFECT = script:GetCustomProperty("HealthAudioEffect"):WaitForObject()

-- get the ID of the video effect
local HEALTH_VIDEO_EFFECT = script:GetCustomProperty("HealthVideoEffect"):WaitForObject()



-- function is called whenever an object overlaps the Trigger's bounding box
function GetHealthPickup(trigger, other)
   
    -- check to make sure this is triggered by a player (rather than another Core Object)
    -- only activate if this Health Pickup has not already been used
    if other:IsA("Player") then

        -- send a message to the Event Log for troubleshooting purposes
        print(trigger.name .. ": Begin Trigger Overlap with " .. other.name)

        -- add points to health
        other.hitPoints = other.hitPoints + HEALING_AMOUNT

        -- if the player's hitpoints are now greater than max, make them equal to max
        if other.hitPoints > other.maxHitPoints then
            other.hitPoints = other.maxHitPoints
        end

        -- play the video and audio effects, pause to let the effects play out
        HEALTH_VIDEO_EFFECT:Play()
        HEALTH_AUDIO_EFFECT:Play()
        Task.Wait(2)

        print("Player Hit Points: " .. other.hitPoints)

       -- destroy the Health Pickup so it is consumed
        if Object.IsValid(HEALTH_PICKUP) then
            HEALTH_PICKUP:Destroy()
        end

    end
end

-- whenever an object overlaps the Trigger bounding box, call the GetHealthPickup() function
TRIGGER.beginOverlapEvent:Connect(GetHealthPickup)


We now have a working Health Pickup!

There are two small things I'd like to fix:

  1. The Health Geo should be set to no collision since I consume it by hitting the Trigger bounding box anyway.
  2. The Health Geo should go invisible when touched, while the effects are played.

Let's start with the collision.

 

Finishing Touches

Just a few more modifications!

Turning Off Collision

First, turn off the collision of the Health Pickup geometry by making sure it is set to Inherit From Parent then go to its parent (Health Geo) and turn off the Game Collision property.  This will allow the player to collect the health Pickup by running through it or otherwise touching it somehow.

Making The Health Pickup Disappear

Now we want the Health Geo to disappear when we "consume" the Health Pickup.

To access the properties of Health Geo, drag it to the script's properties then add this line to the end of the CONSTANTS/Custom section:

local HEALTH_GEO = script:GetCustomProperty("HealthGeo"):WaitForObject()

Find this line:

        print(trigger.name .. ": Begin Trigger Overlap with " .. other.name)
 
and insert this immediately after:
        -- turn off the geometry of the Health Pickup
        HEALTH_GEO.visibility = Visibility.FORCE_OFF
 
Now, when you test the Preview mode, the health Geo will no longer make you get stuck and you will see the Health Geo disappear when you touch it.
 
 

One Last Thing

It is possible to trigger the script more than once by leaving the Trigger and then quickly reentering.  To keep this from happening we should define a local variable for a flag status to keep track of if the item has already been used before it could be destroyed.

At the top of the script, after the --CONSTANTS section, enter the following:
-- variables
-- set a flag to detect if the pickup has already been used once - prevents double-dipping the health pickup
local used = false
Find this line:
    if other:IsA("Player") then
 
...and change it to:
    if (other:IsA("Player") and used == false) then
        -- set the used flag to indicate the Health Pickup has been used
        used = true
 

That's it!

Congratulations on making it to the end of the tutorial!  You should now have a working Health Pickup item.

Don't forget to make it into a template so you can reuse it.

The final script should look like this:


-- CONSTANTS
-- get the Trigger ID from this script's parent
local TRIGGER = script.parent

-- set the amount to heal
local HEALING_AMOUNT = 10

-- get the ID of this object - the parent of the trigger
local HEALTH_PICKUP = script.parent.parent

-- get the ID of the audio effect
local HEALTH_AUDIO_EFFECT = script:GetCustomProperty("HealthAudioEffect"):WaitForObject()

-- get the ID of the video effect
local HEALTH_VIDEO_EFFECT = script:GetCustomProperty("HealthVideoEffect"):WaitForObject()

-- get the ID of the health pickup geometry
local HEALTH_GEO = script:GetCustomProperty("HealthGeo"):WaitForObject()

-- variables
-- set a flag to detect if the pickup has already been used once - prevents double-dipping the health pickup
local used = false


-- function is called whenever an object overlaps the Trigger's bounding box
function GetHealthPickup(trigger, other)
   
    -- check to make sure this is triggered by a player (rather than another Core Object)
    -- only activate if this Health Pickup has not already been used
    if (other:IsA("Player") and used == false) then

        -- set the used flag to indicate the Health Pickup has been used
        used = true


        -- send a message to the Event Log for troubleshooting purposes
        print(trigger.name .. ": Begin Trigger Overlap with " .. other.name)

        -- turn off the geometry of the Health Pickup
        HEALTH_GEO.visibility = Visibility.FORCE_OFF

        -- add points to health
        other.hitPoints = other.hitPoints + HEALING_AMOUNT

        -- if the player's hitpoints are now greater than max, make them equal to max
        if other.hitPoints > other.maxHitPoints then
            other.hitPoints = other.maxHitPoints
        end

        -- play the video and audio effects, pause to let the effects play out
        HEALTH_VIDEO_EFFECT:Play()
        HEALTH_AUDIO_EFFECT:Play()
        Task.Wait(2)

        print("Player Hit Points: " .. other.hitPoints)

       -- destroy the Health Pickup so it is consumed
        if Object.IsValid(HEALTH_PICKUP) then
            HEALTH_PICKUP:Destroy()
        end

    end
end

-- whenever an object overlaps the Trigger bounding box, call the GetHealthPickup() function
TRIGGER.beginOverlapEvent:Connect(GetHealthPickup)


 

Customizing The Health Pickup

It's easy to customize this for different Health Pickups.

Changing The Healing Amount

If you want to change the amount the Health Pickup restores, change the value of HEALING_AMOUNT.

Changing The Audio and Video Effects

If you want to substitute your own Health Video Effect and Health Audio Effect, just delete the existing Effects and be sure to rename the new Effects as Health Audio Effect and Health Video Effect respectively so the script can access them next time.

Once you have renamed the Effects, be sure to drag them to the appropriate empty slot in the Custom Properties of the script.

Changing The Health Pickup Geometry

You can use any 3D Object or group of objects just by dropping them in the Health Geo group.  Don't forget to adjust the bounding box of the Trigger so it encompasses the Health Geo Group.

 

Conclusion

I hope you enjoyed this tutorial and I hope it helps you learn more about the Core Game Engine.  Follow me for more informative articles like this one!

 

Join My Game Dev Journey!

It's nice to have company on a  long trip.

If you want to follow my production progress, check my blog at MakeYourOwnRPG.com.

You can also join the CyborgPrime Discord server, where I post my progress and interact with the community.

Click here for more articles in this series about my Core Game Engine game dev journey.

 

Your Turn.  What Do You Do?

Did you find this article helpful?  Do you have a different way of doing Health Pickups?

Please share with us in the Comments section below. Let me know faster/easier/better ways to do things in Core.

 

If you found this article helpful, please give it a good rating at the top, thanks!


 

E-mail Notification Opt-in

Do you want to follow my Core Game Engine game dev journey?

Sign up on my private mailing list.

YES! Please notify me of new Core Game Engine game dev posts!

You can also join me and my friends on the CyborgPrime Discord server.

 

Related Articles

Information

Make your own RPG in your spare time with minimal skills.

Join me on my journey to create my own RPGs to play online or share with my friends.

I'm an RPG maker and game designer who builds tabletop and electronic RPGs for fun in my spare time.

Contact me if you have questions or suggestions.