May 1, 2026

Adding Lua scripts to a Highrise World requires Highrise Studio (built on Unity), where you create script files via the Project panel, choose one of five script types, write your code, and attach scripts to GameObjects. Lua is the only supported scripting language. This guide walks through every step, from --!Type() annotations to Network Values and the Payments API, and includes a practical technique for verifying your scripts work using the player joined event.
Lua is a lightweight scripting language originally designed for embedded systems and game development. If you’ve ever scripted anything in Roblox, you’ve already used Lua. Highrise chose it for the same reasons: it’s simple to learn, flexible enough for complex game logic, and easy to integrate into existing engines.
In Highrise Studio, Lua is the primary (and only) scripting language. You cannot use C# scripts, even though Studio runs on Unity. This trips up a lot of Unity developers who arrive expecting to write MonoBehaviours. Any C# scripts in your project simply won’t be included in the build.
When you learn how to add Lua scripts to a Highrise World, you’re learning the single skill that separates a static 3D scene from an interactive experience with player movement, object interactions, multiplayer features, and monetization. If you want to see what other creators have already built, browse published Worlds for inspiration before writing your first line of code.
Ready to start building? Download Highrise and install Studio Hub to get your development environment running.
Before you write a single script, you need the right tools installed and a basic understanding of the Unity interface.
The development environment for building 3D Worlds on the Highrise platform. It runs on Unity and adds a custom layer of tools, components, and APIs specific to Highrise. It’s free to use, and you download it through the Studio Hub. For a fuller breakdown of what Studio includes, the Highrise Studio overview covers everything from templates to deployment.
The installer and project manager for Highrise Studio. It handles downloading the correct Unity version and the Highrise Studio Package so you don’t have to manually match versions. Think of it as your launchpad. You can grab it from create.highrise.game/highrise-studio.
Visual Studio Code is the recommended code editor. The Highrise Studio Tools extension adds syntax highlighting, code completion, and type definitions specific to Highrise’s Lua API. Without it, you’re coding blind. Install both before you start.
The Unity window that shows all the files in your project, including scripts, models, textures, and scenes. This is where you right click to create new Lua scripts.
Lists every GameObject in your current scene, organized in a tree structure. When you want to add an empty object to attach a script to, you right click here.
The panel on the right side of Unity’s editor where you view and edit a selected GameObject’s properties. When you attach a Lua script, its Serialized Fields appear here so you can tweak values without touching code.
For the latest updates on Studio features and tools, check the Highrise news page.
Understanding script types is the single biggest conceptual hurdle when learning how to add Lua scripts to a Highrise World. Highrise Studio supports five distinct script types, and picking the wrong one is a common source of bugs.
The official Highrise YouTube channel dedicates an entire episode to this topic, covering client type scripts and Unity lifecycle functions, because the distinction is that important for beginners.
Run on the player’s device. Use these for anything that only affects the local player: camera movement, visual effects, input handling, UI animations. They have access to lifecycle functions like self:Awake(), self:Start(), and self:Update().
When to use: Local interactions, visual feedback, sound effects, input detection.
Run on Highrise’s servers. Use these for authoritative game logic: score tracking, data persistence, payment processing, anything involving Gold. Because they run server side, players can’t tamper with them.
When to use: Leaderboards, in world purchases, spawning shared objects, anti cheat logic.
A hybrid type that runs on both sides. Code is split using prefixed lifecycle functions: ClientAwake(), ServerAwake(), ClientUpdate(), ServerUpdate(), and so on. Variables defined outside these functions are shared; variables inside stay local to that execution context.
When to use: Features that need coordinated behavior, like a door that animates locally (client) but checks a key inventory (server).
Contain reusable functions and variables shared across multiple scripts. They’re singletons, meaning every script that imports a Module gets the same instance. You import them with require("ModuleName").
When to use: Utility functions, shared constants, math helpers, configuration tables.
Specialized scripts for controlling user interface elements. They work alongside UXML files (for structure) and USS files (for styling). Each UI script has an Output property with three settings: World (renders in 3D space), Above Chat (floats above the chat window), and HUD (fixed to the screen).
When to use: Health bars, inventory menus, dialog boxes, scoreboards.
| Script Type | Runs On | Best For |
|---|---|---|
| Client | Player’s device | Input, visuals, local effects |
| Server | Highrise servers | Game logic, payments, persistence |
| Client/Server | Both | Coordinated features |
| Module | Both (as imported) | Reusable code, shared config |
| UI | Client (with server hooks) | Interface elements |
--!Type() AnnotationEvery Lua script in Highrise must start with a type annotation comment. This looks like --!Type(Client), --!Type(Server), --!Type(Module), or --!Type(UI). It tells Highrise Studio where the script should execute.
This is not standard Lua syntax. It’s a Highrise specific directive. If you forget it or misspell it, your script won’t work as expected. When you create a script through the Unity menu, the annotation is added automatically, but if you’re creating files manually or copying code from tutorials, always double check the first line.
If you’re new to the Studio environment and want a gentler introduction before writing scripts, the beginner tutorial glossary covers foundational terms alongside practical examples.
Here’s the actual process for how to add Lua scripts to a Highrise World, from creating the file to seeing it run.
Open your project in Unity. In the Project panel, right click and select Create > Highrise > Lua Script. Choose your script type (Client, Server, ClientAndServer, Module, or UI). Name your script something descriptive and press Enter.
The official documentation walks through this exact flow if you want screenshots.
Double click the script to open it in VS Code. You’ll see the --!Type() annotation already in place. Start simple:
--!Type(Client)
function self:Start()
print("Hello, World!")
end
This prints a message to the console when the script initializes. It’s the smallest possible proof that your script is working.
Back in Unity, right click in the Hierarchy panel and select 3D Object > Create Empty. Rename it to something meaningful (like “GameManager” or “WelcomeMessage”). This empty object serves as a container for your script.
Select your new GameObject in the Hierarchy. Then drag the script file from the Project panel onto the Inspector panel. You’ll see a new component appear, which is called a LuaBehavior. This component is the bridge between your Lua code and the Unity GameObject.
Click the Play button at the top of Unity’s editor. Check the Console window for your “Hello, World!” message. If it appears, congratulations: you’ve successfully added a Lua script to your Highrise World.
When you’re ready to share your World, click the Upload button in Unity. Then head to the Creator Portal at create.highrise.game, navigate to your World’s dashboard, go to Builds, and click Release. Assign a version number and publish.
Practitioners on the Highrise Create Forums report that the publish flow is straightforward, but creator Arylux notes in a video tutorial that beginners often forget the two step nature of publishing: uploading the build is not the same as releasing it. You must do both. For a complete walkthrough of the entire build and publish pipeline, the step by step world creation guide covers every stage.
Lifecycle functions are special functions that Unity calls automatically at specific moments. Understanding their order is essential when you add Lua scripts to a Highrise World, because code that runs at the wrong time produces bugs that are hard to track down.
The execution order is:
The official Highrise YouTube series (Episode Five) covers each of these functions with practical examples, walking through when and why you’d use each one.
In Client/Server scripts, lifecycle functions get prefixes: ClientAwake(), ServerAwake(), ClientStart(), ServerStart(), ClientUpdate(), ServerUpdate(), and so on. This is how Highrise splits execution between the player’s device and the server within a single file.
A “Hello, World!” print statement proves a script runs, but it doesn’t prove it runs correctly in a multiplayer context. One of the most practical verification techniques is hooking into the player joined event so you can confirm your server side logic fires every time a real player enters the World.
Start() and Awake() fire once when a scene loads. They don’t fire again when new players connect. If your World depends on per player setup (assigning spawn points, initializing inventory, greeting players), you need an event that triggers for each individual join. The player joined event fills that gap.
Practitioners on the Highrise Create Forums recommend using it as a debugging checkpoint: if you can see a print statement fire in the server console for every player who enters, you know your script is attached correctly, running on the right execution context, and receiving network callbacks.
Here’s a minimal Server script that logs every player who joins:
--!Type(Server)
function self:ServerStart()
scene.PlayerJoined:Connect(function(scene, player)
print(player.name .. " has joined the World")
end)
end
Attach this to an empty GameObject (call it something like “JoinLogger”), enter Play Mode, and check the Console. When you connect as a test player, you should see the log message. If you don’t, something is wrong with either the script type, the attachment, or the scene setup.
Once you’ve confirmed the event fires, you can replace the print statement with real logic:
FireClient event back to the joining player with a greeting or tutorial prompt.--!Type(Server)
local joinCount = 0
function self:ServerStart()
scene.PlayerJoined:Connect(function(scene, player)
joinCount = joinCount + 1
print("Total joins this session: " .. tostring(joinCount))
print("Welcome: " .. player.name)
end)
end
This pattern is particularly useful when combined with leaderboard and quest systems, where you need to initialize player state the moment they enter.
Putting it in a Client script. The player joined event is a server side callback. If you try to connect to it in a Client script, it either won’t fire or will behave unpredictably. Always use a Server or Client/Server script with the listener inside a ServerStart() function.
Assuming the player’s character is fully loaded. The join event fires when the player connects, but their avatar and associated components may still be loading. If your logic needs to manipulate the player’s character (changing appearance, attaching objects), you may need a short delay or a separate “character ready” check.
Forgetting to test with multiple connections. In Play Mode, you typically connect as a single test player. The event fires once and everything looks fine. But in a live World with many simultaneous joins, race conditions can surface. Test with at least two or three connections if your logic involves shared state like team assignment or limited spawn points.
The player joined event is the simplest way to confirm that a script is alive and responding to real multiplayer activity. Use it early in development as a sanity check, and keep it in place (behind a debug flag if you prefer) as your project grows.
Once your scripts are running, you need ways for them to communicate and react to players. These are the building blocks.
Events handle communication between client and server sides of your code. You create them with Event.new("EventName"), connect listeners with :Connect(function), and fire them across the network using :FireServer(data) or :FireClient(player, data).
Forum discussions on the Highrise Create Forums show that FireClient usage is one of the most common confusion points for new creators. The key detail people miss: FireClient requires you to specify which player should receive the event. You can’t broadcast to all clients with a single FireClient call.
Synchronized variables that share data between client and server. Six types are available: IntValue, StringValue, BoolValue, Vector3Value, NumberValue, and TableValue. You create them with TypeValue.new("Name", initialValue, player).
Each Network Value triggers a Changed event whenever its value updates, making them perfect for scoreboards, health bars, or any data that needs to stay in sync across all players. For a practical example of building a score tracking system, the leaderboard scripting guide walks through a complete implementation.
Mark a variable with the --!SerializeField directive, and it becomes editable in the Inspector panel. This lets you (or a teammate) tweak values like speed, spawn count, or timer duration without opening the script.
--!Type(Client)
--!SerializeField
local speed : number = 5.0
The --! prefix is a Highrise specific directive, not standard Lua. Like the --!Type() annotation, it’s processed by the Studio editor.
A built in component that fires an event when a player taps on an object’s collider. Properties include Check Distance (whether to enforce a maximum interaction range), Distance (the range itself), and Walk To (whether the character auto walks to the object before interacting). The GameObject must have a collider attached, or the TapHandler won’t register taps.
Unity’s physics system gives you OnTriggerEnter, OnTriggerStay, and OnTriggerExit for trigger zones (like doorways or pickup areas), plus OnCollisionEnter and related functions for physical collisions. The official Highrise YouTube Episode Four demonstrates OnTriggerEnter in a practical context, using it to create interactive zones that respond when a player walks through them.
As your World grows, so does your codebase. Keeping things organized from the start saves real pain later.
Module scripts act as shared libraries. Because they’re singletons, every script that calls require("ModuleName") gets the exact same instance. This makes them ideal for configuration tables, utility functions, or shared game state.
The recommended best practice is to organize scripts under Assets/Scripts/ with subfolders by category (Client, Server, Modules, UI). This mirrors the script type system and makes it obvious where to find things.
Beyond simple numbers and strings, Serialized Fields can reference other GameObjects, prefabs, or assets. This is how you wire up a script to a specific 3D model, sound effect, or particle system without hardcoding paths. If you’re interested in designing custom items and assets to reference in your Worlds, explore Highrise Concepts for the asset creation pipeline.
Creators don’t just learn how to add Lua scripts to a Highrise World for the technical satisfaction. They want to build experiences people love, and many want to earn from their work.
Your World is now live. Players on iOS, Android, macOS, Windows, and Steam can find and join it.
The Payments API lets you sell items, abilities, or cosmetics directly inside your World. Revenue splits 90% to the creator and 10% to Highrise. Payment logic must run in Server scripts for security. If you process payments client side, they won’t work and could expose your World to exploits.
For a detailed setup walkthrough, the guide on setting up in world purchases covers everything from product definitions to server side validation.
Beyond direct purchases, Highrise pays creators based on how much time HR+ subscribers spend in their Worlds. This means even a free to play World with no in world purchases can generate income if the experience keeps people engaged. Learn more about how engagement payouts work to understand the earning mechanics.
A programmatic wallet tied to your World that can hold and distribute Gold. Useful for prize pools, quest rewards, or tipping systems. Managed through the Wallet API in server side scripts.
The connection between Lua scripting skills and earning potential is direct. A creator featured in Fast Company’s Most Innovative Companies list reportedly earned around $300,000 from a poker style speakeasy World built with these exact tools. Visit the Highrise shop to understand the Gold and item economy your scripts can tap into.
This warning appears if you have any C# files in your project. Highrise Studio ignores them entirely. Remove or exclude them to keep your project clean.
Using a Client script for logic that should be server authoritative (like payment processing) is a security problem. Using a Server script for something that needs instant visual feedback creates laggy experiences. Match the script type to the job.
In Client/Server scripts, variables defined outside the prefixed functions are shared across both contexts. Variables defined inside ClientUpdate() or ServerStart() stay local to that side. Mixing this up causes nil reference errors that seem random.
Two scripts with the same name, even in different folders, can cause import conflicts with require(). Use unique, descriptive names.
As mentioned earlier, forum threads on the Highrise Create Forums confirm this is a recurring stumbling block. FireClient requires a specific player reference. To notify all players, you need to loop through connected players and fire the event to each one.
If you’ve set up a player joined listener and it never triggers, check three things. First, make sure the script type is Server or Client/Server, not Client. Second, confirm the listener is registered inside ServerStart(), not a client side function. Third, verify the script is actually attached to a GameObject in the scene. An unattached script file sitting in the Project panel does nothing.
If you hit a wall, the Highrise community is active and helpful. Multiple Game Jams (Build An Obby, Spring Jam '25, and others) run on the Create Forums, and participating creators frequently share solutions to scripting problems.
Learning how to add Lua scripts to a Highrise World is a progression. You start with the setup (Studio Hub, Unity, VS Code), create your first script file, understand the five script types, attach code to GameObjects, test in Play Mode, and eventually publish. Along the way, you pick up lifecycle functions, events, Network Values, the player joined event for verification, and the publishing workflow.
The official documentation splits this knowledge across more than fifteen separate pages. This guide consolidates those concepts so you have a single reference point. But the real learning happens when you build something. Start small: a door that opens on tap, a coin counter, a welcome message triggered by the player joined event. Then scale up.
If you’re ready to start building, download Highrise and install Studio Hub to get your development environment running. For ideas about what to build or features you wish existed, submit your ideas to shape the platform’s future.
No. Lua is the only supported scripting language in Highrise Studio. Even though Studio runs on Unity, C# scripts are not included in the build. Any C# files in your project will be ignored.
Client scripts run on the player’s device and handle local things like input, visuals, and sound. Server scripts run on Highrise’s servers and handle authoritative logic like scoring, payments, and data persistence. If something involves security or Gold, it should be a Server script.
You need basic familiarity with Unity’s interface (the Project panel, Hierarchy, and Inspector), but you don’t need deep Unity expertise. Highrise Studio abstracts most of the engine complexity. The official YouTube tutorials cover the Unity basics you need in under 15 minutes.
--!Type() annotation at the top of every script?It’s a Highrise specific directive that tells Studio where the script should execute. Options are --!Type(Client), --!Type(Server), --!Type(ClientAndServer), --!Type(Module), and --!Type(UI). It looks like a Lua comment, but Studio’s toolchain processes it as a configuration instruction.
Use the --!SerializeField directive above the variable declaration. This exposes the variable in Unity’s Inspector panel so you can change its value without editing the script file.
Yes. Creators earn through in world purchases (where you keep 90% of revenue), engagement based payouts from HR+ subscriber time, and other monetization tools like the World Wallet and tipping. Scripting is what makes these features possible.
A Module script holds reusable functions and variables that other scripts can import with require(). It’s a singleton, so every script that imports it shares the same instance. Use Modules for utility functions, configuration constants, or shared game state.
The simplest method is to connect a listener to the player joined event inside a Server script’s ServerStart() function. When a player enters, the listener fires, and you can print a confirmation to the console. This proves your script runs server side and responds to real network events, not just local scene initialization.
The Highrise community forums are active, and creators frequently help each other debug scripting issues. The official YouTube channel also publishes step by step tutorials covering common scripting patterns and pitfalls.
© 2026 Pocket Worlds. Tous droits réservés.