I currently have a script that is supposed to save the CFrame of a model into a DataStore.
At first I figured I could just use :GetModelCFrame() and save that as a value, but after testing and further research, I've figured out you can't save CFrames into a DataStore.
It has to save the Rotation and Position of the Model into a DataStore, but it can't be a CFrame. Not sure how I could work around this, and don't really know where to start. Could really use another thought on here. Thanks!
The solution to transmitting or compacting any incompatible data is known as serialization. Likewise, the receiving or unpacking of any serialized data is known as deserialization.
For a better idea, say I want to drop a 5x5x5 ice cube into a 1x1 square hole. I can serialize the ice cube by converting it back into it's liquid form (water), and the water will easily flow right through the 1x1 opening. I can then deserialize the water by freezing it back into a 5x5x5 ice cube once it's on the other side, and I've successfully transmitted data that we otherwise wouldn't be able to!
Every instance of CFrame
comes in the data type of userdata. It's important to know that when saving data to data store, it's not as simple as just passing it from point A to point B as you would arguments to a function. These incompatible data types need to be properly encoded before being transmitted.
The only data types that don't need to be encoded before they're sent off to ROBLOX's backend, are strings and integers. Now you're probably thinking, "then why are we able to save tables?". This is because ROBLOX actually encodes the table (to a string value) before it's sent off to their data storage. The encoding format they use to do this is known as JavaScript Object Notation (or JSON), which is quite popular. I'll leave you some more information on JSON here if you're interested.
Alright, hopefully I didn't bore you too much with all that information. On to the fun part, implementing all of this in code. Now just like I said before, the only data that can actually be saved to data store are strings and integers. So how can we save other data types...? By converting them into strings or integers!
Sometimes serializing data can be a real pain, because it may not have many descriptive attributes. Luckily for us, encoding and decoding a CFrame is very easy! We can utilize a function called components
which we can call as a method on a CFrame instance that returns all the information about the CFrame as it's unpacked matrix! Here's an example.
local function encodeCFrame(cf) -- Return the tuple of numbers components() returns, and pack it into a table. return {cf:components()} end
This now converts the CFrame data into a table, which is automatically encoded by ROBLOX. You'll now be able to save this CFrame value once it's been passed through this function. All that's left is decoding, which is equally simplistic!
local function decodeCFrame(cf) -- Return a new CFrame, with the exact same orientation as the one we saved. return CFrame.new(unpack(cf)) end
Combining this together to create a save and load system, would yield something like this:
local DataStore = game:GetService("DataStoreService") local storage = DataStore:GetDataStore("Test") local function encodeCFrame(cf) return {cf:components()} end local function decodeCFrame(cf) return CFrame.new(unpack(cf)) end local myCFrame = CFrame.new(1, 5, 1) * CFrame.Angles(0, math.pi, 0) storage:SetAsync("CFrame", encodeCFrame(myCFrame)) print(storage:GetAsync("CFrame")) -- > Our exact CFrame!
Hope I didn't go overboard on explanation, but there it is. If you have any questions, feel free to write a comment and let me know.
It came to my attention that I forgot to mention booleans! Boolean values (like strings, and integers), also do not need to be encoded to be saved directly to data store. The same goes for nil values as well, although it's (still) equivalent to nothing.