Last weekend, my girlfriend and I were immersed in a Roblox gaming session when she presented an intriguing challenge: "Wouldn't it be cool if we could create something that follows us around in the game?" This question sparked my curiosity and presented the perfect opportunity to dive into Lua programming and Roblox scripting. I'm also currently watching Mythic Quest new season which is a sitcom about a group of people who work at a gaming company. Also you may be familiar that vibecoding games with Three.js is very popular these days on X.
In this tutorial, I'll walk you through how we created a companion cube that loyally follows your character throughout any Roblox world and serves as a digital guardian.
Before we dive into the code, let's understand what makes Lua special. Lua was created in 1993 at PUC-Rio (Pontifical Catholic University of Rio de Janeiro) in Brazil by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes. It was developed as a solution for a partnership between the university and Petrobras, the Brazilian oil company, which needed portable and customizable tools.
Lua was never intended to replace system programming languages like C or C++. Instead, it was designed as a scripting language to extend applications. Think of it this way: the core of your system (the "kernel") is written in a compiled language for speed and efficiency, while Lua provides scripting capabilities on top of that system.
This approach offers several advantages:
Lua gained international recognition around 1994-1996 through publications in Dr. Dobb's Journal. Its popularity exploded in 1997 when LucasArts adopted it for the game Grim Fandango, replacing their internal scripting system. The gaming industry quickly embraced Lua for its simplicity, portability, and small footprint - at just around 9,000 lines of C code for the entire language.
Today, Lua powers scripting in numerous popular games including Angry Birds, World of Warcraft, and Roblox. It's also used in applications like Adobe Lightroom, security tools like Wireshark and Snort, and even embedded systems and IoT devices.
In the context of Roblox, Lua serves as the scripting layer on top of the Roblox engine. This allows developers and players to create game logic without having to understand or modify the underlying engine code.
What truly sets Lua apart from other scripting languages is its elegant simplicity combined with powerful features:
Tables as the universal data structure: Unlike most languages that have arrays, dictionaries, objects, etc., Lua uses a single construct - tables - for everything. A table can act as an array, a dictionary, an object, or a combination of these.
First-class functions: Functions in Lua are values that can be stored in variables, passed as arguments, and returned from other functions.
Coroutines: Lua supports cooperative multitasking through coroutines, allowing you to pause and resume execution at specific points.
While playing Roblox, our requirements are to create a companion object:
After some research and experimentation, I realized this could be accomplished with Roblox's physics engine using BodyPosition and BodyGyro—powerful tools that let you control how objects move and orient themselves in the Roblox world.
Before diving into the code, it's important to understand the physics tools we'll be using:
BodyPosition: A physics constraint that applies a force to move a part toward a target position
MaxForce
: Determines how much force can be applied (higher values = faster movement)Position
: The target position the part will try to reachP
: Proportional gain - affects how aggressively the part moves toward the target (higher = more "springy")D
: Damping - affects how quickly oscillations are reduced (higher = less overshooting)BodyGyro: Controls the orientation of a part
MaxTorque
: How much rotational force can be appliedCFrame
: The target orientationP
and D
: Similar to BodyPosition, affect the rotation behavior⚠️ Physics Parameters Can Be Tricky
When adjusting physics parameters like MaxForce and P values, small changes can lead to dramatically different behavior. I once increased the MaxForce value too much and watched in horror as my companion cube launched my character into the stratosphere! Start with conservative values and increase gradually.
These physics constraints work based on PID controllers (Proportional-Integral-Derivative), which is why they have P, I, and D properties. For our companion follower, we'll mainly adjust the P value to fine-tune the movement.
Here's how we'll create our companion follower:
Note: This tutorial uses Lua 5.1, which is the version that Roblox Studio implements. Some features may not work in other Lua versions.
First, we need to create the object that will follow our character:
For our project, I decided to recreate the iconic companion cube from Portal. To give it that distinctive look, you can add textures using decals:
⚠️ Naming Conventions Matter
When working with complex scripts that might grow over time, establishing clear naming conventions from the start will save you hours of debugging later. I use prefixes like "companion" for all objects related to our follower to make them easily identifiable in the Explorer panel. Also will stick to snake_case convention for variable names.
While a simple cube works well, you might want to create a more complex shape. You can use Roblox's built-in parts or create a model from multiple parts. For our guardian companion, adding visual elements that suggest protection can enhance its purpose:
Now for the fun part! We need to create a script that makes our companion cube follow the character. Let's add a script inside the companion cube:
Performance Note: While Lua in Roblox is optimized for game development, infinite loops can significantly impact performance if not implemented correctly. The script below uses a
while true do
pattern with await()
call to prevent the script from consuming excessive CPU resources. Without thewait()
, this would create a "tight loop" that could freeze or crash the game.
The default values work well, but here's how different physics parameters affect your companion's behavior:
Parameter | Low Value Effect | High Value Effect | Recommended Range |
---|---|---|---|
MaxForce | Slow movement, struggles against gravity | Fast, sometimes jerky movement | 10000-50000 |
P Value | Sluggish, slow to respond | Springy, may overshoot | 1000-5000 |
D Value | Bouncy, oscillates | Rigid, dampens quickly | 500-1000 |
You can experiment with these values to get different movement styles:
Here's where we add the logic that continuously updates our companion's position:
Let's enhance the script to make our companion behave differently based on how far it is from the player and act as a guardian when needed:
Instead of hardcoding the master's name, we can add a StringValue object to make our script more flexible:
This way, you can easily change who the cube follows by modifying the StringValue without editing the script.
Let's go a step further and create a complete configuration system that lets players customize their companions:
Then in your main script:
When we first tested our companion cube, it behaved erratically—sometimes flying across the map or getting stuck. Here's how we debugged it:
You can set breakpoints by clicking next to the line number in Roblox Studio's script editor:
For more complex debugging, create a visual debugging system:
After the basic functionality was working, I made some improvements:
For more impressive visual effects, combine multiple particle emitters:
Let's add more intelligent interactions based on what the companion touches, emphasizing its role as a guardian:
You can automate the creation of companion cubes with this script:
Let's add a system that lets players choose from different companion types:
When your game has many players, each with their own companion, performance can become an issue. Here are some optimization techniques:
Important Performance Consideration: In Lua 5.1 (which Roblox uses), infinite
while true do
loops withwait()
calls have certain inefficiencies. The Roblox engine provides RunService events that offer better performance, more consistent timing, and lower overall CPU usage.
When working with Lua in Roblox, especially for companions that need to follow many players, consider these additional optimizations:
Throttle expensive operations:
Cache frequently accessed values:
Use spatial partitioning for threat detection:
Throughout this tutorial, we've explored the process of creating a companion cube follower in Roblox using Lua. What started as a simple idea—"wouldn't it be cool if something followed us around in the game?"—evolved into a deep dive into Roblox's physics system, Lua programming, and creative problem-solving to create a digital guardian.
The guardian companion doesn't just follow its master around; it actively protects them when threats are near, alerts when health is low, and even creates protective barriers when needed. These behaviors transform a simple follower into a truly useful companion that provides both practical assistance and peace of mind when you can't be there to help your friend directly.
There are several advanced topics that could enhance your guardian companion further. Implementing pathfinding would allow for more intelligent navigation around obstacles in the game world. Adding animations would give your creations more personality and visual appeal through fluid movement and expressive behaviors. Custom UI development enables deeper customization options for players to configure their companions. Expanding the AI behavior allows your guardian to recognize and intelligently respond to different types of threats. Finally, implementing persistence would allow the guardian to remain active and protective even when its creator is offline.