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.
Amazing!
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.
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
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
Too lazy to come up with a more elegant solution
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.
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.
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.