Tutorial: Introduction to Lua scripting - ED Forums
 


Notices

Reply
 
Thread Tools Display Modes
Old 02-07-2014, 03:29 AM   #1
Yurgon
Campaign Testers
 
Yurgon's Avatar
 
Join Date: May 2010
Location: Germany
Posts: 6,393
Default Tutorial: Introduction to Lua scripting

While I was creating my first large mission, I had one task that I couldn't achieve with the Mission Editor but that should be easy enough with a bit of Lua scripting magic. It took me a while to familiarize myself with Lua scripting in the ME. I hope this introduction to the topic will allow other mission builders a smoother start at it.

This tutorial is aimed at mission designers with a basic understanding of the DCS Mission Editor (ME) who want to find out about Lua scripting inside the ME.

This introduction currently refers to DCS 1.2.7 and may be incorrect or outdated for older or newer versions.

I'm by no means an expert on the matter and will gladly correct any mistakes, incorporate links and extend this guide. Just write a reply to this thread.

This tutorial is far from complete, it merely scratches the surface. For the time being, it is however the introductory tutorial that I wish someone else had written so that I didn't have to figure all of this stuff out for myself. Then again, I've already learned a huge amount of stuff about Lua scripting in DCS by writing this guide.

Without further ado, let's get started!


What is Lua?

Quoted from the Lua homepage, "Lua is a powerful, fast, lightweight, embeddable scripting language."


DCS and Lua

DCS makes heavy use of Lua. The Simulator Scripting Engine "provides mission designers with access to game world and game objects using scripts. It's possible to change object properties, control object behavior and read object properties."


Getting started with Lua

If you're new to Lua, lua-users.org/wiki/LuaDirectory is an incredibly helpful resource to get an overview. If you have prior programming experience in other languages, you can get a decent overview in just 30 minutes. But even if you have no prior experience with programming languages, Lua is a very simple language to learn and therefore a very good language to start with.

You should have an environment where you can run Lua scripts, test ideas and check if the code you write is even formally valid (or, as we programmers like to call it, "syntactically correct").

The easiest way to do this is to use the live demo on the official Lua site that runs in every browser: www.lua.org/cgi-bin/demo.

But feel free to download and install the Lua interpreter on your local computer so that you become independent of the Lua web site.

Okay, now run this very simple Lua script in your local Lua interpreter or in the live demo:

Code:
sim = "DCS"
version = "1.2.7"

-- Use two dashes to start a comment. This line will be ignored by the interpreter
opinion = "rocks" -- Oh, and comments can also follow after some code

print(sim .. " " .. version .. " " .. opinion)
This is typically called the "Hello World" example, I just chose some other words. Congratulations, you've just run your first Lua script!

You will also find that a decent editor is a very helpful tool for any kind of programming. If you don't have a favorite editor yet, now would be a good time to install Notepad++. It's free and it's a very good editor to begin with. And if you ever intend to edit a DCS config file, Notepad++ will also come in very handy.

Hey, by the way, most DCS config files are written in Lua as well, so if you want to see live examples of Lua in action, you can go and check some of them out now.


Adding Lua into a mission

Now that you have a basic understanding of Lua, it's time to add Lua to a mission.

I'll be using a very simple mission with a single player controlled Su-25T. The focus will be on how to add scripts to a mission, not how to build a useful or good mission. That's up to you.

Now let's fire up the ME. You should place a player controlled unit to get the unit's cockpit view.

This is what we'll do:

Click image for larger version

Name:	01_hello_world.jpg
Views:	2495
Size:	192.2 KB
ID:	94038

The same in writing, follow these steps:
  • Click the "Set rules for trigger" button on the left side
  • On the left column (the "Type" Column), click "New". Set Type to "ONCE" and Event to "NO EVENT". It's also always good practice to name this trigger.
  • On the center column (the "Conditions" column), click "New". Set Type to "TIME MORE" and Seconds to 5.
  • On the right column (the "Actions" column), click "New". Set Action to "DO SCRIPT". In the Text box, enter the code sample from above.

Now save the mission and fly it.

Click image for larger version

Name:	02_hello_world_missing.jpg
Views:	1422
Size:	175.2 KB
ID:	94039

As you can see, the mission goes right through the 5 second mark and nothing special happens.


Getting Lua to log stuff into dcs.log

Lua's print() function doesn't have any meaning in DCS, so let's try something else. Let's make use of the Simulator Scripting Engine environment now. We'll simply change print() to env.info().

Click image for larger version

Name:	03_hello_world_again.jpg
Views:	1453
Size:	174.3 KB
ID:	94040

