Back

StacyPilot BETA


How to use

Please report bugs to me

1. Installation


The easiest way to use StacyPilot is through the plugin. Press the plugin button and install the plugin. In Studio, open the Plugins section and click on StacyPilot. Click the Insert button to insert StacyPilot.

Alternatively, you can download the StacyPilot model directly and insert it into Workspace

No matter which method you choose, keep in mind that StacyPilot does NOT record anything by itself. You need to insert (or make) add-ons for the stuff you want to record (usually into the Addons folder)

Make sure to enable Studio Access to API Services in Game Settings Security

2. Recording


To start recording, simply press the "Start" button on the panel. When you're finished press the "Stop" button and wait a few seconds for everything to save. When filling out the recording name, you should only use letters and numbers

3. Inserting a recording


With the plugin: Open the plugin, put the recording name in the "Recording Name" field and press Insert.

Without the plugin: Copy this code into the Command Bar (View Command Bar) insert the recording name import("HERE") and press enter.

If you want to combine multiple recordings into one, you need to open the RecordingNames script, duplicate the example provided and change the names

The recording should now be visible on the panels

Add-ons


By default StacyPilot doesn't record anything. If you want to record something, you need to add or make an add-on for it. Add-ons don't have to be in the "Addons" folder, but it's a nice way to keep your game organised.
Below are some add-ons I made for my other projects.

Creating add-ons

The Core module


The core module contains functions responsible for recording, replaying and firing events:

- :StartRecording() will start recording
- :StopRecording() will stop the current recording
- :ChangeRecordingName(newName: string) will change the name of the current/next recording
- :IsRecording() will return true if the core is currently recording
- :IsPlaying() will return true if the core is currently replaying a recording
- :GetReplayData() will return all data for the currently playing recording. Values will be nil for pieces of data that have already been replayed
- :FilterData(data: {any}, module: string) will return only data from that module

- :Record(module_name, ...) will record all the arguments passed to it as data for "module_name". Currently StacyPilot can record: numbers, strings, booleans, Color3s and Vector3s (with more coming soon™️).

- :RegisterResponse(module_name, function) will tell the core to run function when replaying some data for module_name

- :Play(...) will start playback of recordings with names passed as parameters (for example :Play("recording1", "recording2"))
- :StopPlaying() will stop the current playback

- .Events.PlaybackStarted(full_data) is fired when replaying a recording, passes full data for that recording
- .Events.PlaybackStopped() is fired when replaying has stopped
- .Events.PlaybackTick(time_passed) is fired every Heartbeat when replaying a recording, passes the amount of seconds that have passed since the replaying started
- .Events.PlaybackTickWithData(time_passed, remaining_data) is fired every Heartbeat when replaying a recording, additionally passes the data that has not yet been replayed
- .Events.PlaybackData(this_data) is fired whenever the core reaches a piece of data and needs to run a function for it
- .Events.RecordingStarted is fired when recording starts
- .Events.RecordingStopped is fired when recording is stopped


The Panels module


The panels module contains functions resposible for displaying a playback on the default panels:

- :GetPixelsForTime(seconds: number) allows you to get the amount of pixels the panels expect for the amount of seconds passed. Useful for Positions and Sizes

- :AddTrack(symbol: string, color: Color3, display_order: number, auto_scroll: boolean) is used to add a Track to the Panel. There's a separator with a display order of 0, so anything with higher display order will be below it

- Track:PopulateWithData({{Duration: number, Text: string, Time: number}}) is used to easily show data on a track, you don't need to use it

- There are also some other Track functions that I'll document soon, you can look at the cameras add-on to see them


Tutorial


Let's say you want to make an addon to record your pyro. I'll assume that the code is not very complex and currently it looks something like this:

local pyroFolder = workspace.Fire
local clickDetector = script.Parent.ClickDetector

clickDetector.MouseClick:Connect(function()
	for _, part in pyroFolder:GetChildren() do
		part.ParticleEmitter.Enabled = true
	end
end)

clickDetector.RightMouseClick:Connect(function()
	for _, part in pyroFolder:GetChildren() do
		part.ParticleEmitter.Enabled = false
	end
end)

The first thing we need to do is include the Core module which is responsible for recording

local core = require(workspace.StacyPilot.Core)

Now for the actual recording part. Remember that StacyPilot shouldn't record results of actions, but their activators. So instead of telling it when every piece of fire went off, we should tell it that we pressed the "On" button. To keep the code clean, let's first move our on/off code to separate functions

local function turnOn()
	for _, part in pyroFolder:GetChildren() do
		part.ParticleEmitter.Enabled = true
	end
end
				
local function turnOff()
	for _, part in pyroFolder:GetChildren() do
		part.ParticleEmitter.Enabled = false
	end
end

