Flight and free-camera movement script for Bully: Scholarship Edition, written in Lua, with an explanation of the direction-vector math that drives it.
The script lets the player fly anywhere in the game world and move the camera independently of the player. It was written to apply ideas about vectors, angles and 3D space from mathematics and physics inside Bully. The project was originally developed by derpy54320, who worked out and applied the mathematical approach in the game; I (Mahdi, known as Fibonacci) rewrote the equations and the code and wrote this explanation.
source.lua contains the main loop, the two direction helpers and the class and curfew callbacks of the game's STimeCycle script, so it can be compiled in place of that script.
- Double-tap the zoom-out button to turn flight mode on or off.
- In flight the player ignores gravity, holds a flying animation and moves on all three axes relative to the camera heading.
- The camera orbits behind the player at an adjustable distance; a second toggle detaches it as a free camera.
- Hold sprint for a 7x speed boost, press melee to fire a projectile along the view direction, and press jump to show a position readout.
| ID | Game action | Role in this script |
|---|---|---|
| 3 | Zoom out | Release twice within 500 ms to toggle flight mode |
| 16, 17 | Movement axes | Move sideways and forward relative to the camera heading |
| 12, 10 | Weapon fire, Lock on | Move up and down |
| 7 | Sprint | Hold to multiply movement by 7 |
| 18, 19 | Camera axes | Turn the camera heading and tilt the pitch |
| 0, 1 | Show secondary tasks, Show tasks | Increase and decrease camera distance |
| 9 | Grapple | Toggle the free camera |
| 6 | Melee attack | Fire a projectile |
| 8 | Jump | Toggle the position readout |
The IDs are the game's control actions (derpy54320's controls reference); the physical key or gamepad button is whatever the game binds to that action.
A vector is a quantity with a magnitude and a direction, usually drawn as an arrow.
F_GetForwardDir2D(h, dist) turns a heading h (radians) and a distance into the x and y components of that vector:
function F_GetForwardDir2D(h,dist)
return -math.sin(h)*dist,math.cos(h)*dist
endThe script calls it twice per frame: once with the camera heading and the forward stick value, and once with the heading rotated a quarter turn (ch + math.pi / 2) and the sideways stick value. Adding both vectors to the player position gives movement that is always relative to where the camera looks. Vertical movement is added directly to z.
F_GetForwardDir3D(p, h, dist) adds a pitch p:
function F_GetForwardDir3D(p,h,dist)
return math.cos(p)*-math.sin(h)*dist,math.cos(p)*math.cos(h)*dist,math.sin(p)*dist
endThe horizontal part is the 2D formula scaled by cos(p) and the vertical part is sin(p), giving a vector of length dist along the view direction. The camera offset is F_GetForwardDir3D(cp, ch, -dist * 0.04): the distance is negative because the camera sits behind the player. The camera is placed at the player position plus this offset, raised by 1.5 units, and looks at the player. The free camera reuses the same vectors to move itself instead of the player.
Trigonometry is used because it converts angles and magnitudes into direction vectors, a standard technique in game development.
- Bully: Scholarship Edition on PC.
- Compile
source.luawith the Bully build of the Lua 5.0.2luac(available from the Bully Scripting Docs) asSTimeCycle.lurand place it inScripts/Scripts.img, or load it with Derpy's Script Loader from its_derpy_script_loader/scriptsfolder.
- derpy54320: developed the original project and applied the mathematical concepts in Bully; author of the Bully LUA Reference.
- Mahdi (Fibonacci): rewrote the equations and the code, and wrote this explanation.