Running Lua from the Command Prompt

Most game developers test their Lua scripts from the command prompt before plugging them into a game—to verify that the script runs as expected without bugs. You can use the Lua interpreter to do that. The Lua binaries include lua5.1.exe (the interpreter) and luac5.1.exe (the script bytecode compiler). To simplify running the programs from the command line, I recommend renaming them to lua.exe and luac.exe. lua.exe (as I will refer to it from now on) is the interpreter, and you will use it to run Lua script files. luac.exe (as it will be known from now on) is the compiler, which converts a script into a bytecode file. You do not need to compile a Lua script in order to run it. You may compile it into bytecode if you don’t want anyone to edit or copy your scripts when you release a program to the public.

Let’s run a script from the command prompt. First, open a command prompt. In Windows, click Start, All Programs, Accessories, Command Prompt. Optionally, you may click Start, Run and type cmd into the text field (then hit Enter).

The first thing you need to do is change the current directory to where your project files are or will be stored. All of the script files demonstrated in this chapter are available on the book’s CD-ROM in the .sourcesch12sample scripts folder.

You can use the cd command to change the current directory. You can type in absolute paths (such as C:Program FilesMicrosoft Visual Studio 2005) as well as relative paths (such as .projects). The goal is to get into the directory where you have extracted the Lua interpreter (lua.exe).

Advice

After you type in a command, such as cd, when you are about to type in a directory or file name, you may type in the first few letters and hit the Tab key (once or multiple times) to cycle through the files and/or folders that begin with the characters you have entered.


If you do not know where the files are located, try copying everything from sourcesch12sample scripts into the root folder of C: or in a subfolder called C:Advanced2D (just as an example). Once you have used cd to reach the folder where the files are located, you can proceed.

Command-prompt programming is how users and programmers used to interact with the operating system in the old days, and it was not user friendly or easy to learn. Most Linux gurus still prefer their beloved shell rather than a fancy GUI because typing is usually much faster than clicking and moving a mouse. Here’s a helpful command:

dir

This gives you a listing of the current directory. But you can also pass a folder name to dir to list the contents of any folder on your system. Figure 12.1 illustrates.

Figure 12.1. Using the command prompt to navigate the file system.


Text Output

Before running the Lua interpreter, we need a script file. You can use Notepad, but while we’re in the command prompt, let’s do it the keyboard way. Type this:

notepad hello.lua.

Notepad will open with a new file called hello.lua, ready for your use. If the file already exists, Notepad will open the file. Here’s a sample script you can type into the hello.lua file:

-- My first Lua program
print("Hello World!")
print("Version: ",_VERSION)

Let’s verify that the script file has been created by using another useful command:

type hello.lua

Figure 12.2 shows the output.

Figure 12.2. Viewing the contents of a file at the command prompt.


Advice

If you are serious about Lua script programming, you will need a better editor. I recommend Notepad++, a free editor that provides colored syntax highlighting and a tabbed interface. Notepad++ is provided on the CD-ROM and is also available for download at http://notepad-plus.sourceforge.net.


The Lua interpreter is so small that you can just copy it into your current project folder so that you can run it from the command line to test your script files. (Just be sure to copy the lua5.1.dll library file with Lua.exe since it’s a dependency.)

Advice

If you run Lua.exe without a script file, it will go into command mode. Just press Ctrl+C to exit.


Assuming you’re in the right folder, you have copied lua.exe and lua5.1.dll into that folder, and you have created your first script file (whew!), let’s give it a test run:

lua hello.lua

Figure 12.3 shows the output from the hello.lua program.

Figure 12.3. Running the hello.lua script program.


Variables

Let’s talk about how Lua handles variables. In Lua, everything is an object. But since “object” is such a generic term these days, to be more specific, Lua uses a dictionary-style container for all variables (where a dictionary stores each data item with a lookup value). Here is an example script showing how variables are declared and used.

-- Doing variables in Lua
--simple variables
myinteger = 100
mydouble = 3.1415926535
mystring = "some string"
print("myinteger = ", myinteger)
print("mydouble = ", mydouble)
print("mystring = ", mystring)

--complex variable (table)
Person = {
  age = 30,
  name = "John"
}
print("Person's name: ", Person.name)
print("Person's age: ", Person.age)

This program produces the following output:

myinteger =     100
mydouble =     3.1415926535
mystring =     some string
Person's name:     John
Person's age:     30

