Skip to content

SpongeBob SquarePants

SpongeBob's Bikini Bottom Pinball is a VPW premium original table built on top of Skillshot's original (based on JPSalas AFM code). It features a full FlexDMD scoring system, Lampz light integration, extensive 3D toy modeling, and support for VR, standalone (iOS/Android), Scorbit, and DOF. Development spanned 95+ internal versions with a large collaborative team.

Build Notes

Development Architecture

The table was built as a team project with specialized roles:

  • gedankekojote97: Project lead, 3D modeling, Blender toolkit baking
  • apophis79: Lampz integration, VLM scripting, toolkit code merges
  • AstroNasty: Playfield art, backglass art, DMD pixel art, cabinet art
  • oqqsan: FlexDMD programming, game rules/scripting, spinner mechanics, mystery mode, wizard mode
  • flux5009: Light state controller, GI control, flasher modulation
  • flupper1: Mesh optimization for performance

Workflow: Blender bake > export toolkit results > apophis merges VLM scripts > flux adds light controller > oqqsan adds FlexDMD/game logic > testing cycle.

File Size Optimization

File size reduction strategies:

  • Convert sounds to OGG (better than MP3, supported by VPX) -- keep Fleep physics sounds as WAV
  • Remove unused images from the table
  • Music files can be externalized to a separate folder
  • Convert images to WebP where possible
  • Reduce toy model texture resolution (1024x1024 sufficient for small objects)
  • Decimate 3D model poly counts aggressively

Physics Tuning

Recommended settings for natural ball feel:

  • Playfield friction: 0.15
  • Flipper strength: 2500
  • Playfield slope: 6 degrees max
  • Add slow-down code on inlanes when ball comes down ramps
  • Ensure tilt mechanism works with nudge

Toolkit Movable Collection Requirements

Objects in the Movable collection need:

  • Correct "sync" property value
  • Names matching VPX objects (no spaces -- toolkit replaces with underscore)
  • Correct origin point (wrong origin causes bad animation)
  • Gates need proper naming matching VPX gate objects
  • Side rails go in Movable collection for show/hide in CabMode
  • Do not put rubber bands in dSleeves collection

Version Management

Observed across 95+ versions:

  • Sequential version numbers in filename (e.g., _WIP_1.095.vpx)
  • Change comments at top: ' 095 - apophis - description of changes
  • One person works on script at a time to avoid merge conflicts
  • Blender bakes handed to script integrator as RAR packages
  • DMD assets managed separately in Dropbox

Scripting

Lampz "Control Light" Method

Step-by-step method to integrate Lampz with an original (non-toolkit) table:

  1. Copy Lampz from Blood Machines
  2. Replace LampTimer_Timer sub with version that reads GetInPlayStateBool
  3. In InitLampsNF, the first object in each MassAssign index must be the "Control Light" -- an invisible light used for state reading
  4. Run Blender toolkit export which outputs VLM Script Helper.vbs
  5. Set control lights invisible via script: .visible=0
Sub LampTimer_Timer()
    dim idx : for idx = 0 to 150
        if Lampz.IsLight(idx) then
            if IsArray(Lampz.obj(idx)) then
                dim tmp : tmp = Lampz.obj(idx)
                Lampz.state(idx) = tmp(0).GetInPlayStateBool
                Lampz.SetColor(idx) = tmp(0).colorfull
            Else
                Lampz.state(idx) = Lampz.obj(idx).GetInPlayStateBool
                Lampz.SetColor(idx) = Lampz.obj(idx).colorfull
            end if
        end if
    Next
    Lampz.Update1
End Sub

Lampz Index Ranges

  • Index 0: GI
  • Index 1-50: Inserts (named L01, L02, etc.)
  • Index 51-149: Flashers (named F51, F52, etc. -- must use unique numbers)
  • Index 150: Reserved for Lit Room

