Skip to content
Physics Simulation

Physics Simulation

Overview

Pelota implements a realistic physics engine that simulates ball motion with gravity, air resistance, spin effects (Magnus force), and bouncing. The simulation is deterministic and can predict ball trajectory for future frames.

Physics Constants

All physics calculations use the following constants (from GameConstants):

ConstantSymbolValueUnitDescription
Gravitygg9.81m/s²Standard Earth gravity acceleration
Ball Damping (Bounce)dvertd_{\text{vert}}0.7-Vertical velocity retention (70% energy)
Ball Damping (Horizontal)dhorizd_{\text{horiz}}0.9-Horizontal velocity retention after bounce
Spin Side Effectksidek_{\text{side}}0.08-Sidespin multiplier for velocity change
Spin Down Forcekdownk_{\text{down}}0.1-Topspin multiplier for Magnus downward force
Air Drag Coefficientcdc_d0.02-Air resistance factor
Ground Levely0y_00.035mBall contact threshold with ground

Velocity Update Algorithm

The ball velocity is updated each physics frame using this sequence:

vn+1=ApplyMagnusAndGravity(vn,s,Δt)ApplyAirDrag(vn+1,Δt)\vec{v}_{n+1} = \text{ApplyMagnusAndGravity}(\vec{v}_n, \vec{s}, \Delta t) \rightarrow \text{ApplyAirDrag}(\vec{v}_{n+1}, \Delta t)

1. Magnus Force & Gravity

The Magnus force is a sideways and downward acceleration caused by ball spin. Combined with gravity:

Fmagnus=(sxksidesykdown0)\vec{F}_{\text{magnus}} = \begin{pmatrix} s_x \cdot k_{\text{side}} \\ -s_y \cdot k_{\text{down}} \\ 0 \end{pmatrix}

Where s=(sx,sy,sz)\vec{s} = (s_x, s_y, s_z) is the spin vector:

  • sxs_x: Sidespin (causes horizontal curve)
  • sys_y: Topspin (positive) or Backspin (negative)
  • szs_z: Forward spin

The total downward acceleration is:

ay=g+Fmagnus.y=gsykdowna_y = -g + \vec{F}_{\text{magnus}}.y = -g - s_y \cdot k_{\text{down}}

The velocity update becomes:

vx(n+1)=vx(n)+sxksideΔtv_x^{(n+1)} = v_x^{(n)} + s_x \cdot k_{\text{side}} \cdot \Delta tvy(n+1)=vy(n)+(gsykdown)Δtv_y^{(n+1)} = v_y^{(n)} + \left(-g - s_y \cdot k_{\text{down}}\right) \cdot \Delta tvz(n+1)=vz(n)v_z^{(n+1)} = v_z^{(n)}

2. Air Drag

Air resistance creates a velocity-dependent drag that slows the ball:

vdrag=v11+cdvΔt\vec{v}_{\text{drag}} = \vec{v} \cdot \frac{1}{1 + c_d \cdot |\vec{v}| \cdot \Delta t}

This exponential decay approximates aerodynamic drag: v(t)=v0ecdv0t\vec{v}(t) = \vec{v}_0 e^{-c_d |\vec{v}_0| \cdot t}

3. Position Update

After velocity is computed, position is integrated:

pn+1=pn+vn+1Δt\vec{p}_{n+1} = \vec{p}_n + \vec{v}_{n+1} \cdot \Delta t

Collision & Bounce Physics

Ground Bounce

When the ball collides with the ground, velocity is decomposed into normal and tangential components:

vnormal=(vn^)n^\vec{v}_{\text{normal}} = (\vec{v} \cdot \hat{n}) \hat{n}

vtangent=vvnormal\vec{v}_{\text{tangent}} = \vec{v} - \vec{v}_{\text{normal}}

Where n^=(0,1,0)\hat{n} = (0, 1, 0) is the ground normal.

Energy-Based Bounce

If the normal velocity magnitude is significant (vnormalvmin|\vec{v}_{\text{normal}}| \geq v_{\min}):

vnormal=vnormaldvert\vec{v}'_{\text{normal}} = -\vec{v}_{\text{normal}} \cdot d_{\text{vert}}vtangent=vtangentdhoriz\vec{v}'_{\text{tangent}} = \vec{v}_{\text{tangent}} \cdot d_{\text{horiz}}vside=vtangent.x+sxkside (spin effect on sidespin)\vec{v}'_{\text{side}} = \vec{v}'_{\text{tangent}}.x + s_x \cdot k_{\text{side}} \text{ (spin effect on sidespin)}

The new velocity is:

vbounce=vnormal+vtangent+(sxkside,0,0)\vec{v}_{\text{bounce}} = \vec{v}'_{\text{normal}} + \vec{v}'_{\text{tangent}} + (s_x \cdot k_{\text{side}}, 0, 0)

Rolling State

If normal velocity is below threshold, the ball rolls without bouncing:

