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