If you run the mission again, it seems as if nothing happens. You just have to know where to look.

Calls to env.info() get logged to dcs.log in the Saved Games directory. In my case, the path is

Code:
C:\Users\Yurgon\Saved Games\DCS\Logs\dcs.log
and it looks like this:

Click image for larger version

Name:	04_log.png
Views:	2803
Size:	138.9 KB
ID:	94041

Note the highlighted line that contains our "Hello World" string. When your scripts get more complicated over time, this will be where you log all kinds of stuff. Before we go on, let's delve into this some more.

env.info() takes an optional second parameter called showMessageBox. This is how it is set:

Click image for larger version

Name:	05_info_w_messageBox.jpg
Views:	1575
Size:	173.8 KB
ID:	94042

If we run the mission again, this is what happens when it runs for 5 seconds:

Click image for larger version

Name:	06_messageBox.png
Views:	1113
Size:	8.2 KB
ID:	94043

In addition to writing the output to dcs.log, the game freezes and Windows displays this messageBox window. Unfortunately (at least in my case), I can't even see the messageBox until I Alt+Tab out of DCS. As a rule of thumb... do not set the showMessageBox parameter to true, you won't make many friends if you incorporate this into your missions.

One more thing on the topic of environment functions: Besides env.info() there are also env.warning() and env.error(), and this is how they work:

Click image for larger version

Name:	07_env_info_warning_error.jpg
Views:	1189
Size:	174.6 KB
ID:	94044

And this is their output to dcs.log:

Click image for larger version

Name:	08_log_info_warning_error.png
Views:	1547
Size:	141.8 KB
ID:	94045

As you can see, the only difference is in the second column of dcs.log. If you have large scripts with lots of debugging, it may come in handy to use these different logging functions.


Doing something useful for a change

Okay, let's use a real function now: trigger.action.outText(string text, Time delay)

We'll go back to our "Hello World" text for this example.

Click image for larger version

Name:	09_trigger_action_outText.jpg
Views:	1770
Size:	173.0 KB
ID:	94046

Now imagine a drum roll... and...

Click image for larger version

Name:	10_outText_displayed.jpg
Views:	2421
Size:	213.8 KB
ID:	94047

Cool, isn't it? You've just displayed your first in-game message by using a Lua script, congratulations.


Understanding variable scope

If you've looked at the Lua tutorial, you might have heard the term "Variable scope". This topic is a tiny bit advanced and you'll probably not run into it right away. On the other hand, if you want your scripts to perform well, you'll want to limit variable scope whenever possible so that "old" variables can be wiped from memory as soon as they're not needed anymore.

It's not obvious at first glance how DCS handles this variable scope, that's why I think it's important to bring this topic up so soon.

By default, any variable is global. If you want a variable to be a local variable, it has to be prefixed with the keyword local.

Inside of one script container, we can mix global and local variables like so:

Click image for larger version

Name:	11_scope.jpg
Views:	1651
Size:	180.3 KB
ID:	94048
Click image for larger version

Name:	12_scope_okay.jpg
Views:	1266
Size:	215.0 KB
ID:	94049

That was easy enough. For the next example, we will add two triggers. The first of these triggers fires after 10 seconds and displays the global variable.

Click image for larger version

Name:	13_scope.jpg
Views:	1167
Size:	178.6 KB
ID:	94050

The second of these triggers fires after 15 seconds and is supposed to display the local variable.

Click image for larger version

Name:	14_scope.jpg
Views:	1030
Size:	178.0 KB
ID:	94051

And this is what it looks like: The global variable gets displayed okay.

Click image for larger version

Name:	15_scope_global_okay.jpg
Views:	1287
Size:	216.1 KB
ID:	94052

But trying to display the local variable makes DCS freeze and shows a message box:

Click image for larger version

Name:	16_scope_local_not_okay.png
Views:	1275
Size:	16.4 KB
ID:	94053

So keep in mind that the scope of local variables in DCS is limited to one DO SCRIPT container. They'll simply cease to exist once that code has been executed, and you cannot use them in another DO SCRIPT container.

If you omit the local keyword, variables are available across DO SCRIPT containers and can be accessed at a later time with no problems.


Preventing DCS from freezing when a Lua error occurs

We've just introduced our first script error into a mission. Such a thing can obviously happen very quickly. While you develop a mission, that's a good thing because you'll know right away that something is wrong.

Once you release a mission to the public, you may want to prevent this from happening. Players are not accustomed to their game just locking up for no apparent reason and then read something about nil values or some such.