vrolling=(vxdhoriz,0,vzdhoriz)\vec{v}'_{\text{rolling}} = (v_x \cdot d_{\text{horiz}}, 0, v_z \cdot d_{\text{horiz}})

This simulates friction and sliding on the court surface.

Trajectory Prediction

The trajectory prediction simulates future ball motion for AI and aiming systems. It runs a deterministic physics loop:

for step in range(TRAJECTORY_PREDICTION_STEPS):
    v = ApplyMagnusAndGravity(v, spin, dt)
    v = ApplyAirDrag(v, dt)
    p += v * dt
    
    if p.y < GROUND_LEVEL:
        v = SimulateBounce(v)
        p.y = GROUND_LEVEL
        bounce_count += 1
    
    if |v| < VELOCITY_THRESHOLD:
        break

Each trajectory point records:

  • Position: (x,y,z)(x, y, z) in world coordinates
  • Time: Elapsed time in seconds
  • Bounce Count: Number of ground contacts so far

Prediction Parameters

ParameterValuePurpose
Steps200Maximum trajectory points
Time Step0.016 s (16 ms)Matches 60 FPS refresh rate
Stop Threshold0.01 m/sMinimum velocity to continue prediction

Velocity Calculation for Targeting

When a player executes a stroke, the AI must calculate the required velocity to land the ball at a target position:

v0=(vx,vy,vz)\vec{v}_0 = (v_x, v_y, v_z)

Given:

  • Start position: p0=(x0,y0,z0)\vec{p}_0 = (x_0, y_0, z_0)
  • Target position: pt=(xt,yt,zt)\vec{p}_t = (x_t, y_t, z_t)
  • Z-velocity constraint: vz=(player skill x power)v_z = \text{(player skill x power)}

The algorithm iteratively adjusts vxv_x and vyv_y to match the target:

Δx=xtSimulateTrajectory(p0,v,s).x\Delta x = x_t - \text{SimulateTrajectory}(\vec{p}_0, \vec{v}, \vec{s}).xΔz=ztSimulateTrajectory(p0,v,s).z\Delta z = z_t - \text{SimulateTrajectory}(\vec{p}_0, \vec{v}, \vec{s}).z

Update rule:

vxvx+Δxηv_x \leftarrow v_x + \Delta x \cdot \etavyvy+sign(vz)Δzηv_y \leftarrow v_y + \text{sign}(v_z) \cdot \Delta z \cdot \eta

Where learning rate η=0.2\eta = 0.2 and typically 8 iterations are performed.

Spin Parameter Conventions

The spin vector s=(sx,sy,sz)\vec{s} = (s_x, s_y, s_z) uses the following conventions:

Component Meanings

ComponentRangeEffectExample
sxs_x (Sidespin)-1 to +1Curves ball left (-) or right (+)0.1 = slight right curve
sys_y (Topspin)-15 to +1Topspin (+) creates downward curve, Backspin (-) creates upward curve-10 = strong backspin, 0.8 = topspin
szs_z (Forward Spin)-1 to +1Rotation along forward axis (cosmetic effect)-0.65 = significant forward spin

Physical Interpretation

  • Topspin (sy>0s_y > 0): Ball dips down rapidly, short arc
  • Backspin (sy<0s_y < 0): Ball stays up longer, float feel
  • Sidespin (sx0s_x \neq 0): Ball curves sideways during flight
  • Slice (high sys_y negative + sxs_x): Combination of backspin and sidespin

Spin-Affected Stroke Examples

Forehand Drive

  • Spin: (0.15,0.5,0.3)(0.15, 0.5, 0.3)
  • Power: 25 m/s
  • Result: Forward-moving with topspin curve, slight sidespin

Backhand Slice

  • Spin: (0.2,8.2,0.65)(0.2, -8.2, -0.65)
  • Power: 18 m/s
  • Result: Low arc with strong backspin and sidespin

Serve

  • Spin: (0.3,0.8,0.4)(0.3, 0.8, 0.4)
  • Power: 35 m/s
  • Result: Fast, flat serve with slight sidespin

Drop Shot

  • Spin: (0.1,10.0,1.0)(0.1, -10.0, -1.0)
  • Power: 5 m/s
  • Result: Short, high arc with heavy backspin

Frame Rate Considerations

The physics engine runs at:

  • Simulation Rate: 60 FPS (0.01667 s per frame)
  • Trajectory Simulation: 240 FPS (0.00417 s per step) for finer prediction
  • Physics Time Step: Δt=1/60\Delta t = 1/60 s in gameplay

This ensures smooth motion and accurate collision detection.

Energy Conservation

The ball loses energy through:

  1. Bounce Damping: dvert=0.7d_{\text{vert}} = 0.7 and dhoriz=0.9d_{\text{horiz}} = 0.9 (10-30% loss)
  2. Air Drag: Exponential decay proportional to velocity
  3. Friction: Horizontal damping during rolling

Total energy is never conserved, realistically modeling real tennis ball behavior.