Create a Studio plugin
Studio plugins extend the Studio map renderer without replacing the map component. Use them for client-side Studio tools such as debug overlays, editor helpers, visual diagnostics, or custom map rendering aids. Studio plugins are client-side rendering extensions. They do not create gameplay authority and they do not replace server-side map, event, or collision logic.Built-in debug plugin
The simplest way to display Studio collision overlays is the compatibility shortcut:hitbox() width and height. This can differ from the visible sprite bounds when
the sprite is larger than its collision box.
Player hitboxes are displayed by default with the same overlay. Set
players: false to hide the player collision box while keeping event collision
boxes visible.
You can also attach it explicitly:
Create a plugin
A Studio plugin is an object with anid and optional renderer hooks.
studioPlugins:
Renderer hooks
UseterrainOptions(ctx) to add options to the Studio terrain renderer:
elementRenderOptions(ctx) to add options to Studio element rendering:
eventLayerPixiChildren(ctx) to add Pixi objects to the event layer. The returned objects are mounted next to Studio events, players, low elements, and wall occlusion sprites.
__studioMapPluginUpdate, Studio calls it every frame while the child is mounted.
Plugin context
Each hook receives:engine: the active client engine.mapanddata: the current Studio map render data.
Map extensions: tools that edit the running map
registerStudioMapExtension (from @rpgjs/studio/client) calls a function each time a Studio map is loaded, for tools that change the map while it runs, such as a map editor. The function receives a context with the engine, the map id and size, the placed elements as stored, and these helpers:
resolveElement(placed, layer): what the runtime draws for a placed element, ornull.readData()andupdateData(patch): read the data the scene shows, and merge new data into it (the scene is drawn again).addElementTileset(tileset): teaches the map an element set it was not loaded with (a set the person added while editing), so that its elements can be resolved, placed, drawn and given collisions.tilesetis the media of the set ({ _id, fileName, metadata: { elements, drawRules } }); a set the map already has is replaced. Resolves once its picture is loaded.rebuildElementCollisions({ low }): make the collisions follow the placed elements (see below).
Fading layers
updateData({ layerOpacity }) draws layers see-through. Each key is optional, from 0 to 1, and defaults to 1. Characters, events and collisions are not affected. Pass layerOpacity: undefined to put everything back.
Params without a reload
updateParams(patch) changes the params of the map the scene shows, without loading it again: the scale, the weather kept in the
params, and the sounds. backgroundMusic and backgroundAmbientSound are the addresses the client plays from: give them
await context.resolveAudio(value) for a value of the map settings (a stored file name, a media, a media id), and undefined to
stop the sound. A change of scale shows on the picture only: the collisions are not made again, so a game that is played after
it should be loaded again.
The size a map gives to an event
A placement of an event on a map (events: [{ eventId, x, y }]) can carry the size this map gives to it, so the same event is bigger here than there:
scale(number above 0, default1): multiplies the character and the hitbox of that placement.hitbox({ width, height }, above 0): the hitbox this map gives to the placement instead of the one of the event, in the same unit as the event’s own hitbox (before the scale).
PUT /api/maps/:id, a value that is not a size is a 400) and sends them with the game’s map; the runtime applies them when it makes the event (character through its graphic scale, hitbox through the hitbox of the event). resolveStudioEventDisplay({ media, frame, hitbox, scale }) (from @rpgjs/studio/client) takes the same scale, so a map editor draws the event at the size the player sees. placeStudioEvent (below) takes them too.
Events that follow the editor
A map editor that runs in the same page as the server (a standalone game) can change the events the game runs without loading the map again. The Studio server map has these methods; positions are those the map stores (the game applies the map scale):placeStudioEvent(record, { x, y, runtimeId, scale?, hitbox? }): puts an event of the project on the map as the load does (media resolved, workflows and hitbox of the project), so an enemy attacks and a character answers. It resolves toruntimeId, orundefinedwhen an event already has that id.recordis the event as the project stores it ({ _id, ... }).moveStudioEvent(runtimeId, x, y): moves an event; resolves tofalsewhen there is none.removeStudioEvent(runtimeId): takes an event off the map;falsewhen there is none.listStudioEvents(): the events the game runs, as{ runtimeId, sourceEventId }. An event of the map has the id of its event, thenid::2,id::3for each other placement of it.
Collisions that follow the elements
The hitboxes of the placed elements are made when the map loads. After a tool adds, moves or removes elements, callrebuildElementCollisions with the placed elements of the elementsLow layer as stored ({ id, tilesetId, x, y, … }):
elementsLow layer, polygon hitboxes, scale, draw rule), and leaves the others (base map, terrain) alone. It works on the client map, which the hero predicts against, and tells the server, which does the same on its map from the tilesets it holds: the client says where the elements are, never what their hitboxes are.
The server only follows a player it allows. Install the server module with an authorize function; without it, the server ignores the message and only the client prediction changes: