Reputation: 1327
I have finally finished my first script using lua and Roblox studio. The goal was to have a set of tiles change colour when the player steps on it.
Here is my code:
local parts = {}
for k, v in pairs(game.Workspace:GetDescendants()) do
if string.sub (v.name, 1, 4) == "Part" then
table.insert(parts, v)
end
end
local char = workspace:WaitForChild("localPlayer")
local hrp = char:WaitForChild("HumanoidRootPart")
local newThread = coroutine.create(function()
game:GetService("RunService").Heartbeat:Connect(function()
local charpos = hrp.Position
for k, v in pairs (parts)do
if (charpos - v.CFrame.Position).magnitude <= 6 then
local pos = v.CFrame.Position
for k, v in pairs(parts) do
if v.CFrame.Position.X == pos.X or v.CFrame.Position.Z == pos.Z then
v.BrickColor = BrickColor.new("Really red")
print("touching")
end
end
return
else
v.BrickColor = BrickColor.new("Medium stone grey")
print("not touching")
end
end
end)
end)
coroutine.resume(newThread)
This works:
But my project needs more tiles and more colors. When I increase the number of platforms etc.. the program becomes very laggy.
The gif doesn't really show how laggy, but it gets worse and worse and after about a minute of playing you can hardly control the player effectively.
As you can see, I tried putting my function inside a co-routine but that didn't help. I am very new to lua and trying making games so I really don't know how to improve performance. The fact that the lagginess gets worse and worse seems to suggest memory is not being freed effectively? I assumed this happened automatically. Anyway, any help much appreciated.
After suggestions, I have tried this:
Hi, I have tried this:
local Platform = {}
local maPart = Platform.part
local function makePart(pX,pY,pZ)
maPart = Instance.new("Part", workspace)
maPart.Position = Vector3.new(pX,pY,pZ)
maPart.Size = Vector3.new(10,1,10)
return maPart
end
local columns = {}
local rows = {}
for i = 1, 12 do
columns[i] = rows
table.insert(columns, rows)
for j = 1, 12 do
maPart = makePart(i*16,0,j*16)
rows[j] = maPart
table.insert(rows, maPart)
end
end
columns[1].[1].BrickColor = BrickColor.new("Really blue")
As an exercise to see if I can get this new concept for me of 'multi dimensional arrays' to work. The last line doesn't work. How would I access an element inside an array inside an array? tx
Upvotes: 1
Views: 1167
Reputation: 7188
Your function isn't inside the coroutine, only the connection to the signal is. But, since the heartbeat code is running every single frame, using a coroutine to dispatch the work to a different thread that won't start until the next frame won't help much. So instead of threading, try to look for ways to minimize the computational effort of the function.
Print statements can slow things down, so comment those out when you know the logic works. Calculations with square roots are expensive, so I think the best optimization is to minimize the number of distance checks.
I would recommend making the Heartbeat function act as a more reliable TouchEnded handler, but only have it do work on platforms that know they have a player touching them. Then you can use a part's Touched handler for knowing which platforms have players.
-- initialize some script globals
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
-- grab all the platforms and create a table of custom objects with data we care about
local platforms = {}
for _, part in ipairs(game.Workspace:GetDescendants()) do
if string.sub (v.name, 1, 4) ~= "Part" then
continue
end
local platformData = {
Part = part, -- the original object
Touches = {}, -- a map of players actively touching it
}
-- initialize any events on these parts
part.Touched:Connect(function(otherPart)
-- when touched, check which player it is, and hold onto that information
local player = Players:GetPlayerFromCharacter(part.Parent)
if not player then
warn(string.format("unexpected object touched a platform. Ignoring %s", part.Parent.Name))
return
end
platformData.Touches[player.PlayerId] = player
end)
-- hold onto this data
table.insert(platforms, platformData)
end
-- make a function that updates colors
RunService.Heartbeat:Connect(function()
for _, platform in ipairs(platforms) do
local isTouched = next(platform.Touches) ~= nil
-- quick escape for inactive platforms
if not isTouched then
continue
end
-- check if the previous players are still touching it
isTouched = false
for id, player in pairs(platform.Touches) do
local platPos = platform.Part.Position
local hrp = player.Character.PrimaryPart
local charPos = hrp.Position
local dist = (platPos - charPos).magnitude
if dist < 6 then
isTouched = true
else
-- this player is no longer touching, remove them
platform.Touches[id] = nil
end
end
-- update the color of the platforms
if isTouched then
platform.Part.BrickColor = BrickColor.new("Really red")
else
platform.Part.BrickColor = BrickColor.new("Really blue")
end
end
end)
I hope this helps! Good luck!
Upvotes: 0
Reputation: 5857
Minor change - move out the loop the construction of new BrickColor. Its value doesn't change across unaffected parts, no reasons to repeatedly create it.
And see if printing lots of debugging information affects the performance, so comment out prints.
Secondly, probably you should reorganize the grid of the blocks into two-dimentional array. So then you don't have to scan all the parts to find the ones that has the same X/Z coordinate. This also will give you the way to quickly find the closest brick, it's trivial on a regular grid.
Upvotes: 1