I'm looking to render grass in my game, but i decided it would be best to just do it on the client so I could choose to only show grass in a set radius.
I'm not sure where to start, how would I consistently place the grass(e.g. grass leaves radius, comes back in and remains in the same spot) should I generate and store all the points or would using a seed work? If I stored all the points in a table I'm not sure if I want to iterate over the entire thing every time the player moves to check if grass is in an area. I'm sure there's a way to organize my points, like in chunks but that's something I've never done..
tl;dr How would you locally generate grass in a set radius around a player?
**Edit: ** I've come up with a solution, but I'm still open to more efficient ideas.
Currently I'm generating all the grass positions in clusters, I take a table place a random point in it, and then from that point I generate more random points within an area and place those in another table within that table so it looks something like: {{point = vector3, grass= {vector3, vector3, vector3}}
From that I can then check for clusters near the camera and render the grass associated with them. It seems to handle 10,000 grass pieces alright, but sometimes the fps dip down for a moment when instancing the grass. I've tested up to 20,000 and have faired alright, but the frame rate drops lower more noticeably when instancing parts. Maybe I can find a way to recycle parts, or sort my data better.
If anyone is interested in the code:
local renderDistance = 100 -- studs radius local player = game.Players.LocalPlayer local camera = Workspace.CurrentCamera local base = Workspace.Test math.randomseed(tick()) function randomPoint(center, size) return Vector3.new( math.random(center.X - size.X/2, center.X + size.X/2), center.Y + size.Y/2, math.random(center.Z - size.Z/2, center.Z + size.Z/2) ) end function inTable(tab, val) for i,v in pairs(tab) do if v == val then return i end end return nil end function fade(part, dir) if dir == "In" then Spawn(function() for i = 1, 0.5, -0.05 do --bleh, too tired to make this function useful... part.Transparency = i wait() end end) elseif dir == "Out" then Spawn(function() for i = 0, 1, 0.05 do part.Transparency = i wait() end end) end end function grass(cf, size) local g = Instance.new("Part") g.Anchored = true g.CanCollide = false g.Name = "Grass" g.BrickColor = BrickColor.new("Grime") g.Transparency = 1 g.Size = size g.CFrame = cf g.Parent = camera local mesh = Instance.new("SpecialMesh", g) mesh.MeshType = "FileMesh" mesh.MeshId = "http://www.roblox.com/asset/?id=17659272" mesh.Scale = (size/2) return g end local grasses = {} --{{Vector3.new(clusterPosition),{grass, grass, grass}},{Vector3.new(clusterPosition),{grass, grass, grass}}} local num = 10000 local clusters = 0 while num > 0 do --grass cluster clusters = clusters + 1 local clusterPoint = randomPoint(base.Position, base.Size) local clusterSize = math.random(5,100) grasses[clusters] = {point=clusterPoint, points = {},grass = {}} for i = 5, clusterSize do local cf = CFrame.new(randomPoint(clusterPoint, Vector3.new(clusterSize, base.Size.Y,clusterSize))) * CFrame.Angles(math.rad(-45),math.rad(1,360),0) - Vector3.new(0,0.25,0) grasses[clusters].points[i] = cf grass(cf, Vector3.new(1,1,1)*math.random(3,4)) end num = num - clusterSize end --render grass local camPos = camera.CoordinateFrame local visibleClusters = {} while wait(0.2) do if camPos ~= camera.CoordinateFrame then camPos = camera.CoordinateFrame for i,v in pairs(grasses) do local dist = (camPos.p - v.point).magnitude local visible = inTable(visibleClusters, v.point) if dist > renderDistance and visible then --hide cluster for _,g in pairs(v.grass) do g:Destroy() end table.remove(visibleClusters, visible) elseif dist < renderDistance and not visible then table.insert(visibleClusters, v.point) for k,pos in pairs(v.points) do v.grass[k] = grass(pos, Vector3.new(3,3,3)) fade(v.grass[k], "In") end end end end end