Celestia/Celx Scripting/Simple CELX Scripting

From Wikibooks, open books for an open world
Jump to: navigation, search

Read on to learn about simple CELX scripting...

If you have comments or questions, please put them on the Celx Questions and Answers page.

Introduction[edit]

This is a tutorial on simple CELX scripting. It is based on the philosophy that the best way to learn is by example, so there are lots of examples. Each example is usually a small step beyond the previous one, but understanding it may depend on any previous example, so make sure you understand each example completely before you go on to the next.

Getting Started[edit]

To get started, you should create a script file for testing purposes. You could call it "test.celx" or something similar. Be sure it has the ".celx" file type suffix so it will be recognized as a Celestia CELX script file. Then you should be able to just double-click on the file icon to cause Celestia to run the script. This is a lot faster than running the script from the Celestia menus. You can keep both Celestia and your text editor running; when you're ready to test your script just save your changes and double-click the file icon. The Celestia window should pop to the front and run your script. After you've observed the result you can return to the editor window to make further changes in the script. And don't overlook the fact that you can cut and paste code directly from the tutorial examples!

Starting Simple[edit]

Here's a simple CELX script:

celestia:flash("Hello, Universe!")
wait(2.0)

When you run this script, the message "Hello, Universe!" will briefly appear in the lower left corner of the Celestia window. The script instructions here are very simple. First, we asked the object named "celestia" to perform its function named "flash" to display the message "Hello, Universe!". Then we asked the script to wait for 2 seconds (so we'd have time to see the message).

The format of our request to the object named "celestia" to perform its function named "flash" is basic to Lua. First we wrote the name of the object, in this case, "celestia". Then we wrote a colon (:) to indicate that we were requesting the object to perform a function. Then we wrote the name of the function we want the object to perform, in this case, "flash". Finally, we wrote the parameters to the function, in this case the string "Hello, Universe!", enclosed in parentheses. (Note that we didn't use quotes around the name of the object or the function, just the parameter.) Other requests to objects to perform a function have a similar format.

Here's another example:

celestia:flash("Hello, Universe!")
wait(2.0)
celestia:flash("Goodbye, Universe!")
wait(2.0)

In this example we've made two requests to the object named "celestia". Both requests asked the object to perform the same function (the one named "flash"), but we specified different message strings as the parameter to the function in each, causing the object to evaluate the function differently in each case, displaying a different message.

A Simple Function[edit]

Now let's write our own function:

function flash(message)
   celestia:flash(message)
   wait(2.0)
end

flash("Hello, Universe!")
flash("Goodbye, Universe!")

In this example we defined our own "flash" function, to include the 2 second wait. Then we asked the script to perform our new function twice, using a different string each time.

Our new function is named "flash", but it's different from the function of that name which belongs to the object named "celestia". Instead, the new function belongs to our script. When we ask the script to perform our "flash" function, we don't need to write the name of an object or a colon (:). We just write the name of the function, followed by the parameter enclosed in parentheses.

But note that when we defined our new function, we did use the object name and colon, because there we do want to ask the object named "celestia" to perform its function named "flash".

The format of a function definition is another Lua basic. First we write the keyword function. Then we write the name of the function, in this case "flash". Then we write the name of the parameter, enclosed in parentheses. In this case, the parameter is named "message". Then we write the sequence of instructions that the script will follow to perform the function. Finally, at the end of the function, we write the keyword end. (Note that we didn't use quotes around the name of the function or its parameter. We only need quotes for literal strings, not for names.)

In this case, the instructions needed to perform our function are to ask the object named "celestia" to perform its function named "flash" using our function's parameter named "message" as the parameter to the object's function. Then we asked the script to wait for 2 seconds.

Note that first we defined our function, then we called it (twice). (When we say "called", we mean that we asked the script to perform the function.) When we defined the function, we named its parameter "message". And we referred to that name in the instructions we used to specify how the function will be performed. Then each time we called the function, we specified a particular string to be the "message" which the script used when performing the function.

But you can also uses names rather than actual strings in a function call, as in the following example

function flash(message)
   celestia:flash(message)
   wait(2.0)
end

flash("Hello, Universe!")
farewell = "Goodbye, Universe!"
flash( farewell)

In this example, we first gave the name "farewell" to the string "Goodbye, Universe!". Then we asked the script to perform our "flash" function using that name, rather than the string itself. The syntax of the instruction by which we gave the name to the string is another Lua basic. It consists of the name, in this case "farewell", followed by an equals sign (=) to indicate that we're assigning the name to what follows, followed by the actual string. The association of a name with a value in this way is called a variable assignment. The reason it's called a variable is that we can later assign the same name to a different value if we choose to. We can also assign a different name to the value. In that case, the value has more than one name. (The parameter name in our function definition is not a variable, strictly speaking, but works very much like one.)

By the way, there's another, slightly different format for function definitions, which we'll use in the next example:

flash =  function (message)
             celestia:flash(message)
             wait(2.0)
            end

flash("Hello, Universe!")
farewell = "Goodbye, Universe!"
flash( farewell)

In the format we've used in this example, the name of the function doesn't come after the function keyword. Instead it comes first, followed by an equals sign (=). Next comes the function keyword, followed directly by parameter enclosed in parentheses. Then comes the sequence of instructions that define the function, ending with the end keyword. The only difference from the way we defined the function previously is the position of the function name, and the addition of the equals sign.

If this new format for a function definition looks a lot like a variable assignment, it's no coincidence: that's exactly what it is. We're simply giving a name to a function, just the way we gave a name to a string in the previous example. We will return to the very important implications of this fact later on. Note that it doesn't matter which format you use. Their meaning is identical.

From this point on, for the sake of brevity, our examples will assume that we've already defined our flash function as in one of the previous examples.

More Examples[edit]

Now let's try using another function provided by the object named "celestia":

time = celestia:gettime()
flash(time)

In this example, we asked the object named "celestia" to perform its function named "gettime", and we gave the name "time" to the result (a number) that the object returned to us. Then we asked script to perform our "flash" function, using the number named "time" as the "message" parameter.

No parameters were needed for the object named "celestia" to perform its function named "gettime", so the parentheses following the function name are empty.

The syntax of the instruction by which we gave the name to the result of this function request is another Lua basic. It consists of the name, in this case "time", followed by an equals sign (=) to indicate that we're assigning the name to what follows, followed by the request to the object to perform the function (and return its result).

When you run this script, you'll see a large number briefly displayed. This number is the current time setting in Celestia, expressed as a Julian date. (it's the number of days since the beginning of the Julian epoch).

In the next example, we'll make this clear:

time = celestia:gettime()
timeMessage = "The current time is " .. time .. " (Julian date)."
flash(timeMessage)

More To Come...[edit]

WARNING: Construction area ahead! Proceed at your own risk!


In the next example, we'll convert this to a more recognizable form:

Fortunately, the celestia object has a function to help us do that:

jdate = celestia:gettime()
flash(jdate)
date = celestia:fromjulianday(jdate)
flash(date.year)

In this example, we asked the celestia object to give us the time, then asked it to give us the result of converting that Julian date to a calendar date, and finally asked the script to use our "flash function" to display the part of the date that indicates the year.

To display the full date, we'll need to join the parts of the date for the year, month and day:

jdate = celestia:gettime()
flash(jdate)
date = celestia:fromjulianday(jdate)
dateString = date.year.."/"..date.month.."/"..date.day
flash(dateString)

The following additional examples await explanation. In the meantime, you're invited to see if you can figure them out on your own!

 saturn = celestia:find("Saturn") -- finds object named Saturn
 radius = saturn:radius() -- gets radius of Saturn
 flash("The radius of Saturn is "..radius.." km.")

 flashRadius = function (bodyName)
                          body = celestia:find(bodyName) -- finds object 
                          radius = body:radius() -- gets radius
                          flash("The radius of " .. body:name() .. " is " .. radius .. " km.")
                       end
      
 flashRadius("Jupiter")

 jupiter = "Jupiter"
 flashRadius(jupiter)

 reallyBigPlanet = "Jupiter"
 flashRadius(reallyBigPlanet)

 selectedPlanet = celestia:getselection() -- gets the planet currently selected by the user
 flashRadius(selectedPlanet:name())

 flashRadius = function (body)
                         if type(body) == "string" then
                             body = celestia:find(body) -- finds object 
                         end
                         radius = body:radius() -- gets radius
                         flash("The radius of " .. body:name() .. " is " .. radius .. " km.")
                       end
 selectedPlanet = celestia:getselection()
 flashRadius(selectedPlanet)

In this example, we've defined a function to flash a message reporting the radius of a specified planet. Then we called the function twice, to display the radius of two different planets. This should all look familiar to you from previous examples. (In future examples, you should assume our flashRadius function is already defined.) In the next example, we'll do the same thing just a bit differently:

 planetList = { "Uranus", "Neptune" }
 flashRadius(planetList[1])
 flashRadius(planetList[2])

 planetList = { "Earth", "Moon", "Mars" }
 for index = 1,3 do
    flashRadius(planetList[index])
 end

 planetList = { "Earth", "Moon", "Mars",  "Venus",  "Mercury" }
 for index,name in ipairs(planetList) do
    flashRadius(name)
 end

selectedObject = celestia:getselection()
flashRadius(selectedObject:name())

cassini = celestia:find("Cassini") -- finds object named Cassini
saturn = celestia:find("Saturn") -- finds object named Saturn
cassiniPosition = cassini:getposition() -- gets position of Cassini
saturnPosition = saturn:getposition() -- gets position of Saturn
distance = cassiniPosition:distanceto(saturnPosition) -- gets distance from  position of Cassini to that of Saturn
distance = distance-saturn:radius() -- adjusts for distance to surface
flash("Current distance from Cassini to Saturn's surface is "..distance)

This one is a little more fun:

-- tour the moons of Saturn
 obs = celestia:getobserver()
 planet = celestia:find("Saturn")
 obs:goto(planet)
 wait(5)
 flash("Welcome to "..planet:name())
 moons = planet:getchildren()
 for index,body in ipairs(moons) do
    if body:type() == "moon" then
        obs:follow(body)
        obs:goto(body)
        wait(5)
        flash("Welcome to "..body:name())
        wait(1)
    end
  end 

-- tour the stars at random
obs = celestia:getobserver() 
stars = celestia:getstarcount() 
for i = 1,stars do 
   j = math.random(stars) 
   obj = celestia:getstar(j) 
   obs:goto(obj,20) 
   while obs:travelling() do wait(0.5) end 
   celestia:flash("Welcome to "..obj:name(),2) 
 end 



back to Celestia/Celx_Scripting
back to Celestia