Hunger System not working as intended

Hi everyone! I’m trying to make a hunger system where your hunger decreases every few seconds and you can increase it by eating food, and if it hits 0, you die.

The problem is it’s glitching out a lot and sometimes it goes over 100 and below 0. There were minor things like this happening before so I tried using math.clamp and made a new function to increase and decrease the hunger and now almost every single time it goes over 100 and below 0. The code feels like a mess now and I don’t know what’s causing it. Another issue is after the hunger decreases by the right amount one time (5), it starts decreasing by double the amount.

I’m trying to solve everything myself right now but it would be great if anyone has any tips or ways I can make the code better or if anyone knows the issue. I’m new to math.clamp so if anyone knows how to fix this it would be amazing!!

Here is a video of the issue: Streamable

This is the entire code for the project:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")

local Remotes = ReplicatedStorage:WaitForChild("Events")
local MainEvent = Remotes:WaitForChild("Eat")

-- HUNGER

local MAX_HUNGER = 100
local MIN_HUNGER = 1

local STARTING_VALUE = MAX_HUNGER
local DECREASE_AMOUNT = 5
local DECREASE_TIME = 2
local START_DECREASE_TIME = DECREASE_TIME * 2
local HUNGRY_AMOUNT = 50

-- SATURATION

local MAX_SATURATION = 100
local SATURATION_STARTING_VALUE = 0
local SATURATION_DECREASE_AMOUNT = 4
local MIN_SATURATION = 0
local SATURATION_DECREASE_TIME = DECREASE_TIME / 4


-- STARVE

local STARVE_INTERVAL = 0.5
local STARVE_DAMAGE = 10

-- EFFECTS

local DEFAULT_SPEED = 16
local SOUR_SPEED = 20
local SOUR_INTERVAL = 5
local EAT_INTERVAL = 3

--


local DEBOUNCE = false

--

local Foods = {
   ["Carrot"] = {["Sour"] = false, ["HungerIncrement"] = 35, ["Saturate"] = 20},
   ["Apple"] = {["Sour"] = true, ["HungerIncrement"] = 30, ["Saturate"] = 35},
   ["Lemon"] = {["Sour"] = true, ["HungerIncrement"] = 10, ["Saturate"] = 10},
   ["Orange"] = {["Sour"] = true, ["HungerIncrement"] = 40, ["Saturate"] = 30}
}

local Drinks = {
   ["Soda"] = {["Sweet"] = true, ["SpeedIncrement"] = 5},
   ["Lemonade"] = {["Sweet"] = false, ["SpeedIncrement"] = 3},
   ["Orange Juice"] = {["Sweet"] = true, ["SpeedIncrement"] = 4},
}

--

local Saturated = true
local State = nil

function RBX_ASSET_ID(number)
   if number then
   	number = tostring(number)
   	return "rbxassetid://".. number
   end
   return false
end

-- Function to change/set hunger/saturation by number
function Change(object, amount, method)
   if tonumber(method) == 1 then
   	object.Value += tonumber(amount)
   elseif tonumber(method) == 2 then object.Value -= tonumber(amount) elseif not method or tonumber(method) == 3 then object.Value = tonumber(amount) end
   
   return object.Value
end