As you can see from the output, when using the print() function, multiple parameters can be separated by a comma, which inserts a tab character into the output stream. If you want to just append text, you must use Lua’s unusual text concatenation operator, .. (double dot), like so:

print("mystring = " .. mystring)

Random Numbers

Lua has a weak random-number generator because it cannot be easily initialized with a random seed. Although Lua provides a function called math.randomseed(), it does not work properly because Lua does not provide an epoch-based millisecond timer. Lua initializes its timer when the script begins to run, which means the best we can do is send 0 to math.randomseed().We need to mix up the random-number generator (math.random()) with some large numbers and the use of modulus (math.mod()) to produce a pseudo-random result. It’s messy. But it can be put into a reusable function.

function gen_random(max)
    local temp = math.mod((42 * math.random() + 29573), 139968)
    local ret = math.mod( ((100 * temp)/139968) * 1000000, max)
    return round(ret + 0.5)
end

This function has a dependency—a function called round. Here is the round function (which supports rounding to any number of decimal places):

function round(num, places)
  local mult = 10^(places or 0)
  return math.floor(num * mult + 0.5) / mult
end

Those two functions work pretty well to produce a random number up to the passed maximum value parameter. But more often than not, we need to generate a random number within a fixed range (such as 100 to 150). That’s easy enough:

function random_range(min,max)
  return gen_random(max-min) + min
end

We can now create random numbers with pretty good consistency. Here is an example:

math.randomseed( os.time() )
output = ""
for n = 1,10 do
  a = random_range(1,1000)
  output = output .. tostring(a) .. ","
end
print(output)

The output from this program is a list of 10 random numbers in the range of 1 to 1,000:

63,52,806,319,700,189,617,981,852,15,

Arrays and Tables

Lua variables can contain complex data types, similar to a C++ struct (and even a class through some convoluted code). To define a table, set a variable name equal to an empty set of brackets:

grades = {}

After defining the table, you can fill it in with data like so:

grades[1] = 90.5
grades[2] = 78.3
grades[3] = 85.8
grades[4] = 76.2
grades[5] = 68.1
grades[100] = 50.3
grades[200] = 100.0

Did you notice that the last two elements in the grades table are out of order (100 and 200)? Lua allows you to do that, but it will not fill in the missing elements leading up to those numbers—they will all be nil unless they are defined (which is true of any variable in Lua). Just remember that every variable is an entry in Lua’s dictionary container. In that case, grades[100] is more of a name than an array element (though that’s a simplification). Let’s examine a more complex type of Lua table—one containing multiple named items:

persons = {}
persons[1] = { name = "John", age = 30, weight = 180, IQ = 120 }
persons[2] = { name = "John", age = 18, weight = 150, IQ = 113 }
persons[3] = { name = "Sue", age = 19, weight = 110, IQ = 125 }
persons[4] = { name = "Dave", age = 20, weight = 160, IQ = 110 }
persons[5] = { name = "Laura", age = 24, weight = 100, IQ = 118 }
persons[6] = { name = "Don", age = 18, weight = 130, IQ = 122 }
persons[7] = { name = "Julie", age = 22, weight = 120, IQ = 105 }
persons[8] = { name = "Craig", age = 21, weight = 180, IQ = 112 }
persons[9] = { name = "Sarah", age = 20, weight = 115, IQ = 130 }

The persons table contains four properties: name, age, weight, and IQ. You can legally violate the syntax of any one element in the table, and Lua will not complain—although you could wind up with a program crash. For instance, I could redefine persons[8] like so:

persons[8] = { something = 1, something_else = "blah" }

and Lua will accept it. But, although you can do this, it makes no sense to do so because the most common use for a table is for iteration.

Lua provides a library called table that you can use to get information about one of your tables. The function table.getn() will tell you how many items are contained in your table. For instance:

print( table.getn( persons ) )

will display the number of elements in the persons table (which is 9). Odd as it may seem to your C++ training, table is a Lua library, and your own custom-defined tables are not objects with methods such as size or length. You must pass the name of your table to table.getn() instead. With this new information, we can print out the data in the persons table. Note in this example that you can access properties in a table using two different formats (interchangeably)—either the property name as an index or the property name with the dot operator.

print("PERSONS")
size = table.getn(persons)
for n = 1, size do
  print( "Person #" .. n )
  print( "    Name    = " .. persons[n]["name"] )
  print( "    Age     = " .. persons[n].age )
  print( "    Weight  = " .. persons[n]["weight"] )
  print( "    IQ      = " .. persons[n].IQ )
