This script clones grass stems, then raycasts to find floor, then positions grass on the stem and finally simulates windy grass that constantly changes based on wind flow values found in the workspace.
As I’ve tested things it had increased and decreased.
Current Script Activity averages at around 9.8%, so I’m looking for it to be brought down a lot.
--// Create Storage for Grass
local GrassStorage = game.ReplicatedStorage.PlayerObjects.Foliage.Grass.GrassStorage:Clone()
GrassStorage.Parent = script.Parent
--// Tables
local GrassFolder = {}
local initalStemCFrameFolder = {}
local XVelFolder = {}
local ZVelFolder = {}
local XUpFolder = {}
local ZUpFolder = {}
--local newRotFolder = {}
--local rotReachedFolder = {}
local XNewRotFolder = {}
local ZNewRotFolder = {}
local LastGrassBladePosition = {}
local LastStemPosition = {}
local GrassDespawnDebounce = {}
local GrassSpawnDebounce = {}
local GrassReady = {}
local TickActive = {}
local TickNum = {}
local TickRate = {}
local MaxVelX = {}
local MinVelX = {}
local MaxVelZ = {}
local MinVelZ = {}
local VelChangeRateX = {}
local VelChangeRateZ = {}
--// Create Grass Spawner
local GrassSpawner = game.ReplicatedStorage.PlayerObjects.Foliage.Grass.GrassSpawners.GrassSpawn1:Clone()
GrassSpawner.Parent = script.Parent
--// Itentify services
local PhysicsService = game:GetService("PhysicsService")
local CollectionService = game:GetService("CollectionService")
--// Set Values
local SetGrassAmount = 300
local GrassAmount = 0
--// Other Values
local grassfolder = "Grass"
local initalStemCFrame = game.ReplicatedStorage.PlayerObjects.Foliage.Grass.GrassBlade.Stem.CFrame
local InitialSpawnDebounce = false
local SpawnDebounce = false
local DeSpawnDebounce = false
local DistanceTickNum, DistanceTickRate, DistanceTickActive = 0,2,true
local CheckDebounce = false
--// Pre-Register all operators
local CFrame_new = CFrame.new
local CFrame_Angles = CFrame.Angles
local math_random = math.random
local math_rad = math.rad
local Vector3_new = Vector3.new
local RaycastParams_new = RaycastParams.new
local Ray_New = Ray.new
local math_floor = math.floor
local math_sqrt = math.sqrt
local math_abs = math.abs
local function round(n)
return math_floor(n + 0.5)
end
function SpawnGrass(part, amount, setting) -- Settings: "Random", "Distance", "Rows"
local distance = nil
--local maximum = part.
for i = 1, amount, 1 do
local Grass = game.ReplicatedStorage.PlayerObjects.Foliage.Grass.GrassBlade:Clone()
Grass.Parent = GrassStorage
local grasstype = math_random(1,3)
if grasstype == 1 then
Grass.GrassBlade2:Destroy()
Grass.GrassBlade3:Destroy()
Grass.GrassBlade1.Name = "GrassBlade"
Grass.GrassBlade.GrassBlade1Front.Name = "GrassBladeFront"
Grass.GrassBlade.GrassBlade1Back.Name = "GrassBladeBack"
PhysicsService:SetPartCollisionGroup(Grass.GrassBlade.GrassBladeFront, grassfolder)
PhysicsService:SetPartCollisionGroup(Grass.GrassBlade.GrassBladeBack, grassfolder)
PhysicsService:SetPartCollisionGroup(Grass.Stem, grassfolder)
elseif grasstype == 2 then
Grass.GrassBlade1:Destroy()
Grass.GrassBlade3:Destroy()
Grass.GrassBlade2.Name = "GrassBlade"
Grass.GrassBlade.GrassBlade2Front.Name = "GrassBladeFront"
Grass.GrassBlade.GrassBlade2Back.Name = "GrassBladeBack"
PhysicsService:SetPartCollisionGroup(Grass.GrassBlade.GrassBladeFront, grassfolder)
PhysicsService:SetPartCollisionGroup(Grass.GrassBlade.GrassBladeBack, grassfolder)
PhysicsService:SetPartCollisionGroup(Grass.Stem, grassfolder)
elseif grasstype == 3 then
Grass.GrassBlade1:Destroy()
Grass.GrassBlade2:Destroy()
Grass.GrassBlade3.Name = "GrassBlade"
Grass.GrassBlade.GrassBlade3Front.Name = "GrassBladeFront"
Grass.GrassBlade.GrassBlade3Back.Name = "GrassBladeBack"
PhysicsService:SetPartCollisionGroup(Grass.GrassBlade.GrassBladeFront, grassfolder)
PhysicsService:SetPartCollisionGroup(Grass.GrassBlade.GrassBladeBack, grassfolder)
PhysicsService:SetPartCollisionGroup(Grass.Stem, grassfolder)
end
if setting == "Random" then
local X1 = round(part.Position.X + part.Size.X/2)
local X2 = round(part.Position.X - part.Size.X/2)
local Z1 = round(part.Position.Z + part.Size.Z/2)
local Z2 = round(part.Position.Z - part.Size.Z/2)
local XRandom = math_random(X2, X1)
local ZRandom = math_random(Z2, Z1)
XRandom = XRandom + math_random(-90,90)/100
ZRandom = ZRandom + math_random(-90,90)/100
Grass.Stem.Position = Vector3_new(XRandom, part.Position.Y - 1, ZRandom)
end
lookforplatform(Grass.Stem)
end
end
function lookforplatform(Stem)
-- Set an origin and directional vector
local ray = Ray.new(Stem.Position, Vector3.new(0, -50, 0))
local rayOrigin = Stem.Position
local rayDirection = Vector3_new(0, -100, 0)
-- Build a "RaycastParams" object and cast the ray
local raycastParams = RaycastParams_new()
raycastParams.FilterDescendantsInstances = {Stem.Parent}
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
local part, pos, _, material = workspace:FindPartOnRayWithIgnoreList(ray, {Stem.Parent})
if part then
--print(part.Name)
if material == Enum.Material.Grass then
--print"landed"
GrassAmount += 1
else
Stem.Parent:Destroy()
return
end
end
if raycastResult then
--printRegion(raycastResult.Position)
--print(raycastResult.Parent.Name)
local distance = Stem.Position.Y - raycastResult.Position.Y
Stem.Position = raycastResult.Position
--local GrassType = Stem.GrassMainScript:GetAttribute("GrassType")
local grass = Stem.Parent.GrassBlade.GrassBladeFront
Stem.Parent.GrassBlade.GrassBladeFront.Position = Vector3_new(grass.Position.X, grass.Position.Y - distance, grass.Position.Z)
Stem.Parent.GrassBlade.GrassBladeBack.Position = Stem.Parent.GrassBlade.GrassBladeFront.Position
end
setBladePosition(Stem)
end
function setBladePosition(stem)
local CPoint = stem.Position
local Gf = nil
Gf = stem.Parent.GrassBlade.GrassBladeFront
local Gb = stem.Parent.GrassBlade.GrassBladeBack
Gf.Position = Vector3_new((CPoint.X + (Gf.Size.X/2)), (CPoint.Y + (Gf.Size.Y/2)), (CPoint.Z + (Gf.Size.Z/2)))
Gb.Position = Gf.Position
local GBWeld = Instance.new("Weld", stem.Parent.GrassBlade.GrassBladeFront)
GBWeld.Part0 = stem.Parent.GrassBlade.GrassBladeFront
GBWeld.Part1 = stem.Parent.GrassBlade.GrassBladeBack
local GBM6D = stem.GBM6D
GBM6D.Name = "GBM6D"
GBM6D.Part0 = stem.Parent.GrassBlade.GrassBladeFront
GBM6D.Part1 = stem
local offset = CFrame_new(Gf.Size.X/2,-(Gf.Size.Y/2),0)
GBM6D.C0 = GBM6D.Part0.CFrame:inverse() * GBM6D.Part1.CFrame * offset
GBM6D.C1 = CFrame_Angles(0,math_random(-180,180),0)
table.insert(XVelFolder, 0)
table.insert(ZVelFolder, 0)
table.insert(XUpFolder, false)
table.insert(ZUpFolder, false)
--table.insert(newRotFolder, 0)
--table.insert(rotReachedFolder, true)
table.insert(XNewRotFolder, 0)
table.insert(ZNewRotFolder, 0)
table.insert(GrassFolder, stem)
table.insert(GrassDespawnDebounce, false)
table.insert(GrassSpawnDebounce, false)
table.insert(GrassReady, true)
table.insert(TickNum, 1)
table.insert(TickRate, 5)
table.insert(TickActive, false)
table.insert(MaxVelX, 0.5)
table.insert(MinVelX, -0.5)
table.insert(MaxVelZ, 0.5)
table.insert(MinVelZ, -0.5)
table.insert(VelChangeRateX, 0.01)
table.insert(VelChangeRateZ, 0.01)
table.insert(initalStemCFrameFolder, stem.CFrame)
end
local distance = 0
local debounce1 = false
local Camera = game:GetService("Workspace").CurrentCamera
local CameraLocation = nil
local distanceX = 0
local distanceY = 0
local distanceZ = 0
Below is the cause of the script activity. Its the code that constantly moves all the grass stems.
local function UpdateDistance(object, i, CameraLocation)
distance = (object.Position - CameraLocation).Magnitude
if i >= 1 then
if distance >= 200 then
if GrassDespawnDebounce[i] == false then
GrassDespawnDebounce[i] = true
GrassReady[i] = false
local TheParent = GrassFolder[i].Parent
TheParent.Parent = game.Lighting.GrassCache
GrassSpawnDebounce[i] = false
end
TickActive[i] = false
else
GrassDespawnDebounce[i] = false
if GrassSpawnDebounce[i] == false then
GrassSpawnDebounce[i] = true
GrassReady[i] = true
local TheParent = GrassFolder[i].Parent
TheParent.Parent = script.Parent.GrassStorage
end
end
if distance <= 40 then
TickRate[i] = 0
VelChangeRateX[i] = 0.01
VelChangeRateZ[i] = 0.01
MaxVelX[i] = 0.4
MinVelX[i] = -0.4
MaxVelZ[i] = 0.4
MinVelZ[i] = -0.4
elseif distance > 40 and distance <= 60 then
TickRate[i] = 1
VelChangeRateX[i] = 0.03
VelChangeRateZ[i] = 0.03
MaxVelX[i] = 0.7
MinVelX[i] = -0.7
MaxVelZ[i] = 0.7
MinVelZ[i] = -0.7
elseif distance > 60 and distance <= 75 then
TickRate[i] = 4
VelChangeRateX[i] = 0.1
VelChangeRateZ[i] = 0.1
MaxVelX[i] = 1
MinVelX[i] = -1
MaxVelZ[i] = 1
MinVelZ[i] = -1
elseif distance > 75 and distance <= 90 then
TickRate[i] = 7
VelChangeRateX[i] = 0.15
VelChangeRateZ[i] = 0.15
MaxVelX[i] = 1.2
MinVelX[i] = -1.2
MaxVelZ[i] = 1.2
MinVelZ[i] = -1.2
end
return distance, TickRate[i], MaxVelX[i], MinVelX[i], MaxVelZ[i], MinVelZ[i], VelChangeRateX[i], VelChangeRateZ[i], GrassDespawnDebounce[i], GrassSpawnDebounce[i], GrassReady[i]
else
return distance
end
end
local function UpdateVel(UpValue, VelValue, VelChangeRate, MaxVelValue, MinVelValue, i, Type)
if UpValue[i] == true then
if VelValue[i] > MaxVelValue[i] then
VelValue[i] = MaxVelValue[i]
else
VelValue[i] += VelChangeRate[i]
end
elseif XUpFolder[i] == false then
if VelValue[i] < MinVelValue[i] then
VelValue[i] = MinVelValue[i]
else
VelValue[i] -= VelChangeRate[i]
end
end
if Type == "X" then
return XUpFolder[i] == UpValue, XVelFolder[i] == VelValue, VelChangeRateX[i] == VelChangeRate
elseif Type == "Z" then
return ZUpFolder[i] == UpValue, ZVelFolder[i] == VelValue, VelChangeRateZ[i] == VelChangeRate
end
end
local function UpdateGrass(i, GWX, GWZ)
local GrassOrientationX = GrassFolder[i].Orientation.X
local GrassOrientationZ = GrassFolder[i].Orientation.Z
UpdateVel(XUpFolder, XVelFolder, VelChangeRateX, MaxVelX, MinVelX, i, "X")
UpdateVel(ZUpFolder, ZVelFolder, VelChangeRateZ, MaxVelZ, MinVelZ, i, "Z")
if (XUpFolder[i] == true and GrassOrientationX >= XNewRotFolder[i]) or (ZUpFolder[i] == true and GrassOrientationZ >= ZNewRotFolder[i])
or (XUpFolder[i] == false and GrassOrientationX <= XNewRotFolder[i]) or (ZUpFolder[i] == false and GrassOrientationZ <= ZNewRotFolder[i]) then
--local pressure = math_random(1,5)
XNewRotFolder[i] = GWX + math_random(-3,3)
ZNewRotFolder[i] = GWZ + math_random(-3,3)
if XNewRotFolder[i] >= GrassOrientationX then
XUpFolder[i] = true
else
XUpFolder[i] = false
end
if ZNewRotFolder[i] >= GrassOrientationZ then
ZUpFolder[i] = true
else
ZUpFolder[i] = false
end
end
GrassFolder[i].CFrame = initalStemCFrameFolder[i] * CFrame_Angles(math_rad(GrassOrientationX + XVelFolder[i]),0,math_rad(GrassOrientationZ + ZVelFolder[i]))
return XUpFolder[i], ZUpFolder[i], XVelFolder[i], ZVelFolder[i]
end
game:GetService("RunService").RenderStepped:Connect(function(Step)
if InitialSpawnDebounce == false then
InitialSpawnDebounce = true
SpawnGrass(GrassSpawner, SetGrassAmount, "Random")
end
Camera = workspace.CurrentCamera
CameraLocation = Vector3_new(Camera.CFrame.Position.X, Camera.CFrame.Position.Y, Camera.CFrame.Position.Z)
UpdateDistance(GrassSpawner, -1, CameraLocation)
if distance <= 220 then
if DistanceTickActive then
for i = 1, GrassAmount, 1 do
UpdateDistance(GrassFolder[i], i, CameraLocation)
end
end
if DistanceTickNum < DistanceTickRate then
DistanceTickNum += 1
DistanceTickActive = false
elseif DistanceTickNum >= DistanceTickRate then
DistanceTickNum = 0
DistanceTickActive = true
end
CheckDebounce = false
end
if distance > 270 then
if CheckDebounce == false then
local GrassCache = game.Lighting.GrassCache
for i = 1, GrassAmount, 1 do
if GrassFolder[i].Parent.Parent.Name == "GrassStorage" then
GrassFolder[i].Parent.Parent = GrassCache
end
end
CheckDebounce = true
end
end
for i = 1, GrassAmount, 1 do
UpdateTickrate(i, distance)
if TickActive[i] == true and GrassReady[i] == true then
local GWX = workspace.ServerStats.Weather.GeneralWindX.Value
local GWZ = workspace.ServerStats.Weather.GeneralWindZ.Value
UpdateGrass(i, GWX, GWZ)
end
end
end)
I appreciate and will look forward to any help you can provide on this issue!