Welcome to the Instance Recorder Module
If you just want the module marketplace link
Get the module here: https://create.roblox.com/marketplace/asset/3172715879/Recorder-Module
About the Module
What is the Instance Recorder Module?
The Instance Recorder Module is a module that allows you to record… instances. This essentially means is you can record any property or attribute of any instance in the game to later utilize the saved changes in your game for many different effects.
Do you have any examples of possible effects a developer can implement with this module?
Yes, you can make a time reversal effect (as showcased in the example place below) along with other effects, such as a camera recording, where a player can watch back the footage, or to a clone that follows the players movements. Any effect where you could find recording changes in any instance over time useful this module is for you.
How optimized is this module?
The module isn’t as optimized as I would like it to be, but its CPU and memory usage should be better compared to the older version of this module. The CPU and memory usage of the module depends on how many instances, properties and the frequency of calls to the update method, and as such is hard to pinpoint. Changes in the newer version of this module should alleviate issues with memory usage, as you can call update less frequently when recording and still get smooth playback.
How to use the Module
If you used the older version of the module, the changes below will break your game, as such its recommended staying on the older version until you can update your scripts.
The instance recorder module is fairly simple to use, and is really nice for managing data more than it is for something more tangible, the reversing effect in the example place has far more lines of code than the module, but the module helps make the code easier to write and read.
The instance recorder module is ultizing a roblox/lua OOP style, with classes and methods (and not so much properties).
To create a new “recorder” instance which manages everything, you would call the module’s .new()
method or just call the module itself.
local recorder = require(3172715879)
local new_recorder = recorder(recorder.InterpolateFrames) -- or recorder.new(recorder.InterpolateFrames)
Frame Modes, numbers and “enums”.
Frame mode "enums" are defined in the same space as the .new() method. So require the module, and do module.InterpolateFrames as an example.
recorder.InterpolateFrames = 0
-- This is the default mode, :Update and other methods return an <INTERP_UPDATE_FRAME>
recorder.FullFrames = 1
-- This is a frame mode where :Update and other methods return a <FULL_UPDATE_FRAME>
recorder.BasicFrames = 2
-- This is a frame mode where :Update and other methods return a <UPDATE_FRAME>
The returned object is a recorder class object which has many different methods.
Basic Getter/Setter methods.
new_recorder:SetFrameMode(number)
-- This method sets the frame mode, which dictates what type of update frames are returned by functions that return an update frame of any type.
new_recorder:GetFrameMode()
-- Returns the frame mode set before.
new_recorder:SetPlaybackSpeed(number)
-- This method sets the speed of playback, can be negative or positive, just not 0.
new_recorder:GetPlaybackSpeed(): number
-- Returns the playbackspeed.
new_recorder:SetCurrentTime(number)
-- This method sets the current time "playback head" in seconds, used during playback to set a start time.
new_recorder:GetCurrentTime(): number
-- Returns the current playback head time in seconds.
new_recorder:SetRecordLength(number)
-- If set to 0, there is no record length, otherwise set to the number of seconds maximum to record, useful to limit memory usage.
new_recorder:GetRecordLength(): number
-- Returns the max number of seconds to record. 0 means there is no limit.
new_recorder:SetShouldOverrideFrames(boolean)
-- Sets whether it should override old frames if it reaches record length, or to stop recording.
new_recorder:GetShouldOverrideFrames(): boolean
-- Returns if the recorder will override old frames.
new_recorder:SetRecordingFrequency(number)
-- Sets the number of seconds between recorded frames. Useful to limit the "FPS" of recording, by denoting the length of time that needs to pass for a new recorded frame to occur.
new_recorder:GetRecordingFrequency(): number
-- Returns the recording frequency.
new_recorder:GetTimeSinceLastRecordedFrame(): number
-- Returns the amount of time elapsed since the last recorded frame. 0 indicates that at the time of calling this function the recorder recorded a frame.
new_recorder:IsPlaying(): boolean
-- Returns if the recorder is currently playing. (Recorder can both be playing and recording.)
new_recorder:IsRecording(): boolean
-- Returns if the recorder is currently recording. (Recorder can both be playing and recording.)
new_recorder:GetInstances(): {[Instance]: {[1]: {[string]: boolean}, [2]: {[string]: boolean}, [3]: {<BASIC_FRAME>}}}
-- More for internal use, but can be useful to get a list of instances the recorder is keeping track of.
Basic Recorder Methods
new_recorder:StartPlaying(boolean?): (<INTERP_UPDATE_FRAME>|<FULL_UPDATE_FRAME>|<UPDATE_FRAME>)?
-- Starts recorder playback, does not stop recording (make sure to call :StopRecording()), optional parameter if set to true, function will return an update frame with the type defined by FrameMode.
new_recorder:StopPlaying(boolean?): (<INTER_UPDATE_FRAME>|<FULL_UPDATE_FRAME>|<UPDATE_FRAME>)?
-- Stops recorder playback, does not start recording (make sure to call :StartRecording()), optional parameter if set to true, will return an update frame with the type defined by FrameMode.
new_recorder:StartRecording()
-- Starts recording on recorder, does not stop playback (make sure to call :StopPlaying()).
new_recorder:StopRecording()
-- Stops recording on the recorder, does not stop playback (make sure to call :StopPlaying()), more closer to PauseRecording, as resuming recording can cause jumps in playback.
Instance Management Methods
new_recorder:AddInstance(Instance, {[string]: boolean}?, {[string]: boolean}?)
-- Adds passed instance to be recorded, 2nd and 3rd parameter denote what to record on the instance, 2nd parameter is properties, and 3rd is attributes. They are optional. The table has string keys which is the property/attribute names, and the boolean value of these keys is whether the value should be interpolated (setting to false can help make certain values you dont want interpolated be bypassed and interpolate other data).
new_recorder:RemoveInstance(Instance)
-- Removes the passed instance from the recorder's tracking.
new_recorder:HasInstance(Instance): boolean
-- Returns true if the instance is being tracked, otherwise false.
Instance Specific Methods
new_recorder:GetFullFrameFromInstance(Instance, number): <FULL_FRAME>?
-- (RETURNED DATA IS SAFE TO CHANGE), returns a <FULL_FRAME> (See below what that is) in the tracked passed instance in the index passed as parameter 2. This operation allocates more memory, but means any data in the table can be modified without messing up tracking.
new_recorder:InterpolateFrameFromInstance(Instance, number, number): <INTERP_FRAME>?
-- (RETURNED DATA IS SAFE TO CHANGE), returns a <INTERP_FRAME> (See below what that is) in the tracked passed instance in the index passed as parameter 2. The 3rd parameter is the "time difference" between this frame and the last frame, used for interpolation (NOT A VALUE FROM 0 - 1, but a value between 0 and the length of the current frame). This operation calls the method above, as such allocates more memory, but that means data from the returned table is safe to modify.
new_recorder:GetFrameFromInstance(Instance, number): <FRAME>?, number, number
-- (RETURNED DATA IS NOT SAFE TO CHANGE), returns a <FRAME> (See below what that is) in the tracked passed instance at the timestamp in seconds passed as the second parameter. This operation returns an INTERNAL datatable, and as such data inside the table should ONLY BE READ, not modified. COPY the table (DEEP COPY), if you want to be able to modify the data. The other returned values is the index of the frame in the frames table, along with the time difference from the passed timestamp and the length of the frame. Used to interpolate the frame.
new_recorder:GetInstanceRecordedLength(Instance): number
-- Returns the seconds of recorded data in the passed tracked instance, or 0 if nothing was recorded.
Instance Specific DataTypes.
ALL DATATYPES HAVE THIS FORMAT ({length: number, props: {[string]: any}, attr: {[string]: any}})
-- <FRAME>: A simple frame data. This frame data may not have every "tracked" property or attribute in the props and attr tables, and as such a programmer should ensure not to access a nil value.
-- <FULL_FRAME>: Same data format as above, but ensures the props and attr tables have every tracked property and attribute stored in them, as such its safer to assume a specific value is in the table, as long as the property or attribute where tracked.
-- <INTERP_FRAME>: Same data format as both above. Similar to a <FULL_FRAME>, where its safe to assume a tracked property or attribute are in the props and attr tables, but will also interpolate the values between this and the next frame.
Module-wide Methods
new_recorder:DeleteFramesAfter(number)
-- Deletes the frames after the passed timestamp in seconds, useful to delete frames for "flawless" recording after playback, or to wipe all data (passed timestamp is 0).
new_recorder:GetFullFrame(number): <FULL_UPDATE_FRAME>
-- Returns a <FULL_UPDATE_FRAME> at the passed timestamp in seconds. Read below on what a <FULL_UPDATE_FRAME> is.
new_recorder:InterpolateFrame(number): <INTERP_UPDATE_FRAME>
-- Returns a <INTERP_UPDATE_FRAME> at the passed timestamp in seconds. Read below on what a <INTERP_UPDATE_FRAME> is.
new_recorder:GetFrame(number): <UPDATE_FRAME>
-- Returns a <UPDATE_FRAME> at the passed timestamp in seconds. Read below on what a <UPDATE_FRAME> is.
new_recorder:GetRecordedLength(): number
-- Returns the number of seconds recorded. It does this by checking for any tracked instance and getting the recorded length of that. Returns 0 if there is no recorded data.
new_recorder:Update(number): (<UPDATE_FRAME>|<FULL_UPDATE_FRAME>|<INTERP_UPDATE_FRAME>)?
-- Updates the recorder. If the recorder is playing, itll return an update frame for use in whatever you need. The update frame type is based on the "mode" passed to .new, and the :SetFrameMode methods. Default's to <INTERP_UPDATE_FRAME>.
Module-wide DataTypes.
ALL DATATYPES BELOW HAVE THE SAME FORMAT. ({[Instance]: {frame = (<FRAME> or <FULL_FRAME> or <INTERP_FRAME>), props = {[string]: boolean}, attr = {[string]: boolean}}})
<UPDATE_FRAME>: Simple update frame. This essentially uses <FRAME> datatype for the data.
<FULL_UPDATE_FRAME>: Same as above, but uses a <FULL_FRAME> datatype for data, and ensures all data is safe to modify in the return (so even the instance tracked props and attr tables)
<INTERP_UPDATE_FRAME>: Same as above, but uses a <INTERP_FRAME> datatype for data, and ensures all data is safe to modify in the return (so even the instance tracked props and attr tables)
This is all the current methods, but changes can happen, and will happen. Best to import the module into your game instead of requiring it via the marketplace id.
How to get the module
Get the module from the roblox marketplace here, https://create.roblox.com/marketplace/asset/3172715879/Recorder-Module
Update Log
18/01/2024 3:00 PM - New V2 Update, added interpolation, API breaking changes.
18/01/2024 6:00 PM - Added a method to denote the type of <UPDATE_FRAME>s returned from :Update, :StopPlaying and :StartPlaying. Fixed small bugs and issues with interpolation. Bug added (though really a side effect of the WRONG way I used before) where if the recording update frequency is too low, itll cause the rotating platform to rotate incorrectly, just increase update frequency (changed in example place to be .25 seconds instead of 1 second).
22/01/2024 10:00 PM - Added new API to denote a “recording frequency” which is essentially a way to limit the recording “FPS” by denoting how much “time” in seconds needs to pass for it to attempt to record a new frame. This can help boost performance and limit memory even more, while making it easier to manage varying framerates for recording and playing (essentially your update loop can be identical no matter the recorder object’s state, thus allowing smooth playback where needed, and better memory/performance when recording). Simple API added to the documentation underneath basic getter/setter methods. By default the update frequency is 0, which means essentially every call to update will lead to a new recorded frame, you set the value in seconds to limit this.
22/01/2024 10:00 PM - Bonus implementation, interpolation will attempt to interpolate the last recorded frame (the frame last recorded) with current value properties to help smoothen playback with slower recording frequencies. There are still issues with this, specifically around the fact that this method does not account for the fact that the object may’ve moved between this and the last frame, meaning for length of the recording frequency, it could have weird logarithmic-based <INTERP_FRAME>s. I may add a toggle to disable this behavior in the future, or remove it entirely. A way to combat this is to call an undocumented method, :RecordAFrame(delta)
, the reason this method is undocumented is due to future breaking changes this method could happen, and the fact that it may not really be useful for a lot of things. This method is essentially :Update(delta)
but ignores recording frequency and state.
Examples
Example place below, uses the same place showcased in the video.
InstanceRecorderExample.rbxl (63.0 KB)
Another Example place, where the player is also reversed.
InstanceModuleExample_PlayerReversed.rbxl (63.2 KB)
Video: