Hi, I’m making a Star Wars droid companion system where the primary, secondary and tertiary colours are fully customisable. The model is structured like this:

This is the current code I have, though it doesn’t seem to work and I just wanted to know if there was a better and actually functional way of doing this:
-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
-- Droid Module
local DroidModule = require(ReplicatedStorage:FindFirstChild("Droids").DroidModule)
-- RemoteEvent
local DroidEvent = ReplicatedStorage:FindFirstChild("Droids").DroidEvent
-- Datastore
local DataStore = DataStoreService:GetDataStore("Development1")
-- Save Function
local function SaveData(Player, Character)
local DroidName
print("Data saved")
for i,v in pairs(DroidModule) do
if Character:FindFirstChild(v.Title) then
DroidName = Character:FindFirstChild(v.Title)
end
end
local DroidColours = {
["DroidName"] = DroidName.Name,
["PrimaryRed"] = DroidName.Body.Primary.Head.Color.R,
["PrimaryGreen"] = DroidName.Body.Primary.Head.Color.G,
["PrimaryBlue"] = DroidName.Body.Primary.Head.Color.B,
["SecondaryRed"] = DroidName.Body.Secondary.Head.Color.R,
["SecondaryGreen"] = DroidName.Body.Secondary.Head.Color.G,
["SecondaryBlue"] = DroidName.Body.Secondary.Head.Color.B,
["TertiaryRed"] = DroidName.Body.Tertiary.Head.Color.R,
["TertiaryGreen"] = DroidName.Body.Tertiary.Head.Color.G,
["TertiaryBlue"] = DroidName.Body.Tertiary.Head.Color.B,
}
DataStore:SetAsync(Player.UserId, function()
return DroidColours
end)
end
-- Load Function
local function LoadData(Player, Character)
local DroidColours
local success, err = pcall(function()
DroidColours = DataStore:GetAsync(Player.UserId)
end)
if success then
for i,v in ipairs(DroidColours) do
local NewDroid = ReplicatedStorage.Droids:FindFirstChild(DroidColours[1]):Clone()
for i,primary in (NewDroid.Body.Primary:GetChildren()) do
primary.Color = Color3.fromRGB(DroidColours[2], DroidColours[3], DroidColours[4])
end
for i,secondary in (NewDroid.Body.Secondary:GetChildren()) do
secondary.Color = Color3.fromRGB(DroidColours[5], DroidColours[6], DroidColours[7])
end
for i,tertiary in (NewDroid.Body.Secondary:GetChildren()) do
tertiary.Color = Color3.fromRGB(DroidColours[8], DroidColours[9], DroidColours[10])
end
NewDroid.Parent = Character
end
end
end
-- Player
game.Players.PlayerAdded:Connect(function(Player)
local Character = Player.Character or Player.CharacterAdded:Wait()
LoadData(Player, Character)
end)
game.Players.PlayerRemoving:Connect(function(Player)
local Character = Player.Character or Player.CharacterAdded:Wait()
SaveData(Player, Character)
end)
Could you try printing the table after setting it?
Do you have a BindToClose function? When testing in studio it could be that the game closes before it had time to save.
Also it’s a lot more efficient to store your colors as hex instead of R G and B values
How would I convert it to hex values?
using the Color3:ToHex() method
1 Like
Printing doesn’t seem to work.
-- Save Function
local function SaveData(Player, Character)
local DroidName
print("Data saved")
for i,v in pairs(DroidModule) do
if Character:FindFirstChild(v.Title) then
DroidName = Character:FindFirstChild(v.Title)
end
end
local DroidColours = {
["DroidName"] = DroidName.Name,
["PrimaryRed"] = DroidName.Body.Primary.Head.Color.R,
["PrimaryGreen"] = DroidName.Body.Primary.Head.Color.G,
["PrimaryBlue"] = DroidName.Body.Primary.Head.Color.B,
["SecondaryRed"] = DroidName.Body.Secondary.Head.Color.R,
["SecondaryGreen"] = DroidName.Body.Secondary.Head.Color.G,
["SecondaryBlue"] = DroidName.Body.Secondary.Head.Color.B,
["TertiaryRed"] = DroidName.Body.Tertiary.Head.Color.R,
["TertiaryGreen"] = DroidName.Body.Tertiary.Head.Color.G,
["TertiaryBlue"] = DroidName.Body.Tertiary.Head.Color.B,
}
print(DroidColours.DroidName)
DataStore:SetAsync(Player.UserId, function()
return DroidColours
end)
end
Print the whole table and check if anything is nil
Doesn’t print anything when saving data, though when loading data I get invalid argument #1 to 'ipairs' (table expected, got nil) for line for i,v in ipairs(DroidColours) do
Function:
-- Load Function
local function LoadData(Player, Character)
local DroidColours
local success, err = pcall(function()
DroidColours = DataStore:GetAsync(Player.UserId)
end)
if success then
for i,v in ipairs(DroidColours) do
local NewDroid = ReplicatedStorage.Droids:FindFirstChild(DroidColours[1]):Clone()
for i,primary in (NewDroid.Body.Primary:GetChildren()) do
primary.Color = Color3.fromRGB(DroidColours[2], DroidColours[3], DroidColours[4])
end
for i,secondary in (NewDroid.Body.Secondary:GetChildren()) do
secondary.Color = Color3.fromRGB(DroidColours[5], DroidColours[6], DroidColours[7])
end
for i,tertiary in (NewDroid.Body.Secondary:GetChildren()) do
tertiary.Color = Color3.fromRGB(DroidColours[8], DroidColours[9], DroidColours[10])
end
NewDroid.Parent = Character
end
end
end
It should print, if it is not printing then your data is nil which means loading it will pass nil and will not pass a table with the data.
I rewrote the code to a much simpler version, I think it is an issue with defining Character when the player is leaving, any ideas?
-- DataStoreService
local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("Development")
-- Services
local Players = game:GetService("Players")
-- Save Function
local function SaveData(Player, Character)
local key = Player.UserId
local data = {
Player.Droids.Equipped.Value,
Player.Droids.Owned.Value
}
local success, err = pcall(function()
DataStore:SetAsync(key, data)
end)
if success then
print("Data saved")
end
end
game.Players.PlayerRemoving:Connect(function(Player)
local Character = Player.CharacterRemoving:Wait()
SaveData(Player, Character)
end)
game:BindToClose(function()
for i,Player in (Players:GetPlayers()) do
local Character = Player.CharacterRemoving:Wait()
SaveData(Player, Character)
end
end)
-- Load Function
local function LoadData(Player, Character)
local key = Player.UserId
local data
local success, err = pcall(function()
data = DataStore:GetAsync(key)
end)
if success and data then
Player.Droids.Equipped.Value = data[1]
Player.Droids.Owned.Value = data[2]
end
print(Character.Name)
end
game.Players.PlayerAdded:Connect(function(Player)
local Character = Player.CharacterAdded:Wait()
LoadData(Player, Character)
end)
No need for the character here.
-- DataStoreService
local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("Development")
-- Services
local Players = game:GetService("Players")
-- Save Function
local function SaveData(Player)
local key = Player.UserId
local data = {
Player.Droids.Equipped.Value,
Player.Droids.Owned.Value
}
local success, err = pcall(function()
DataStore:SetAsync(key, data)
end)
if success then
print("Data saved")
end
end
game.Players.PlayerRemoving:Connect(function(Player)
SaveData(Player)
end)
game:BindToClose(function()
for _, Player in pairs(Players:GetPlayers()) do
SaveData(Player)
end
end)
-- Load Function
local function LoadData(Player)
local key = Player.UserId
local data
local success, err = pcall(function()
data = DataStore:GetAsync(key)
end)
if success and data then
Player.Droids.Equipped.Value = data[1]
Player.Droids.Owned.Value = data[2]
else
warn("Failed to load data: " .. err)
end
end
game.Players.PlayerAdded:Connect(function(Player)
Player.CharacterAdded:Connect(function(Character)
LoadData(Player)
end)
end)
I understand, but for a much later version where it’ll save colours of parts inside the Character it’ll be necessary to use Character.
You could use Player.CharacterRemoving which will give you the Character but this will be triggered when the character respawns.