Here's the good news: It's really easy to prevent DCS from displaying a message box when it encounters an error in a Lua script: Simply set env.setErrorMessageBoxEnabled(boolean on) to false:

Code:
env.setErrorMessageBoxEnabled(false)
Click image for larger version

Name:	17_setErrorMessageBoxEnabled_false.jpg
Views:	945
Size:	177.6 KB
ID:	94054

Let's test it. I just run the same mission as before.

Click image for larger version

Name:	18_messageBox_suppressed.jpg
Views:	863
Size:	220.4 KB
ID:	94055

In the example screenshot, I placed it in my first trigger, which is good enough for now. I'll leave it to you to place it in a trigger at MISSION START and possibly set a variable for the parameter so that you can easily toggle between debugging and live environments.

One question remains: Will all future scripts be broken because of such errors? Well, let's just test it. I keep the previous mission, and after 20 seconds fire another trigger that, once again, displays my global variable msg1.

Click image for larger version

Name:	19_action_after_error.jpg
Views:	837
Size:	183.1 KB
ID:	94056

As you can see, the trigger gets executed just fine:

Click image for larger version

Name:	20_okay_after_error.jpg
Views:	972
Size:	211.0 KB
ID:	94057

That means that a Lua script error will halt execution inside of one script container, but it will not prevent other script containers from getting executed.

Of course that doesn't mean that your scripts won't suffer from such a problem if different containers rely on each other.


Conclusion

What we've discussed so far was an introduction into Lua scripting with the Simulator Scripting Engine and how to inject very simple and basic scripts into a mission using the DO SCRIPT trigger action.

There are other ways to include scripts that I may (or may not) cover in a second part of this tutorial. In any case I hope this tutorial served as a primer so that you can get into DCS Lua scripting easier than I did.


Further reading

There are lots of extremely useful resources that go into much more detail. In no particular order (and repeating some that have already been mentioned before), some of them are:

Last edited by Yurgon; 12-21-2016 at 02:25 AM. Reason: Added new link LDT to link list
Yurgon is offline   Reply With Quote
Old 02-07-2014, 03:29 AM   #2
Yurgon
Campaign Testers
 
Yurgon's Avatar
 
Join Date: May 2010
Location: Germany
Posts: 6,393
Default

(Reserved)
Yurgon is offline   Reply With Quote
Old 02-07-2014, 08:30 AM   #3
tintifaxl
Senior Member
 
Join Date: Mar 2009
Posts: 1,478
Default

Very nice and comprehensive tutorial.
__________________
Windows 10 64bit, Intel i5-7600@3.8Ghz, 32 Gig RAM, Nvidia GTX 1070, 1 TB SSD, 43" 2160p monitor.
tintifaxl is offline   Reply With Quote
Old 02-07-2014, 09:37 AM   #4
Joe Kurr
Member
 
Joe Kurr's Avatar
 
Join Date: Nov 2004
Location: Almere, The Netherlands
Posts: 648
Default

Very nice start indeed!

Here are some additions that might come in handy

It is possible to write object-oriented code in Lua-script. In fact, if you're scripting for DCS, you're already using objects.

Let's go through some basics.

To define a 'class', or object template, you can do this.

First, we need to create a table (all objects in lua are tables), with the properties of our class and their default values.

Code:
MyClass = {
	Property1 = "Hello World",	-- a string
	Property2 = 15,			-- a number
	Property3 = {}			-- a table
}
Now we need to define a constructor, so we can use this as a proper class:

Code:
function MyClass:New(p1, p2, p3)
	local obj = {
		Property1 = p1,
		Property2 = p2,
		Property3 = p3
	}
	setmetatable(obj, { __index = MyClass })
	return obj
end
To use this class, we can define some variables, and assign new instances of our class to them:

Code:
MyObject1 = MyClass:New("First object", 1, { "one", "two", "three" })
MyObject2 = MyClass:New("Second object", 2, { 4, 5, 6 })

env.info(MyObject1.Property1) -- Writes "First object" to dcs.log
env.info(MyObject2.Property1) -- Writes "Second object" to dcs.log
The real use of these objects starts when we add some functions to them.
Let's add a function to increment Property2 by a given number.

Code:
function MyClass:IncrementP2(value)
	self.Property2 = self.Property2 + value
end
Notice the use of the keyword self here.
It is a hidden parameter that automatically gets passed a reference to the object if you call the function using a colon:

Code:
MyObject1:IncrementP2(5)
This is the same as:

Code:
MyObject1.IncrementP2(MyObject1, 5)
Consequently, our IncrementP2 function can also be written like this:

Code:
function MyClass.IncrementP2(self, value)
	self.Property2 = self.Property2 + value
