How do I make this WAYYY more optimised?

So like I own a TD game and like I wanna optimise this way further… Haven’t tried using the BezierPath module yet but I just wanna get some general feedback on how I can optimise this. Doesn’t use Humanoids btw.

function mob.LerpTo(newMob, target)
	local alpha = 0
	local startCFrame = newMob.Mover.Position
	local loop = nil
	local reachedTarget = Instance.new("BindableEvent")
	local distance = (newMob.Mover.Position - target).Magnitude

	loop = RunService.Heartbeat:Connect(function(delta)
		if newMob and newMob:FindFirstChild("Config") then
			local speed = newMob:GetAttribute("Speed")*workspace.Info.Timescale.Value

			local relativeSpeed = distance / speed
			local goalCFrame = startCFrame:Lerp(target, alpha)
			if newMob.Config:FindFirstChild("DontTurn") then
				if newMob.Config:FindFirstChild("DontTurn").Value == true then
					local lookAtCFrame = CFrame.lookAt(newMob.Mover.Position, Vector3.new(target.X,target.Y,target.Z))
					TweenService:Create(newMob.Mover, TweenInfo.new(.2, Enum.EasingStyle.Sine), { CFrame = lookAtCFrame }):Play()
				end
			else
				local lookAtCFrame = CFrame.lookAt(newMob.Mover.Position, Vector3.new(target.X,target.Y,target.Z))
				TweenService:Create(newMob.Mover, TweenInfo.new(.2, Enum.EasingStyle.Sine), { CFrame = lookAtCFrame }):Play()
			end

			newMob.Mover.Position = goalCFrame

			alpha += delta / relativeSpeed
			if alpha >= 1 then
				loop:Disconnect()
				reachedTarget:Fire()
			end
		else
			loop:Disconnect()
		end
	end)

	reachedTarget.Event:Wait()
end

function mob.Move(newMob, map, XZOffset, oldmovepoint, oldLane)
	local Root = newMob.PrimaryPart
	local newPos
	local waypoints
	local previouspoint=1

	if oldLane and oldLane ~=0 then
		waypoints = map.Waypoints[oldLane]
	else
		waypoints = map.Waypoints[newMob.WaypointNumber.Value]
	end

	if oldmovepoint and oldmovepoint~=0 then
		previouspoint = oldmovepoint
	end

	for waypoint=previouspoint, #waypoints:GetChildren() do
		newMob.MovingTo.Value = waypoint

		if waypoint == 1 then
		else
			if waypoints[waypoint]:FindFirstChild("A") and waypoints[waypoint]:FindFirstChild("B") then
				for t = 0,1,0.025 * (newMob:GetAttribute("Speed")*workspace.Info.Timescale.Value) do
					local waypoint1 = waypoints[waypoint].Position
					local waypoint2 = waypoints[waypoint].A.Position
					local waypoint3 = waypoints[waypoint].B.Position
					newPos = interpolation.QuadBezier(waypoint1,waypoint2,waypoint3,t)
					mob.LerpTo(newMob,Vector3.new(newPos.X + XZOffset,newPos.Y,newPos.Z + XZOffset))
				end
			else
				newPos =  waypoints[waypoint].Position
				mob.LerpTo(newMob,Vector3.new(newPos.X + XZOffset,newPos.Y,newPos.Z + XZOffset))
			end
		end
	end

	local enemyStats = EnemyDataModule.Enemies[newMob.Name]

	workspace.Info.Timescale:GetPropertyChangedSignal("Value"):Connect(function()
		newMob.Humanoid.WalkSpeed = (enemyStats.Speed - 0.5 or 1)
		print("Speed Changed.")
	end)



	takeDamageEvent:FireAllClients()
	map.Base.Humanoid:TakeDamage(newMob:GetAttribute("Health"))
	newMob:Destroy()
end--]]
1 Like

Don’t use bindable events.
Don’t use attributes or value objects.
Don’t use humanoids.
Cache the path beforehand.

1 Like

What can I replace BindableEvents with?

Custom signal module with an array or just redesign your code to not use events

Custom signal modules usually work on bindables too, they are pretty usefull, why would you don’t use them? events are pretty much lightweight, i’ve run few hundred thousands loops that created them and received and nothing, if properly handled of course

Custom events made by cache & callback are very annoying to deal with, especially when you need to manually fire listeners

No good custom signal module uses a bindable event. All good ones are pure luau and will be faster. Bindables cant pass data by reference. If you want to pass a large table, it will copy the whole thing which is a performance loss. Not to mention the reduced performance just for being a new instance

1 Like

You don’t need to optimize your code, you need to make it more redable, your code is nested, you can use guard clausess and invert your if statements to make it 100% better, then you can throw out half of it into a module or function or anything else

example:

if not newMob.Config:FindFirstChild("DontTurn") then
    -- do your simplier code
else
    if not newMob.Config.Turn.Value then return end
    -- Your code
end

We removed 1 level of nesting by simply inverting your statements
Note: You don’t need FindFirstChild for things like referencing, only if statements and first time reference

