Script running twice for no real reason?

While working on my bunnyhop module, i decided to get it from testing idea to an actual module, after finishing it up, every value is correct, BUT the constructor seems to be called twice by the same line? I at first thought ‘oh, its being requested and used by another script’, i went to an empty baseplate and turned the module into a local script, and the issue persists, images of relevance:

Screenshot_30
Screenshot_32
Screenshot_33


the threads arent the issue, i triple checked

3 Likes

I think this is happening because of setmetatable and the return bunnyhop.new()(assuming the return line is inside the bunnyhop module). Try returning bunnyhop instead and create the instance on another script with bunnyhop.new().

1 Like

When you return an invocation of the constructor inside of the module you are using the singleton pattern. Somewhere else in your code you are requiring the module and calling the constructor again.

3 Likes

Unlikely reason, but they can occur:

Sometimes when joining the game, the character “spawns twice” and has the code run twice.

If you are using a Normal Script with a Different RunContext, the script will both run in the Container and In the Character, as far as I know this only happens with “Starter” containers.

Are you using a regular local script or a script with the RunContext client? If the script is where the RunContext=Client, then you should add a check to the beginning of the script, for example this

if script.Parent.Name~=game.Players.LocalPlayer.Name then return end

(if script parent name ~= character (player) name)
or

if script.Parent.Name=="StarterCharacterScripts" then return end

(if script is in folder)

OR
Just make the localscript (Not script with RunContext)

14 Likes

Looking at the code, this seems like the most likely reason

1 Like

i did think about this, but i looked at the code very carefully and the most relevant references that are even remotely related to the constructor was just me asserting a value to the object variables, after decoupling the constructor call into a separate script, still unfortunately the same issue, and i’m starting to doubt my sanity so, if you want to take a look at the whole thing here it is:

local bunnyhop = {}
bunnyhop.__index = bunnyhop

--[[SETTINGS]]

local LOOKVECTOR_UPDATE_TICK = 0.1 -- How fast to update the direction of the velocity ( avoiding turn delay )
local MINIMUM_VELOCITY = 0 -- The default velocity of the bhop, recommend to keep at 0
local MAXIMUM_VELOCITY = 300-- Self explanatory
local VELOCITY_MULTIPLIER = 2.5 -- by what increment to increase velocity
local VELOCITY_DEGRADATION = 0.5 -- how much to decrease velocity per tick
local VELOCITY_DEGRADATION_TICK = 0.1 -- how frequent to degrade velocity relative to above constant
local CUSTOM_BODY_VELOCITY = nil -- by default the script creates a body velocity,if you want a specific one put the path here
local STANDING_STILL_SAFEGUARD = true -- bhop wont fire if the player is standing still (true | false)
local VELOCITY_VALIDATOR_TICK = 0.7 -- The time frame a user is allowed to continue a bunnyhop
local velocityIndex = 0 -- Holds velocity multiplication data (VITAL)
local jumpValueHolder = false

--[[MAIN]] --------------------------------------------------

local UserInputService = game:GetService('UserInputService')

-- [[Client dependencies]]
local Player = game.Players.LocalPlayer;
local Character = Player.Character or Player.CharacterAdded:Wait();
local Humanoid = Character:WaitForChild("Humanoid");
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart");


function bunnyhop.new()
	warn(debug.traceback("constructor caller:"))
	local self = setmetatable({}, bunnyhop)

	self._JumpRequestConnection = nil
	self._LandedRequestConnection = nil

	self._loopBreakCondition = true

	self._velocityObject = nil

	self:Enable()
	return self
end



function bunnyhop:Enable()
	print(debug.traceback("caller:"))
	local VelocityObj 

	if CUSTOM_BODY_VELOCITY == nil then
		VelocityObj = self._createVelocityObject()
	else
		assert(typeof(CUSTOM_BODY_VELOCITY) == "BodyVelocity", "Custom body velocity of invalid type, expected type 'BodyVelocity' ")
		VelocityObj = CUSTOM_BODY_VELOCITY
	end

	self._loopBreakCondition = true
	self._velocityObject = VelocityObj

	self._JumpRequestConnection = UserInputService.JumpRequest:Connect(function()
		if STANDING_STILL_SAFEGUARD then
			if Humanoid.MoveDirection == Vector3.new() then
				velocityIndex = MINIMUM_VELOCITY
				return
			end
		end

		print("Bunnyhopped")
		velocityIndex += VELOCITY_MULTIPLIER / 3
		jumpValueHolder = true
	end)

	self._LandedRequestConnection = Humanoid.StateChanged:Connect(function(New, Old)


		if New == Enum.HumanoidStateType.Landed then
			jumpValueHolder = false
			print("Landed?")
			task.delay(VELOCITY_VALIDATOR_TICK, function()
				if not jumpValueHolder then
					velocityIndex = MINIMUM_VELOCITY

				end
			end)
		end
	end)

	self:_SpawnThreads()

end

function bunnyhop:_SpawnThreads()
	task.spawn(function()
		print("Thread #1")
		while task.wait(VELOCITY_DEGRADATION_TICK) do
			if not self._loopBreakCondition then break end
			if velocityIndex <= MINIMUM_VELOCITY then continue end
			velocityIndex -= VELOCITY_DEGRADATION
		end
	end)

	task.spawn(function()
		print("Thread #2")
		while task.wait(LOOKVECTOR_UPDATE_TICK) do
			self._velocityObject.Velocity = HumanoidRootPart.CFrame.LookVector * velocityIndex
		end
	end)
end

function bunnyhop:Disable()
	self:_terminateVelocityObject()
	self._loopBreakCondition = false
	self._JumpRequestConnection:Disconnect()
	self._LandedRequestConnection:Disconnect()
end

function bunnyhop:_terminateVelocityObject()

	if self._velocityObject then 
		self._velocityObject:Destroy() 
		self._velocityObject = nil
	end

end

--[[Static methods]]-------------------------------------------
function bunnyhop._createVelocityObject()
	local BodyVelocity = Instance.new("BodyVelocity", HumanoidRootPart)
	BodyVelocity.MaxForce = Vector3.new(MAXIMUM_VELOCITY, 0, MAXIMUM_VELOCITY)
	return BodyVelocity
end


--[[Auto start]] ----------------------------------------------




print("IS THIS BEING CALLED TWICE OMG")
return bunnyhop

Screenshot_37

oh and, im a big fan of your posts samjay, your guides are great

( im aware that im kind of violating SRP, but since the system is pretty small it would have made things harder )

1 Like

to note is that the whole thing was moved to an EMPTY baseplate with nothing in it, only these scripts

It appears that the reason your code is producing unexpected behavior is due to the functionality of StarterCharacterScripts. Upon character spawning, it duplicates all content within its designated folder, including any module scripts. Consequently, if a module is “loaded” during the character spawn, it will generate another module that requires loading every time the player respawns leading to the issue you are experiencing. Moving the module to RepStorage should resolve this issue, allowing for the use of a singleton per client by returning the constructor.

Your appreciation is greatly appreciated, and it motivates me to contribute further. When I first started my series on clean code I didn’t really expect much, but it really means a lot that I am truly impacting developers on the platform. It makes it all worth it.

1 Like

I have tried doing this before also, and apparently still a no bono, I wonder, could this be an engine bug?

Screenshot_39
Screenshot_40

Can you send me the place file?

issue.rbxl (47.9 KB)

I fixed the problem, view the changes.

issue.rbxl (48.7 KB)

Thank you, and on an unrelated note, i noticed that the velocity from the bhop isn’t applying, even though checking the value shows the right thing, no velocity is actually being applied for some reason?

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