What you will learn in this tutorial:

Starting out

In order to even have a hope of getting any remotely useful script, you want to see an output. There are several different outputs a script can make, but let us start with writing text on the screen. Something that is obvious and quick. Something to encourage you to continue.

The simplest display

To start, there is a reserved global word lsnes uses. Several actually, but we're starting with on_paint. If you define a global function named on_paint, lsnes will call it whenever it needs to update the display. Do not call it yourself, it will be called whenever important.
But what to put into this function? There is a set of functions whose primary use is in on_paint, and I will refer to one now: gui.text. This function takes three parameters: Two numbers indicating coordinates and a string to display. Optionally, you can add two more numbers after the string, affecting fore-color then back-color, but we'll get into colors later.
So, a very bare function would be:
function on_paint()
   gui.text(1,1,"Hello world.")
end
Run it, and... No text. Well, not yet. Advance one frame, then you'll see it. Why?

Immediate display upon running your script

The act of starting a script does not tell lsnes to update the display. It just tells lsnes to run through the script. Unless the script itself requests it through a function call, lsnes will only display what it did before.
This function call is gui.repaint. This function can be called anywhere you like, though do not put it inside on_paint. Once your lua script is done running for the moment, lsnes will then update its display and call on_paint for you.
function on_paint()
   gui.text(15,12,"Hello world.")
end
gui.repaint() -- Tell lsnes to update display when we run this script!
Now you get to see an immediate change upon running the script!

Other ways of defining on_paint

As a consequence of Lua syntax, there is more than one way to define on_paint. If you are familiar with Lua, these will be obvious to you already, but it isn't harmful to point them out here.
on_paint = function()
   gui.text(2,2,"Having fun?")
end

local function MyOwnCode()
   gui.text(8, 8,"Insert something witty!")
   gui.text(8,24,"Make it very witty.")
end
on_paint = MyOwnCode
Both blocks up there will work. Like any other variable, you can reassign a new function to on_paint at any time. If you're just starting out, this isn't something you're likely to do, yet.
Be careful when using the local keyword, however...
local function on_paint()
   gui.text(3,99,"You'll never see this text!")
end
lsnes is looking for a global on_paint. It will never see your local on_paint. Furthermore, any references to on_paint after your local function declaration will point to your local version, and not the global version lsnes uses.
In short, be more careful where you place the local keyword.

Using on_paint to read memory

Although fun to have text floating around on your screen, it is generally impractical unless you have it displaying useful numbers or other information around the game you're playing.
It's time we introduce memory.readbyte. This takes a number, an address to the RAM you want to look at. This returns a number from 0 to 255, from the location in memory.
function on_paint()
   local Some_RAM_Value = memory.readbyte(0x7E0000)
   gui.text(1,1,Some_RAM_Value)
end
If you know of some useful addresses, you can now keep some handy values on screen. There are more memory reading functions than just a single byte at a time, but this page deals in the very basics of what is needed to have a minimally useful script. You are advised to get more familiar with how on_paint works by experimenting with it before you let yourself get overwhelmed by new functions.

Breaking old habits

If you have worked with Lua scripting in other emulators, you're likely familiar with the following:
while true do
   gui.text(5,5,"Hello world.")
   emu.frameadvance()
end
Or perhaps you know this instead:
gui.register(function()
   gui.text(5,5,"Hello again, world.")
end)
In the first case, you have a while true loop. Unless you have ways of breaking out of that loop, having such a loop anywhere in your code means you're doing something wrong. Since emu.frameadvance() doesn't exist in lsnes anyway, attempting to call it will give an error and break out of the loop, so thankfully no lock-up. There is no function to call to say "here, emulator. I'm returning control to you."
In the second case, you're calling a function to put another function in some special spot. This is closer to what lsnes is like, but it's not quite there. Rather, lsnes will call functions that you give certain global names.
Some callbacks in lsnes have the privilege to call certain functions, such as on_paint with most gui functions. In most other emulators, you can call gui.text whenever you like and expect something visible, but lsnes only lets it have any effect inside an on_paint or on_video callback. Once you learn of other callbacks, realize you need them to affect variables that on_paint will then read, as you can't use gui.text directly in those callbacks.

EmulatorResources/Lsnes/LuaTutorials/GettingStarted last edited by FatRatKnight on 3/26/2013 7:50 PM
Page History Latest diff List referrers View Source