Optimization help regarding a custom pathfinding script thing

So I’ve been trying to make a function that uses a position, direction, jump height, walk speed, and other variables to calculate a jumping trajectory to find a valid node for the rest of the pathfinding script.

Currently I have a working version; the problem is that the function is far too resource-intensive, and I don’t know how I could optimize it without breaking it or making it too slow using waits. :confused:

Also, the function uses some other functions to work and is in a module script.

Basically, how can I optimize this function or the functions that it uses?

The function in question.

function module:Hop(From_Position:Vector3, Direction:Vector3, Grid:table, limit:Vector3, Jinfo:Vector2, Params:RaycastParams, PF)
	local Speed, JumpHeight = Jinfo.X, Jinfo.Y-- /\ /\ /\ Grid is a 3d table, limit is size of table and PF is a table of all the parts that Grid Nodes use
	local Delta, Vy, Arrow = 0, math.sqrt(2*workspace.Gravity*JumpHeight), Is_In_SD(Direction)	
	local CD = true--goes to other direction | /\ /\ /\ delta is the angle, Vy is needed for calculations, Arrow is given direction in array
	local Tinterval = (2*Vy)/workspace.Gravity--Time increase interval
	while Delta < 4 do
		local DIRECTION, Chspeed = SD_array[FSNX(Arrow+Delta)], Speed-- Direction from resulting delta
		while Chspeed >= 2 do
			local Time = Tinterval/2-- The current time
			local prev = From_Position-- the previous position
			while Time < 2*Tinterval do
				local Result = Vector3.new(From_Position.X+DIRECTION.X*Chspeed*Time, From_Position.Y+Vy*Time-(workspace.Gravity/2)*Time^2, From_Position.Z+DIRECTION.Z*Chspeed*Time)	
				local D = (Result-prev)--  /\ /\ /\ Resulting position
				local RAY = workspace:Raycast(prev, D, Params)-- ray
				if pcall(function() local a = RAY.Instance end) then
					local Hit_Loc = GCNgp(From_Position + (RAY.Position-From_Position)+Vector3.new(0, 0.2, 0), PF)
					if not Is_Valid(Hit_Loc, limit) then-- /\ /\ /\ returns the grid position from world position
						break-- checks if grid_location is valid
					end
					local Hit_Node = Grid[Hit_Loc.Y][Hit_Loc.Z][Hit_Loc.X]-- Node
					if Hit_Node.Type == "Jump" or Hit_Node.Label == "Blocked" then
						break-- if node is not walk, edge, climb then break loop
					end
					task.wait()
					return Hit_Node-- return node if found
				end			
				prev = Result-- set previous and increase time
				Time += Tinterval/2
			end
			Chspeed /= 2-- decrease speed
		end
		if CD then-- 0, 2, -2, 4
			CD = false
			if Delta < 0 then
				Delta *= -1
			end
			Delta += 2
		else	
			CD = true
			Delta *= -1
		end
	end
	return nil-- if nothing found then return nil
end

The local functions that this function uses

local Q, Delta = Vector3.new(1, 0, 1)/math.sqrt(1^2+1^2), math.pi/8
local SD_array = {
	Q,
	Vector3.new(math.cos(Delta)*Q.X+math.sin(Delta)*Q.X, 0, -math.sin(Delta)*Q.Z+math.cos(Delta)*Q.Z),
	Vector3.new(math.cos(2*Delta)*Q.X+math.sin(2*Delta)*Q.X, 0, -math.sin(2*Delta)*Q.Z+math.cos(2*Delta)*Q.Z),
	Vector3.new(math.cos(3*Delta)*Q.X+math.sin(3*Delta)*Q.X, 0, -math.sin(3*Delta)*Q.Z+math.cos(3*Delta)*Q.Z),
	Vector3.new(math.cos(4*Delta)*Q.X+math.sin(4*Delta)*Q.X, 0, -math.sin(4*Delta)*Q.Z+math.cos(4*Delta)*Q.Z),
	Vector3.new(math.cos(5*Delta)*Q.X+math.sin(5*Delta)*Q.X, 0, -math.sin(5*Delta)*Q.Z+math.cos(5*Delta)*Q.Z),
	Vector3.new(math.cos(6*Delta)*Q.X+math.sin(6*Delta)*Q.X, 0, -math.sin(6*Delta)*Q.Z+math.cos(6*Delta)*Q.Z),
	Vector3.new(math.cos(7*Delta)*Q.X+math.sin(7*Delta)*Q.X, 0, -math.sin(7*Delta)*Q.Z+math.cos(7*Delta)*Q.Z),
	Vector3.new(math.cos(8*Delta)*Q.X+math.sin(8*Delta)*Q.X, 0, -math.sin(8*Delta)*Q.Z+math.cos(8*Delta)*Q.Z),
	Vector3.new(math.cos(9*Delta)*Q.X+math.sin(9*Delta)*Q.X, 0, -math.sin(9*Delta)*Q.Z+math.cos(9*Delta)*Q.Z),
	Vector3.new(math.cos(10*Delta)*Q.X+math.sin(10*Delta)*Q.X, 0, -math.sin(10*Delta)*Q.Z+math.cos(10*Delta)*Q.Z),
	Vector3.new(math.cos(11*Delta)*Q.X+math.sin(11*Delta)*Q.X, 0, -math.sin(11*Delta)*Q.Z+math.cos(11*Delta)*Q.Z),
	Vector3.new(math.cos(12*Delta)*Q.X+math.sin(12*Delta)*Q.X, 0, -math.sin(12*Delta)*Q.Z+math.cos(12*Delta)*Q.Z),
	Vector3.new(math.cos(13*Delta)*Q.X+math.sin(13*Delta)*Q.X, 0, -math.sin(13*Delta)*Q.Z+math.cos(13*Delta)*Q.Z),
	Vector3.new(math.cos(14*Delta)*Q.X+math.sin(14*Delta)*Q.X, 0, -math.sin(14*Delta)*Q.Z+math.cos(14*Delta)*Q.Z),
	Vector3.new(math.cos(15*Delta)*Q.X+math.sin(15*Delta)*Q.X, 0, -math.sin(15*Delta)*Q.Z+math.cos(15*Delta)*Q.Z)}

