Arc 1 · Make it fly / Tutorial 01 of 16
By the end of this page you will have a script that counts down, lifts off, tips over once, and shuts down at a target apoapsis. It will be crude — that is deliberate. Every tutorial after this one earns its keep by beating it.
Everything below is what you need for this tutorial and nothing more. Skim it, don't memorise it — you'll come back.
WAIT 0. yields one physics tick.x.az = compass bearing (90 = east). pitch = degrees above the horizon, so 90 is straight up.This is the single idea that makes kOS click. Get it now and the next fifteen tutorials are mostly arithmetic.
SET evaluates the right-hand side immediately and stores the resulting number. LOCK stores the expression itself and re-runs it every time the value is read. Steering and throttle are read many times per second, so a locked expression becomes a live control law.
// Run this on the pad and watch the difference. SET frozen TO SHIP:ALTITUDE. // a number, captured once LOCK live TO SHIP:ALTITUDE. // an expression, re-read forever PRINT frozen. // e.g. 76 PRINT live. // e.g. 76 WAIT 10. // (go fly around, or just let time pass) PRINT frozen. // still 76 — it was never anything but a number PRINT live. // whatever the altitude is right now
So this is a control law that works:
LOCK STEERING TO HEADING(90, 90 - SHIP:ALTITUDE / 500).
…and this one points at a fixed direction forever, because the subtraction happened once, on the pad, when altitude was about 76 m:
SET STEERING TO HEADING(90, 90 - SHIP:ALTITUDE / 500).
A kOS program runs top to bottom and then quits. Your rocket is only under script control for as long as the script is still executing — so the script has to stay alive for the whole ascent.
Two ways to stay alive. WAIT UNTIL blocks silently until a condition flips:
WAIT UNTIL SHIP:ALTITUDE > 1000.
An UNTIL loop stays alive and lets you do work each pass — printing telemetry, checking several conditions, changing the control law:
UNTIL SHIP:APOAPSIS > 75000 { PRINT "ALT " + ROUND(SHIP:ALTITUDE) + " " AT (0,4). PRINT "AP " + ROUND(SHIP:APOAPSIS) + " " AT (0,5). WAIT 0. // yield one tick — never spin a loop without this }
WAIT in it burns the whole instruction budget every tick and makes the game stutter. WAIT 0. gives up the rest of the tick. Put one in every loop you write.
The trailing spaces in those PRINT … AT strings aren't a typo. Printing at a fixed position overwrites characters but doesn't clear the rest of the line, so 1000 replacing 10000 leaves a stray digit behind.
FROM {LOCAL t IS 5.} UNTIL t = 0 STEP {SET t TO t - 1.} DO { PRINT "T-MINUS " + t. WAIT 1. }
One number decides whether you leave the pad at all, and it isn't thrust. Work this out yourself before opening the panel.
Thrust-to-weight ratio is thrust divided by weight — and weight is mass times the local gravitational acceleration, not a constant:
Kerbin: g0 = 9.81 m/s², R = 600 000 m
1 — Vertical acceleration. Two forces act along the vertical: thrust up, weight down.
Now substitute F = TWR · m g and the mass cancels:
Read that carefully: TWR = 1 gives zero acceleration. You hover, burning fuel, going nowhere. Below 1 you don't move. This is why a launch clamp release at TWR 1.05 feels so awful.
2 — Time to 100 m/s. a = 9.81 × (1.6 − 1) = 5.886 m/s². Then t = v/a = 100 / 5.886 ≈ 17.0 s, and you'd have covered ½at² ≈ 850 m doing it.
3 — Why TWR climbs anyway. The numerator is fixed but the denominator shrinks for two independent reasons:
Mass is by far the bigger effect during a first stage. But there's a third thing you may have written down that isn't a TWR change at all: on Kerbin, most engines get more thrust as ambient pressure drops, so F also rises. Give yourself credit for that — SHIP:AVAILABLETHRUST already accounts for it, which is exactly why we read it live rather than hardcoding a number.
Write it yourself. You have every command you need above — nothing new is required.
MECO, and hand steering back to the player.launch1.ks in Ships/Script/ and run it with RUN launch1.
Requirement 5 says "hold it". Requirement 7 says "hand steering back". Think about what each one means for how your script is structured — and about what happens between them if you use WAIT UNTIL for step 6.
If your script works and looks nothing like this, that's fine — compare the reasoning, not the characters.
// launch1.ks — fixed pitch-over ascent. Crude on purpose. CLEARSCREEN. SET targetAp TO 75000. SET turnAlt TO 1000. SET azimuth TO 90. FROM {LOCAL t IS 5.} UNTIL t = 0 STEP {SET t TO t - 1.} DO { PRINT "T-MINUS " + t AT (0,0). WAIT 1. } SAS OFF. RCS OFF. SET pitch TO 90. LOCK THROTTLE TO 1. LOCK STEERING TO HEADING(azimuth, pitch). STAGE. PRINT "LIFTOFF " AT (0,0). // --- vertical climb --- UNTIL SHIP:ALTITUDE > turnAlt { telemetry(). WAIT 0. } // --- one crude pitch-over, then hold --- SET pitch TO 80. PRINT "PITCH PROGRAM " AT (0,0). UNTIL SHIP:APOAPSIS > targetAp { telemetry(). WAIT 0. } // --- MECO --- LOCK THROTTLE TO 0. PRINT "MECO " AT (0,0). WAIT 1. UNLOCK STEERING. UNLOCK THROTTLE. SAS ON. FUNCTION telemetry { PRINT "ALT " + ROUND(SHIP:ALTITUDE) + " " AT (0,2). PRINT "AP " + ROUND(SHIP:APOAPSIS) + " " AT (0,3). PRINT "SPD " + ROUND(SHIP:VELOCITY:SURFACE:MAG,1) + " " AT (0,4). PRINT "PITCH " + pitch + " " AT (0,5). }
Trap one — the pitch variable. Steering is locked to HEADING(azimuth, pitch) once, before liftoff, and never re-locked. Yet changing pitch later with a plain SET steers the rocket. That's the whole lesson of this page: LOCK captured the expression, so it re-reads whatever pitch currently holds. This is the pattern every remaining tutorial builds on — one lock at the top, a value that evolves underneath it.
Trap two — the loop, not the wait. Requirement 6 says keep printing throughout. WAIT UNTIL SHIP:APOAPSIS > targetAp. would satisfy requirements 5 and 7 perfectly and leave you staring at a frozen display for four minutes. Blocking waits are for moments; loops are for phases.
And the UNLOCK at the end matters more than it looks. Without it the script ends with steering still locked, kOS relinquishes control mid-command, and the ship tumbles. SAS ON after unlocking hands you a stable vessel.
FUNCTION can be declared after the code that calls it. Keeping helpers out of the flight sequence keeps the sequence readable — you'll have a lot more of both by Tutorial 03.Quicksave first. Each of these teaches something the working version hides.
SET STEERING TO HEADING(azimuth, pitch). and fly it. Where does the rocket point, and why that direction specifically?UNLOCK STEERING. line. Watch what happens the instant the script ends.WAIT 0. from the ascent loop. Watch your framerate, then check the terminal for an instruction-limit message.STAGE. to before the throttle lock. Explain the delay you see.