I am making a EXP system for a group and I need a liitle feedback

Hello, I am a developer working on a Napoleonic group and I am currently trying to add a exp system and I’ve managed to do that so far but the problem now is that I have to figure out how to add exp when a player kills another player and how to save levels and exp when the player leaves the game. Here’s the scripts I’ve made:

-- [ Aiden_Hatzfeldt ] --

-- Player Variables -- 
local player = game.Players.LocalPlayer
local level = player:WaitForChild("Level")
local current = level:WaitForChild("Current")
local max = level:WaitForChild("Max")

-- UI Variables --
local gui = script.Parent
local exterior = gui:WaitForChild("Exterior")
local label = exterior:WaitForChild("Level")
local exp = exterior:WaitForChild("Exp")
local bar = exterior:WaitForChild("Bar")

-- Change stats when join -- 
label.Text = "Level "..level.Value
exp.Text = current.Value.."/"..max.Value.." EXP"
bar.Size = UDim2.new(current.Value/max.Value, 0, 1, 0)

level.Changed:Connect(function(val)
	label.Text = "Level "..level.Value
	exp.Text = current.Value.."/"..max.Value.." EXP"
	bar.Size = UDim2.new(current.Value/max.Value, 0, 1, 0)
end)

current.Changed:Connect(function(val)
	exp.Text = current.Value.."/"..max.Value.." EXP"
	bar.Size = UDim2.new(current.Value/max.Value, 0, 1, 0)
end)

Thats the main script

-- [ Aiden_Hatzfeldt ] --

game.Players.PlayerAdded:Connect(function(player)
	local level = Instance.new("IntValue", player)
	level.Name = "Level"
	level.Value = 1
	
	local exp = Instance.new("IntValue", level)
	exp.Name = "Current"
	exp.Value = 0
	
	local maxExp = Instance.new("IntValue", level)
	maxExp.Name = "Max"
	maxExp.Value = 1000
	
	
	exp.Changed:Connect(function(val)
		if exp.Value >= maxExp.Value then
			-- Do stuff
			
			level.Value = level.Value + 1
			exp.Value = 0
		end
	end)
end)

And here is the system itself so if someone could maybe give me a little feedback or changes I can make so it runs better, also please help me with the saving levels and exp, and how to give the player exp when that player kills another player.

3 Likes

2 things:
1- the exp required to level up (maxExp) should be determined by the level, for example: maxExp.Value = level.Value * 100, this way, in level 1 you would need 100 exp to level up, in level two you would need 200 exp and so on
2- instead of setting exp back to 0 when the player levels up, its better if you do exp.Value -= maxExp.Value. so if the player needed 100 exp to level up and got 200 exp, he would keep the excess of exp. this way is possible to level up multiple times if you get a lot of exp

1 Like

You would want to make usage of datastores for storing xp - Data Stores | Roblox Creator Documentation

I would change the parent of level, exp, and maxExp to a folder of whatever you want your player’s stats to be, here is also a way to save your player’s data with the new changes.

local DataStoreService = game:GetService("DataStoreService")

local playerData = DataStoreService:GetDataStore("SetToWhatYouWant")



local function onPlayerJoin(player)
	
	local PlayerStats = Instance.new("Folder")
	PlayerStats.Name = "PlayerStats"
	PlayerStats.Parent = player
	
	--//Stats\\--
	
	local level  = Instance.new("IntValue")
	level .Name = "level "
	level .Parent = PlayerStats

	local exp = Instance.new("NumberValue")
	exp.Name = "exp"
	exp.Parent = PlayerStats
	
	local maxExp = Instance.new("IntValue")
	maxExp.Name = "maxExp"
	maxExp.Parent = PlayerStats


	local playerUserId = "Player_" .. player.UserId
	local data = playerData:GetAsync(playerUserId)

	if data then

		level.Value = data['Level']
		exp.Value = data['XP']
	else

		level.Value = 1
		exp.Value = 0
	end
end


local function create_table(player)

	local player_stats = {}

	for _, stat in pairs(player.PlayerStats:GetChildren()) do

		player_stats[stat.Name] = stat.Value
	end

	return player_stats
end


local function onPlayerExit(player)


	local player_stats = create_table(player)
	local success, err = pcall(function()

		local playerUserId = "Player_" .. player.UserId

		playerData:SetAsync(playerUserId, player_stats) 
	end)


	if not success then

		warn('Could not save data!')
	end
end


game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)

Then I would make a LevelServer which handles when a player levels up and when a player kills someone.