end

This produces the output:

PERSONS
Person #1
    Name    = John
    Age     = 30
    Weight  = 180
    IQ      = 120
Person #2
    Name    = John
    Age     = 18
    Weight  = 150
    IQ      = 113
Person #3
    Name    = Sue
    Age     = 19
    Weight  = 110
    IQ      = 125
Person ...

Timing

Lua provides functions to retrieve the current date and time. The os.date() function returns the current date with a default format that includes the time (and you may modify the format if you wish):

Date: 04/12/08 13:04:24

The os.time() function returns the number of seconds that have passed since January 1, 1970 (on most systems—though this start date may vary from system to system):

Time: 1208030664

Dividing this number by 60 produces the minutes since the beginning of the epoch. Dividing by another 60 results in hours. Then, dividing by 24 hours and again by 360 days, you will calculate the number of years. Now let’s look into more specific timing features of the Lua language.

There are times when you may wish to profile or benchmark your script code to see whether it’s slowing down the game much (if at all). We can use Lua’s os library to get the current time in a number of ways. I’ve mentioned already that Lua does a poor job of seeding the random number generator due to its lack of an epoch-based millisecond timer. But once the program starts up, you can retrieve milliseconds in order to profile your script code. Here is a function called Stopwatch() that I find useful for slowing down the output of profiling code (for instance, limiting the print calls to once per second):

start_time = 0
function Stopwatch(ms)
  if Timer() > start_time + ms then
    start_time = Timer()
    return true
  else
    return false
  end
end

Stopwatch() just returns true or false depending on whether the desired number of milliseconds has passed. To profile a function in Lua, as in C++, you must call it many times, getting a baseline time value, and then divide that time by the number of iterations. This is the only way to get the actual time taken to call a function since the processor can perform a function call in a matter of microseconds (millionths of a second) or nanoseconds (one billionth of a second), while a millisecond is only one thousandth of a second.

The Stopwatch() function has a dependency on another function we have not yet seen. The Timer() function returns the number of milliseconds since the program started with the help of os.clock(). This function normally returns just seconds, but it also provides a decimal value containing the milliseconds as well. By multiplying os.clock() by 1,000, we can convert floating-point seconds into integer milliseconds.

function Timer()
  return os.clock() * 1000
end

Here is an example that prints out the raw clock value and the converted millisecond value once every second:

repeat
  if Stopwatch(1000) then
    print("os.clock(): " .. os.clock())
    print("Timer(): " .. Timer())
  end
until false

That script program produces the following output:

os.clock(): 1.015
Timer(): 1015
os.clock(): 2.015
Timer(): 2015
os.clock(): 3.031
Timer(): 3031
os.clock(): 4.046
Timer(): 4046

The millisecond timer is indeed returning milliseconds, as you can see in this output, because the data is being printed out once per second.

The last thing I want to go over with you regarding timing is a function profiling program. The purpose of this program is to demonstrate how to test the runtime of a single function. To slow down the function call, we will calculate square root—which is notoriously difficult for most processors to calculate. First, we’ll get a random number, then get the square root of the number using math.sqrt(), and return the value for good measure.

function SlowMathFunction()
  r = random_range(1,999999)
  num = math.sqrt(r)
  return num
end

Why do we set the square root value equal to a variable first, before returning it? The Lua interpreter might be smart enough to optimize code like this, so we need to physically store the result of the calculation in a variable to actually use a processor cycle.

This program uses a for loop that runs the function a couple million times in order to calculate the time taken to run the function once, and the results are printed out.

print("Profiling function...")
TOTAL = 2000000

start = Timer()
for n = 1,TOTAL,1 do
  var = SlowMathFunction()
end
finish = Timer()
delta = finish-start
print("Total run time: " .. delta .. " ms" )
print("Function run time: ")
milli = round(delta / TOTAL, 8)
micro = round(milli * 1000, 8)
nano = round(micro * 1000, 8)
print(" milliseconds: " .. milli )
print(" microseconds: " .. micro )
print(" nanoseconds : " .. nano )

Here is some sample output. As you can see, it takes about one and a half microseconds to run the SlowMathFunction() just once.

Profiling function...
Total run time: 3218 ms
Function run time:
  milliseconds: 0.001609
  microseconds: 1.609
  nanoseconds : 1609