end
This is a major pitfall in developing lua scripts, as calling MyObject1.IncrementP2(5) or MyObject1:IncrementP2(MyObject1, 5)won't throw an error, it simply does nothing (at best) or something unexpected.
If you're using objects from DCS and your code doesn't work, check this first. It will save you a lot of debugging time!
__________________
Dutch Flanker Display Team | LLTM 2010 Tiger Spirit Award
Joe Kurr is offline   Reply With Quote
Old 02-07-2014, 04:26 PM   #5
Yurgon
Campaign Testers
 
Yurgon's Avatar
 
Join Date: May 2010
Location: Germany
Posts: 6,393
Default

Quote:
Originally Posted by Joe Kurr View Post
Here are some additions that might come in handy
Wow, that's really cool, thanks!
Yurgon is offline   Reply With Quote
Old 02-07-2014, 09:56 AM   #6
Joe Kurr
Member
 
Joe Kurr's Avatar
 
Join Date: Nov 2004
Location: Almere, The Netherlands
Posts: 648
Default

Small addition to the above, here is some documentation I found while writing my cross-country airrace script:

Pragmatic LUA basics in 30 minutes
Pragmatic Lua - Error Handling, OOP, Closure and Coroutine
__________________
Dutch Flanker Display Team | LLTM 2010 Tiger Spirit Award
Joe Kurr is offline   Reply With Quote
Old 02-07-2014, 04:28 PM   #7
Silver_Dragon
ED Translator
 
Silver_Dragon's Avatar
 
Join Date: Apr 2005
Location: Arafo, Tenerife, Canary Islands, Spain
Posts: 5,570
Send a message via MSN to Silver_Dragon
Default

great post
__________________
More news to the front
Wishlist: ED / 3rd Party Campaings
My Rig: Intel I-5 750 2.67Ghz / Packard Bell FMP55 / 16 GB DDR3 RAM / GTX-1080 8 GB RAM / HD 1Tb/2Tb / Warthog / 2 MDF / TFPR

DCS: Roadmap (unofficial):https://forums.eagle.ru/showthread.php?t=116893
DCS: List of Vacant models: https://forums.eagle.ru/showthread.php?t=104115
21Squad DCS: World News: https://www.facebook.com/21Squad-219508958071000/
Silver_Dragon Youtube
Spanish 4Th Perrus Squadon Member: http://www.4thperrus.com
Silver_Dragon is offline   Reply With Quote
Old 02-18-2014, 11:04 AM   #8
Joe Kurr
Member
 
Joe Kurr's Avatar
 
Join Date: Nov 2004
Location: Almere, The Netherlands
Posts: 648
Default

If you want to reference scripts in your C:\Users\username\Saved Games\DCS\Scripts folder, use lfs.writedir:

Code:
dofile(lfs.writedir().."/Scripts/yourscript.lua");
Took me quite some time to find it, so I post it here for easy reference
__________________
Dutch Flanker Display Team | LLTM 2010 Tiger Spirit Award
Joe Kurr is offline   Reply With Quote
Old 02-25-2014, 03:02 PM   #9
HiJack
Veteran
 
HiJack's Avatar
 
Join Date: Jul 2009
Location: Norway
Posts: 6,727
Default

I got the altitude above sea level worked out but how can I get a units current above ground level?


__________________
Windows 7 64 bit FTW!
My DCS Tools:
Quick Airfield information , Better trim with the X52 Pro , Tacview Glideslopes

Spoiler:

ASUS Z97-PRO, Socket-1150
Intel® Core i7-4770K Processor overclocked to @ 4.20 GHz
32GB Corsair Dominator Platinum DDR3 2133MHz CL9 4x8GB
Sapphire Radeon TRI-X R9 390X 8GB "OC"
Samsung SSD 850PRO 1TB SSD
WD 2TB Black 3,5", SATA64MB Cache, Dual Processor, 7200RPM
Windows 7 64-bit
Philips 40" 4K LED BDM4065UC, ASUS 27” and Dell 24” screens
HiJack is offline   Reply With Quote
Old 02-25-2014, 03:44 PM   #10
towsim
Member
 
towsim's Avatar
 
Join Date: Mar 2012
Location: Bavaria, Germany
Posts: 637
Default

Position_vec3 = YourObject:getPoint()
elevation = land.getHeight{x = Position_vec3 .x, y = Position_vec3 .z}
now you can subtract the elevation from your position z value.
__________________
towsim is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

All times are GMT. The time now is 05:19 PM. vBulletin Skin by ForumMonkeys. Powered by vBulletin®.
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.