Let’s imagine a simple structure of windmill.
We first want to make sure that the rotor or the “center” part of the collection of blades has a pivot point that is centered. We gonna use that point to rotate the collection of blades around it.
One thing that we know for sure is that the rotor will need to be able to rotate full 360 degrees.
We also need to know how we can increment the angle over time. One way to approach this problem would be to continuously incrementing an integer.
i = 0
Some Loop
i += 1
end
But that would be silly since the number would just keep growing a lot and that would not be the best thing for the memory. Instead, we gonna be a bit more clever and we will use the system time. This can be done with os.time()
method.
print(os.time()) --> 1663530069
-- Some time later...
print(os.time()) --> 1663530071
As you can see, each time we call this function, the number gets bigger each time, that’s exactly what we wanted, but now we need to make sure that it falls in the 0 to 360 range. Mod (%) operator will be perfect for this.
print(os.time() % 360) --> 309
-- Some time later...
print(os.time() % 360) --> 311
Now we need a way to loop, and best method for physics related “looping” is the heartbeat event which can be implemented like this.
local RunService = game:GetService("RunService")
RunService.Heartbeat:Connect(function()
local angle = os.time() % 360
end)
Since the model will be anchored, we will need to use either CFrame
or the Orientation
property. Today, we will use the CFrame property. We also need to make sure that it works with the pivot point we set in the beginning, therefore, we will use the GetPivot()
and PivotTo()
methods.
local RunService = game:GetService("RunService")
local rotor = script.Parent.Rotor
RunService.Heartbeat:Connect(function()
local angle = os.time() % 360
rotor:PivotTo(rotor:GetPivot())
end)
As of now, we are just setting the pivot point to the current pivot point, but we need to rotate it, this is where CFrame.Angles()
comes in to play. It has 3 parameters which are X, Y, and Z, they all take a number that represents an angle in radians. With that being said, we need to convert our angle in degrees to radians, this can be done with math.rad()
.
Since we want to change the angle of the rotor, we will multiply the current pivot point CFrame by CFrame.Angles()
. We don’t want to change all of the angles, so we will need to find the one angle that will make our rotor rotate in direction that we desire, we can do that by simply passing our angle variable into one of the axis and leave the rest as 0, and we will keep switching the axis where we put our angle variable until we get the desired behavior. In my case it was the Z axis (third).
local RunService = game:GetService("RunService")
local rotor = script.Parent.Rotor
RunService.Heartbeat:Connect(function()
local angle = math.rad(os.time() % 360)
rotor:PivotTo(rotor:GetPivot() * CFrame.Angles(0, 0, angle))
end)
This works, but we don’t have any control over the rotation speed. We can do that by multiplying our angle variable by the speed.
local RunService = game:GetService("RunService")
local rotor = script.Parent.Rotor
local speed = 0.01
RunService.Heartbeat:Connect(function()
local angle = math.rad(os.time() % 360) * speed
rotor:PivotTo(rotor:GetPivot() * CFrame.Angles(0, 0, angle))
end)
This works, but there is one issue. If player happens to have higher FPS, the rotor will spin faster than for the player with 60 FPS, this is because our callback function will be called more often due to higher FPS, and vice versa. This can be fixed by multiplying the angle by delta time.
local RunService = game:GetService("RunService")
local rotor = script.Parent.Rotor
local speed = 0.01
RunService.Heartbeat:Connect(function(deltaTime)
local angle = math.rad(os.time() % 360) * (deltaTime * speed)
rotor:PivotTo(rotor:GetPivot() * CFrame.Angles(0, 0, angle))
end)
After doing this, you might have noticed that the speed of rotor is drastically slower, this is because of the delta time. We can easily fix this by increasing the speed.
local RunService = game:GetService("RunService")
local rotor = script.Parent.Rotor
local speed = 0.5
RunService.Heartbeat:Connect(function(deltaTime)
local angle = math.rad(os.time() % 360) * (deltaTime * speed)
rotor:PivotTo(rotor:GetPivot() * CFrame.Angles(0, 0, angle))
end)
![RobloxStudioBeta_wXPOkUa6Hv](/secure-media-uploads/uploads/original/4X/3/5/2/35224e883efa1f3e7f167666436786790462e4d5.gif)
By now you should have a working, and spinning windmill. I hope this helped! ![:grin: :grin:](https://doy2mn9upadnk.cloudfront.net/images/emoji/twitter/grin.png?v=12)