local RunService = game:GetService("RunService")
local Cells = game.Workspace.Cells:GetChildren()
local Sqrt = math.sqrt
local Friction = 1.05
local Distancing = 1.5
for i,v in pairs(Cells) do
v.Position += Vector3.new(0,math.random(-10000,10000)/1000000,0)
end
function GetMagnitude(X,Z)
return Sqrt(
X^2 +
Z^2
)
end
for i,v in pairs(Cells) do
local Hates = game.Lighting.Hates:FindFirstChild(v:FindFirstChildOfClass("StringValue").Name):GetChildren()
for i2,v2 in pairs(Hates) do
v2:Clone().Parent = v:FindFirstChildOfClass("StringValue")
end
wait()
end
script.Parent:WaitForChild("Humanoid").Touched:Connect(function(hit)
if hit and hit.Name == "Cell" then
hit.Velocity += script.Parent:WaitForChild("Humanoid").RootPart.Velocity/100
end
end)
RunService.Stepped:Connect(function(t,dt)
for i,v in pairs(Cells) do
local Position = (v.Position+(v.Velocity/Friction))
if math.abs(Position.X) > 50 then
v.Position = Vector3.new(math.clamp(-v.Position.X,-99,99),v.Position.Y,v.Position.Z)
end
if math.abs(Position.Z) > 50 then
v.Position = Vector3.new(v.Position.X,v.Position.Y,math.clamp(-v.Position.Z,-99,99))
end
for i2,v2 in pairs(Cells) do
if v2 ~= v then
local Delta = (v2.Position-Position)
local Distance = GetMagnitude(Delta.X,Delta.Z)^2
if Distance > 100 then
Distance = 0
else
if v:FindFirstChildOfClass("StringValue"):FindFirstChild(v2:FindFirstChildOfClass("StringValue").Name) then
Distance = -Distance/2
Distancing =-1
else
Distancing = 1
end
if Distance < Distancing then
Distance = -math.abs(Distance)
end
v.Velocity += (Delta/Distance)/100
end
end
end
v.Velocity = v.Velocity/Friction
v.Position += Vector3.new(v.Velocity.X,0,v.Velocity.Z)
end
end)
use ipairs instead of pairs to iterate through :GetChildren(),
ipairs meant for arrays
pairs or next meant for dictionaries
do not make a custom magnitude function. Just use the default .Magnitude key that is automatically given for each Vector object constructed.
use runserviceHeartbeat:Wait() to yield for a frame instead of wait()
yield for the humanoid before assigning the listener or else you are completely ruining the purpose of the :WaitForChild() method
local humanoid = script.Parent:WaitForChild("Humanoid")
if humanoid then
humanoid.Touched:Connect(function(hit) -- I wasn't aware it was possible to assign a touched connection to a humanoid 0.o
if hit and hit.Name == "Cell" then
hit.Velocity += script.Parent:WaitForChild("Humanoid").RootPart.Velocity/100
end
end)
end
when you are using the generic for loop it is good practice to always mark variables you don’t need as “_” since that variable will actually be garbage collected saving memory.
-- Not good
for i2,v2 in pairs(Hates) do
v2:Clone().Parent = v:FindFirstChildOfClass("StringValue")
end
-- Good
for _,v2 in pairs(Hates) do
v2:Clone().Parent = v:FindFirstChildOfClass("StringValue")
end
your runservice connection seems pretty expensive, considering its running a for loop doing a bunch of calculations per frame. I’m not sure if there is a more optimal solution but if there isn’t then your connection is fine.
when you are using the generic for loop it is good practice to always mark variables you don’t need as “_” since that variable will actually be garbage collected saving memory.
I don’t think you know how garbage collection in Luau works. Marking a variable as _ will not be garbage collected immediately, people mark those variables as _ because it tells the reader that that variable has no use. Those variables will be garbage collected after the do scope ends assuming they have no strong references.
-- Good
for i2,v2 in pairs(Hates) do
v2:Clone().Parent = v:FindFirstChildOfClass("StringValue")
end
-- Also Good but readability slightly better
for _ ,v2 in pairs(Hates) do
v2:Clone().Parent = v:FindFirstChildOfClass("StringValue")
end
Why not? You can have an optimization where you call the function multiple times:
There was also another post about this that I can’t find.
Edit:
Turns out this was the fastest method back then when I used to be active on the forums, now using Magnitude is the fastest methodology thanks to changes. I apologize I wasn’t aware of the change.
Another note, using ipairs instead of pairs on an array makes a very small difference. Numerical for loops are faster than the two, but it optimizes very little.
local childTable = Instance:GetChildren()
for n = 1, #childTable do
local child = childTable[n]
end
is an example of how you would use a numerical loop with GetChildren()
FindFirstChild() is actually 30% slower than the dot operator
Do this:
for i,v in pairs(Cells) do
local childStringValue = v:FindFirstChildOfClass("StringValue")
local Hates = game.Lighting.Hates[childStringValue.Name]:GetChildren()
for i2,v2 in pairs(Hates) do
v2:Clone().Parent = childStringValue
end
runService.Heartbeat:Wait()
end
Instead of
for i,v in pairs(Cells) do
local Hates = game.Lighting.Hates:FindFirstChild(v:FindFirstChildOfClass("StringValue").Name):GetChildren()
for i2,v2 in pairs(Hates) do
v2:Clone().Parent = v:FindFirstChildOfClass("StringValue")
end
wait()
end
numerical loops are not recommended because of the fact ipairs caches the value. Indexing a key in a table is not instantaneous.
-- Not Cached Automatically
for i = 1, #array do
local value = array[i]
print(value)
end
-- Cached
for index, value in ipairs(array) do
print(value)
end
local array = {}
for i = 1, 100 do
array[#array + 1] = math.random(1, 10)
end
for i = 1, 100 do
local numericTime = nil
local ipairsTime = nil
local startTime = os.clock()
for i = 1, #array do
local value = array[i]
end
numericTime = os.clock() - startTime
startTime = os.clock()
for index, value in ipairs(array) do
end
ipairsTime = os.clock() - startTime
print(numericTime < ipairsTime and ('numeric is faster: %s'):format(numericTime) or ('ipairs is faster: %s'):format(ipairsTime))
end
i’ve tested this benchmark numerous times, and the generic is faster than numeric over 95% of the time. Only time I would use numeric is when iterating through an array backwards or by certain multiples, etc… If you are concerned about speed use ipairs.
Your “simple trick” is slower than the native .Magnitude property of all Vector3s. Look at these benchmarks:
local ITERATIONS = 1e7;
local TEST_VECTOR = Vector3.new(1,2,3);
local function GetMagnitude(vec: Vector3): number
local x,y,z = vec.X,vec.Y,vec.Z;
return math.sqrt(x*x+y*y+z*z);
end
local startTick = tick();
for _=1,ITERATIONS do
local magnitude = GetMagnitude(TEST_VECTOR);
end
local timeGetMagnitude = tick()-startTick;
startTick = tick();
for _=1,ITERATIONS do
local magnitude = TEST_VECTOR.Magnitude;
end
local timeNativeMagnitude = tick()-startTick;
startTick = tick();
for _=1,ITERATIONS do
local magnitude = GetMagnitude(Vector3.new(1,2,3));
end
local timeInstantiateGetMagnitude = tick()-startTick;
startTick = tick();
for _=1,ITERATIONS do
local magnitude = Vector3.new(1,2,3).Magnitude;
end
local timeInstantiateNativeMagnitude = tick()-startTick;
print(string.format("Timing Comparisons:\n\nGetMagnitude(): %fs\nNative .Magnitude: %fs\nGetMagnitude with new Vector3s: %fs\nNative .Magnitude with new Vector3s: %fs\n\nMagnitude = %f",timeGetMagnitude,timeNativeMagnitude,timeInstantiateGetMagnitude,timeInstantiateNativeMagnitude,TEST_VECTOR.Magnitude));
Timing Comparisons:
GetMagnitude(): 1.105992s
Native .Magnitude: 0.385828s
GetMagnitude with new Vector3s: 1.905560s
Native .Magnitude with new Vector3s: 1.138401s
It is because every time you use a vector3.new operator, the magnitude is internally calculated. Using your function would just use more performance, since you’re recalculating the magnitude again.
No we’re not on the same page here, I know your function uses less performance. However, just by doing Vector3.new() the magnitude was already calculated. What’s the use of calculating it again with your function.
the magnitude was already calculated. What’s the use of calculating it again with your function.
It isn’t like I said, it is only when you try to index it. Why would it calculate the magnitude unnecessarily, it will do if it is needed and that’s by indexing it. >.<