The toolkit uses the number portion of the name to place lightmaps in the correct Lampz index.

Light State Controller

The light state controller provides advanced flasher effects with PWM modulation:

lightCtrl.PulseLight f3, 3  'Pulse f3 with default PWM Profile, Repeat 3 times
lightCtrl.PulseLightWithProfile f3, Array(30,67,100,10,100,40,0), 0  'Custom PWM

Works on any light, not just flashers. Can be retrofitted onto existing tables.

FlexDMD on VPX Flasher for VR

FlexDMD can render directly to a VPX flasher object without opening an external window:

  • Set FlexDMD.Show = False when in VR mode
  • Use DMD_Flasher sub to read FlexDMD.DmdColoredPixels and render to flasher
  • Unchecking "Capture External DMD" in VPVR settings improves brightness and performance

B2S Conditional Loading

Prevent crashes when B2S server is unavailable:

Dim Controller
Sub startcontroller
    If vrroom < 1 Then
        Set Controller = CreateObject("B2S.Server")
        Controller.B2SName = "Spongebob"
        Controller.Run
        B2SOn = True
    End If
End Sub

Options DMD System

An in-game options overlay using FlexDMD runs on the regular DMD display. Features include volume controls, high score reset with confirmation, and LUT selection. Access via magna buttons. The options DMD pauses the regular display while open.

Scorbit Integration

Key integration points:

  • Check StartGame = 1 for game start
  • Use currentmode variable (0=none, 1-9=modes, 11=multiball, 20=wizard1, 21=wizard2)
  • Mode string format: GameModeStr="NA{yellow}:TIKI"
  • Always null-check: If Not IsNull(Scorbit) Then ...

DOF Event IDs

E101-E102: Flippers (0/1 toggle)
E103-E109: Slingshots, bumpers (pulse, value 2)
E111: Ball release/drain
E112-E118: Target groups
E120-E125: GI, start button, plunger lane, ball save
E126-E130: Spinners, gates, extra ball, level finish
E131: Multiball (0/1 toggle)
E132: Jackpot
E141-E147: Individual flashers

Standalone Compatibility

Key changes for VPX standalone (iOS/Android):

  • Replace Array(x, x, x) patterns with proper Class objects
  • Add If Controller Is Nothing Then B2SOn = False check
  • Property setters cannot use same variable name as property name
  • Set keyword not needed for Dictionary object assignments
  • Linux is case-sensitive for font file paths

3D & Art

Reducing High-Poly Models

Flupper's modifier stack approach (100k+ tris to 2k tris):

  1. Solidify modifier -- slightly enlarge model (thickness ~0.0007) to prevent holes
  2. Remesh modifier -- creates clean topology
  3. Decimate modifier -- further reduces count (ratio 0.5 for aggressive, 0.1 for extreme)

After applying: create manual seams, UV unwrap, bake high-poly to low-poly (1024x1024 sufficient for small toys). SpongeBob character went from ~100k to 1834 tris with acceptable quality at 4K.

Baking Multi-Material Models

To convert multi-material 3D models to a single texture for VPX:

  1. Join parts, clean up, merge vertices
  2. Create a new "bake" texture node (5000x5000) in the first material
  3. Copy/paste into every other material, keeping node selected
  4. In Edit Mode, create new UV map, Smart UV Projection
  5. Bake "Diffuse" with only "Color" checked
  6. Delete the first UV map

Backglass Format

  • 16:9 aspect ratio for both backglass image and full image including speaker grill
  • Provide 2-screen and 3-screen versions in 4K
  • For VR: standard DMD aspect ratio, backbox can be shorter

GI Light Splitting for Ball Shadows

To enable ray-traced ball shadows, GI lights need splitting so each casts different shadows:

  1. Identify GI lights near ball paths (slings, inlanes)
  2. Assign each to its own giXXX name in Blender
  3. In VPX, put split lights in their own collection
  4. Not every GI light needs splitting -- only those near the ball