local function Is_In_SD(Direction:Vector3)-- Uses given array so to use premade directions instead
	local Simp = Direction * Vector3.new(1, 0, 1)
	local Max = math.huge
	local Array, N = {}, 0
	for _, D in ipairs(SD_array) do
		local Dist = (D-Direction).Magnitude
		if Dist < Max then
			Max = Dist
			table.insert(Array, table.find(SD_array, D))
			N += 1
		end
	end
	return Array[N]
end

local function FSNX(N:number)-- converts number for use in SD_Array
	if N < 1 then
		return N+16	
	end
	if N > 16 then
		return N-16
	end
	return N
end

local function GCNgp(From:Vector3, PF_children:table)-- returns Grid_Location of a node from given world position
	local temp, index, max = {}, 0, 9999
	for _, child in ipairs(PF_children) do
		local dist = (child.Position - From).Magnitude
		if dist < max then
			max = dist
			index += 1
			temp[index] = child
		end
	end
	return temp[index]:GetAttribute("Grid_Location")
end

local function Is_Valid(pos:Vector3, limit:Vector3)-- checks if given grid_location is inside of grid size
	if pos.Y < 1 or pos.Y > limit.Y then
		return false
	end
	if pos.Z < 1 or pos.Z > limit.Z then
		return false
	end
	if pos.X < 1 or pos.X > limit.X then
		return false
	end
	return true
end	

Ive tried using #native with !optimize2 but it hasn’t helped much and ive tried waits but they make the function way to slow sooooooooooooooooooooooooooooooooooooooo yea.

1 Like

I didnt knew that you speak enchantment table language :skull:

Why are you defining a method if you never use self?
Also type “table” does not exist
table has no generic table type
You have to define its type properly like:
{[string]:boolean} for example

If you are using typechecker then at least use it properly :skull:
Also you could use native mode but that problem of code itself as to how its structured :skull:
If you want to use native code generation then everything must be properly typechecked in a --!strict mode

  1. That’s how I write
  2. Never have fully understood how self works :confused:
  3. Ohhhhhhh so that’s how you define its type
  4. What do you mean by using type checker properly?
  5. Ive never really understand what native did tbh
  1. remove that pcall, its pointless. You can just check if ray ~= nil.
  2. you can precompute some variables such as gravity so youre not recalling it everytime.
  3. predefine your math functions, like: local sqrt = math.sqrt.

native compiles to machine code instead of bytecode so its faster

Native applies AOT optimizations more in depth and compiles it into machine code
However if you haven’t typechecked native code then you are doing something horribly wrong.
Interpeter mode does have own AOT optimizations too and if you are not making something very low level that requires math calculations/buffers then dont use native mode

That kinda helped but its not enough sadly :confused:

1 Like

Wrong.
Optimize 2 does that already in a million times more optimized manner

1 Like

Wait witch of these should i use? --!native or --!optimize 2

  1. Robloxs optimizer may be smart but its not omniscient. Its not going to optimize everything the way youd expect it to. especially in more complex scenarios. You shouldnt just flat out rely on it.

  2. Global lookups are slower, even when being optimized. local variables are stored in registers and would always be faster.

  3. Its more readble and in general is good practice.


Manually caching math functions is still a good optimization, especially in tight loops. Its not wrong, its just being proactive rather than assuming the engine will always do the best thing for you.

test them both, compare them and see which does better

I’ve tested both, and they’re basically the same in terms of performance. :confused:
And timed both end up similar.

You can use both BUT IF YOU USE NATIVE YOU MUST BE IN A STRICT MODE
or else results will be horrible…

Yk its kinda annoying having to spoonfeed people Performance - Luau

Only case where you have to do that is with non global variables like custom defined tables or instances etc

What is Strict mode ?
Both gave similar results.

just invite all 3 friends toghether :joy: :

--!strict
--!optimize 2
--!native

Uh dont invite evil twin of --!stirct named “–!nolint” (he is evil)

hmmmmmmmmmmmmmmm


so what to do about table.find and the 50 other non usable things?

I told you already that your code is not type safe already.
You have to fix code by giving strict types on everything and doing type-casting is nessesary.

Ooooooooooooooooooooooooooooooooh ok!

I believe the native Luau vector library is slightly faster than Vector3s, as it avoids metamethods and other things.