The game you linked is not uncopylocked.
Sorry about that! I fixed it. it is good to go.
This looks great and much better than what I was using before, the WindShake module.
What I was using before in my game:
https://i.gyazo.com/704160f96ec145fe1f0051bc279a3aee.gif
This module:
https://i.gyazo.com/2597be9677d462b1381ad273d1a78587.gif
I will look forward to if you choose to create an official release of this on the client as although optimized I am still worried about this being on the server.
Thanks for the support mako! When implementing this to my game, I made this modified version that handles the animations locally on the client, uses the tween service for smooth animations and uses occlusion culling!
[PlaceIn_ReplicatedFirst - Creator Store (https://create.roblox.com/store/asset/94248172114274/PlaceInReplicatedFirst)
This is the complete local sided source code
local camera=workspace.CurrentCamera
workspace:WaitForChild("Trees")
local function IsInView(object,cameraViewportSize)
local objectPosition = camera:WorldToViewportPoint(object.Position)
-- Check if the object is within the camera's viewport
if objectPosition.X <= cameraViewportSize.X and
--objectPosition.Y <= cameraViewportSize.Y and
objectPosition.Z > 0 then
return true
elseif objectPosition.X <= cameraViewportSize.X and
objectPosition.Y <= cameraViewportSize.Y and
objectPosition.Z > 0 then -- Z > 0 means the object is in front of the camera
return true
else
return false
end
end
print("Hello world!")
local format=require(script.AnimateLeaves)
local format2=require(script.AnimateTrunk)
local candidates={}
local count=0
local function registertree(obj)
local leaf= obj:FindFirstChild("Leaf")
if leaf then
count+=1
candidates[count.."t"]={func=format.Initialize(leaf),obj=leaf}
local index=Instance.new("StringValue")
index.Name="AnimationIndex"
index.Value=#candidates
index.Parent=leaf
end
if not leaf then
local trunk=obj:FindFirstChild("Trunk")
if trunk then
count+=1
candidates[count.."t"]={func=format2.Initialize(trunk),obj=trunk}
local index=Instance.new("StringValue")
index.Name="AnimationIndex"
index.Value=#candidates
index.Parent=trunk
end
end
end
local function ejecttree(obj)
local leaf= obj:FindFirstChild("Leaf")
if leaf then
local index=leaf:FindFirstChild("AnimationIndex")
if index then
candidates[index.Value]=nil
end
end
end
for i,v in workspace.Trees:GetChildren() do
registertree(v)
end
workspace.Trees.ChildAdded:Connect(registertree)
workspace.Trees.ChildRemoved:Connect(ejecttree)
--while true do
--game:GetService("RunService").:Connect(function()
local timer=.0666
while true do
local lastpos=camera.CFrame
task.wait(timer)
local cameraViewportSize=camera.ViewportSize
for i,v in candidates do
if IsInView(v.obj,cameraViewportSize) then
v.func()
end
end
end
Some of the main differences is I am using ToWorldSpace to respect the original orientation of your tree and leaves. I tested this with 40 different tree models and it works beautifully! You can include a trunk or only a Trunk to sway the trunk of the tree by using the leaf calculations multiplied by .5 to increase efficiency when using both at the same time.
As of my knowledge this is the most efficient way to do this type of tree animations. I may test further by implementing tween service instead of directly changing the CFrame.
Use these Trees with my Open Sourced Large Language Model agent System! Put Aurora in a Forest of trees and let the AI see the trees!
whats wrong with windshake, the gifs look the same with windshake just having very high settings
It’s overly complicated and not as optimized as this code is. I’ve never used windshake module.
But I optimized this code from it’s very basic elements and tried many different settings to achieve this level of performance. It’s important that you use the most efficient one so you can use it without worrying about that.
Also, whoever wrote windshake module didn’t respect the Original CFrame of the leaves like the original code didn’t. I noticed this issue becuase I tested 40 trees (it would cause trees leaves to reset to 0,0,0 making them all face the same direction given the noise parameters). I solved it. Also you can sway your tree trunks with this code and it is aligned with the leaves so they are double jointed and sway together without making the geometry out of its intentional alignment.
Additionally, the newer version uses tweening, and frustrum culling for local sided animation solution of higher fidelity.
The one demonstrated in the clip was on the server.
never had any of these issues youve mentioned, along with the performance has been fine
the module stock is a bit overkill to show what it does, tone down the settings and itll be visually fine
From what I understand this one basically works the same way, both are based off the same initial algorithm, and perhaps they caught the same issue the orignal algorithm had.
But can you sway the tree limbs?
In consequence of being based off the same open source algorithm both resources work the same way which is by the object called “Leaf”. Additionally with this one you can use only a Trunk, or a Trunk and Leaves. It’s also only 120 lines long and has modules that make it designed to be expanded upon if someone wished to do so.
For example a easy new addition would be objects tagged as “:Plants” and run them with the Trunk or Leaf or a third module Algorithm would make for swaying plants.
It’s good to see you’re using the trunk setting as well! There is a nice subtle sway in the trunk.
This module looks a lot more realistic compared to WindShake in my opinion, without any extra setup required, all I did was change the module. The framerate you see in the WindShake gif is not a fault of my computer or gyazo, it legitimately looks like that in game (clunky, unrealistic imo).
There’s nothing wrong with WindShake as I was using it before I came across this module.
To add, this is a gif of me using WindShake in another project, and after spending some time lowering the wind speed almost as much as possible and tweaking the module script (namely the refresh rate), this is the best I could achieve with WindShake.
https://gyazo.com/204732e96f898780603a095436bf305e
To me this still has very noticeable unrealistic swaying, and even some noticeable ‘clunkiness’.
*Keep in mind this is all on the almost lowest WindSpeed with the refresh rate at a higher than default amount.
Maybe I am using the module wrong or missing something, but until that is revealed to me I’ll continue to use this module.
I use it too! I would suggest the the local script over the server sided script provided in the example. New update coming soon to this module! I will be adding plant support similar to those interactive grass plugins that allow you to interact with moving plants
This i nice but i wished it could get GlobalWind
every update or so instead of once
Thanks for pointing that out! I didn’t notice. To fix it you can ctrl+F → Type wind → Replace all iterations of wind with workspace.GlobalWind. I’ll be posting an update soon!
Cool project but I think there’s some work to be done before you can call this very optimized or more optimized than windshake
A few general suggestions:
-
You should throttle the updates in a way where it only does as much work as it can in a frame before it crosses some threshold(like 0.5 ms or so). Your implementation won’t scale. If you have a crazy amount of trees in an area, it’ll process all of them per update and your frames will tank
-
This is something you should be multi threading, you could be assigning x amount of trees to process across 8 actors or so, where each actor does the math or whatever and returns results to the main thread with a buffer to then CFrame
-
You should be using BulkMoveTo to CFrame stuff, as it’ll speed up everything by like 20(?)%
-
You should be culling stuff based off your camera frustum like what windshake does, instead of updating things that don’t need to update. It’s also a good idea to adjust update rate based on some heuristic like how far the tree is from your camera
The local version handles all of that!
local camera=workspace.CurrentCamera
workspace:WaitForChild("Trees")
local function IsInView(object,cameraViewportSize)
local objectPosition = camera:WorldToViewportPoint(object.Position)
-- Check if the object is within the camera's viewport
if objectPosition.X <= cameraViewportSize.X and
--objectPosition.Y <= cameraViewportSize.Y and
objectPosition.Z > 0 then
return true
elseif objectPosition.X <= cameraViewportSize.X and
objectPosition.Y <= cameraViewportSize.Y and
objectPosition.Z > 0 then -- Z > 0 means the object is in front of the camera
return true
else
return false
end
end
local format=require(script.AnimateLeaves)
local format2=require(script.AnimateTrunk)
local candidates={}
local count=0
local function registertree(obj)
local leaf= obj:FindFirstChild("Leaf")
if leaf then
count+=1
candidates[count]={func=format.Initialize(leaf),obj=leaf}
local index=Instance.new("StringValue")
index.Name="AnimationIndex"
index.Value=#candidates
index.Parent=leaf
end
if not leaf then
local trunk=obj:FindFirstChild("Trunk")
if trunk then
count+=1
candidates[count]={func=format2.Initialize(trunk),obj=trunk}
local index=Instance.new("StringValue")
index.Name="AnimationIndex"
index.Value=#candidates
index.Parent=trunk
end
end
end
local function ejecttree(obj)
local leaf= obj:FindFirstChild("Leaf")
if leaf then
local index=leaf:FindFirstChild("AnimationIndex")
if index then
candidates[index.Value]=nil
end
end
end
for i,v in workspace.Trees:GetChildren() do
registertree(v)
end
workspace.Trees.ChildAdded:Connect(registertree)
workspace.Trees.ChildRemoved:Connect(ejecttree)
--while true do
--game:GetService("RunService").:Connect(function()
local timer=.0666
local playergui=game.Players.LocalPlayer.PlayerGui
while true do
local lastpos=camera.CFrame
task.wait(timer)
if playergui:FindFirstChild("Bars") and playergui.Bars:WaitForChild("Running") and playergui.Bars.Running.Value==false then
local cameraViewportSize=camera.ViewportSize+Vector2.new(100,100)
for i,v in candidates do
if IsInView(v.obj,cameraViewportSize) then
local leaf=v.func()
if leaf.Parent==nil then
table.remove(candidates,i)
end
end
end
end
end
I use this in a procedurally generated map of hundreds of trees streaming in and out, It works because the state of a locally manipulated object reverts back to its server state when streamed out.
It’s very scalable, although the example place does not include the local script yet. I included both links to give developers more options.
I optimized every variable from several iterations, while monitoring the differences in performance, to surprising effect. Such as, localizing every reused variable in the algorithm did not give it better performance. I performed similar tests when scaling it up as well.
I’m very keen on optimization because I wanted to use this without worrying about performance, I would suggest the local script called PlaceInReplicatedFirst. Let me know what you think of that! It’s designed to work with streaming enabled, but works without it.
Additionally, the trunk can be used with almost no difference in performance because it uses the calculations from the leaf (if any). This results in the unique effect of tree trunks swaying with less intensity but in the same direction as the leaves.
When I get the time I’ll be looking at it again, I plan to add plants in the future to the same effect.
The code is working super well for my application and scales with streaming enabled.
I just haven’t had any issue with performance. Most issues you would have with performance would be attributed to the polygon count which is why I included a mixture of imposter trees high poly and low poly trees.
To include a magnitude check for every tree would be not very efficient, in conclusion this is open sourced and the implementation is bare bones, I would improve it with using CollectionService and tagging tree models instead of putting them in a directory.
local CollectionService=game:GetService:("CollectionService")
for i,v in workspace.Trees:GetChildren() do
CollectionService:AddTag("Tree",treemodel)
end
Finally, I decided to use TweenService to animate the trees smoothly without executing a cframe change every frame. I’ll be looking into the bulkmoveto soon!
Is this project still active? there hasn’t been anything new since October
I’m still using it as is. Except I’m also using tags to retrieve a separate array of trees. This is a complete project concerning trees, I also have made a new feature for it to work on basepart objects tagged with “sway”. To animate plants.
When I created this project I put performance first by debugging several basic methods and determining which one was fastest to surprising results. So ‘if it’s not broke don’t fix it.’ But in terms of improvement If I could by add variables to influence the stiffness and dampening that would be alright. But I’m currently very busy, But when I get back to looking at the algorithm I’ll see about updating it with a stiffness attribute in a separate version, and converting the current library to use tags with some functions.