-- Create hunger and playerData for a new player, called when a player joins the servrer
function new(Player)
   -- Check if there is a player data folder, if not, create one
   local PlayerData = nil
   if not Player:FindFirstChild("playerData") then
   	PlayerData = Instance.new("Folder")
   	PlayerData.Name = "playerData"
   	PlayerData.Parent = Player
   else PlayerData = Player:WaitForChild("playerData") end

   -- Create the hunger value
   local Hunger = Instance.new("IntValue")
   Hunger.Name = "Hunger"
   Hunger.Value = STARTING_VALUE
   Hunger.Parent = PlayerData

   -- Create the saturation value
   local Saturation = Instance.new("IntValue")
   Saturation.Name = "Saturation"
   Saturation.Value = SATURATION_STARTING_VALUE
   Saturation.Parent = PlayerData

   -- Set attributes function so variables can be accessed by other scripts, it is a function so it can be called multiple times
   local function setAttributes(HUMANOID)
   	if HUMANOID then
   		HUMANOID:SetAttribute("MaxHunger", MAX_HUNGER)
   		HUMANOID:SetAttribute("MinHunger", MIN_HUNGER)
   	end
   end

   --  Hunger/saturation decrease
   
   Player.CharacterAdded:Connect(function(Character)
   	local Humanoid = Character:WaitForChild("Humanoid")
   	
   	-- Call set attributes each time the character gets added
   	setAttributes(Humanoid)

   	task.wait(START_DECREASE_TIME) -- Wait the start decrease time amount before the players hunger starts decreasing

   	while task.wait() do
   		-- HUNGER (Uses two if statements to check if saturated to make the code look less messy)
   		if not Saturated then
   			if (Hunger.Value >= MIN_HUNGER) and not (Change(Hunger, DECREASE_AMOUNT, 2) <= MIN_HUNGER) then
   				-- DECREASE
   				task.wait(DECREASE_TIME)
   				Change(Hunger, DECREASE_AMOUNT, 2)
   				Hunger.Value = math.clamp(Hunger.Value, MIN_HUNGER, MAX_HUNGER)
   				if Hunger.Value <= HUNGRY_AMOUNT then
   					State = "Hungry"
   				end
   			else
   				-- STARVING
   				task.wait(STARVE_INTERVAL)
   				Change(Hunger, MIN_HUNGER)
   				Hunger.Value = math.clamp(Hunger.Value, MIN_HUNGER, MAX_HUNGER)
   				if Humanoid then
   					Humanoid:TakeDamage(STARVE_DAMAGE)
   				end
   				State = "Starving"
   			end
   		end

   		-- SATURATION
   		if Saturated then
   			if (Saturation.Value >= MIN_SATURATION) and not (Change(Saturation, SATURATION_DECREASE_AMOUNT, 2) <= MIN_SATURATION) then
   				task.wait(SATURATION_DECREASE_TIME)
   				Change(Saturation, SATURATION_DECREASE_AMOUNT, 2)
   				Saturation.Value = math.clamp(Saturation.Value, MIN_SATURATION, MAX_SATURATION)
   			else Saturation.Value = 0 Saturated = false end
   			if (Change(Saturation, SATURATION_DECREASE_AMOUNT, 2) == MIN_SATURATION) then
   				task.wait(SATURATION_DECREASE_TIME)
   				Change(Saturation, MIN_SATURATION)
   				Saturation.Value = math.clamp(Saturation.Value, MIN_SATURATION, MAX_SATURATION)
   			end
   		end

   		Humanoid.Died:Connect(function()
   			Change(Hunger, MAX_HUNGER, 3)
   			Change(Saturation, MAX_SATURATION, 3)
   		end)
   	end
   end)
end


Players.PlayerAdded:Connect(new)