Now, when the button is pressed, we will turn on the pyro and tell StacyPilot to record it. The core:Record() function needs at least 1 parameter: the name of the module that will be saved. In this case we'll use "Fire" but it could be anything. In a more professional scenario you should probably record all pyro as "Pyro" to improve save/load times

clickDetector.MouseClick:Connect(function()
	turnOn()
	core:Record("Fire", "On")
end)

clickDetector.RightMouseClick:Connect(function()
	turnOff()
	core:Record("Fire", "Off")
end)

Now StacyPilot can record when we activated or deactivated fire, but it can't replay it yet. To do that, we need to run the core:RegisterResponse() function which StacyPilot will run whenever it finds a piece of data from this module. The first parameter needs to be the same as in the :Record function ("Fire" in our case). The data below is all data passed into :Record packed into a table. In out case the first entry is either going to be "On" or "Off"

core:RegisterResponse("Fire", function(data)
	local actionName = data[1]
	
	if actionName == "On" then
		turnOn()
	elseif actionName == "Off" then
		turnOff()
	end
end)

And that's it! StacyPilot can now record your fire. Full code:

local core = require(workspace.StacyPilot.Core)
local pyroFolder = workspace.Fire
local clickDetector = script.Parent.ClickDetector
					
local function turnOn()
	for _, part in pyroFolder:GetChildren() do
		part.ParticleEmitter.Enabled = true
	end
end
					
local function turnOff()
	for _, part in pyroFolder:GetChildren() do
		part.ParticleEmitter.Enabled = false
	end
end
					
clickDetector.MouseClick:Connect(function()
	turnOn()
	core:Record("Fire", "On")
end)
					
clickDetector.RightMouseClick:Connect(function()
	turnOff()
	core:Record("Fire", "Off")
end)
					
core:RegisterResponse("Fire", function(data)
	local actionName = data[1]
					
	if actionName == "On" then
		turnOn()
	elseif actionName == "Off" then
		turnOff()
	end
end)

If you need more examples, you can look at the source code of the addons I made, which are available above

But let's say you also want to display when the fire is going to get turned on or off on the panels. To do that we need to use the Panels module:

local panels = require(workspace.StacyPilot.Panels)

Then we need to create a new track on the panels, in this example the track will be indicated by a fire emoji. It'll also have a brownish background, be placed below the separator and will scroll automatically

local track = panels:AddTrack("🔥", Color3.fromRGB(97, 65, 0), 1, true)

Now we will listen for when a replay starts, when it does, we will filter out the data so we only have data for the fire

core.Events.PlaybackStarted:Connect(function(data)
	data = core:FilterData(data, "Fire")
	...

Next we will transform the data a bit, we will only show the line when the fire is on and we will give the lines a slightly green background

	...
	local transformedData = {}
	for i, fireData in data do
		if fireData.Data[1] == "On" then
			local nextData = next(data, i)
			local duration = if data[nextData] then data[nextData].Time - fireData.Time else 2
			table.insert(transformedData, {
				Duration = duration,
				Text = fireData.Data[1],
				Time = fireData.Time,
				BackgroundColor = Color3.fromRGB(0, 113, 26)
			})
		end
	end
	...

Finally, we'll tell the panels to display that data

	...
	track:PopulateWithData(transformedData)
end)

And now we will be able to see when the fire turns on and off. Full code:

local core = require(workspace.StacyPilot.Core)
local panels = require(workspace.StacyPilot.Panels)
local pyroFolder = workspace.Fire
local clickDetector = script.Parent.ClickDetector
local track = panels:AddTrack("🔥", Color3.fromRGB(97, 65, 0), 1, true)

local function turnOn()
	for _, part in pyroFolder:GetChildren() do
		part.ParticleEmitter.Enabled = true
	end
end

local function turnOff()
	for _, part in pyroFolder:GetChildren() do
		part.ParticleEmitter.Enabled = false
	end
end

clickDetector.MouseClick:Connect(function()
	turnOn()
	core:Record("Fire", "On")
end)

clickDetector.RightMouseClick:Connect(function()
	turnOff()
	core:Record("Fire", "Off")
end)

core:RegisterResponse("Fire", function(data)
	local actionName = data[1]

	if actionName == "On" then
		turnOn()
	elseif actionName == "Off" then
		turnOff()
	end
end)

core.Events.PlaybackStarted:Connect(function(data)
	data = core:FilterData(data, "Fire")
	local transformedData = {}
	for i, fireData in data do
		if fireData.Data[1] == "On" then
			local nextData = next(data, i)
			local duration = if data[nextData] then data[nextData].Time - fireData.Time else 2
			table.insert(transformedData, {
				Duration = duration,
				Text = fireData.Data[1],
				Time = fireData.Time,
				BackgroundColor = Color3.fromRGB(0, 113, 26)
			})
		end
	end
	track:PopulateWithData(transformedData)
end)