SteamVR/Environments/Scripting/Linking Lua with Hammer
This tutorial shows how to link functions in a VScript Lua script to Hammer entity logic.
Scripts Called by Map Entities
If you would like entities in your map to call script functions (such as triggers), you’ll need to create another Lua script file to your scripts\vscripts
directory.
Launch Hammer from the SteamVR Workshop Tools and create a new map from File > New.
Create a logic_script
with the Entity Tool (light bulb icon on the tools bar). The location does not matter, but it will be easy to find if you place it near the world origin. Double click on it to open its properties:
In the Name
field, call it what you want. This is what other entities on your scene will reference it as.
- Warning:Do not reference
addon_game_mode.lua
in the map entities or your script will run multiple times. If you have map entities that call script functions, you will need to create another Lua file that is not calledaddon_game_mode.lua
.
Arcade Toss only has arcade_toss.lua
(not an addon_game_mode.lua
) for simplicity purposes.
Click on the + sign next to Misc and put the name of the Lua file you want to call (you will not need the .lua extension).
Create the basic map requirements such as the floor, an info_player_start, and a light_omni.
Create a trigger_multiple (again, you can use the Turret Tutorial for instructions on creating mesh entities). Open its properties by double-clicking on it or selecting it and pressing Alt+↵ Enter.
In the Outputs tab, add an output named "OnStartTouch". Set the logic_script
as the target. The input should be "CallScriptFunction" and the parameter should be the name of the function you want to be called.
In your Lua file referenced by your logic_script
, add a simple function to print a message in the console:
function MyFunction()
print("Trigger has been activated")
end
Compile the map and you should now be able to walk into the trigger_multiple in the game and see the message printed in the console.
Example 1
There’s a trigger_multiple that detects if a player has crossed the line to throw balls into the targets. It’s called “red_team_check”.
You can find it in the outliner in Hammer by typing “red_team_check” in the filter field:
If you double click on it and click on the Outputs tab, you can see it references the logic_script
and uses CallScriptFunction
with a parameter override of the name of the function you want to call in the Lua file.
You will then need to create a function in the Lua script that matches the one you are calling in the Entity I/O:
---------------------------------------------------------------------------
--Red Team
---------------------------------------------------------------------------
function RedTeamCheckStartTouch()
--A player is over the line on the Red Team side
red_team_counter = red_team_counter + 1
end
function RedTeamCheckEndTouch()
red_team_counter = red_team_counter - 1
end
To create a function, you'll need to follow this same syntax with function
MyFunction() and end
to close it.
--
will tell Lua to ignore that line of text. This will comment out a line. This helps to organize your script so you can remember what each part does and will help other people see how you did it. You can also divide your script into different sections with comments as seen above.In this example, there’s an integer variable in the script called red_team_counter
and it starts at 0.
-- Global Variables
local red_team_counter = 0 -- Players behind the Red Team Line
local blue_team_counter = 0 -- Players behind the Blue Team Line
When a player touches the trigger, the number increases by 1. When a player is no longer touching the trigger, the number decreases by 1.
The think function shown in the Lua Scripting Intro checks every second if the counter is higher than 0. If so, no points are awarded and there’s a message displayed on the board.
This chunk in the Think
function in Arcade Toss checks if any player is over the line:
if red_team_counter > 0 then
print("Red Team is over the line!")
RedTeamOverTheLine()
else
RedTeamBehindTheLine()
end
These functions are called if a player is over the line or if all players are in legal positions:
function RedTeamOverTheLine()
bIsRedTeamBehindTheLine = false
--Panel popup for notification
DoEntFire( "red_team_check_panel", "AddCSSClass", "Activated", 0, self, self )
end
function RedTeamBehindTheLine()
bIsRedTeamBehindTheLine = true
DoEntFire( "red_team_check_panel", "RemoveCSSClass", "Activated", 0, self, self )
end
Example 2
There's a trigger_multiple in each of the target holes for the balls to make contact with to tell the script when the player has scored.
Open the trigger_multiple in the green target hole on the Team Blue side (named “blue_team_hole_1”). Find this trigger in Hammer and double click on it or select it and press Alt+↵ Enter.
Each of the fields that have a + mark beside it can be expanded:
It is set to trigger when Physics Objects touch it. It also has a filter (filter_activator_name) that says it will only trigger when an object named “blue_ball” touches it.
This is what the filter_activator_name looks like:
This tells it that it will only activate when a physics object named “blue_ball” collides with it.
The output of the trigger_multiple calls the CallScriptFunction
in the logic_script
file and in the parameter is the name of the function you want to call in the Lua script.
In the Lua file, you wil need to create any function you call in the entity logic. In this example, you’ll need to create a function called BlueHoopRow1
.
Note: You don’t need to include the () in the Hammer I/O, just in the Lua function.
function BlueHoopRow1(trigger)
--Calls another function for scoring with the parameters for the team and the number of points
UpdateScoreboard(2, 5)
--Specifies which info_particle_system to start for the scoring effects
local hoop = "blue_trigger_bot_particle"
HoopEffects(hoop, 2)
end
Spawning Entities
Many times you will need to spawn entities into the world.
For example, in Arcade Toss, the Think
function checks every second to see how many red balls exist. If there are less than 4, it will spawn more unless red balls exist in the spawner locations.
See the BallReplenish
function in the Arcade Toss script.
To spawn an object, you will need to create a table that contains the Raw Data
for the entity you wish to spawn. To make this easier, you can place one of the entities you want to spawn in Hammer and then open its properties.
Click on the settings icon to view the Raw Data
.
This is the properties box with Raw Data
enabled:
From this, we know what to call the keys in the table:
function RedBallLeftSpawner()
--print("Spawning a Red Ball")
local ballTable =
{
origin = "224 128 24",
targetname = "red_ball",
model = "models/props_gameplay/red_ball001.vmdl"
}
ballTable["spawnflags#6"] = "1"
local redBall = SpawnEntityFromTableSynchronous( "prop_physics_override", ballTable )
redBall:Attribute_SetIntValue( "objectID", 1 )
EmitSoundOn( "ball_spawn", redBall )
end
You don’t need to include the keys and values for the default properties.
Note: For the prop_physics_override entity used in the example above, a change in the spawnflags was required. Because of Lua’s scripting syntax, the #
symbol could not be read except as an insert later.
To change a spawnflag, you will need to have a similar method with “1” being checked on and “0” being checked off.
Precaching Resources
If you spawn an entity that does not exist in your Hammer map, you will need to precache the resources. For example, spawning the red and blue balls in Arcade Toss needed this code in the arcade_toss.lua
file:
function Precache( context )
PrecacheModel( "models/props_gameplay/red_ball001.vmdl", context )
PrecacheModel( "models/props_gameplay/blue_ball001.vmdl", context )
end
Other resources can be precached with these functions:
Function | Signature | Description |
---|---|---|
PrecacheModel | ( string modelName, context )
|
Manually precache a single model. |
PrecacheModelFolder | ( string folderName, context )
|
Recursively precache models within a folder. |
PrecacheParticle | ( string particleName, context )
|
Manually precache a single particle. |
PrecacheParticleFolder | ( string folderName, context )
|
Recursively precache particles within a folder |
PrecacheSoundFile | ( string soundFileName, context )
|
Manually precache a single sound file |