Inoob8C

Grass Physics Module - Strict

Jul 6th, 2024 (edited)
301
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 23.37 KB | None | 0 0
  1. --!native
  2. --!strict
  3. local GrassPhysics = {}
  4. GrassPhysics.__index = GrassPhysics
  5.  
  6. -- Services
  7. local Players = game:GetService("Players"); Players.LocalPlayer.CharacterAdded:Wait()
  8. local CollectionService = game:GetService("CollectionService")
  9. local ReplicatedStorage = game:GetService("ReplicatedStorage")
  10. local TweenService = game:GetService("TweenService")
  11. local RunService = game:GetService("RunService")
  12.  
  13. -- Constants
  14. local Root = workspace
  15. local Camera = Root.CurrentCamera
  16.  
  17. local Player = Players.LocalPlayer
  18.  
  19. local Character: Model = nil
  20. local Humanoid: Humanoid = nil
  21. local HumanoidRootPart: BasePart = nil
  22.  
  23. type WindShakeConfig = {
  24.     Enabled: boolean,
  25.     ScriptPath: {
  26.         Enabled: boolean,
  27.         Path: string?
  28.     }
  29. }
  30.  
  31. type ScriptPathConfig = {
  32.     Enabled: boolean,
  33.     Path: string?
  34. }
  35.  
  36. GrassPhysics.Config = {
  37.     WindShake = {
  38.         Enabled = false,  -- Enable/disable wind shake effect, ENABLING THIS DOES NOT ADD IT INTO YOUR CODE! You must set it up yourself, but turn this on if you have it shake the grass using wind.
  39.         ScriptPath = {
  40.             Path = nil -- Path to the wind shake script, not the module, the script that requires it, example: Players.LocalPlayer.PlayerScripts.WindControllerClient
  41.             } :: ScriptPathConfig
  42.         } :: WindShakeConfig,
  43.  
  44.     OcclusionCulling = {
  45.         HideGrass = {
  46.             Enabled = false  -- Enable/disable hiding grass when its not visible on the screen, can improve performance but may cause bugs.
  47.         },                  -- ONLY KEEP ONE OF THESE ENABLED, THE OTHER ONE FALSE
  48.         DisablePhysics = {
  49.             Enabled = false  -- Enable/disable disabling grass physics when the grass isn't visible on the screen, can improve performance.
  50.         },
  51.     },
  52.  
  53.     Sound = {
  54.         Enable = true,  -- Enable/disable sound effects
  55.         Volume = 0.1,  -- Volume of the plant sound effects (0.0 - 1.0)
  56.  
  57.         PitchRange = {  -- Range for randomizing pitch of plant sounds
  58.             Min = 0.8,
  59.             Max = 1.0
  60.         },
  61.  
  62.         Reverb = {  -- Reverb settings for the sound
  63.             Enable = false,
  64.             DecayTime = 2.0,
  65.             ReflectLevel = 0.5
  66.         },
  67.  
  68.         Spatial = {  -- Spatial audio settings
  69.             Enable = true,
  70.             RolloffMode = Enum.RollOffMode.Linear,
  71.             MinDistance = 10,
  72.             MaxDistance = 100
  73.         }
  74.     },
  75.  
  76.     Tween = {
  77.         Duration = 2,  -- Default duration of animation tweens in seconds
  78.         EasingStyle = Enum.EasingStyle.Elastic,  -- Default easing style for animation tweens
  79.         EasingDirection = Enum.EasingDirection.Out,  -- Default easing direction for animation tweens
  80.         TweenName = "pivotTween",  -- Default name for the tween instance
  81.  
  82.         -- Object-specific tween settings
  83.         ObjectTweens = {
  84.             ["ExampleObject"] = {
  85.                 Duration = 1.4,
  86.                 EasingStyle = Enum.EasingStyle.Exponential,
  87.                 EasingDirection = Enum.EasingDirection.Out
  88.             },
  89.         }
  90.     },
  91.    
  92.     Physics = {
  93.         HitboxRadius = 200,  -- Radius around the hitbox within which effects will be activated
  94.         Intervals = {
  95.             UpdateInterval = 0.1,  -- Update interval for checking activations and camera positions
  96.             CameraUpdateFactor = 2,  -- Factor to control how often camera updates are checked
  97.             ActivationUpdateFactor = 2  -- Factor to control how often activation updates are checked
  98.         }
  99.     },
  100.  
  101.     Grass = {
  102.         Tag = "Grass",  -- Tag used to identify grass objects, changing this can break the plugin
  103.         HitboxTag = "Hitbox",  -- Tag used to identify hitbox objects, changing this can break the plugin
  104.         Transparency = 1,  -- Transparency level of the hitbox
  105.  
  106.         CollisionResponse = {  -- Collision response settings for the grass
  107.             Bounce = 0.1,
  108.         }
  109.     },
  110.  
  111.     Camera = {
  112.         FieldOfViewThreshold = 2,  -- Additional degrees to the camera's field of view for angle checks
  113.         MovementThreshold = 1  -- Minimum movement in position or rotation to consider camera as moved
  114.     },
  115.  
  116.     SoundAsset = {
  117.         SoundEnabled = true,
  118.         SoundLimit = 50, -- Keep this low, but not too low or your sounds will sound ugly.
  119.         SoundMappings = {
  120.             Grass = {
  121.                 SoundId = "rbxassetid://8008438167",
  122.                 VolumeRange = { Min = 0, Max = 0.25 }
  123.             },
  124.         }
  125.     },
  126.  
  127.     Debug = {
  128.         EnableLogging = false,  -- Enable or disable debug logging
  129.         ExtraLogging = false, -- Extra debug logging, may flood your output; must have Enable Logging set to true for this to work
  130.         LogFrequency = 5  -- Number of updates between log entries
  131.     }
  132. }
  133.  
  134. --[[
  135. The following code is created by varuniemcfruity for the "Interactive Grass" plugin on ROBLOX.
  136. If you need to contact me, please send a private message through the developer forum or reach out to me via my profile.
  137.  
  138. Please respect my work by not stealing or reselling this code. If you have purchased the plugin, you are welcome to use it in all of your games.
  139. However, redistribution for profit or sharing the code for free is prohibited.
  140. ]]--
  141.  
  142. -- Internal Data
  143. local grassInfo = {}
  144. local hitboxInfo = {}
  145. local activeObjects = {}
  146. local playerEvents = {}
  147. local physicsEvents = {}
  148. local hitboxEvents = {}
  149.  
  150. local timeTracker = 0
  151. local updateCounter = 0
  152.  
  153. local lastCamCFrame = Camera.CFrame
  154.  
  155. local TW_INFO = TweenInfo.new(
  156.     GrassPhysics.Config.Tween.Duration,
  157.     GrassPhysics.Config.Tween.EasingStyle,
  158.     GrassPhysics.Config.Tween.EasingDirection
  159. )
  160.  
  161. local UP_VECTOR = Vector3.FromNormalId(Enum.NormalId.Back)
  162. local ROT_CFRAME = CFrame.Angles(0, math.rad(-90), math.rad(90))
  163. local SQRT_3 = math.sqrt(3)
  164. local INTERVAL = GrassPhysics.Config.Physics.Intervals.UpdateInterval
  165. local HITBOX_RADIUS = GrassPhysics.Config.Physics.HitboxRadius
  166.  
  167. -- Debugging Functions
  168. local function logDebug(message)
  169.     if GrassPhysics.Config.Debug.EnableLogging then
  170.         print("[GrassPhysics Debug]: " .. message)
  171.     end
  172. end
  173.  
  174. -- Tweening Functions
  175. local function createTween(obj: Instance, info, properties): Tween
  176.     local tween = TweenService:Create(obj, info, properties)
  177.     tween:Play()
  178.  
  179.     return tween
  180. end
  181.  
  182. local function getTweenInfoForPart(partName: string): TweenInfo
  183.     local objectTweenConfig = GrassPhysics.Config.Tween.ObjectTweens[partName]
  184.     if objectTweenConfig then
  185.         return TweenInfo.new(
  186.             objectTweenConfig.Duration,
  187.             objectTweenConfig.EasingStyle,
  188.             objectTweenConfig.EasingDirection
  189.         )
  190.     else
  191.         return TW_INFO
  192.     end
  193. end
  194.  
  195. local function initiateTween(name: string, basePart: BasePart, targetCFrame: CFrame): ()
  196.     if basePart:FindFirstChild(name) then
  197.         local tween = basePart:FindFirstChild(name)
  198.         tween:Destroy()
  199.     end
  200.  
  201.     local tweenInfo = getTweenInfoForPart(basePart.Name)
  202.     local tween = createTween(basePart, tweenInfo, { CFrame = targetCFrame })
  203.     tween.Name = name
  204.     tween.Parent = basePart
  205.  
  206.     tween.Completed:Connect(function()
  207.         tween:Destroy()
  208.     end)
  209. end
  210.  
  211. -- Sound Functions
  212. local function playSound(hitbox: BasePart, part: BasePart)
  213.     local partParent = part.Parent :: BasePart
  214.     local partType = partParent.Name
  215.     local soundConfig = GrassPhysics.Config.SoundAsset.SoundMappings[partType]
  216.    
  217.     local soundCount = 0
  218.     for _, child in pairs(hitbox:GetChildren()) do
  219.         if child:IsA("Sound") then
  220.             soundCount = soundCount + 1
  221.         end
  222.     end
  223.  
  224.     if soundCount >= GrassPhysics.Config.SoundAsset.SoundLimit then
  225.         logDebug("Hitbox sound limit reached. New sound not added.")
  226.         return
  227.     end
  228.  
  229.     local partParent = part.Parent :: BasePart
  230.     local partType = partParent.Name
  231.     local soundConfig = GrassPhysics.Config.SoundAsset.SoundMappings[partType]
  232.     if GrassPhysics.Config.Sound.Enable and soundConfig then
  233.         local sound = Instance.new("Sound", hitbox)
  234.         sound.SoundId = soundConfig.SoundId
  235.         local pitchShift = Instance.new("PitchShiftSoundEffect", sound)
  236.         pitchShift.Octave = math.random(
  237.             GrassPhysics.Config.Sound.PitchRange.Min * 100,
  238.             GrassPhysics.Config.Sound.PitchRange.Max * 100
  239.         ) / 100
  240.         sound.Volume = math.random(
  241.             soundConfig.VolumeRange.Min * 100,
  242.             soundConfig.VolumeRange.Max * 100
  243.         ) / 100
  244.         sound:Play()
  245.         delay(sound.TimeLength, function()
  246.             sound:Destroy()
  247.         end)
  248.  
  249.         if GrassPhysics.Config.Sound.Reverb.Enable then
  250.             local reverb = Instance.new("ReverbSoundEffect", sound)
  251.             reverb.DecayTime = GrassPhysics.Config.Sound.Reverb.DecayTime
  252.             reverb.Density = GrassPhysics.Config.Sound.Reverb.ReflectLevel
  253.         end
  254.  
  255.         if GrassPhysics.Config.Sound.Spatial.Enable then
  256.             sound.RollOffMode = GrassPhysics.Config.Sound.Spatial.RolloffMode
  257.             sound.RollOffMinDistance = GrassPhysics.Config.Sound.Spatial.MinDistance
  258.             sound.RollOffMaxDistance = GrassPhysics.Config.Sound.Spatial.MaxDistance
  259.         end
  260.     end
  261. end
  262.  
  263. function GrassPhysics:toggleSoundsEnabled(boolean: boolean)
  264.     local soundConfig = GrassPhysics.Config.Sound
  265.     soundConfig.Enabled = boolean
  266. end
  267.  
  268. -- Windshake Functions
  269. local function toggleWindShake(boolean: boolean)
  270.     local windShakeConfig = GrassPhysics.Config.WindShake
  271.  
  272.     logDebug("WindShake Enabled: " .. tostring(windShakeConfig.Enabled))
  273.    
  274.  
  275.     if windShakeConfig.Enabled then
  276.         local windShakePath = windShakeConfig.ScriptPath
  277.  
  278.         if windShakePath then
  279.             logDebug("WindShake ScriptPath: " .. tostring(windShakePath))
  280.             local windShakeScript = Players.LocalPlayer.PlayerScripts:FindFirstChild(tostring(windShakePath.Path))
  281.  
  282.             if windShakeScript then
  283.                 logDebug("Found WindShakeScript: " .. windShakeScript.Name)
  284.                 if windShakeScript:IsA("Script") then
  285.                     windShakeScript.Disabled = not boolean
  286.                     logDebug("WindShakeScript Disabled set to: " .. tostring(windShakeScript.Disabled))
  287.                 else
  288.                     logDebug("WindShakeScript is not a Script object.")
  289.                 end
  290.             else
  291.                 logDebug("WindShakeScript not found at path: " .. tostring(windShakePath.Path))  -- Correct logging
  292.             end
  293.         else
  294.             logDebug("WindShake.ScriptPath is not defined.")
  295.         end
  296.     else
  297.         logDebug("WindShake is not enabled; no action taken.")
  298.     end
  299. end
  300.  
  301. function GrassPhysics:toggleWindEnabled(boolean: boolean)
  302.     local windShakeConfig = GrassPhysics.Config.WindShake
  303.     windShakeConfig.Enabled = boolean
  304. end
  305.  
  306. -- Physics Functions
  307. local function activateEffect(hitbox: BasePart): ()
  308.     physicsEvents[hitbox] = {
  309.         Touched = hitbox.Touched:Connect(function(part: BasePart)
  310.             if not grassInfo[part] or not grassInfo[part].Eligible then return end
  311.             if table.find(activeObjects, part) then return end
  312.             local partParent = part.Parent :: BasePart
  313.             table.insert(activeObjects, part)
  314.             local rotation = partParent:GetAttribute("Rotation")
  315.             local info = grassInfo[part]
  316.             local direction = ((hitbox.Position - part.Position) * -Vector3.new(1, 0, 1)).Unit * part.Size.Y / SQRT_3
  317.             local targetLook = info.Top + direction
  318.             local targetPos = info.Base + (targetLook - info.Base).Unit * part.Size.Y / 2
  319.             local finalCFrame = CFrame.lookAt(targetPos, targetLook, UP_VECTOR) * ROT_CFRAME * CFrame.Angles(rotation.X, rotation.Y + math.rad(90), rotation.Z)
  320.  
  321.             initiateTween(GrassPhysics.Config.Tween.TweenName, partParent, finalCFrame)
  322.             playSound(hitbox, part)
  323.             logDebug("Activated effect for part: " .. part.Name)
  324.             toggleWindShake(false)
  325.         end),
  326.  
  327.         TouchEnded = hitbox.TouchEnded:Connect(function(part: BasePart)
  328.             local idx = table.find(activeObjects, part)
  329.  
  330.             if not grassInfo[part] or not idx then
  331.                 return
  332.             end
  333.  
  334.             local partParent = part.Parent :: BasePart
  335.             local touchingParts = hitbox:GetTouchingParts()
  336.             local stillTouching = false
  337.  
  338.             table.remove(activeObjects, idx)
  339.  
  340.             initiateTween(GrassPhysics.Config.Tween.TweenName, partParent, part.CFrame)
  341.  
  342.             logDebug("Ended touch effect for part: " .. part.Name)
  343.  
  344.             for _, touchingPart in touchingParts do
  345.                 if touchingPart:IsA("BasePart") and grassInfo[touchingPart] and table.find(activeObjects, touchingPart) then
  346.                     stillTouching = true
  347.                     break
  348.                 end
  349.             end
  350.             local windShakeConfig = GrassPhysics.Config.WindShake
  351.             local windShakePath = windShakeConfig.ScriptPath
  352.             local windShakeScript = Players.LocalPlayer.PlayerScripts:FindFirstChild(tostring(windShakePath.Path))
  353.             if not stillTouching then
  354.                 toggleWindShake(true)
  355.             end
  356.         end)
  357.     }
  358. end
  359.  
  360. local function deactivateEffect(hitbox: BasePart): ()
  361.     if physicsEvents[hitbox] then
  362.         physicsEvents[hitbox].Touched:Disconnect()
  363.         physicsEvents[hitbox].TouchEnded:Disconnect()
  364.         physicsEvents[hitbox] = nil
  365.  
  366.         logDebug("Deactivated effect for hitbox: " .. hitbox.Name)
  367.     end
  368. end
  369.  
  370. -- Grass Functions
  371. function GrassPhysics:enableGrass(grass: BasePart): ()
  372.     local hitbox = grass:FindFirstChild("Hitbox") :: BasePart
  373.     hitbox.CanTouch = true
  374. end
  375.  
  376. function GrassPhysics:disableGrass(grass: BasePart): ()
  377.     local hitbox = grass:FindFirstChild("Hitbox") :: BasePart
  378.     local idx = table.find(activeObjects, hitbox)
  379.  
  380.     if idx then
  381.         table.remove(activeObjects, idx)
  382.     end
  383.  
  384.     hitbox.CanTouch = false
  385.  
  386.     if grass:FindFirstChild(GrassPhysics.Config.Tween.TweenName) then
  387.         local tween = grass:FindFirstChild(GrassPhysics.Config.Tween.TweenName) :: Tween
  388.         tween:Cancel()
  389.     end
  390.  
  391.     grass.CFrame = hitbox.CFrame
  392. end
  393.  
  394. local function resetGrass(grass: BasePart): ()
  395.     local hitbox = grass:FindFirstChild("Hitbox") :: BasePart
  396.     local idx = table.find(activeObjects, hitbox)
  397.  
  398.     if idx then
  399.         table.remove(activeObjects, idx)
  400.     end
  401.  
  402.     if grass:FindFirstChild(GrassPhysics.Config.Tween.TweenName) then
  403.         local tween = grass:FindFirstChild(GrassPhysics.Config.Tween.TweenName) :: Tween
  404.         tween:Cancel()
  405.     end
  406.  
  407.     grass.CFrame = hitbox.CFrame
  408. end
  409.  
  410. local function setupGrassInstance(grass: BasePart): ()
  411.     grass:SetAttribute("Rotation", Vector3.new(math.rad(grass.Orientation.X), math.rad(grass.Orientation.Y), math.rad(grass.Orientation.Z)))
  412.  
  413.     local grassHitbox: BasePart = nil
  414.     local grassRotation = grass:GetAttribute("Rotation")
  415.  
  416.     if grass:FindFirstChild("Hitbox") then
  417.         local hitbox = grass:FindFirstChild("Hitbox") :: BasePart
  418.  
  419.         grassHitbox = hitbox
  420.     else
  421.         grassHitbox = Instance.new("Part", grass)
  422.     end
  423.  
  424.     grassInfo[grassHitbox] = {
  425.         Top = grass.Position + Vector3.new(0, grass.Size.Y / 2, 0),
  426.         Base = grass.Position - Vector3.new(0, grass.Size.Y / 2, 0),
  427.         Eligible = true,
  428.         Updated = false
  429.     }
  430.  
  431.     if not grass:FindFirstChild("Hitbox") then
  432.         grassHitbox.Size = grass.Size
  433.         grassHitbox.CanCollide = false
  434.         grassHitbox.Anchored = true
  435.         grassHitbox.Transparency = GrassPhysics.Config.Grass.Transparency
  436.         grassHitbox.Name = "Hitbox"
  437.         grass.CFrame = CFrame.lookAt(grass.Position, grassInfo[grassHitbox].Top, UP_VECTOR) * ROT_CFRAME * CFrame.Angles(grassRotation.X, grassRotation.Y + math.rad(90), grassRotation.Z)
  438.         grassHitbox.CFrame = grass.CFrame
  439.     end
  440. end
  441.  
  442. local function removeGrassInstance(grass: BasePart): ()
  443.     local hitbox = grass:FindFirstChild("Hitbox") :: BasePart
  444.     GrassPhysics:disableGrass(grass)
  445.     if grassInfo[hitbox] then
  446.         grassInfo[hitbox] = nil
  447.     end
  448.     hitbox:Destroy()
  449.     logDebug("Removed grass instance: " .. grass.Name)
  450. end
  451.  
  452. -- Hitbox Functions
  453. local function setupHitboxInstance(hitbox: BasePart): ()
  454.     if not hitbox:IsDescendantOf(Root) then
  455.         return
  456.     end
  457.     hitboxInfo[hitbox] = { Eligible = true, Updated = false }
  458.     activateEffect(hitbox)
  459.     logDebug("Setup hitbox instance: " .. hitbox.Name)
  460. end
  461.  
  462. local function removeHitboxInstance(hitbox: BasePart): ()
  463.     deactivateEffect(hitbox)
  464.     hitboxInfo[hitbox] = nil
  465.     if hitboxEvents[hitbox] then
  466.         hitboxEvents[hitbox]:Disconnect()
  467.         hitboxEvents[hitbox] = nil
  468.     end
  469.     logDebug("Removed hitbox instance: " .. hitbox.Name)
  470. end
  471.  
  472. -- Character Functions
  473. local function handleCharacterAddition(character: Model)
  474.     Character = character
  475.     HumanoidRootPart = Character:WaitForChild("HumanoidRootPart") :: BasePart
  476.     Humanoid = Character:WaitForChild("Humanoid") :: Humanoid
  477.  
  478.     -- Log character and HumanoidRootPart details
  479.     logDebug("Handling character addition: " .. character.Name)
  480.     logDebug("HumanoidRootPart Position: " .. tostring(HumanoidRootPart.Position))
  481.     logDebug("HumanoidRootPart CFrame: " .. tostring(HumanoidRootPart.CFrame))
  482.  
  483.     -- Check if a hitbox with the same CFrame already exists
  484.     local hitboxExists = false
  485.  
  486.     logDebug("Checking for existing hitboxes in player: " .. tostring(Character.Name))
  487.  
  488.     local existingHitbox = Character:FindFirstChild("Hitbox")
  489.     if existingHitbox then
  490.         hitboxExists = true
  491.         logDebug("Hitbox with matching CFrame found: " .. tostring(existingHitbox.Name))
  492.     end
  493.  
  494.     if not hitboxExists then
  495.         logDebug("No matching hitbox found. Adding new hitbox.")
  496.         local newHitbox = ReplicatedStorage.InteractiveGrass.Hitbox:WaitForChild("Hitbox"):Clone()
  497.         newHitbox.Parent = Character
  498.  
  499.         -- Create an attachment in the HumanoidRootPart
  500.         local humanoidRootAttachment = Instance.new("Attachment")
  501.         humanoidRootAttachment.Name = "HumanoidRootAttachment"
  502.         humanoidRootAttachment.Parent = HumanoidRootPart
  503.  
  504.         -- Set the hitbox's CFrame to the HumanoidRootPart's CFrame
  505.         newHitbox.CFrame = HumanoidRootPart.CFrame
  506.  
  507.         logDebug("New hitbox created at CFrame: " .. tostring(newHitbox.CFrame))
  508.  
  509.         -- Set up constraints for the hitbox
  510.         local attachment = Instance.new("Attachment", newHitbox)
  511.         local alignPosition = Instance.new("AlignPosition", newHitbox)
  512.         alignPosition.RigidityEnabled = true
  513.         alignPosition.Mode = Enum.PositionAlignmentMode.OneAttachment
  514.         alignPosition.Attachment0 = attachment
  515.         alignPosition.Attachment1 = humanoidRootAttachment
  516.         alignPosition.MaxForce = math.huge
  517.         alignPosition.MaxVelocity = math.huge
  518.         alignPosition.Responsiveness = 50
  519.         alignPosition.Enabled = true
  520.  
  521.         local alignOrientation = Instance.new("AlignOrientation", newHitbox)
  522.         alignOrientation.RigidityEnabled = true
  523.         alignOrientation.Mode = Enum.OrientationAlignmentMode.OneAttachment
  524.         alignOrientation.Attachment0 = attachment
  525.         alignOrientation.Attachment1 = humanoidRootAttachment
  526.         alignOrientation.MaxTorque = math.huge
  527.         alignOrientation.Responsiveness = 50
  528.         alignOrientation.Enabled = true
  529.  
  530.         -- Connect the AlignPosition and AlignOrientation to the HumanoidRootPart's attachment
  531.         hitboxEvents[newHitbox] = RunService.Stepped:Connect(function()
  532.             alignPosition.Position = newHitbox.Parent.PrimaryPart.Position
  533.             alignOrientation.CFrame = newHitbox.Parent.PrimaryPart.CFrame
  534.         end)
  535.  
  536.         CollectionService:AddTag(newHitbox, GrassPhysics.Config.Grass.HitboxTag)
  537.         logDebug("New hitbox added and tagged: " .. newHitbox.Name)
  538.     else
  539.         logDebug("Hitbox with the same CFrame already exists; not adding a new one.")
  540.     end
  541.  
  542.     Humanoid.Died:Connect(function()
  543.         logDebug("Humanoid died, resetting grass and hitboxes.")
  544.         for _, grass in pairs(activeObjects) do
  545.             local grassPart = grass.Parent :: BasePart
  546.             resetGrass(grassPart)
  547.             logDebug("Reset grass: " .. grassPart.Name)
  548.         end
  549.         activeObjects = {}
  550.         logDebug("Player died, deactivated all grass and hitboxes")
  551.     end)
  552. end
  553.  
  554. local function handleCharacterRemoval(character: Model): ()
  555.     local hitboxPart = character:FindFirstChild("Hitbox")
  556.     if hitboxPart then
  557.         hitboxPart:Destroy()
  558.     end
  559.     logDebug("Character removed: " .. character.Name)
  560. end
  561.  
  562. -- Camera Functions
  563. local function cameraPositionChanged(hitboxOnly: boolean): ()
  564.     task.desynchronize()
  565.  
  566.     local controlVec = lastCamCFrame.LookVector
  567.     local threshold = Camera.FieldOfView + GrassPhysics.Config.Camera.FieldOfViewThreshold
  568.     local infoType = if hitboxOnly then hitboxInfo else grassInfo
  569.  
  570.     for obj: BasePart, data in pairs(infoType) do
  571.         local directionVec = obj.Position - lastCamCFrame.Position
  572.         local angle = math.deg(directionVec.Unit:Angle(controlVec))
  573.         local playerPosVec = Character and HumanoidRootPart and (obj.Position - HumanoidRootPart.Position)
  574.         local distance = playerPosVec and (Vector3.new(playerPosVec.X, 0, playerPosVec.Z)).Magnitude
  575.  
  576.         if angle <= threshold and distance and distance <= HITBOX_RADIUS then
  577.             if not data.Eligible then
  578.                 data.Eligible = true
  579.                 data.Updated = false
  580.             end
  581.         else
  582.             if data.Eligible then
  583.                 data.Eligible = false
  584.                 data.Updated = false
  585.             end
  586.         end
  587.     end
  588.  
  589.     lastCamCFrame = Camera.CFrame
  590.  
  591.     if GrassPhysics.Config.Debug.ExtraLogging == true then
  592.         logDebug("Camera position changed. Hitbox only: " .. tostring(hitboxOnly))
  593.     end
  594.  
  595.     task.synchronize()
  596. end
  597.  
  598. local function updateCameraCFrame(deltaTime: number): ()
  599.     timeTracker += deltaTime
  600.  
  601.     if timeTracker > INTERVAL * GrassPhysics.Config.Physics.Intervals.CameraUpdateFactor then
  602.         timeTracker = 0
  603.         if (Camera.CFrame.Position - lastCamCFrame.Position).Magnitude > GrassPhysics.Config.Camera.MovementThreshold or Camera.CFrame.Rotation ~= lastCamCFrame.Rotation then
  604.             updateCounter = 0
  605.             task.spawn(cameraPositionChanged, false)
  606.         else
  607.             updateCounter += 1
  608.             if updateCounter > GrassPhysics.Config.Debug.LogFrequency then
  609.                 updateCounter = 0
  610.                 task.spawn(cameraPositionChanged, true)
  611.             end
  612.         end
  613.     end
  614.  
  615.     if GrassPhysics.Config.Debug.ExtraLogging == true then
  616.         logDebug("Camera CFrame updated.")
  617.     end
  618. end
  619.  
  620. local function updateActivations(deltaTime: number): ()
  621.     timeTracker += deltaTime
  622.  
  623.     if timeTracker > INTERVAL * GrassPhysics.Config.Physics.Intervals.ActivationUpdateFactor then
  624.         timeTracker = 0
  625.  
  626.         for obj, data in grassInfo do
  627.             local objParent = obj.Parent :: BasePart
  628.  
  629.             if data.Eligible then
  630.                 if not data.Updated then
  631.                     data.Updated = true
  632.                     GrassPhysics:enableGrass(objParent)
  633.                 end
  634.             else
  635.                 if not data.Updated then
  636.                     data.Updated = true
  637.                     GrassPhysics:disableGrass(objParent)
  638.                 end
  639.             end
  640.         end
  641.  
  642.         for obj, data in hitboxInfo do
  643.             if data.Eligible then
  644.                 if not data.Updated then
  645.                     data.Updated = true
  646.                     activateEffect(obj)
  647.                 end
  648.             else
  649.                 if not data.Updated then
  650.                     data.Updated = true
  651.                     deactivateEffect(obj)
  652.                 end
  653.             end
  654.         end
  655.  
  656.         if GrassPhysics.Config.Debug.ExtraLogging == true then
  657.             logDebug("Activations updated.")
  658.         end
  659.     end
  660. end
  661.  
  662. function GrassPhysics:Init()
  663.     Players.PlayerAdded:Connect(function(player)
  664.         playerEvents[player.Name] = {
  665.             CharacterAdded = player.CharacterAdded:Connect(handleCharacterAddition),
  666.             CharacterRemoving = player.CharacterRemoving:Connect(handleCharacterRemoval)
  667.         }
  668.         if player.Character then
  669.             handleCharacterAddition(player.Character)
  670.         end
  671.     end)
  672.  
  673.     Players.PlayerRemoving:Connect(function(player)
  674.         local events = playerEvents[player.Name]
  675.         if events then
  676.             events.CharacterAdded:Disconnect()
  677.             events.CharacterRemoving:Disconnect()
  678.             playerEvents[player.Name] = nil
  679.         end
  680.     end)
  681.  
  682.     CollectionService:GetInstanceAddedSignal(GrassPhysics.Config.Grass.Tag):Connect(setupGrassInstance)
  683.     CollectionService:GetInstanceRemovedSignal(GrassPhysics.Config.Grass.Tag):Connect(removeGrassInstance)
  684.  
  685.     CollectionService:GetInstanceAddedSignal(GrassPhysics.Config.Grass.HitboxTag):Connect(setupHitboxInstance)
  686.     CollectionService:GetInstanceRemovedSignal(GrassPhysics.Config.Grass.HitboxTag):Connect(removeHitboxInstance)
  687.  
  688.     RunService:BindToRenderStep("CameraUpdateStep", Enum.RenderPriority.Camera.Value, updateCameraCFrame)
  689.     RunService:BindToRenderStep("ActivationUpdateStep", Enum.RenderPriority.Camera.Value + 2, updateActivations)
  690.  
  691.     for _, grass in ipairs(CollectionService:GetTagged(GrassPhysics.Config.Grass.Tag)) do
  692.         setupGrassInstance(grass)
  693.     end
  694.  
  695.     for _, hitbox in ipairs(CollectionService:GetTagged(GrassPhysics.Config.Grass.HitboxTag)) do
  696.         setupHitboxInstance(hitbox)
  697.     end
  698.  
  699.     for _, player in ipairs(Players:GetPlayers()) do
  700.         playerEvents[player.Name] = {
  701.             CharacterAdded = player.CharacterAdded:Connect(handleCharacterAddition),
  702.             CharacterRemoving = player.CharacterRemoving:Connect(handleCharacterRemoval)
  703.         }
  704.         if player.Character then
  705.             handleCharacterAddition(player.Character)
  706.         end
  707.     end
  708.  
  709.     local hidegrassEnabled = GrassPhysics.Config.OcclusionCulling.HideGrass.Enabled
  710.     script.Parent.Parent.Culling.GrassCullingHideGrass.Enabled = hidegrassEnabled
  711.     local disablephysicsEnabled = GrassPhysics.Config.OcclusionCulling.DisablePhysics.Enabled
  712.     script.Parent.Parent.Culling.GrassCullingDisablePhysics.Enabled = disablephysicsEnabled
  713. end
  714.  
  715. GrassPhysics:Init()
  716. return GrassPhysics
Advertisement
Add Comment
Please, Sign In to add comment