DeepMind Lab provides a number of ways for Lua callback functions to hook into various game events.
On construction of the environment, the levelName
setting specifies a game
script file name. Game script files live in the game_scripts/levels
directory
and end in .lua
. They consist of Lua code that should return an object
implementing some of the API functions below.
The environment will periodically attempt to call these functions in order to determine its behaviour. However, in most cases there is some appropriate default behaviour that takes place if the corresponding API function is not defined.
In order to successfully load a map, at least nextMap
should be provided by
the game script.
The calling code that calls into these API functions can be found in:
- deepmind/engine/context.cc.
- deepmind/engine/context_actions.cc.
- deepmind/engine/context_entities.cc.
- deepmind/engine/context_events.cc.
- deepmind/engine/context_game.cc.
- deepmind/engine/context_observations.cc.
- deepmind/engine/context_pickups.cc.
All callback functions are invoked by the environment in a specific order:
Called at beginning of the level to populate the level with in-game bots.
Returns an array of tables, each of which has a name
and skill
entry. Each
skill
should be a number between 1
and 5
.
Called before loading a map and sets the game type being played these can be
accessed from common.game_types
. If not implemented FREE_FOR_ALL is returned.
local game_types = require 'common.game_types'
function api:gameType()
return game_types.CAPTURE_THE_FLAG
end
If the game type is team based the team a player belongs to can be set via
team
().
Called for each player joining the game. The team can be selected by returning one of the following characters. A team must be selected if game mode is a team game.
Character | Team |
---|---|
'p' | Any Team (Default) |
'r' | Red Team |
'b' | Blue Team |
's' | Spectator |
local TEAMS = {'r', 'b'}
function api:team(playerId, playerName)
return TEAMS[playerId % 2 + 1]
end
Called for each player joining the game. Each model/team combination has a textures that can be overridden.
Model | team | Textures |
---|---|---|
'crash' |
free | 'models/players/crash/skin1.tga' |
'crash' |
red | 'models/players/crash/redskin.tga' |
'crash' |
blue | 'models/players/crash/blueskin.tga' |
'crash_color' |
all | 'models/players/crash_color/skin_base.tga' |
'crash_color/skin1' |
all | 'models/players/crash_color/skin_base1.tga' |
'crash_color/skin2' |
all | 'models/players/crash_color/skin_base2.tga' |
'crash_color/skin3' |
all | 'models/players/crash_color/skin_base3.tga' |
'crash_color/skin4' |
all | 'models/players/crash_color/skin_base4.tga' |
'crash_color/skin5' |
all | 'models/players/crash_color/skin_base5.tga' |
'crash_color/skin6' |
all | 'models/players/crash_color/skin_base6.tga' |
'crash_color/skin7' |
all | 'models/players/crash_color/skin_base7.tga' |
local redTeamSkin = 'crash_color/skin1'
local blueTeamSkin = 'crash_color/skin2'
function api:playerModel(playerId, playerName)
return playerId % 2 == 0 and redTeamSkin or blueTeamSkin
end
function api:modifyTexture(name, texture)
if name == 'models/players/crash_color/skin_base1.tga' then
texture:add{255, 0, 0, 0}
return true
end
if name == 'models/players/crash_color/skin_base2.tga' then
texture:add{0, 0, 255, 0}
return true
end
return false
end
The environment calls this function to decide whether the item with ID spawnId
is allowed to be picked up. Return false
to disallow pickup. Defaults to
true
if the callback function isn't implemented.
Will not be called if spawnId is not a positive integer; this is normally
ensured by updateSpawnVars()
.
The environment calls this function at the very beginning to determine the
initial engine console commands. The old_commandline
parameter is the default
value, which you should return as-is if you do not want to customize the command
sequence.
Example:
function api:commandLine(old_command_line)
return '+set r_fullscreen "1"'
end
Lists of available commands in Quake III Arena can be found online.
Returns a table with keys name
, classname
, model
, quantity
, type
, and
an optional typeTag
and moveType
. (tag
deprecated.)
Pickups are rendered bobbing and spinning in the air. If you require them to be
static, use pickups.move_type.STATIC
as moveType
.
DMLab defines two item types not found in Quake III Arena:
pickups.type.REWARD
: an item which when picked up changes the agent's score, positive or negative.pickups.type.GOAL
: an item which when picked up changes the agent's score and causes the current level to restart.
See game_scripts/common/pickups.lua for examples.
Whenever the runtime cannot load the asset referenced by model
, it will
invoke createModel
with the contents of that string.
Returns a table with the geometry of the model referenced by model_name
. The
format of such table is described in Model Generation.
customDiscreteActionSpec
is called after init
, it returns an additional
array of extra actions supplied by the script.
Each action must contain a name
, type
and shape
.
name
Name of the action reported by the environment.min
Min value of actions.max
Max value of actions.
When specified customDiscreteActions
is called with an array current actions
for that frame.
function api:customActionSpec()
return {
{name = 'SWITCH_GADGET', min = -1, max = 1},
{name = 'SELECT_GADGET', min = 0, max = 10},
}
end
local currentCustomActions = actions
function api:customDiscreteActions(actions)
currentCustomActions = actions
end
function api:modifyControl(controls)
if currentCustomActions[1] ~= 0 then
-- SWITCH_GADGET action.
end
if currentCustomActions[2] ~= 0 then
-- SELECT_GADGET action.
end
end
(For information on DeepMind Lab's tensor library, see Tensor.)
When called it must return a tensor of a matching shape and type as specified in
customObservationSpec
.
Example:
local order = 'Find Apples!'
local observationTable = {
LOOK_PITCH = tensor.Tensor{0},
ORDER = tensor.ByteTensor(order:byte(1, -1)),
LOCATION = tensor.Tensor{0, 0, 0},
}
function api:customObservation(name)
return observationTable[name]
end
Called after init
, it returns an additional array of extra observation types
supplied by the script.
Each entry must contain a name
, type
and shape
.
name
Name of the observation reported by the environment.type
Type of tensor returned by the environment. May only be "bytes" or "doubles".shape
An array of integers denoting the shape of the tensor. If the size of any dimension can change between calls, it must be set to zero here.
-- See customObservation how to implement these.
function api:customObservationSpec()
return {
{name = 'LOCATION', type = 'doubles', shape = {3}},
{name = 'ORDER', type = 'bytes', shape = {0}},
{name = 'LOOK_PITCH', type = 'doubles', shape = {1}},
}
end
When this function is specified the environment may call observation
with any
name
specified in the spec.
Called at game startup with the table settings representing a key-value store for all settings passed at the start of the environment which weren't consumed by the engine itself. See python_api.md for settings consumed by the engine.
Example:
function api:init(settings)
print('Script Settings')
for k, v in pairs(settings) do
print(k .. ' = ' .. v)
end
io.flush()
end
Called at the end of every frame with the elapsed time as an argument to determine if the episode has finished and the game evaluation should continue with the next episode.
The default implementation returns false
until 2 minutes 30 seconds have
passed.
Called whenever a map needs to be loaded. Must return the name of a map discoverable by the engine.
For more information on maps, see Maps below.
Called when a game event occurs in engine. The event data is the data associated with an event and is event specific. May be called multiple times within a frame.
Events generated by the engine:
None
Event handler when the item with id spawnId is picked up. Returns the respawn
time, although this value is ignored if the item has a non-nil wait
spawnVar.
Will not be called if spawnId is not a positive integer; this is normally
ensured by updateSpawnVars()
.
The environment calls this function at the start of each episode, with:
- A number episode, starting from 0.
- A number seed, the random seed of the game engine. Supplying the same seed is intended to result in reproducible behaviour.
Example:
function api:start(episode, seed)
print('Entering episode no. ' .. episode .. ' with seed ' .. seed)
io.flush()
end
Called once per spawnVars
entry when a new map is loaded. The spawnVars
argument is a Lua table with the internal Quake III Arena spawnVars
key-value
setting. Quake III Arena maps define a
list of entities (light sources, player start points, items, ...). Implementing
this function allows overriding the settings for each map entity.
Returning nil
will have the effect of deleting the map entity.
The default implementation returns spawnVars
unchanged.
- angle - angle (in degrees) that the entity is facing. A value of 0 corresponds to facing along the +X axis.
- classname - Required. Determines the functionality and type of the entity.
- id - an arbitrary identifier. If it is not a (string-encoded) integer then
canPikcup()
andpickup()
will not be called. - model - The geometry and texture of the entity, specified by an MD3 file.
- origin - space-delimited string with XYZ coordinate values. (+Z is up)
- spawnflags - For most entities, setting this to
1
indicates that the object is free-floating, and will not attempt to place the entity on the ground, but respect the given Z-axis value for height. - wait - Delay before the entity is respawned after being picked up; -1 means
never respawn. Leave zero (unspecified) to allow
pickup()
to specify respawn time.
Called once after all built-in updateSpawnVars
are processed. Must return an
array of new entities that will be added to a scene. Each entity must be a
string-string table containing the key 'classname'. These should match internal
Quake III Arena spawnVars
key-value settings. Note updateSpawnVars
is not
called for the entities created explicitly.
This example will add two apples to the scene at the locations specified.
function api:extraEntities()
return {
{
classname = 'apple_reward',
model = 'models/apple.md3',
origin = '550 450 0',
},
{
classname = 'apple_reward',
model = 'models/apple.md3',
origin = '600 450 0',
},
}
end
Called once per modelName
when a new model is loaded. If a replacement name is
required return a new name. Otherwise return nil. The model can have it's
textures modified with a prefix with an additional return value. This allows us
to load the same model multiple times with different textures. Returned strings
have a maximum size and the operation will fail if set to be too long.
Called once per textureName
when a new map is loaded. If a replacement name is
required return a new name. Otherwise return nil.
Called once per textureName
when a new map is loaded. If a replacement name
was specified then that name is used here. If we want to override the built-in
texture loading return a tensor.ByteTensor(H, W, 4) of the texture. Otherwise
return nil.
Called once per textureName
when a new map is loaded. image
is a ByteTensor
of the texture loaded. The script has an opportunity to modify the contents of
the texture in-place. image
will be invalidated after the call is complete so
a copy of image must be made if to be used outside of the callback.
The callback must return whether the texture was modified.
Called when the map has finished loading.
spawnInventory
is called on each avatar when spawned. updateInventory
is
called on each avatar every frame. Must return a same table as passed in if
fields are edited or nil. The data in the table can be updated before returning.
There is a helper table used to interact with the loadOut
:
local inventory = require 'common.inventory'
If the function is defined loadOut
must be returned, which can be modified
with inventory.View
. See Inventory View
local inventory = require 'common.inventory'
function api:spawnInventory(loadOut)
-- This is actually the default load out for a player.
-- These can be adjusted per player and per respawn.
local view = inventory.View(loadOut)
view:setGadgets{
inventory.GADGETS.IMPULSE,
inventory.GADGETS.RAPID,
-- inventory.GADGETS.ORB,
-- inventory.GADGETS.BEAM,
-- inventory.GADGETS.DISC,
}
view:setGadgetAmount(inventory.GADGETS.IMPULSE, inventory.UNLIMITED)
view:setGadgetAmount(inventory.GADGETS.RAPID, 100)
-- Max health of the player. If health is greater than this the health counts
-- down to this value.
view:setMaxHealth(100)
-- Health counts down to loadOut.stats[inventory.STATS.MAX_HEALTH]
view:setHealth(100)
-- Initial armor for player to start with.
view:setArmor(0)
-- Must return original table.
return view:loadOut()
end
Valid Gadgets:
inventory.GADGETS.IMPULSE -- Contact gadget.
inventory.GADGETS.RAPID, -- Rapid fire gadget.
inventory.GADGETS.ORB, -- Area damage gadget. (Knocks players)
inventory.GADGETS.BEAM, -- Accurate and very rapid fire beam.
inventory.GADGETS.DISC, -- Powerful but lond period between firing.
Valid amount are in range [0, 999)
or inventory.UNLIMITED
-- Returns/sets list of gadgets.
function View:gadgets()
function View:setGadgets(gadgets)
-- Returns/sets gadget's amount.
function View:gadgetAmount(gadget)
function View:setGadgetAmount(gadget, amount)
-- Adds gadget with optional amount.
function View:addGadget(gadget, amount)
-- Removes gadget.
function View:removeGadget(gadget)
-- Returns whether powerup is active.
function View:hasPowerUp(powerUp)
-- Returns/sets player's armor
function View:armor()
function View:setArmor(amount)
-- Returns/sets player's health. If health() > maxHealth() health will reduce
-- until it matches maxHealth().
function View:health()
function View:setHealth(amount)
-- Returns/sets player's max health.
function View:maxHealth()
function View:setMaxHealth(amount)
-- Returns player's eye position.
function View:eyePos()
-- Returns players view direction in Euler angles degrees.
function View:eyeAngles()
-- Returns players gadget.
function View:gadget()
-- Returns players id.
function View:playerId()
Called once per frame. actions
is a lua table containing six keys:
lookDownUp
, lookLeftRight
, moveBackForward
, strafeLeftRight
,
crouchJump
and buttonsDown
, denoting the actions retrieved from the
controller. The script has an opportunity to modify the actions and return it to
override the actions to be applied. If nothing or nil is returned, the method
makes no effect.
Callback to support RlCApi
's write_property
.
Return true
if property[key]
is assigned value
. Return false
if
property[key]
cannot hold value
and return nil
if property[key]
does
not exist.
Callback to support RlCApi
's read_property
.
Return value of property[key]
if it exists otherwise return nil
.
Callback to support RlCApi
's list_property
.
Return true if property[key]
is a list and call callback(key, type)
for
each sub-property of property[key]
where key is the full length key to the
sub-property and type a string containing any combination of 'r', 'w' and 'l'.
If the type contains:
- 'l' - Listable and
listProperty(key)
maybe called. - 'r' - Readable and
readProperty(key)
maybe called. - 'w' - Writable and
writeProperty(key, value)
maybe called.
Called during level loading. The classname of any object which may be spawned
after the map has loaded must be returned here. See
pickups_spawn
.
Called by a func_lua_mover entity. kwargs
provides the ID and position of the
mover entity, as well as the triggering player's current position and velocity.
Returns a player position delta table and a player velocity delta table.
Called once per frame when the player focuses on a lookat trigger with id
entityId
. Once the player looks away a final call is made with lookedAt
set
to false
.
The lookat position relative to the trigger's bounding box is given as
position
.
Called once per player joining the game (including bots).
Called whenever there is a reward event. This allows the level script to adjust
the reward received by the engine and to propagate events externally. The
kwargs
are:
- score - Integer default reward associated with in-game event.
- reason - String associated with the event.
- playerId - Player receiving the reward.
- otherPlayerId (optional) - Other player associated with event if there is one.
- location (optional) - Location of the event of there exists one.
- team - One of "free", "red", "blue".
Return either score or nil for default behaviour.
See common/rewards.lua for tools for logging these events.
PICKUP_REWARD
-playerId
touched reward pickup.PICKUP_GOAL
-playerId
touched goal pickup.TARGET_SCORE
- Level triggered a reward atplayerId
.TAG_SELF
-playerId
tagged self.TAG_PLAYER
-playerId
taggedotherPlayerId
CTF_FLAG_BONUS
-playerId
picked up enemy flag.CTF_CAPTURE_BONUS
-playerId
has captured the opposing team's flag.CTF_TEAM_BONUS
-playerId
is part of a team that has captured the opposing team's flag.CTF_FRAG_CARRIER_BONUS
-playerId
tagged opponent(otherPlayerId
) flag carrier.CTF_RECOVERY_BONUS
-playerId
has returned their team flag to the team's base.CTF_CARRIER_DANGER_PROTECT_BONUS
-playerId
is on the same team as the flag carrier and tagged opponent(otherPlayerId
) who dammaged our flag carrier.CTF_FLAG_DEFENSE_BONUS
-playerId
tagged opponent(otherPlayerId
) whileplayerId
orotherPlayerId
is nearplayerId
's flag.CTF_CARRIER_PROTECT_BONUS
-playerId
tagged opponent(otherPlayerId
) whileplayerId
orotherPlayerId
is nearplayerId
's flag carrier.CTF_RETURN_FLAG_ASSIST_BONUS
-playerId
returned the team flag just before a payerId's team captured opponent team's flag.CTF_FRAG_CARRIER_ASSIST_BONUS
-playerId
tagged opponent team's flag carrier just before a capturing event occurred.
A number of helper functions can be found in game_scripts/common
. They can be
used in game scripts via Lua's require
statement, for example:
local make_map = require 'common.make_map'
function api:nextMap()
return make_map.makeMap('G I A P', 'my_map')
end
Returns a Lua object offering a makeMap
and a
commandLine
function.
Returns a Lua object offering a type
and defaults
table for defining pickup
objects.
Game scripts can interact with DeepMind Lab using the dmlab.system.game
module, which can be loaded using local game = require 'dmlab.system.game'
.
The module provides the following functions:
Adds a score to the total score of the current player.
Adds a score to the total score of the player with playerId (1 indexed).
Adds 'command' ready for processing by game engine. Console commands are
all issued directly after api:modifyControl
callback. See Quake 3 reference
materials to for the commands available.
Here are some examples:
Command | Effect |
---|---|
set | Sets any internal to . |
set cg_drawFPS 1 | Draws the FPS counter to the screen |
set cg_fov 70 | Sets hoizontal field of view to 70. |
weapon | Selects gadget. |
weapon 1 | Selects first gadget. |
weapon 2 | Selects second gadget. |
weapnext | Selects next gadget. |
weapprev | Selects previous gadget. |
setviewpos x y z yaw | Sets the player's x y z location and yaw. |
Here are some custom commands:
Command | Effect |
---|---|
dm_pickup id | Lets the player pickup all items with the id 'id'. |
Finishes the current map.
Allows replacing the contents of a previously-loaded texture with the image data
in tensorData
. Raises an error if the texture cannot be found.
Returns the game time spent in the current episode in seconds.
Returns the state of your player in a table,
{
-- Player position in world units.
-- In our text mazes, one grid square is 100 world units.
pos = {forward, left, up},
-- Player velocity in world units per second.
vel = {forward, left, up},
-- Player orientation in degrees.
-- In Euler angles (see https://en.wikipedia.org/wiki/Euler_angles).
-- The relationship between the `pos` and `vel` and `angles` is,
--
-- yaw = 90 degrees
-- +left axis
-- ^
-- |
-- |
-- yaw = 180 degrees <----+----> yaw = 0 degrees
-- -forward axis | +forward axis
-- |
-- v
-- yaw = -90 degrees
-- -left axis
--
angles = {pitch, yaw, roll},
-- Player angular velocity in degrees per second.
anglesVel = {pitch, yaw, roll},
-- Height of the camera above player position.
-- A single value in world units.
height = h,
}
Returns the temporary folder where temporary assets are built and removed when the context is released. This folder can also be used for script temporary storage.
{
width = width, -- Required, sets width of tensor returned.
height = height, -- Required, sets height of tensor returned.
-- Required, sets position in game units of camera.
pos = {x, y, z},
-- Sets the pitch, roll, yaw in degrees of camera.
look = {pitch, roll, yaw},
-- Optional default true, whether to render player entity.
renderPlayer = true,
}
Returns a byte tensor containing the render of the current game at that position and look rotation. The images's origin is top left and interlaced. Note +ve pitch looks down.
Returns the folder where assets are stored. Most assets are placed in a sub-folder called 'baselab'.
Returns 1 if 'endPos' is visible from startPos and less than 1 otherwise.
If the fraction is less than one the first point of obstruction will be:
(endPos - startPos) * fraction + startPos
.
Returns whether the vector connecting startPos
and endPos
falls within the
angular range spanned by fov
with respect to the Euler frame defined by
angles
.
Sets the contents of toFileName
with the contents of fromFileName
. An error
is produced if the file the operation fails for any reason. (It will use
file_reader_override if provided.)
Returns contents of fileName in a string. An error is produced if the file doesn't exist. (It will use file_reader_override if provided.)
Returns contents of fileName in a tensor.ByteTensor. An error is produced if the file doesn't exist. (It will use file_reader_override if provided.)
Game scripts read information about entities with DeepMind Lab using the
dmlab.system.game_entities
module, which can be loaded using local game_entities = require 'dmlab.system.game_entities'
. The module provides the
following functions.
Returns a list of entities that are active that frame. The optional argument is a list of classnames. If provided, the result is filtered such that only entities that match one of the classnames provided are returned.
Each entity in the list returned in a table:
entity = {
entityId = 0, -- Internal id.
id = 0, -- Id provided via spawnVars.
type = 0, -- Type matching 'pickups.type' in common.pickiups.
visible = true, -- Whether the entity is visible. (Placed pickups
-- are marked invisible when picked up.)
position = {x, y, z}, -- Location the pickup is this frame.
classname = "classname", -- The classname of the pickup.
}
Example usage:
local game_entities = require 'dmlab.system.game_entities'
local entities = game_entities:entities{'apple_reward', 'lemon_reward'}
-- Print all apple_reward and lemon_reward locations.
for _, v in ipairs(entities) do
print(unpack(v.position))
end
Game scripts can interact with DeepMind Lab using the dmlab.system.events
module, which can be loaded using local event = require 'dmlab.system.events'
.
The module provides the following functions.
Adds events to be read by the user. Each event has a list of observations. Each observation may be one of string, ByteTensor or DoubleTensor.
Example script:
local events = require 'dmlab.system.events'
local tensor = require 'dmlab.system.tensor'
local api = {}
function api:start(episode, seed)
events:add('Event Name', 'Text', tensor.ByteTensor{3}, tensor.DoubleTensor{7})
end
Spawns an entity into the current scene. The entity must be a string-string
table containing the key 'classname'. They should match internal Quake III Arena
spawnVars
key-value settings.
Example:
local pickups_spawn = require 'dmlab.system.pickups_spawn'
local function _respondToEvent()
pickups_spawn:spawn{
classname = 'strawberry_reward',
origin = '750 450 30',
count = '5',
}
end
-- If the entity is not in the original map it must be registered; otherwise it
-- is not visible.
function api:registerDynamicItems()
return {'strawberry_reward'}
end
DeepMind Lab provides a number of Lua files with auxiliary helper functions in
the game_scripts/helpers
directory. Among them are factories, which are of
the form
local factory = {}
function factory.createLevelApi(kwargs)
local api = {}
-- ...
return api
end
return factory
For examples, see any game_scripts/factories/*_factory.lua
file. Factory
functions can be used as a higher-level API for creating game scripts. As an
example, the lt_chasm.lua
game script is implemented via
local factory = require 'factories.lt_factory'
return factory.createLevelApi{mapName = 'lt_chasm'}
DeepMind Lab maps are identified by strings representing a file name. This
file must contain a Quake III Arena map, see Level
Generation for a description of those.
DeepMind Lab also defines a text level
format which provides a simple text format for creating levels. The Lua module
game_scripts/common/make_map.lua
provides a convenient interface for making
maps from ASCII text level strings on the fly, namely
Generates a map with a given name from a text level description.
args
is a table containing:
mapName
- Name of the map. Acts as a file name and should be unique.mapEntityLayer
- Text level description. For details on the format, see Text Levels.mapVariationsLayer
- Optional part of the text level description, which allows changing the appearance of specific map locations. See Text Levels.useSkybox
- Whether to use a skybox. Ceilings are open when a skybox is used and closed otherwise. Default: true.theme
- Name of the texture set to use in the generated level. For a list of available themes see Text Levels.allowBots
- Whether to generate Area Awareness System (AAS) for bots to navigate the map. Default is false.
An example usage of makeMap
is
local make_map = require 'common.make_map'
...
function api:nextMap()
mapText = 'G I A P'
api._count = api._count + 1
return make_map.makeMap{
mapName = 'luaMap' .. api._count,
mapEntityLayer = mapText
}
end
where api._count
would have been set up in start
. The
tests/empty_room_test.lua
game script file provides a self-contained
example.
Called to enable the script to render text on the screen at arbitrary locations.
args
is a table containing:
width
- Virtual screen width. (Always 640.)height
- Virtual screen height. (Always 480.)line_height
- Distance to move vertically between lines. (Always 20.)max_string_length
- Maximum length of string per message (Always 79.)
The user must return an array of messages. Each message shall contain:
message
- String for the screen to render.x
- X location to render the string. (0 is left edge, 640 is right.)y
- Y location to render the string. (0 is top edge, 480 is bottom.)alignment
- 0 left aligned, 1 right aligned, 2 center aligned.shadow
- (default true). - Whether to add black drop shadow.rgba
- (default {1, 1, 1, 1} ). - Text color and transparency.
Helpers for rendering some message types are in common.screen_message
:
local screen_message = require 'common.screen_message'
function api:screenMessages(args)
local message_order = {
message = 'Find an apple!',
x = args.width / 2,
y = (args.height - args.line_height) / 2,
alignment = screen_message.ALIGN_CENTER,
}
return { message_order }
end
For an example, see: demos/screen_decoration/text.lua.
Called to enable the script to render a filled rectangle at arbitrary locations.
args
is a table containing:
width
- Virtual screen width. (Always 640.)height
- Virtual screen height. (Always 480.)
The user must return an array of tables. Each table shall contain:
x
- X location to render the rectangle. (0 is left edge, 640 is right.)y
- Y location to render the rectangle. (0 is top edge, 480 is bottom.)width
- width to render the rectangle. (0 is left edge, 640 is right.)height
- height to render the rectangle. (0 is top edge, 480 is bottom.)rgba
- colour {R, G, B, A} to render rectangle.
function api:filledRectangles(args)
local redCenterRect = {
x = args.width / 2 - 30,
y = args.height / 2 - 30,
width = 60,
height = 60,
rgba = {1, 0, 0, 1},
}
return { redCenterRect }
end
For an example, see: demos/screen_decoration/rectangles.lua.