Scripting Tips and Tricks

liquid8d edited this page Jan 21, 2017 · 5 revisions

This is a collection of code snippets or tricks that have been learned to ease scripting:

#Using Tables Tables in squirrel are super handy.. you can store multiple values in a single object, similar to an array - but using named references. This is helpful when creating layouts because you can store your layout configurations or other values in a single table and reference it within your code:

  local myConfig = {
    color = [ 0, 200, 0 ],
    text = "A saved value"
  }

  local myText = fe.add_text(myConfig.text, 0, 0, fe.layout.width, 20)
  myText.set_rgb(myConfig.color[0],myConfig.color[1],myConfig.color[2])

You can use single named values with an equals sign, but if you want to use numbers or spaces in variable names, you'll need to use quotes with a colon to store those in a table:

 local config = { "my variable": value }

#Supporting Multiple Aspects The first step into supporting multiple aspects is to stop hardcoding your values! :) You can use the table settings to make supporting multiple aspect ratios super easy! Here's a quick example:

   local aspect = "4x3"  //make this a config option!
   local config = {
      "4x3": {
         "text1": { msg = "[Title]", x = 0, y = 0, width = fe.layout.width, height = 20 }
      }
   }
   local text1 = fe.add_text(config[aspect].text1.msg, config[aspect].text1.x, config[aspect].text1.y, config[aspect].text1.width, config[aspect].text1.height)

It may even be easier for you to reverse the table to be config.object[aspect] like so:

   local settings = {
      text1 = {
         "4x3": { x = 0, y = 0, width = flw, height = flh * 0.01 },
         "16x9": { x = 0, y = 0, width = flw, height = flh * 0.02 }
      }
   }
   //now you can access values like this
   local text1 = fe.add_text("[Title]", settings.text1[aspect].x, settings.text1[aspect].y, settings.text1[aspect].width, settings.text1[aspect].height)

This way, all your properties for each object and all aspects are all together, at the top of the layout.

#setProps function To make setting multiple props on objects even easier, use this setProps function:

//set properties on any object
::setProps <- function(target, props) {
    foreach( key, val in props )
        try {
            target[key] = val
        } catch(e) { ::print("setProps: error setting property: " + key + "\n") }
}

This function will attempt to set all properties in a given table on an object, so you can do this:

local config = { x = 0, y = 0, width = fe.layout.width, height = 20 }
local text1 = fe.add_text("", -1, -1, 1, 1)
 setProps(text1, config)

We just created a text offscreen, then set all the properties for it at once. We made the setProps function a global value so it can be accessed from other scripts...

#Global (Root table) Values You may be familiar with local values to initialize a variable, but these only make your variable available in the local environment (aka - that script file). If you need variables to be available when other .nut files are called, you can register them in the 'root' table, using double colons:

::myGlobals <- {
  myValue = 1
}

Funny thing is - EVERYTHING in squirrel - is stored in one big table. The fe variable is stored in the root table, which is why you have access to it from all scripts. Note the arrow-dash, which 'pushes' your value into the root table.

HOWEVER, keep in mind that the global variables are shared - so you might run into naming conflicts with other modules or scripts that you are including, which could cause errors in your layout.

#Screen Percentages A lot of layouts tend to use this flw and flh variables to shorten fe.layout.width and fe.layout.height, and make it easier to do percentages of the screen for object placement:

local flw = fe.layout.width
local flh = fe.layout.height
local text = fe.add_text("[Title]", 0, 0, flw * 0.5, flh * 0.01)

I like to use this per function, just a personal preference:

//use percentages
local flw = fe.layout.width
local flh = fe.layout.height
::per <- function(percent, val) {
    if ( percent == 0 ) return 0
    return ( percent / 100.0 ) * val.tofloat()
}

local text = fe.add_text("[Title]", 0, 0, per(50, flw), per(1, flh))

Either method allows you to set percentages of the screen, which can simplify locations (no more fixed pixel locations!) and help when making your layout work with multiple aspect ratios. Keep in mind though - percentages might be okay for multiple horizontal aspects, but it's going to look a lot different (potentially unusable) if your percentages are for horizontal aspects and it is used on a vertical aspect.

#Debugging Layouts If you are debugging a script, you probably already run it in windowed mode to see print output - but sometimes it is nice to see values onscreen, in the layout. Here's a quick Debugger class you can use:

class Debugger
{
    obj = null
    reload_key = "custom1"
    constructor() {
         obj = ::fe.add_text("", 0, fe.layout.height - 40, fe.layout.width, 30)
         obj.set_rgb(200, 200, 0)
         ::fe.add_signal_handler(this, "on_signal")
    }
    function msg(msg) {
      obj.msg = msg
    }
    function on_signal(str) {
        if ( str == reload_key ) ::fe.signal("reload")
    }
}

local myText = fe.add_text("[Title]", 50, 50, 300, 30)
local debugger = Debugger()
debugger.msg("myText loc = x:" + myText.x + " y:" + myText.y + " w: " + myText.width + " h:" + myText.height)