-- Define the script's data and inputs.type MyNode = {}-- Called once when the script initializes.function init(self: MyNode): boolean return trueend-- Called every frame to advance the simulation.-- 'seconds' is the elapsed time since the previous frame.function advance(self: MyNode, seconds: number): boolean return falseend-- Called when any input value changes.function update(self: MyNode) end-- Called every frame (after advance) to render the content.function draw(self: MyNode, renderer: Renderer) end-- Return a factory function that Rive uses to build the Node instance.return function(): Node<MyNode> return { init = init, advance = advance, update = update, draw = draw, }end
To be able to instantiate components at runtime, you will need to have a basic understanding of
Data Binding, Components, and Script Inputs.See the following example showing how to set up your components, view models, and scripts:
Copy
Ask AI
type Enemy = { artboard: Artboard<Data.Enemy>, position: Vector,}export type MyGame = { -- This is the component that we will dynamically add to our scene -- See: https://rive.app/docs/scripting/script-inputs enemy: Input<Artboard<Data.Enemy>>, enemies: { Enemy },}function createEnemy(self: MyGame) -- Create an instance of the artboard local enemy = self.enemy:instance() -- Keep track of all enemies in self.enemies local entry: Enemy = { artboard = enemy, position = Vector.xy(0, 0), } table.insert(self.enemies, entry)endfunction init(self: MyGame) createEnemy(self) return trueendfunction advance(self: MyGame, seconds: number) -- Advance the artboard of each enemy for _, enemy in self.enemies do enemy.artboard:advance(seconds) end return trueendfunction draw(self: MyGame, renderer: Renderer) -- draw each enemy for _, enemy in self.enemies do renderer:save() enemy.artboard:draw(renderer) renderer:restore() endendreturn function(): Node<MyGame> return { init = init, advance = advance, draw = draw, enemy = late(), enemies = {}, }end
Frame rates can vary between devices and scenes. If your script moves or animates objects based directly on the frame time, faster devices will move them farther each second, while slower ones will appear to lag behind.To keep movement and timing consistent, you can advance your simulation in fixed time steps instead of relying on the variable frame rate. This technique is called a fixed-step update or fixed timestep.
Copy
Ask AI
--- Fixed Timestep Advance--- Keeps movement consistent across different frame rates--- by advancing the simulation in fixed time steps.export type CarGame = { speed: Input<number>, accumulator: number, fixedStep: Input<number>, direction: number, currentX: number, currentY: number,}-- Prevent the script from running too many catch-up steps-- after a long pause or frame drop.local MAX_STEPS = 5function advance(self: CarGame, seconds: number): boolean -- Add the time since the last frame to the accumulator. self.accumulator += seconds local dt = self.fixedStep local steps = 0 -- Run the simulation in small, fixed steps. -- If the frame took longer than one step, multiple steps may run this frame. while self.accumulator >= dt and steps < MAX_STEPS do -- Move forward by speed * time. -- Using a fixed dt keeps movement stable even if the frame rate changes. self.currentX += self.speed * math.cos(self.direction) * dt self.currentY += self.speed * math.sin(self.direction) * dt -- Subtract one fixed step from the accumulator -- and repeat until we've caught up to real time. self.accumulator -= dt steps += 1 end return trueend-- Create a new instance of the CarGame script with default values.-- The simulation runs 60 fixed steps per second.return function(): Node<CarGame> return { speed = 100, accumulator = 0, direction = 0, fixedStep = 1 / 60, currentX = 0, currentY = 0, }end