function Activated(Player, Name, FoodInstance)
   -- Define the players character
   local Character = Player.Character
   if not Character or not Character.Parent then
   	Character = Player.CharacterAdded:Wait()
   end
   local Humanoid = Character:WaitForChild("Humanoid")
   
   -- Food
   if Foods[Name.Value] then
   	local Food = Foods[Name.Value]
   	local PlayerData = Player:FindFirstChild("playerData")
   	local Hunger = PlayerData:FindFirstChild("Hunger")
   	local Saturation = PlayerData:FindFirstChild("Saturation")
   	if PlayerData and Hunger and not DEBOUNCE then
   		DEBOUNCE = true
   		-- Eating animation
   		if FoodInstance then
   			local Animator = Humanoid:WaitForChild("Animator")

   			local EatAnimation = Instance.new("Animation")
   			EatAnimation.AnimationId = RBX_ASSET_ID(9039489111)

   			local EatAnimationTrack = Animator:LoadAnimation(EatAnimation)
   			EatAnimationTrack:Play()
   			Debris:AddItem(FoodInstance, 3)
   			task.wait(EAT_INTERVAL) DEBOUNCE = false
   		end
   		-- Increase hunger
   		if not (Change(Hunger, Food["HungerIncrement"], 1) >= MAX_HUNGER) then
   			Change(Hunger, Food["HungerIncrement"], 1)
   			Hunger.Value = math.clamp(Hunger.Value, MIN_HUNGER, MAX_HUNGER)
   		else Hunger.Value = MAX_HUNGER State = "Full" end
   		-- Increase saturation
   		if Food["Saturate"] then
   			Saturated = true
   			if not (Change(Hunger, Food["Saturate"], 1) >= MAX_SATURATION)then
   				Change(Hunger, Food["Saturate"], 1)
   				Saturation.Value = math.clamp(Saturation.Value, MIN_SATURATION, MAX_SATURATION)
   			else Saturation.Value = MAX_SATURATION end
   		end
   		-- Check if the food is sour
   		if Food["Sour"] then
   			if Humanoid.WalkSpeed == SOUR_SPEED then repeat task.wait() until Humanoid.WalkSpeed ~= SOUR_SPEED end
   			Humanoid.WalkSpeed = SOUR_SPEED
   			task.wait(SOUR_INTERVAL)
   			Humanoid.WalkSpeed = DEFAULT_SPEED
   		end
   	end
   end
   -- Drink
   if Drinks[Name.Value] then
   	local Drink = Drinks[Name.Value]
   end
end

MainEvent.OnServerEvent:Connect(Activated)

Any help is appreciated!! If anyone knows even the smallest way I can make the code better or if you want more information, please tell me. Thank you so much!!

3 Likes

if Hunger.Value >= MIN_HUNGER

Your Change function is still being called when Hunger.Value == MIN_HUNGER. Do note you are clamping back to MIN_HUNGER so it’s an endless cycle

2 Likes

Can you tell me what I would write to fix this?

1 Like

In ```while task wait() do
if not saturated


Remove the = from ``` Hunger.Value >= MIN_HUN ```

Also 

Add ``` math.clamp(Hunger.Value) ``` to the Change function at the very bottom so it looks like

```local function Change() 
   ***Everything already in Change***
   math.clamp(Hunger.Value)
end

I’m sorry for not just fixing the code but I’m on mobile right now and its crazy how slow it is

1 Like

It’s fine thank you so much!!!

So

function Change(object, amount, method)
	if tonumber(method) == 1 then
		object.Value += tonumber(amount)
	elseif tonumber(method) == 2 then object.Value -= tonumber(amount) elseif not method or tonumber(method) == 3 then object.Value = tonumber(amount) end
	object.Value = math.clamp(object.Value, MIN_HUNGER, MAX_HUNGER)
	
	return object.Value
end

on the change function and change the hunger decrease thing to

if (Hunger.Value > MIN_HUNGER) and not (Change(Hunger, DECREASE_AMOUNT, 2) <= MIN_HUNGER) then
	-- DECREASE
	task.wait(DECREASE_TIME)
	Change(Hunger, DECREASE_AMOUNT, 2)
	if Hunger.Value <= HUNGRY_AMOUNT then
		State = "Hungry"
	end
else

?

Oh everything is fixed, thank you so much!!! I changed all of >= and <= to just > and <

1 Like

Yeah, if it doesn’t work we can look farther into it

1 Like

Oh I didn’t realize, it still goes down by the correct number (5) on the first loop, then it starts decreasing by double the amount (10). If I set the decrease amount to 10 then it would decrease by 10 on the first loop then it would start decreasing by 20. Do you know how to solve this?

1 Like

Can you put print(object) into the Change function and tell me what its printing?

1 Like

It prints twice even though I’ve managed to put a debounce recently. I’m gonna test it a few more times just to make sure.

1 Like

Nevermind, I’m pretty sure this is fixed I changed a few things in the code. Thank you so much for the help!

1 Like