Layout
Customizing the game UI probably requires declaring several layouts. Each layout provides a strategy how a given Space visually lays out the elements inside it. Each layout declaration has 3 parts:
- The container that uses this layout
- The contents that it applies to
- The rules for the layout
The container could be the Game, or any Element or group of elements in the game. These are usually Spaces, but can also be Pieces if you ever put Pieces on top of other Pieces. The contents are always immediate children of the container. Often the layout applies to all Pieces in the container, but sometimes just to a subset, or even just to one particular element. The rules are the meat of the layout, and contain all the declarations for sizing, overlapping, stretching, alignment, etc.
Here's a typical example of a layout declaration:
$.deck.layout(Card, { alignment: 'left' }
In this example, the container is the deck space $.deck
, the contents
are any Card
s in the deck, and the rules are that these cards should left
align.
Layout declarations might declare that for a given area, items should be laid out in a row, or a stack, or a grid. When many items enter the same space, it might declare whether they should scale down to fit, should squish together, or whether only a certain number of them should actually be visible.
All layout declarations go in the layout
of the main
render
function, e.g.:
render(setup, {
...
layout: game => {
// layout declarations go here
}
});
The Boardzilla layout engine is powerful and can be complicated, but it is easy to just try things and observe their effects in real-time as you play test. The sandbox is also available as a means of experiementing to get a feel for how it works.
The layout engine works by applying different layout strategies to different
game elements. By default, each element has a simple layout that divides up its
space equally to hold as many items as it has and fits them exactly within the
allotted space. This is the layout you see when you start building your
board. Each layout strategy you apply creates a new invisible grid layer on top
in which things can fit. This invisible grid will grow or shrink as needed to
fill as much of the area as it can. Layouts are applied by calling
layout
on an element or group of
elements. When you apply a new layout
to a space, you declare what element(s), or class of elements, it should apply
to. Any element that enters this space gets put into one of the layouts
depending on whether it applies. If multiple layouts apply, Boardzilla
prioritizes from more specific to more general (e.g. a specific element
overrides one that applies to a class of elements). If no layouts apply, the
default layout gets applied at the very bottom.
Each layout in a space operates completely independently of the others in that space. E.g. if you have a space that might have both Cards and Tokens in it and you apply one layout to Cards and another to Tokens, each Card and Token in that space will be laid out in accordance with its respective layout rules, but Cards will not affect how Tokens are laid out, and vice versa. They are therefore completely free to overlap each other if the layouts use the same area. Later layouts will appear over top of earlier ones in this case.
In the example pictured here, two layouts are declared for a space, one for Circle elements and one for Rectangle elements. Each declares a different grid for their respective elements. When Circles and Rectangles are both added to the space, they overlap each other without interfering with each others grid. The last one declared (Rectangles) appears on top.
Layout parameters
Creating a layout for an area of the board is a series of questions, with different parameters used to answer each one.
- What part of the area should be used to display its contents? (
area
ormargin
) - How large should the grid be to accommodate its contents? (
rows
andcolumns
) - How tightly should they be packed? (
gap
oroffsetRow
/offsetColumn
) - Should they be in a square grid, or something non-orthogonal like a hexagon?
(
gap
oroffsetRow
/offsetColumn
) - How should they fill up their grid as more are added? (
direction
,alignment
andlimit
) - How should they stretch, shrink, or squish together to fit in the area?
(
scaling
andmaxOverlap
)
How these parameters interact can become complicated. The best approach is to experiment in order to find what you're looking for. A helpful layout sandbox is provided here to let you try different parameters and see the results with different elements. Also browsing the sample games to see how their layouts work is a good resource. Of course, refer to the API documentation for the complete descriptions.
Most of the sizing attributes in the layout
parameters are percentage
values. These are generally percentages of the container element's layout, so
e.g.
$.deck.layout(Card, {
area: { left: 25, top: 25, width: 50, height: 50 },
});
Would place the Card
s in the deck
space into an inner region that is 50% of
the deck's width and height.
Note that the layout code is evaluated every time the board changes, so if
e.g. you have a layout on $.field
for only the cards for which card.color
is "red"
, then the layout
declaration might look like:
$.field.layout(
$.field.all(Card, {color: "red"}), {
...
}
);
This will be applied to each Card
in the field
Space with color
equal to
"red"
, and as cards enter or leave the field or change color, the layout will
automatically be re-applied given the new set of red cards in the field.
While working on a particular layout declaration, adding a showBoundingBox
attribute causes the layout's area to visibly appear on screen as a dotted
bounding box. You can give every layout a bounding box by calling
game.showLayoutBoundingBoxes()
. This is an easy way to visually see if your
layout is correct.
Layouts can also be applied to multiple elements at once by calling layout
on
an ElementCollection
. For example, in a card game you would typically supply
layout for all the player's hands with some common rules, e.g.:
game.all('hand').layout(Card, {
columns: { min: 8 },
rows: 1,
});
Customize Controls
The controls (floating boxes with player prompts and inputs) can have their location customized to be contextual with the action taken. There are three methods for controlling placement of these, depending on whether you want the placement to be specific to an Action or specific to a step in the Flow, or general to all:
These all apply layout rules to a control by specifying the
Action
in the case of
game.layoutAction
, or the
playerActions.name
in the case of
game.layoutStep
. Finally,
game.layoutControls
applies to all steps
and actions unless a more specific one applies. Whichever function you use, you
then provide parameters to anchor it to the player's view of the game in a
particular location. This is to allow your game to place these in a suitable
location of the playing area that corresponds to the action needed, and does not
obscure game elements. See ActionLayout
for
details.
Example to place all prompts in the top center of the overall playing area:
game.layoutControls({
element: game,
top: 0,
center: 50,
width: 20
});
The controls can also be targetted with CSS like all Boardzilla elements. The
controls box always have the HTML class player-controls
applied, and other
classes are used for specific elements and actions. See CSS for details.