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:
- Copy Lampz from Blood Machines
- Replace
LampTimer_Timersub with version that readsGetInPlayStateBool - In
InitLampsNF, the first object in eachMassAssignindex must be the "Control Light" -- an invisible light used for state reading - Run Blender toolkit export which outputs
VLM Script Helper.vbs - 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 = Falsewhen in VR mode - Use
DMD_Flashersub to readFlexDMD.DmdColoredPixelsand 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 = 1for game start - Use
currentmodevariable (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 = Falsecheck - Property setters cannot use same variable name as property name
Setkeyword 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):
- Solidify modifier -- slightly enlarge model (thickness ~0.0007) to prevent holes
- Remesh modifier -- creates clean topology
- 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:
- Join parts, clean up, merge vertices
- Create a new "bake" texture node (5000x5000) in the first material
- Copy/paste into every other material, keeping node selected
- In Edit Mode, create new UV map, Smart UV Projection
- Bake "Diffuse" with only "Color" checked
- 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:
- Identify GI lights near ball paths (slings, inlanes)
- Assign each to its own
giXXXname in Blender - In VPX, put split lights in their own collection
- 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¶
- Font source: dafont.com
- Generator: SnowB Bitmap Font Generator
- For 128x32 DMD: max font height ~16px for 2 lines, 32px for single line
Sound Preparation Pipeline¶
- Source audio clips with timestamps
- Clean up: remove background noise
- Export to WAV at highest quality
- Normalize to -14 LUFS, trim, convert to OGG