game.Players.PlayerAdded:Connect(function(player)
	
	local PlayerStats = player:WaitForChild('PlayerStats')
	local level = PlayerStats:WaitForChild('level')
	local exp = PlayerStats:WaitForChild('exp')
	local maxExp = PlayerStats:WaitForChild('maxExp')
	
	player.CharacterAdded:Connect(function(character)
		character.Humanoid.Died:Connect(function(died)
			
			local killer = character:FindFirstChild('creater')
			if killer ~= nil and killer.Value ~= nil then
				
				exp.Value += 10 --Change if you want players to get more exp per kill!
			end
		end)
	end)
	
	maxExp.Value = level.Value * 100 --Change if you want it to take longer or shorter to level up!
	
	level.Changed:Connect(function(Changed)
		if Changed then
			
			maxExp.Value = level.Value * 100 --Change if you want it to take longer or shorter to level up!
		end	
	end)
	
	exp.Changed:Connect(function(Changed)
		if exp.Value >= maxExp.Value then
			
			level.Value += 1
			exp.Value = 0
			maxExp.Value = level.Value * 100 --Change if you want it to take longer or shorter to level up!
		end		
	end)
end)

None of this is tested so tell me if it works!

You should loop the if statement for:

if exp.Value >= maxExp.Value then

This way if the player gains 1000 exp when their requirement is 100, they can continue leveling up with the extra exp they have in store unless your intention is to not carry over the extra exp. Also, try using a leaderstats system to store your variables, mainly a folder of sort. Learning Datastore will allow you to learn both leaderstats and how to save stats when the player leaves.

I’d also recommend using the ProfileService module to remove most of the problems with Data Stores as well as efficient and easy to read code.

Alright so I tested it and it seems to work but I think mu script for expbar UI is interfering with the one you made

If you want I could post that script here as well?

Yeah sure go ahead and post it I might be able to find the issue! I can wait until tomorrow.

Apologies but can you wait until tomorrow?

local player = game.Players.LocalPlayer
local level = player:WaitForChild("level")
local current = level:WaitForChild("Current")
local max = level:WaitForChild("Max")

-- UI Variables --
local gui = script.Parent
local exterior = gui:WaitForChild("Exterior")
local label = exterior:WaitForChild("Level")
local exp = exterior:WaitForChild("Exp")
local bar = exterior:WaitForChild("Bar")

-- Change stats when join -- 
label.Text = "Level "..level.Value
exp.Text = current.Value.."/"..max.Value.." EXP"
bar.Size = UDim2.new(current.Value/max.Value, 0, 1, 0)

level.Changed:Connect(function(val)
	label.Text = "Level "..level.Value
	exp.Text = current.Value.."/"..max.Value.." EXP"
	bar.Size = UDim2.new(current.Value/max.Value, 0, 1, 0)
end)

current.Changed:Connect(function(val)
	exp.Text = current.Value.."/"..max.Value.." EXP"
	bar.Size = UDim2.new(current.Value/max.Value, 0, 1, 0)
end)

Alright here’s the script, its a local script located in a GUI in StarterGui

Maybe try this?

local player = game.Players.LocalPlayer
local level = player:WaitForChild("level")
local exp = player:WaitForChild("exp")
local max = player:WaitForChild("maxExp")

-- UI Variables --
local gui = script.Parent
local exterior = gui:WaitForChild("Exterior")
local label = exterior:WaitForChild("Level")
local expGui = exterior:WaitForChild("Exp")
local bar = exterior:WaitForChild("Bar")

-- Change stats when join -- 
label.Text = "Level "..level.Value
expGui.Text = exp.Value.."/"..max.Value.." EXP"
bar.Size = UDim2.new(exp.Value/max.Value, 0, 1, 0)

level.Changed:Connect(function(val)
	label.Text = "Level "..level.Value
	expGui.Text = exp.Value.."/"..max.Value.." EXP"
	bar.Size = UDim2.new(exp.Value/max.Value, 0, 1, 0)
end)

exp.Changed:Connect(function(val)
	expGui.Text = exp.Value.."/"..max.Value.." EXP"
	bar.Size = UDim2.new(exp.Value/max.Value, 0, 1, 0)
end)

Ok so this line gets a error

	local data = playerData:GetAsync(playerUserId)

There where 2 errors after i tried ur first 2 scripts and when you send the third one that fixed on of them but theres still one left

Try local data = playerData:GetAsync(playerUserId) or 0

No that didn’t help but theres also in the Output saying “Infinte yield possible on” these 2 lines

local levels = PlayerStats:WaitForChild('level')

and

local level = player:WaitForChild('level')

The lines are in different scripts

Ok so the first line seems fine, could you share what you are setting PlayerStats to. The second line is because you need to look in PlayerStats, not player so try local PlayerStats = player:WaitForChild('PlayerStats') and then local level = PlayerStats:WaitForChild('level') this should solve both your issues. Also, the local data = playerData:GetAsync(playerUserId) or 0 can just be local data = playerData:GetAsync(playerUserId) because we are checking if there is data already.

Hmm, sadly that didn’t help and this the error being caused by local data = playerData:GetAsync(playerUserId) or 0

Try turning this on:

Ok thank you that fixed that but theres still the “Infinite yield” thing which I really don’t know what it means

It means that the object it doesn’t exist.
Try capitalizing the “Level” instead of “level”. I think that’s your problem.

Ok so my last problem now is that the script isn’t changing the text for the exp bar, it still says 0/0 EXP as I set its name in properties but script should set the text to however much exp the player have, this worked before but its not anymore and I am not getting anymore errors