Just out of curiosity, let’s see how much of that 1.6 microsecond figure is taken up in the function calls (including the random function). Here’s a revised version of the program that has the math calculation embedded in the for loop:

Total run time: 2796 ms
Function run time:
  milliseconds: 0.001398
  microseconds: 1.398
  nanoseconds : 1398

Look at that! There’s a difference of 211 nanoseconds. Interestingly, this program calls the SlowMathFunction() two million times, so there seems to be a noticeable but infinitesimally small amount of overhead in each function call. The value will differ from one system to the next and will depend on processor speed, but I calculated one-tenth of a picosecond (which is, for all practical purposes, too small to be relevant).

Distance

In general, you will want to code your math functions in C++, rather than in Lua, because despite Lua’s solid performance, it is an interpreted language and it cannot compete with a compiled binary for performance. But there are times when a designer may wish to just perform some range tests or gameplay tests to see whether a game is doing what it’s supposed to do in unusual situations. One common calculation is to derive the distance between two points. A point can represent the position of any game entity. The following program prints out the distance between two points:

function distance(x1,y1,x2,y2)
  return math.sqrt( (x2-x1)^2 + (y2-y1)^2 )
end
x1 = 100
y1 = 150
x2 = 780
y2 = 620
print("Point 1: " .. x1 .. "," .. y1)
print("Point 2: " .. x2 .. "," .. y2)
print("Distance = " .. distance(x1,y1,x2,y2) )

Feel free to change the point locations. The output using these points is:

Point 1: 100,150
Point 2: 780,620
Distance = 826.6196223173

Velocity

We explored linear velocity and incorporated velocity calculations in the Math class back in Chapter 10. We can code these functions in Lua as well. Here are the Lua versions of VelocityX() and VelocityY() with some test code:

function VelocityX(angle)
  return math.cos( (angle-90) * math.pi/180)
end
function VelocityY(angle)
  return math.sin( (angle-90) * math.pi/180)
end
ang = 120
vx = round(VelocityX(ang),2)
vy = round(VelocityY(ang),2)
print("Velocity(" .. ang .. ") = " .. vx .. "," .. vy)

This example script produces the following output:

Velocity(120) = 0.87,0.5

Targeting

For good measure, I’ll throw in one last math function that has been converted from its C++ equivalent in the Math class: calculating the angle between two points. Like the C++ atan2() function, Lua’s math.atan2() calculates an angle based on delta Y and delta X for two points.

function target_angle(x1,y1,x2,y2)
  deltaX = x2-x1
  deltaY = y2-y1
  return math.atan2( deltaY, deltaX )
end
x1 = 100
y1 = 100
x2 = 900
y2 = 600
print("Point 1 = " .. x1 .. "," .. y1 )
print("Point 2= " .. x2 .. "," .. y2 )
rangle = target_angle(x1,y1,x2,y2)
rangle = round(rangle,4)
dangle = math.deg( target_angle(x1,y1,x2,y2) )
dangle = round(dangle,4)
print("Target Angle in radians = " .. rangle )
print("Target Angle in degrees = " .. dangle )

(Just grab a copy of the round() function from one of the other script listings for use in this program—it was covered earlier in the chapter.) Here’s the output:

Point 1 = 100,100
Point 2= 900,600
Target Angle in radians = 0.5586
Target Angle in degrees = 32.0054

Guessing Game

Now let’s make a simple game entirely in Lua script! You can run this game from a command prompt using the Lua interpreter. The guessing game script first generates a random number from 1 to 100, and then asks the user to continue guessing until he or she gets the answer right. Each time a number is entered, the game tells the player whether the answer is higher or lower.

Here is the source code for the game. Not shown are the common functions we’ve gone over previously: round(), gen_random(), and random_range().

function GetInput()
  return io.stdin:read("*l")
end
print "Try To Guess My Secret Number (1-100)"
math.randomseed( os.time() )
answer = random_range(1,100)
guess = 0
total = 0
repeat
  input = GetInput()
  guess = tonumber(input)
  if guess > answer then
    print("THE ANSWER IS LOWER")
  elseif guess < answer then
    print("THE ANSWER IS HIGHER")
  end
  total = total + 1
until guess == answer
print("You got it in " .. total .. " tries.")

Figure 12.4 shows the output of the guessing game script in a command prompt.

Figure 12.4. The Guessing Game script program.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset