> For the complete documentation index, see [llms.txt](https://docs.overdare.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.overdare.com/manual/studio-manual/object/simulationball.md).

# SimulationBall

### Overview <a href="#undefined" id="undefined"></a>

SimulationBall is an object that calculates the ball's movement in advance and then plays back the result. Compared to a regular physics ball, its trajectory is easier to predict, and it can represent the same result more reliably even in environments where multiple players are watching together.

It is especially useful in the following situations.

* Games where the ball's trajectory is important, such as soccer or basketball
* When you need to stage the movement of a ball bouncing off walls or floors
* When you want to preview the ball's movement path while adjusting gameplay

### How to Use <a href="#undefined" id="undefined"></a>

#### Creating a SimulationBall and Setting the Collision Target Trace Channel <a href="#simulationball-bound-trace-channel" id="simulationball-bound-trace-channel"></a>

<figure><img src="/files/IsYg6201u1teigWDHnhD" alt=""><figcaption></figcaption></figure>

Create a `SimulationBall` in the Level Browser and place it where you want to use it.

<figure><img src="/files/f1swXRenFffuRbE7qWeP" alt=""><figcaption></figcaption></figure>

Which objects the SimulationBall collides with is defined through the **Collision Profile** and **StaticObjectTypes**.

* `BallMeshCollisionProfile`: The Collision Profile of the SimulationBall itself. It is referenced when other objects collide with the SimulationBall. However, this profile is not used in the actual simulation.
* `StaticObjectTypes`: The Object Type channels used when the SimulationBall performs its collision simulation. During the actual simulation, the trajectory is calculated based on collisions with these channels.

Therefore, for objects that the ball should collide with, such as walls or floors, you must add their ObjectType to `StaticObjectTypes`.

{% hint style="info" %}
If the `StaticObjectTypes` array is empty, only the `WorldStatic` channel is targeted for collision by default. If the ball also needs to collide with objects on other channels, make sure to add those channels.
{% endhint %}

#### Simulation Preview in the Editor <a href="#simulation-preview" id="simulation-preview"></a>

Finding the right physics values for a simulation ball in your game normally requires testing repeatedly while changing the numbers. To help with this, a preview feature is provided that lets you run various simulations at edit time without actually playing the game.

By entering different values into the SimulationBall's `EditorBallSimParams`, you can immediately see in the editor what kind of trajectory the ball follows. Keeping `EnablePathMarker` turned on at this time displays the calculated trajectory as path markers, making it easy to compare values.

#### Controlling the Simulation with a Script <a href="#simulation-script" id="simulation-script"></a>

Find the SimulationBall, set the starting position and velocity, and run the simulation.

`Simulate()` does not complete its result immediately after being called. Since the simulation is internally divided and calculated asynchronously, **APIs that query the simulation results**, such as `FindNextBallBounce()` or `GetCFrameAtTime()`, must be called after the calculation is finished. On the other hand, `Play()`, which starts playback, can be called right after `Simulate()` as in the example below.

```lua
local Workspace = game:GetService("Workspace")
local Ball = Workspace:WaitForChild("SimulationBall")

-- EnablePathMarker draws the simulated trajectory on screen for preview
Ball.EnablePathMarker = true


local Params = BallSimParams.new()
Params.Mass = 0.43
Params.InitialCFrame = CFrame.new(0, 100, -800)

-- InitialSpeed is in km/h and InitialDirection must be a unit vector
local InitialVelocity = Vector3.new(300, 900, 0) -- velocity vector in km/h scale
Params.InitialSpeed = InitialVelocity.Magnitude
Params.InitialDirection = InitialVelocity.Unit

Params.Simsteps = 120
Params.StepsPerSecond = 30

-- With spin values below, the ball can bounce along its spin direction on the ground
-- or curve in the air by the Magnus effect
--Params.InitialSpinAxis = Vector3.new(0, 1, 0)
--Params.InitialSpinSpeed = 0

Ball:Simulate(Params, false)
Ball:Play()
```

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FhRPi87oM9ttlk5nyu7L7%2Fuploads%2FCL2hnpT5fooLEHZP4qo8%2Fsimball.mp4?alt=media&token=187f8f40-32aa-49fd-9d6f-0f6738d9fc1d>" %}

#### Main Properties of BallSimParams <a href="#ballsimparams" id="ballsimparams"></a>

You can adjust the ball's movement by changing the main settings of `BallSimParams`.

<table><thead><tr><th width="221.6666259765625">Property</th><th>Description</th></tr></thead><tbody><tr><td><strong>InitialCFrame</strong></td><td>Defines the starting position and initial rotation of the ball.</td></tr><tr><td><strong>InitialSpeed</strong></td><td>Defines the launch speed of the ball in km/h.</td></tr><tr><td><strong>InitialDirection</strong></td><td>Defines the direction the ball flies in as a unit vector.</td></tr><tr><td><strong>InitialSpinAxis</strong></td><td>Defines the axis the ball rotates around as a unit vector.</td></tr><tr><td><strong>InitialSpinSpeed</strong></td><td>Defines the rotation speed of the ball in RPM (revolutions per minute), in the range -12000 to 12000. The larger the absolute value, the stronger the Magnus force.</td></tr><tr><td><strong>Mass</strong></td><td>Defines the mass of the ball in kg. Used for collision impulse and rotational inertia calculations.</td></tr><tr><td><strong>BaseGravity</strong></td><td>Adjusts how strongly the ball falls downward (gravitational acceleration) in cm/s². The default is 980; if set to 0, the ball is weightless, and if negative, gravity works in the opposite direction.</td></tr><tr><td><strong>Restitution</strong></td><td>Defines the coefficient of restitution on collision (0–1). The closer to 1, the more the ball bounces.</td></tr><tr><td><strong>Friction</strong></td><td>Defines the friction coefficient against the ground (0–1). The larger the value, the faster the sliding speed decreases on ground contact.</td></tr><tr><td><strong>RollingFriction</strong></td><td>Defines the rolling resistance coefficient (0–1). Determines how quickly the ball slows down while rolling on the ground.</td></tr><tr><td><strong>SpinMagnusWeight</strong></td><td>Defines the weight of the Magnus effect applied when the ball spins (0.0–0.1). The larger the weight, the more the trajectory curves; a natural effect at the level of a soccer or golf ball is around 0.01–0.015.</td></tr><tr><td><strong>Simsteps</strong></td><td>Defines how many steps the simulation is divided into (1–14400). Higher values can improve precision but also increase calculation cost.</td></tr><tr><td><strong>StepsPerSecond</strong></td><td>Defines the simulation frequency in Hz (steps per second), in the range 1–480. Together with `Simsteps`, it determines the total simulation length (`Simsteps ÷ StepsPerSecond`) and precision.</td></tr></tbody></table>

For example, if you want the ball to travel farther, increase the `InitialSpeed` value, and if you want it to start from a higher point, raise the height value of `InitialCFrame`.

{% hint style="info" %}
`EnablePathMarker` is a property of the **SimulationBall object**, not of `BallSimParams`. To display the trajectory on screen, set it directly on the ball object, as in `Ball.EnablePathMarker = true` in the example above.
{% endhint %}

### Using Additional SimulationBall Features <a href="#simulationball-features" id="simulationball-features"></a>

Unlike a typical real-time physics simulation, SimulationBall pre-calculates the ball's trajectory and collisions based on the BallSimParams provided in advance, and then plays the result. This allows you to find out the ball's movement path or collision positions before calling Play.

#### Getting the Ball's Next Bound Position <a href="#bound" id="bound"></a>

After the simulation is finished, SimulationBall can find out information about the next point where the ball will bounce in advance.

This feature is useful in the following cases.

* When you want to check in advance which wall the ball will bounce off
* When you want to place an effect at the next bound position
* When AI or game logic needs to predict the next collision point

```lua
local Workspace = game:GetService("Workspace")
local Ball = Workspace:WaitForChild("SimulationBall")

local Params = BallSimParams.new()

Params.Mass = 0.43
Params.InitialCFrame = CFrame.new(0, 100, -800)

-- InitialSpeed is in km/h and InitialDirection must be a unit vector
local InitialVelocity = Vector3.new(300, 900, 0) -- velocity vector in km/h scale
Params.InitialSpeed = InitialVelocity.Magnitude
Params.InitialDirection = InitialVelocity.Unit

Params.Simsteps = 120
Params.StepsPerSecond = 30


Ball:Simulate(Params, false)
-- Wait until the async simulation finishes before querying results
task.wait()
local NextBounce = Ball:FindNextBallBounce()

if NextBounce.BouncedTime > 0 then
    print("Next Bound Time:", NextBounce.BouncedTime)
    print("Next Bound Position:", NextBounce.BouncedPosition)
end

Ball:Play()
```

The code above uses `FindNextBallBounce()` to get the next bound information.\
The returned value contains the time and position where the bound occurs, so you can check in advance where the ball will bounce.

However, if you call `FindNextBallBounce()` immediately after `Simulate()`, the calculation may not be finished yet and you may not get the desired values. As in the example above, you should wait briefly with `task.wait()` before calling it. If `Simsteps` is large and the calculation is heavy, waiting a single frame may not be enough, so check repeatedly until a valid value is returned, or allow sufficient time before querying.

#### Getting the Ball's Physics Values After N Seconds <a href="#n" id="n"></a>

SimulationBall can also retrieve the state of the ball after a specific amount of time in advance.

This feature is useful in the following cases.

* When you want to know the ball's position after N seconds
* When you want to check how fast the ball is moving
* When you want to predict the future state, including the rotation speed

```lua
local Workspace = game:GetService("Workspace")
local Ball = Workspace:WaitForChild("SimulationBall")

local Params = BallSimParams.new()
Params.Mass = 0.43
Params.InitialCFrame = CFrame.new(0, 100, -800)

-- InitialSpeed is in km/h and InitialDirection must be a unit vector
local InitialVelocity = Vector3.new(300, 900, 0) -- velocity vector in km/h scale
Params.InitialSpeed = InitialVelocity.Magnitude
Params.InitialDirection = InitialVelocity.Unit

Params.Simsteps = 120
Params.StepsPerSecond = 30

Ball:Simulate(Params, false)
-- Wait until the async simulation finishes before querying results
task.wait()

local CheckTime = 2.0

local FutureCFrame = Ball:GetCFrameAtTime(CheckTime)
local FutureVelocity = Ball:GetLinearVelocityAtTime(CheckTime)
local FutureSpeed = Ball:GetSpeedAtTime(CheckTime)
local FutureAngularVelocity = Ball:GetAngularVelocityAtTime(CheckTime)

print("Position in 2s:", FutureCFrame.Position)
print("Velocity vector in 2s:", FutureVelocity)
print("Speed in 2s:", FutureSpeed)
print("Angular velocity in 2s:", FutureAngularVelocity)
```

The example above retrieves the ball's physics values `2 seconds` later in advance.

* `GetCFrameAtTime()` : position and rotation at that time
* `GetLinearVelocityAtTime()` : direction and magnitude of the movement speed at that time
* `GetSpeedAtTime()` : only the speed as a number at that time
* `GetAngularVelocityAtTime()` : rotation speed at that time

In other words, SimulationBall does more than simply play the ball; it can also **query the position, velocity, and rotation at future points in time** in advance.

Here as well, avoid reading values immediately after `Simulate()`; it is better to wait briefly with `task.wait()` before querying.

Also, if `CheckTime` exceeds the current simulation range, you may not get the expected values, so make sure `Simsteps ÷ StepsPerSecond` is sufficiently larger than the time you want to query.

#### Automatically Aiming the Ball at a Target Position <a href="#simulatetotarget" id="simulatetotarget"></a>

With `SimulateToTarget()`, you do not need to calculate the launch speed and direction yourself; simply specify a target position, and it automatically calculates the speed and direction that reach that point and runs the simulation.

This feature is useful in the following cases.

* When you want to implement trick shots that throw the ball exactly to a specific position
* When you want an NPC or AI to throw the ball toward a player's position
* When you want to build a trajectory from only a target point instead of manually calculating the launch speed/direction

```lua
local Workspace = game:GetService("Workspace")
local Ball = Workspace:WaitForChild("SimulationBall")

local Params = BallSimParams.new()
Params.Mass = 0.43
Params.InitialCFrame = CFrame.new(0, 100, -800)
Params.Simsteps = 120
Params.StepsPerSecond = 30

local TargetPosition = Vector3.new(0, 0, 800)

-- UseDesiredSpeed = false: search for both speed and direction to reach the target
-- AutoPlay = true: start playback immediately after the simulation completes
local Result = Ball:SimulateToTarget(Params, TargetPosition, false, true)

if Result.bHit then
    print("Hit time:", Result.HitTime)
    print("Actual launch speed (km/h):", Result.ActualSpeed)
else
    print("Could not find a trajectory that reaches the target")
end
```

`SimulateToTarget()` returns the calculation result immediately as a `BallSimTargetResult`, so unlike `Simulate()`, you can use the return value right away without waiting. You can use `bHit` to check whether a trajectory reaching the target was found, `HitTime` for the arrival time, and `ActualSpeed`/`Direction` for the actual speed and direction used.

#### Using Collision Events <a href="#collision-events" id="collision-events"></a>

SimulationBall fires events when it collides with other objects during playback. Use them to implement collision-based logic such as goal detection, playing sounds, or displaying effects.

* `Touched`: Called when the ball collides with another part.
* `TouchEnded`: Called when the ball separates from a part it was in contact with.
* `Bounded`: Called only when the ball **bounces (collision reflection)** off a part. Sliding contacts are not included.

```lua
local Workspace = game:GetService("Workspace")
local Ball = Workspace:WaitForChild("SimulationBall")

-- Called on every collision
Ball.Touched:Connect(function(otherPart)
    if otherPart.Name == "Goal" then
        print("Goal!")
    end
end)

-- Called only when the ball actually bounces off a part
Ball.Bounded:Connect(function(otherPart, bounce)
    print("Ball bounced off:", otherPart.Name)
    print("Bounce position:", bounce.BouncedPosition)
end)
```

#### Controlling Playback <a href="#playback-control" id="playback-control"></a>

Simulation playback can be controlled in various ways beyond `Play()`.

* `Pause()`: Pauses playback. Calling `Play()` again resumes from where it stopped.
* `Stop()`: Stops playback. Calling `Play()` afterwards replays from the beginning of the simulation.
* `Play(bReset)`: Passing `true` for `bReset` resets the playback time (`PlaybackTime`) before playing. If omitted, it defaults to `false`.
* `SetPlaybackTime(time)`: Moves the playback time to an arbitrary point. It can be called even while playing, so it can be used for rewinding or jumping to a specific moment.
* `SlomoFactor`: The playback speed multiplier. 1.0 is normal speed, 0.5 plays at half speed (slow motion), and 2.0 plays at double speed.

```lua
local Workspace = game:GetService("Workspace")
local Ball = Workspace:WaitForChild("SimulationBall")

Ball:Play()

-- Pause and resume from the same point
Ball:Pause()
Ball:Play()

-- Jump to the 2.5 second mark (also works while playing)
Ball:SetPlaybackTime(2.5)

-- Play at half speed for a slow-motion effect
Ball.SlomoFactor = 0.5

-- Stop, then replay from the beginning
Ball:Stop()
Ball:Play()
```

#### Recalculating the Trajectory Mid-Flight (ReSimulate) <a href="#resimulate" id="resimulate"></a>

You can also recalculate the trajectory of a ball that is currently playing, starting from a specific point in time. This is useful for effects such as intercepting a pass or changing direction in mid-air.

* `ReSimulateWithDelay()`: After the specified delay from the current playback time, recalculates the trajectory with a new direction, speed, and spin.
* `ReSimulateToTargetWithDelay()`: After the delay, recalculates the trajectory to head toward a target position. The spin is calculated automatically based on the current angular velocity.
* `ReSimulateSpinToTargetWithDelay()`: After the delay, recalculates the trajectory toward a target position using the specified spin axis and spin speed.

```lua
local Workspace = game:GetService("Workspace")
local Ball = Workspace:WaitForChild("SimulationBall")

local TargetPosition = Vector3.new(100, 0, 50)

-- After 1 second, recalculate the trajectory toward the target
local Result = Ball:ReSimulateToTargetWithDelay(
    1.0,            -- delay from the current playback time (seconds)
    TargetPosition, -- target position
    100,            -- desired speed (km/h)
    120,            -- step count
    false           -- search for both speed and direction
)

if Result.bHit then
    print("Hit time:", Result.HitTime)
end
```

Like `SimulateToTarget()`, `ReSimulateToTargetWithDelay()` and `ReSimulateSpinToTargetWithDelay()` return a `BallSimTargetResult`.

### Notes <a href="#undefined" id="undefined"></a>

* `Simulate()` does not complete immediately and is processed asynchronously internally. Wait briefly with `task.wait()` before querying simulation results with `FindNextBallBounce()`, `GetCFrameAtTime()`, and similar APIs; if the calculation is heavy, you may need to check repeatedly until a valid value is returned.
* You must run the simulation before playing the ball.
* If `StaticObjectTypes` is empty, only the `WorldStatic` channel is targeted for collision. Make sure to add the channels of the objects the ball should bounce off.
* If the speed value is too small, the ball may appear to barely move.
* When querying future points in time, make sure `CheckTime` does not exceed the total simulation length (`Simsteps ÷ StepsPerSecond`).
* During testing, keeping `EnablePathMarker` enabled helps with verification.

### Reference Documents <a href="#undefined" id="undefined"></a>

{% content-ref url="/pages/ZaoG57umzS7C4yIBXybF" %}
[SimulationBall](/development/api-reference/classes/simulationball.md)
{% endcontent-ref %}

{% content-ref url="/pages/YqJaPfrqUpMzOMSW6u1P" %}
[BallSimParams](/development/api-reference/datatype/ballsimparams.md)
{% endcontent-ref %}

{% content-ref url="/pages/oWk6LzQdpvxHPVc76lcD" %}
[BallSimTargetResult](/development/api-reference/datatype/ballsimtargetresult.md)
{% endcontent-ref %}

{% content-ref url="/pages/v1qaySVS9nEJPM5N38Aw" %}
[BallBounce](/development/api-reference/datatype/ballbounce.md)
{% endcontent-ref %}
