Gameplay
14 min readScripting Games
Learn Voxly Luau basics, events, services, object properties, attributes, and simple gameplay patterns.
Voxly uses Luau
Voxly scripts are written in Luau, a Lua-based scripting language for readable gameplay logic.
Scripts make worlds interactive. They can respond when players join, touch objects, trigger events, collect items, complete objectives, or interact with world systems.
Voxly scripting is event-driven. Write setup code directly, then connect functions to events when you want something to happen.
Voxly may add optional lifecycle helpers later, but current creator examples should use direct setup code and event connections.
voxly.log("Hello from Voxly Luau")
Script types
- Script runs on the server and is used for authoritative gameplay rules.
- LocalScript runs for a player and is used for player-side input, camera, and interface behavior.
- ModuleScript stores shared Luau code that other scripts can require and reuse.
- ServerScriptService is the safe place for server-only scripts.
- ReplicatedStorage is the place for shared objects, ModuleScripts, RemoteEvents, and RemoteFunctions.
Core built-ins
- game:GetService("ServiceName") gets a built-in service such as Players, RunService, ReplicatedStorage, ServerScriptService, or CollectionService.
- workspace contains the visible world objects players interact with.
- Instance.new("ClassName") creates an object such as Part, Script, LocalScript, ModuleScript, RemoteEvent, or RemoteFunction.
- object.Parent places an object into the world or into a service.
- object.Name gives an object a readable name for finding and debugging.
- voxly.log(message) prints a message to the Voxly script log while testing.
Create and place a part
Use Instance.new to create an object, set its properties, then parent it into workspace so it appears in the world.
local platform = Instance.new("Part")
platform.Name = "StarterPlatform"
platform.Position = { x = 0, y = 2, z = 0 }
platform.Size = { x = 12, y = 1, z = 12 }
platform.Anchored = true
platform.Parent = workspace
voxly.log("Platform created")
Find objects
Name important objects clearly so scripts can find them later. Use FindFirstChild when an object might not exist yet.
local door = workspace:FindFirstChild("CastleDoor")
if door then
voxly.log("Found " .. door.Name)
else
voxly.log("CastleDoor is missing")
end
Events and Connect
Events let scripts respond to things that happen. Connect attaches a function to an event. The function runs when the event fires.
local checkpoint = workspace:FindFirstChild("Checkpoint")
if checkpoint then
checkpoint.Touched:Connect(function(hit)
voxly.log("Checkpoint touched by " .. hit.Name)
end)
end
Attributes
Attributes are custom values stored on objects. They are useful for gameplay settings like damage, team name, checkpoint number, or whether a door is locked.
local hazard = Instance.new("Part")
hazard.Name = "LavaBlock"
hazard.Parent = workspace
hazard:SetAttribute("Damage", 25)
hazard:GetAttributeChangedSignal("Damage"):Connect(function()
voxly.log("Damage is now " .. tostring(hazard:GetAttribute("Damage")))
end)
hazard:SetAttribute("Damage", 40)
Players service
The Players service lets server scripts respond when players join or leave the world.
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
voxly.log(player.Name .. " joined")
end)
CollectionService tags
Tags let one script manage many objects. For example, tag every lava part as DamageZone, then connect behavior to all matching parts.
local CollectionService = game:GetService("CollectionService")
local function attachDamage(zone)
zone.Touched:Connect(function(hit)
local damage = zone:GetAttribute("Damage") or 10
voxly.log(hit.Name .. " took " .. tostring(damage) .. " damage")
end)
end
CollectionService:GetInstanceAddedSignal("DamageZone"):Connect(attachDamage)
local lava = Instance.new("Part")
lava.Name = "Lava"
lava.Parent = workspace
lava:SetAttribute("Damage", 25)
CollectionService:AddTag(lava, "DamageZone")
RunService timing
RunService is for frame-based logic. Use Heartbeat when behavior should run repeatedly while the world is active.
local RunService = game:GetService("RunService")
RunService.Heartbeat:Connect(function(dt)
-- Runs after the script update step.
end)
RemoteEvents
RemoteEvents send messages between LocalScripts and server Scripts. Use them when a player action needs to request a server-side gameplay change.
-- Server Script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remote = Instance.new("RemoteEvent")
remote.Name = "CoinPickup"
remote.Parent = ReplicatedStorage
remote.OnServerEvent:Connect(function(player, coinName)
voxly.log(player.Name .. " picked up " .. coinName)
end)
RemoteFunctions
RemoteFunctions are for request-and-response flows. Use them sparingly because the caller waits for a result.
-- Server Script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remote = Instance.new("RemoteFunction")
remote.Name = "GetRoundInfo"
remote.Parent = ReplicatedStorage
remote.OnServerInvoke = function(player)
return "Lobby", 30
end
Safety rules
- Use Script for world-changing rules and LocalScript for client-side presentation.
- Do not trust LocalScript for rewards, currency, trading, or important progression.
- Use RemoteEvents to ask the server for a change; let the server decide if the change is allowed.
- Prefer events over constantly checking everything every frame.
- Wrap risky calls with pcall when failure is expected and recoverable.
- Keep scripts small, named clearly, and focused on one system.