View Page Source

Revision (current)
Last Updated by FatRatKnight on 3/26/2013 7:50 PM
Back to Page

%%TOC%%

What you will learn in this tutorial:
* __Callbacks__
** on_paint(boolean not_synth)
* __Functions__
** gui family
*** gui.text(number x, number y, string text, [[number forecolor]], [[number backcolor]])
*** gui.repaint()
** memory family
*** memory.readbyte(number address)

!!!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:

%%SRC_EMBED lua
function on_paint()
   gui.text(1,1,"Hello world.")
end
%%END_EMBED

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.

%%SRC_EMBED lua
function on_paint()
   gui.text(15,12,"Hello world.")
end
gui.repaint() -- Tell lsnes to update display when we run this script!
%%END_EMBED

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.

%%SRC_EMBED lua
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
%%END_EMBED

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...

%%SRC_EMBED lua
local function on_paint()
   gui.text(3,99,"You'll never see this text!")
end
%%END_EMBED

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.

%%SRC_EMBED lua
function on_paint()
   local Some_RAM_Value = memory.readbyte(0x7E0000)
   gui.text(1,1,Some_RAM_Value)
end
%%END_EMBED

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:

%%SRC_EMBED lua
while true do
   gui.text(5,5,"Hello world.")
   emu.frameadvance()
end
%%END_EMBED

Or perhaps you know this instead:

%%SRC_EMBED lua
gui.register(function()
   gui.text(5,5,"Hello again, world.")
end)
%%END_EMBED

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.