Leveling system's math

  1. What do you want to achieve? I want to create a “Leveling System”

  2. What is the issue? I tried doing setXP method myself but nothing worked, i don’t want to use a while loop because of performance issues (it also didn’t work as i expect it to).

  3. What solutions have you tried so far? I tried looking for tutorials, and looked at other forum posts but i didn’t see what I was looking for.

“I don’t want the whole script, just the math behind predicting what level a player will achieve with the xp that will be set”

--The types
type class = {
	__index: class,

	--Base class
	addPlayer: (player: Player, level: number, xp: number) -> object,
	getPlayer: (player: Player) -> object?,
	removePlayer: (player: Player) -> (),

	getRequiredXP: (level: number) -> number,

	--Object methods
	getLevel: (self: object) -> number,
	getXP: (self: object) -> number,
	setLevel: (self: object, level: number, force: boolean?) -> (),
	setXP: (self: object, xp: number) -> (),
}

export type object = typeof(setmetatable({} :: {
	owner: Player,
	
	level: number,
	xp: number,
}, class :: class))

--THIS IS NOT THE WHOLE SCRIPT (i can post the whole script if anyone wants)
local XP_PER_LEVEL = 180

function class.getRequiredXP(level)
	return (( level ^ 2 ) + (XP_PER_LEVEL * level) - (level ^ 2)) + 20
end

function class:setLevel(level, force)
	if self.level == level then 
		return end
	if self.level <= level and not force then
		warn("Tried setting level below current level (no force)") end
	
	local player = self.owner
	local levelsGained = level - self.level
	if levelsGained > 1 then
		for i = self.level, level, 1 do
			if not levelList[i] then continue end
			levelList[i](self.owner) --Execute the OnLevelCompleted function
		end
		
		local requiredXP = class.getRequiredXP(level)
		
		self.level = level
		player:SetAttribute('level', level)
		player:SetAttribute('requiredXP', requiredXP)
		
		return
	end
	
	if levelList[level] then levelList[level](self.owner) end --Execute the OnLevelCompleted function
	local requiredXP = class.getRequiredXP(level)
	
	--Set the variables and attributes
	self.level = level
	player:SetAttribute('level', level)
	player:SetAttribute('requiredXP', requiredXP)
end

function class:setXP(xp)
	--This is where i have the problem
end

I believe a loop is required for this as the number of xp can surpass the required amount for numerous next levels. Preformance shouldnt be an issue here, if the amount of xp earned is low, the loop could only run about twice or so. If the xp earned is high, to the point where someone could earn 20 levels for it, I would add a small delay, task.wait(0.05) could be sufficient.

i would really prefer loops but i think this should work we can calculate the new level based on given xp and update it if it’s increased and set the new xp

function class:setXP(xp)
    assert(xp >= 0, "Attempted to set negative XP")

    local currentLevel = self.level
    local currentTotalXP = 0
    for i = 1, currentLevel do
        currentTotalXP = currentTotalXP + class.getRequiredXP(i)
    end
    currentTotalXP = currentTotalXP + self.xp

    if xp < currentTotalXP then
        self.xp = xp - (currentTotalXP - self.xp)
        self.owner:SetAttribute('xp', self.xp)
        return
    end

    local newLevel = currentLevel
    local newTotalXP = currentTotalXP

    while newTotalXP + class.getRequiredXP(newLevel + 1) <= xp do
        newLevel = newLevel + 1
        newTotalXP = newTotalXP + class.getRequiredXP(newLevel)
    end

    if newLevel > currentLevel then
        self:setLevel(newLevel)
    end

    self.xp = xp - newTotalXP
    self.owner:SetAttribute('xp', self.xp)
end

Note that we can’t really avoid loops but i tried to avoid it as much as possible

1 Like

Before I start I want to mention Im bad at explaining :pray:

If it’s for every new level you would need +100 exp to level up, you could use mx+b = y.

The math: Added Exp * Level + 0
If the exp increased by 100 and you were level 5 it would be 100*5 which is 500 exp required to get to level 6. If you were level 1 you would require 100 exp to reach lvl 2.

The other approach is using exponential functions. Their form: y = s*a^x. Let’s say you wanted the exp needed to be double the previous, it would be like this: 100*2^(lvl-1). If you were lvl 1 it would be 100 exp required but if you were lvl 2 it would be 200.

I highly recommend you do some research on exponential and linear equations because I’m not the best at explaining

Using loops are less of a headache and should not cause performance issues at all for a simple calculation. If you are really trying to avoid loops, considering your getRequiredXP uses a formula to get the required experience only for that level, then you would need to get the summation of your equation.

Anyways, I’m not sure why your formula contains (level ^ 2) ... - (level ^ 2) since both of those will cancel out, so your formula just looks like:
REQUIRED_EXPERIENCE = (EXPERIENCE_PER_LEVEL * level) + STARTING_REQUIRED_EXPERIENCE. (substituted 20 for STARTING_REQUIRED_EXPERIENCE)

Your formula contains a constant c (STARTING_REQUIRED_EXPERIENCE) and n^1 (level), the summation formulas are cn and (n^2+n)/2 respectively. Converting your function will become:

TOTAL_EXPERIENCE = (((level ^ 2 + level) / 2) * EXPERIENCE_PER_LEVEL) + (level * STARTING_REQUIRED_EXPERIENCE).

Assuming you want this to calculate from 0 experience, you can substitute your level variable with level - 1, giving you a final function of:

TOTAL_EXPERIENCE = ((((level - 1) ^ 2 + (level - 1)) / 2) * EXPERIENCE_PER_LEVEL) + ((level - 1) * STARTING_REQUIRED_EXPERIENCE).

Now you just solve for a and convert this to Lua and you have yourself a non-looping based calculation function.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.