There is very usefull rule that longer the function the simplier it should be, soo for instance main script that loads a bunch of modules is pretty much simple function, soo 100+ lines is nothing to worry about, but let’s say we have very complex AI system, function should be shorter and probably call sub-functions that will do the job

example:

local function OnPlayerAdded(player: Player)
    ChatModule:Load(player)
    Inventory:Load(player)
    Commands.setPlayerPermissions(player)
    Commands.init(player)
    PlayerData:LoadValues(player)
    Framework:Check(player)
    -- And 10+ more
end

local function CalculateEnemyPath(start: Vector3, finish: Vector3, mode: string)
    -- Some very complex code that takes 10 lines
end

But does it really matter? we talk about minor performance boost, bindables comes at cost of being slower, but also they have advantage of being easier to use and more maintainable in some cases

EDIT: Also sending large tables is bad design, you should send only required data, rest can be accessed from other script, simpliest example is module script for tools data

Its not just not being able to send large tables, but you cant send anything by reference. Pure lua tables are just as maintainable. They are trivial to make, if you struggling you should go back to programming basics

i’m not strugling but i don’t see any real reason why don’t use built in bindables, they can be connected to multiple functions at once, not only one, also why i need to send anything by reference? most of the time you can send id and it will work the same as reference, also still why would you really need to send big tables? because it’s lazy solution, for real game development you don’t need 200 different values at once

In summary your solution is not practical for 99% of uses, references can be replaced by id system which takes few minutes to make and you usually send only few values over functions

It takes like 5 mins max to make a pure lua signal system and it will be better than bindables in every way. You can connect multiple functions to a lua signal too so i dont understand your point

1 Like

Sry, my mistake, i’ve mistaked it with references stuff, but back to the lua vs built-in, it’s simply no difference and for most of the time using bindables is enough, i used custom lua signals and they are pretty much useless unless you work with pure lua, 99% of the time using them is complicating stuff, and few miliseconds of speed aren’t worth it

Ok to finish this talk, if you like pure lua, then go for it, simply telling that they are better because of minor speed difference and flexibility in some situations is not that important in most of cases, bindables are easy solution for most of the time

NOTE: For 50% of times bindables can be replaced by module scripts callback :confused:

If you’re skilled enough, then there’s no reason to use bindables over a pure lua implementation like FastSignal (New Version).

Signals are better than module script callbacks because you can do additional operations like :Wait() and :Once() that module script callbacks, which are just functions, cannot do.

Bindables are fine for new developers who may not be skilled enough to use a signal library, but saying “minor speed difference and flexibility […] is not that important in most of cases” is quite dismissive.

How writing entire module to make button work is better? Also bindable events like MouseButton1Down or PlayerAdded cannot be replaced easily, and yea they are bindable events, if we talked about this custom object then sure you’re right, i used them only once or twice ever because other things can be made with module script callbacks

And last thing, signals are module script callbacks too, or table callbacks but still modules are tables, but in different way, if you know how custom signals work it’s simply:

  • Write function callback to a table
  • Call this function when you want to (:Fire methood usually)
  • Do whatever you want with this function in a table, you can add once, wait,
  • Call disconnect on a function to remove it from callbacks list and nulify reference

EDIT: Bindable event is yk, RBXScriptSignal, bc you know, you can bind to other stuff

MousButton1Down and stuff are not bindable events. They are RBXScriptEvents which are very different as they are designed to be events interacting with the underlying c++

Then probably i misunderstood your question, in some places they are called bindable events because you can bind them, yep bindables as instances are kindof bad, as i said i used them once or twice in 7 years, maybe for few solutions they are good solution but still functions are better

These are not bindable events, these are signals/events, and they are more closely related to a Signal library than bindable events.

This is over-complicating what a Signal library does, and you would have to add that to every single class or module which needs events. A Signal library would abstract all of that inside a very simple object that’s easy to use.

A callback is typically:

local function func(callback)
  -- some code
   callback() -- calling the callback
end

Trying to do callback lists and stuff like that is, again, over-complicating something that should be done in a Signal Library.

Lastly, callbacks is not necessarily identical to signals. With signals, the listeners get called in parallel, whereas with callbacks, they get called synchronously typically. You almost always want listeners to get called in parallel so that’s why callback lists is not a well known programming concept, whereas signals is very well-known is many different languages.

SHUT UP, they’re never called bindable events, they’re just called RBXScriptSignals, or more simply, signals.


Bindable events (and functions) should not exist anymore, i believe they are older than new technology such ModuleScripts, and are definitely older than the task library - which allows us to make pure lua Signal implementations without using bindables underneath).

Both ModuleScripts and Signal libraries offer a better alternative, which allow you to write event-based code more seamlessly than creating an instance in replicated storage for every event you need (a solution that becomes unworkable as the number of events grow).

Here’s some posts about signals for those who want to get started:

ok thx for explanation, still i don’t think using one or two bindables per game is bad idea, it’s more of a micro-optimization