Interactive Grass Glitch

Hello!

I am trying to make interactive grass for my Roblox game and I am coming across this glitch:

I have a client and a server script doing different things:

Server: Player Control

local GrassFolder = workspace.Grass
local TS = game:GetService("TweenService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-----|| Settings ||-----
local multiplier = 30 -- 28
local tweentime = .7  -- .8
local cooldown_devision = 8  -- 8
local tweenInfoNormal = TweenInfo.new(tweentime * 2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)
local tweenInfoTouched = TweenInfo.new(tweentime)
local RandomSize = false

-----| Debounce Dictionary |-----
local CoolDown = {}


for i, grass in ipairs(GrassFolder:GetChildren()) do
	if RandomSize then
		grass.Size = Vector3.new(math.random(2, 8), grass.Size.Y, math.random(2, 8))
	end

	grass.HitBox.Touched:Connect(function(hit)
		if hit.Parent:FindFirstChild("Humanoid") then
			CoolDown[grass] = true
			local hum = hit.Parent:FindFirstChild("Humanoid")
			local movedir = hum.MoveDirection.Unit

			if movedir.Magnitude > 0 then
				local facevector = grass.RotateMain.CFrame * CFrame.Angles(math.rad(movedir.X * multiplier), math.rad(0), math.rad(movedir.Z * multiplier))
				local TweenMove = TS:Create(grass, tweenInfoTouched, {CFrame = facevector})

				TweenMove:Play()

				wait(tweentime / cooldown_devision)
				CoolDown[grass] = false
				return
			end

			CoolDown[grass] = false
		end
	end)

	grass.HitBox.TouchEnded:Connect(function(hit)
		if hit.Parent:FindFirstChild("Humanoid") then
			local hum = hit.Parent:FindFirstChild("Humanoid")
			local movedir = hum.MoveDirection.Unit
			local facevector = grass.RotateMain.CFrame * CFrame.Angles(math.rad(0), math.rad(0), math.rad(0))

			local TweenNormal = TS:Create(grass, tweenInfoNormal, {CFrame = facevector})

			TweenNormal:Play()
		end
	end)
end

Client: Managing the wind

local TweenService = game:GetService("TweenService")
local tweenInfoTouched = TweenInfo.new(0.2, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut)
local GrassFolder = workspace.Grass
local plr = game.Players.LocalPlayer.Name
local character = game.Workspace:WaitForChild(plr)

while wait(0.2) do
	local wind = game.Workspace.GlobalWind
	local windStrength = wind.Magnitude
	if windStrength > 0 then
		for i, grass in ipairs(GrassFolder:GetChildren()) do
			local d = (grass.Position - character.HumanoidRootPart.Position).magnitude
			--wait(0.1)
			if d < 50 then -------------------- CHANGE TO PLAYER SETTINGS
				------------- IF GRASS IS NOT TOUCHED
				local direction = wind.Unit
				local radians = math.atan2(direction.Z, direction.X)
				local yaw = math.deg(radians) + 180

				local random = Random.new()
				local final = windStrength / 20
				local x = random:NextNumber(2, 3) + final
				local z = random:NextNumber(2, 3) + final

				local movedir = Vector3.new(x, yaw, z)

				local CFrameDifference = grass.RotateMain.CFrame * CFrame.Angles(math.rad(movedir.X), math.rad(yaw), math.rad(movedir.Z))

				local windtween = TweenService:Create(grass, tweenInfoTouched, {CFrame = CFrameDifference})
				windtween:Play()
			end
		end
	end
end

Let me know if you have ideas!

2 Likes

I think the issue lies in your current method of rotation. You need to implement orbital rotation instead.

1 Like

Can you show me an example of how I would accomplish this?

1 Like

Sure, this script rotates the part around its base

local part = script.Parent
local originalCf = part.CFrame

local rotation = math.rad(45)

-- Create a vector for the part's bottom
local bottomVector = Vector3.new(0, -part.Size.Y / 2, 0)

-- Rotates the part by the X axis
part.CFrame = originalCf * CFrame.new(bottomVector) * CFrame.Angles(rotation, 0, 0) * CFrame.new(-bottomVector)

How does it work?…

2 Likes

Thanks for your reply!

I am having trouble implementing this into my code… do you think you could help?

2 Likes

What are you having trouble with?

1 Like

Like I just dont understand the code and idk how to replace the tween

local facevector = grass.RotateMain.CFrame * CFrame.Angles(math.rad(movedir.X * multiplier), math.rad(0), math.rad(movedir.Z * multiplier))				
local TweenMove = TS:Create(grass, tweenInfoTouched, {CFrame = facevector})

TweenMove:Play()

Also why would this fix the issue I am encountering?

2 Likes

I have made adjustments to make it work so it rotates based on the players direction

    -- Getting the direction between the player and the part (Grass)
	local direction = ((plr.Character.Torso.Position - Vector3.new(0, 3, 0)) - part.Position).Unit * 0.1
    -- Calculate the pitch and yaw based on the direction
	local pitch = math.asin(direction.Y)
	local yaw = math.atan2(direction.X, direction.Z)
    -- Clamping it so it doesn't go monkey nuts
	pitch = math.clamp(pitch*tiltPower, -0.7, 0.7)
    -- Setting the cframe for the part
	part.CFrame = originalCf * CFrame.new(bottomVector) * (CFrame.fromEulerAnglesYXZ(pitch, yaw, 0) * CFrame.new(-bottomVector)) * CFrame.fromEulerAnglesYXZ(0, -yaw, 0)

here is how you would do it using a tween

local TweenMove = TS:Create(grass, tweenInfoTouched, {CFrame = originalCf * CFrame.new(bottomVector) * (CFrame.fromEulerAnglesYXZ(pitch, yaw, 0) * CFrame.new(-bottomVector)) * CFrame.fromEulerAnglesYXZ(0, -yaw, 0)})
2 Likes

Thats for the wind. And the second part seems to have many nil values.

I just need a way to prevent this from happening:

Trail Script:

local GrassFolder = workspace.Grass
local TS = game:GetService("TweenService")

-----||      Settings      ||-----
local multiplier = 28
local tweentime = .8
local cooldown_devision = 8
local tweenInfoNormal = TweenInfo.new(tweentime * 2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)
local tweenInfoTouched = TweenInfo.new(tweentime)
local RandomSize = false

-----| Debounce Dictionary |-----

local CoolDown = {}
for i, grass in ipairs(GrassFolder:GetChildren()) do

	--grass.PrimaryPart.Transparency = 0
	--grass.RotateMain.Transparency = 1
	if RandomSize then 	grass.Size = Vector3.new(math.random(2,8),grass.PrimaryPart.Size.Y,math.random(2,8)) end

	grass.HitBox.Touched:Connect(function(hit:BasePart)
		if hit.Parent:FindFirstChild("Humanoid") then
			CoolDown[grass] = true
			local hum = hit.Parent:FindFirstChild("Humanoid")
			local movedir = hum.MoveDirection.Unit

			if movedir.Magnitude > 0 then

				local facevector = grass.RotateMain.CFrame * CFrame.Angles(math.rad(movedir.X * multiplier), math.rad(0), math.rad(movedir.Z * multiplier))				
				local TweenMove = TS:Create(grass, tweenInfoTouched, {CFrame = facevector})

				TweenMove:Play()

				task.wait(tweentime / cooldown_devision)
				CoolDown[grass] = false
				return end

			CoolDown[grass] = false
		elseif hit.Name == "Wheel" then
			CoolDown[grass] = true
			local movedir = hit.AssemblyLinearVelocity
			if movedir.Magnitude > 0 then

				local facevector = grass.RotateMain.CFrame * CFrame.Angles(math.rad(movedir.X * multiplier), math.rad(0), math.rad(movedir.Z * multiplier))				
				local TweenMove = TS:Create(grass, tweenInfoTouched, {CFrame = facevector})

				TweenMove:Play()

				task.wait(tweentime / cooldown_devision)
				CoolDown[grass] = false
				return end

			CoolDown[grass] = false

		end
	end)

	grass.HitBox.TouchEnded:Connect(function(hit:BasePart)
		local hum = hit.Parent:FindFirstChild("Humanoid")

		if hum then
			local movedir = hum.MoveDirection.Unit
			local facevector = grass.RotateMain.CFrame * CFrame.Angles(math.rad(0), math.rad(0), math.rad(0))

			local TweenNormal = TS:Create(grass, tweenInfoNormal, {CFrame = facevector})

			TweenNormal:Play()
		elseif hit.Name == "Wheel" then

			local movedir = hit.AssemblyLinearVelocity


			local facevector = grass.RotateMain.CFrame * CFrame.Angles(math.rad(0), math.rad(0), math.rad(0))


			local TweenNormal = TS:Create(grass, tweenInfoNormal, {CFrame = facevector})

			TweenNormal:Play()
		end
	end)
end

Wind Client Script:

local TweenService = game:GetService("TweenService")
local tweenInfoTouched = TweenInfo.new(0.2, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut)
local GrassFolder = workspace.Grass
local plr = game.Players.LocalPlayer.Name
local character = game.Workspace:WaitForChild(plr)

while wait(0.2) do
	local wind = game.Workspace.GlobalWind
	local windStrength = wind.Magnitude
	if windStrength > 0 then
		for i, grass in ipairs(GrassFolder:GetChildren()) do
			local d = (grass.Position - character.HumanoidRootPart.Position).magnitude
			--wait(0.1)
			if d < 50 then -------------------- CHANGE TO PLAYER SETTINGS
				------------- IF GRASS IS NOT TOUCHED
				local direction = wind.Unit
				local radians = math.atan2(direction.Z, direction.X)
				local yaw = math.deg(radians) + 180

				local random = Random.new()
				local final = windStrength / 20
				local x = random:NextNumber(2, 3) + final
				local z = random:NextNumber(2, 3) + final

				local movedir = Vector3.new(x, yaw, z)

				local CFrameDifference = grass.RotateMain.CFrame * CFrame.Angles(math.rad(movedir.X), math.rad(yaw), math.rad(movedir.Z))

				local windtween = TweenService:Create(grass, tweenInfoTouched, {CFrame = CFrameDifference})
				windtween:Play()
			end
		end
	end
end
1 Like

What is your script meant to do? If the glitch your mentioning is the constant “flashing” from the grass, then it’s because light is bouncing off the side of the blade of grass. And i dont think it’s possible to change that.

2 Likes

So I have 2 scripts

With one script everything works fine but when I have both I think they are trying to override each other, hence the glitchy rotation.

1 Like

Ohhhhh, i completely misunderstood you.
I dont know if it’s possible if one is a server script and the other is a client script. either they have to be a server script or a client script.

You might consider using your CoolDown dictionary to check if the grass is moving. (Assuming you have both scripts combined into one)

while wait(0.2) do
	local wind = game.Workspace.GlobalWind
	local windStrength = wind.Magnitude
	if windStrength > 0 then
		for i, grass in ipairs(GrassFolder:GetChildren()) do
			if CoolDown[grass] then continue end
            -- Continue code here
1 Like

Yeah… I was just wondering if there was any other way to do it without combining the 2 scripts

Would a Remote Event work?

1 Like

I would recommend using a remote function instead.

2 Likes

Okay… Ill try it out tmr and tell u how it went

1 Like

Okay… I have made edits, but I am still not pleased.

Before: (Both scripts, 1 client, 1 server)

Everything but the remote function

After: (Both scripts, 1 client, 1 server)
Part of server:

CoolDowns.OnServerInvoke = function(Player)
	return CoolDown
end 

Part of client:

local CoolDown = CoolDowns:InvokeServer()
if CoolDown[grass] then continue end

Anyone got ideas?

30chssssssssssssssss

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.