Skip to content

Motors

Motors are the mechanism that Otter uses to animate values. Motors receive a goal and animate towards it. There are two kinds of motors: single motors, which animate a single value, and group motors, which animate multiple values in parallel.

Creating motors

Motors are created using createSingleMotor and createGroupMotor, which create a single and group motor, respectively. These functions take one argument each: the initial value(s) that the motor uses. For single motors, this is one value. For group motors, this is a dictionary of each value that the motor will be animating.

Note

When creating a group motor, all values must be specified at creation. You cannot add a value to a group motor after it is created.

local singleMotor = Otter.createSingleMotor(0)
local groupMotor = Otter.createGroupMotor({
    x = 0,
    y = 0,
})

Using motors

Assigning goals

Motors will animate when given goals to move towards. To supply a goal to a motor, use setGoal:

singleMotor:setGoal(Otter.spring(1))

groupMotor:setGoal({
    x = Otter.spring(300),
    y = Otter.spring(150),
})

This will start both singleMotor and groupMotor and make them move towards their position.

Extracting values

In order to use the values that a motor is animating, call onStep with a callback. The callback will be called once per frame while the motor moves towards its goals. For single motors, the callback will be passed the current value; for group motors, it will be passed a dictionary of all values (even if a value has reached its goal).

singleMotor:onStep(function(transparency)
    -- do something with transparency
end)

groupMotor:onStep(function(values)
    local x = values.x
    local y = values.y

    -- position something somewhere
end)

Demo: Following the mouse

This code, when run on the client, creates a frame that follows the mouse.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local Otter = require(ReplicatedStorage.Otter)

local localPlayer = Players.LocalPlayer
local mouse = localPlayer:GetMouse()

local container = Instance.new("ScreenGui")
container.ResetOnSpawn = false
container.Parent = localPlayer:WaitForChild("PlayerGui")

local frame = Instance.new("Frame")
frame.AnchorPoint = Vector2.new(1, 1)
frame.Size = UDim2.new(0, 20, 0, 20)
frame.BorderSizePixel = 0
frame.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
frame.Parent = container

local positionMotor = Otter.createGroupMotor({
    x = 0,
    y = 0,
})

positionMotor:onStep(function(values)
    frame.Position = UDim2.new(0, values.x, 0, values.y)
end)

local function updateMotor()
    positionMotor:setGoal({
        x = Otter.spring(mouse.X),
        y = Otter.spring(mouse.Y),
    })
end

updateMotor()
mouse.Move:Connect(updateMotor)

Manually running motors

Normally, motors automatically start when they are given a goal, and stop when they reach it. In some cases you may need to manually stop or start a motor, which you can do with the start and stop methods of the motor.

Stopping the motor while it is moving towards a goal will freeze it in time. It will retain its current velocity and goal, but will not move towards it, and any functions passed to onStep will not be called.

Starting the motor will resume the motor from this paused state. If the motor has already reached its goal, the motor will immediately stop again.