Designing a Trick Shot Level With Ballistics, Not Guesswork
Challenge mode is fifteen handbuilt trick shot levels: blockers, floating rings, angled bank boards, sweeping panels. Level 9 was supposed to be simple on paper. Shoot past the rim, hit an angled bank board, rebound back through the hoop. In practice it was the hardest level to place in the whole set, because “angled board somewhere behind the rim” is not a design, it is a guess, and guesses do not tell you whether a shot is possible before you have spent an afternoon nudging a transform in the editor.
So I stopped guessing and simulated it.
The model
A basketball in flight is a projectile with drag we can mostly ignore at these speeds and distances, plus a bounce. I modeled the ball as a point mass with radius 0.125m, gravity at 9.81 m/s², and a coefficient of restitution of 0.6 for the board contact, which is roughly what a rubber-coated composite ball does against a stiff, close to vertical panel. The unknowns were launch speed and launch angle, the two things a player actually controls with their arm.
Rather than trying to solve the trajectory analytically for an arbitrary board position, which gets messy fast once you fold in the post-bounce arc back to the rim, I swept the parameter space. For a candidate board placement, angle, and distance behind the rim, I ran thousands of simulated launches across a grid of speeds and angles, stepped the ball’s position frame by frame the same way the engine would, checked for the board collision, applied the restitution to get a rebound velocity, and then kept stepping until the ball either passed cleanly through the rim’s cylinder or missed.
Each launch became one pixel in a small heatmap: green if it went in, red if it did not. That heatmap is the real output of this process. A good trick shot placement does not just have a solution, it has a solution with room around it, a visible green region a player can land in without needing frame perfect input. A placement whose only green pixels are a single isolated dot is not a fun level, it is a lockpicking puzzle.
The cliff
Sweeping distance behind the rim as an outer parameter is where the useful discovery showed up. As I pushed the board further back, the green region did not shrink gradually. It held a usable size out to about 2.1m past the rim, and then collapsed almost to nothing over the next few centimeters of distance.
The reason is not subtle once you see it plotted: the rebound has to climb back up to rim height using only the vertical velocity component left over after the bounce takes its cut of the ball’s energy. Past a certain launch distance, the ball simply cannot carry enough vertical speed into the bounce for 0.6 restitution to return it to rim height before gravity pulls it back down past the rim line. There is no clever angle that fixes it, because the constraint is energy, not aim. It is a hard cliff, not a slope, so a placement 1.9m out and a placement 2.3m out look similar in the editor and behave completely differently in play.
What this changed
Knowing the cliff exists changed how I place every board level now. I run the sweep first, before touching the scene, and use the heatmap to pick a distance and angle with a comfortably sized green region, then build the visual dressing (backboard, panel, rim skin) around numbers that are already known to work. The editor is for making it look good, not for discovering whether it is possible.
The simulator itself is a small offline tool, not part of the shipping build. It shares the ball radius, gravity, and restitution values with the Unity project so the two stay honest with each other, but it runs standalone so I can sweep thousands of launches in a couple of seconds instead of playtesting them one at a time in headset.
Fifteen levels in, this is the one process change from Challenge mode I would keep no matter what I build next. Trick shot design is a physics problem wearing a level design costume, and it is much faster to solve when you stop pretending otherwise.