How To Make Interactive Grass [OPTIMIZED]

I had made a post earlier this year about interactive grass, and many had asked how I did it, so I’m making a post here so people know there’s a tutorial out

EDIT (2/4/2023)
I have made a new video on how you can optimize the grass. You can check that out here

Keep in mind that all optimization methods are variable; some settings will be good for one game, while another game will need different settings.

If you have questions, please comment them here and I will answer them to the best of my ability.

26 Likes

Amazing! :smiley:
However, I found a couple of drawbacks.
First (not really important), the grass does not update its position when you move inside the hitbox

And second
When you walk through the grass, everything looks great.
But when another player does it, the tween doesn’t play.
This is a pretty serious problem if for example… in your game you need to locate the other player by the movement of grass.

1 Like

Ah.

The reason why is because WeldConstraints even though created on the local client, give network ownership of the welded parts to the other player, if welded to that character, and if physics is simulated by that client, they won’t detect the hitboxes on your local client because their client created different hitbox parts. I didn’t think of this, thanks!

A solution would be a fake weld, where, in the character added connection, instead of creating a WeldConstraint, you use AlignPosition with an Attachment, like so:

local hitboxConnections : {[BasePart] : RBXScriptConnection} = {} -- New variable
local function characterAdded(character : Model)
	local primaryPart : BasePart = character:WaitForChild('HumanoidRootPart')
	local newHitbox : BasePart = hitbox:Clone()
	
	local attachment = Instance.new('Attachment')
	attachment.Parent = newHitbox
	
	local alignPosition : AlignPosition = Instance.new('AlignPosition') do
		alignPosition.RigidityEnabled = true
		alignPosition.Mode = Enum.PositionAlignmentMode.OneAttachment
		alignPosition.Attachment0 = attachment
		alignPosition.Enabled = true
		alignPosition.Parent = attachment
		
		hitboxConnections[newHitbox] = RunService.Stepped:Connect(function()
			alignPosition.Position = primaryPart.Position
		end)
	end
	
	newHitbox.Position = primaryPart.Position
	newHitbox.Parent = hitboxFolder
	CollectionService:AddTag(newHitbox, 'Hitbox')
end

And then in our removeHitbox function, we add:

	if (hitboxConnections[hitboxInstance]) then
		hitboxConnections[hitboxInstance]:Disconnect()
		hitboxConnections[hitboxInstance] = nil
	end
2 Likes

Why don’t you uncopylock the game? Or just give us the RBXL file?

5 Likes

Two more things, When AlignPosition is used, the Hitbox rotates as it pleases. Fixed by adding AlignOrientation in characterAdded function.
Also, the setupGrass function resets the original orientation of the grass. I solved this by adding an orientation attribute and then multiplying grass cframe with it.

--Add this in activateHtibox function anywhere above finalCFrame variable:
local rot = partTouched.Parent:GetAttribute("rot") 

--New finalCFrame variable
local finalCFrame = CFrame.lookAt(newPosition, newLookAt, upAxis) * rotationCFrame * CFrame.Angles(rot.X, rot.Y, rot.Z)

and this in setupGrass function:

--Add grass orientation in radians as Vector3 attribute
grassInstance:SetAttribute("rot", Vector3.new(math.rad(grassInstance.Orientation.X), math.rad(grassInstance.Orientation.Y), math.rad(grassInstance.Orientation.Z)))

--Edit "grassInstnace.CFrame = " line like so:
grassInstance.CFrame = CFrame.lookAt(grassInstance.Position, grassData[grassHitbox].Top, upAxis) * rotationCFrame * CFrame.Angles(grassInstance:GetAttribute("rot").X, grassInstance:GetAttribute("rot").Y, grassInstance:GetAttribute("rot").Z)

Im sorry for that goofy aah long lines :joy:
Too lazy to come up with a more elegant solution

2 Likes

If you are using a sphere, the rotation of the hitbox won’t matter. I would understand if you are using cubes, but for spheres, any orientation results in the same look.

As for the reset of the CFrame, thats all for personal preference.

Oh yes, I used a cube. Because I thought it would be better for optimization than spheres. But not sure.

Any of roblox’s primitive parts shouldn’t cost a load of performance compared to any complex meshes in terms of collision geometry, but use what you think is best; meshes might work out better for you.

1 Like

@ADRENALXNE, if you allow me, I would like to ask a question.

Why you use brackets with if statements? for example:

if (physicsConnections[hitboxInstance]) then

it makes sense to put brackets if you have a complex condition like this:

if (a or b) and c then

but in your case you do it always. Is it a habit, or is there some specific reason?

Other programming languages require that you have parentheses (C++, Java). So when I move back into luau, I just keep the same syntax so I don’t have to worry about remembering which languages require parentheses. Its just a habit of mine since I’m using all these languages frequently.

2 Likes

your tutorial isn’t showing some parts of the script…