Troubleshooting

File Size Bloat from Meshes

The table went from 351MB to 525MB despite images/sounds totaling less. Cause: mesh triangles jumped from ~2.5M to ~10M. Key offenders were character toys. Solution: Flupper's modifier stack brought the table to 287MB.

8K Nestmaps

8K nestmaps caused 600MB file size, flipper mesh mapping issues, severe stuttering, and visual glitches. Stick to 4K nestmaps until VPX 10.8.

Negative Scale on Mirrored Primitives

Mirroring objects in Blender can give exported primitives a -1 scale in VPX, causing see-through rendering and wrong normals. Fix: apply scale in Blender before export (Ctrl+A > Apply Scale). Better approach: create two separate models.

.interval vs .blinkinterval

Using .interval on a light object causes a runtime crash. The correct property is .blinkinterval.

VR Performance Degradation

Progressive frame rate degradation (smooth at start, unplayable by 4th mode) was caused by FlexDMD logging overhead. Updating FlexDMD.log.config resolved it.

High Score Overflow

Scores exceeding ~2.4 billion cause the game to fail to load. The CLng function overflows at large values. Fix: change CLng to Int in high score loading code.

Pale Playfield Flashing in GL Build

Triggered when ball goes above playfield in OpenGL mode. Fixes: disable Max Ambient Occlusion, ensure AO and SCSP are off as default, set Max Reflection Mode to "Balls Only."

Ball Save on Scoop Ejects

Scoop ejects can cause unfair drains when the ball hits the back of the left flipper. Add a 2-3 second ball save on scoop eject. Reference: Rush (Stern) has ball save on scoop eject by default.

Drain Kicker Detection

If the ball misses the drain kicker, ball save does not trigger. Fix: increase kicker radius and reduce hit accuracy (from 1.0 to 0.7).

Game Knowledge

Mode Progression

  • Mode Qualification: Each ramp/bank/lane corresponds to a mode. Three shots qualifies that mode.
  • Mode Start: First qualified remaining mode starts next. Must score at least one jackpot before starting the next mode.
  • Jackpot Levels: 3 jackpots during a mode earns mega jackpot + permanent JP level increase. 5 jackpots earns additional JP level + bonus.
  • Hurry-up: 2x JP level + 25M points.

Mystery Mode

Activated by ramp hits to scoop (starts at 2 ramps, caps at 5):

  • 25% small points (500k), 15% medium points (1m), 12% jackpot
  • 7% each: locked ball, +1 second ball save, instant ball save, extra ball letter, advance JP
  • 3% each: extra ball, quick multiball

Wizard Modes

  • Wizard Mode 1: After 9 modes, 240 switch hits. Can complete with one ball. Add-a-ball via ramp shots.
  • Wizard Mode 2: After completing all modes twice. Single ball, two shots per mode.

Multiball

Open pineapple bank, lock ball for super jackpot. Each subsequent SJP requires one additional shot first. Add-a-ball requires 3 ramps first time, then +1 each time.

Spinner Mechanics

  • Outside mode: Spin count tracks to levels (20 spins for first level, +5 per completed). Each level increases value.
  • Inside mode: Hits start a timed hurry-up that increases all jackpots by 500k.

Ball Save

Completing two rollovers above pop-bumpers permanently increases ball save by 1 second. Starts at 9 seconds, max 18 seconds. Two outlane saves per player, reset on new multiball or wizard mode.

Resources

Software Requirements

  • VPX 10.8 beta 5 or later
  • FlexDMD 1.9 (must be the release version of 1.9.0.0)
  • Freezy external DMD 2.0 or later

FlexDMD Bitmap Fonts

Sound Preparation Pipeline

  1. Source audio clips with timestamps
  2. Clean up: remove background noise
  3. Export to WAV at highest quality
  4. Normalize to -14 LUFS, trim, convert to OGG