XMLGameEngine

An engine for describing games built around Xerces, exprtk, and SFML.

View the Project on GitHub beefviper/XMLGameEngine

29. Groups: shared description, separate objects

Status: built (<group> in assets/xmlgameengine.xsd and game_xml.cpp; games/frogger.xml and games/spacerace.xml are written with it; tests/test_group.cpp)

The problem

A lane in Frogger is three logs that share a shape, a speed, a row and a rule (wrap) and differ only in where they start. A lane in Space Race is three bits of debris that share the same things. Written as separate <object>s each one was about 22 lines, so 42 of Frogger’s 59 objects and 27 of Space Race’s 37 repeated what their neighbours already said. A <grid> cannot help: it has one spacing and one velocity for its cells, and its cells are all the same shape.

The tag

A <group> sits beside <object> under <objects>. It says what its members share, then lists the members. A member says only what is different.

<group name="logrow3" class="logs">
  <sprite> ... one brown rectangle ... </sprite>
  <position><y>3 * cell + inset</y></position>
  <velocity><x>-1</x><y>0</y></velocity>
  <collisions>
    <enabled>true</enabled>
    <collision edge="horizontal"><wrap /></collision>
  </collisions>
  <member><position><x>20</x></position></member>
  <member><position><x>272</x></position></member>
  <member><position><x>524</x></position></member>
</group>

One rule covers all of it: whatever a member leaves out, it takes from its group, and after that it must be complete, the same as an <object>; if it is not, the load fails naming the member. So:

Members are ordinary objects. A group is a way of writing objects, not a new kind of thing at run time: game_xml.cpp reads it as one raw object per member and the rest of the engine never sees the difference. Each log wraps by itself, each pad die()s by itself. The engine only remembers which group a member came from (Object::groupName), so the group’s name can be used wherever a name is.

Names follow the grid: a member is logrow3.2, the group’s name and its number counted from 1 in the order written, unless the member carries name="...". The group’s name means all of it (<show object="logrow3" />, object="pads" in a rule or condition, <reset object="logrow3" />) and finds the first member for a lookup by name. Drawing order is file order, members in the order written, so a group is drawn where it stands in the file.

Lockstep. A group whose <collisions> says <lockstep>true</lockstep> gives all its members one lockstep number, so they move and bounce as one block, like the cells of a <grid>. Without it a group only shares a description. (Frogger and Space Race do not use it.)

What it does not do

Is it more compact?

Measured on the shipped files, before and after they were written with groups (same one-element-per-line layout on both sides):

  Lines Bytes Tags
frogger.xml 1526 → 1073 (-30%) 37,614 → 26,079 (-31%) 1047 → 699 (-33%)
spacerace.xml 983 → 697 (-29%) 23,569 → 16,674 (-29%) 674 → 460 (-32%)

About a third smaller, not a fraction of the size, for two reasons:

The rest of each file is untouched: in Frogger, 569 of the 1073 lines are scenery, the frog, the text objects and the states.

Options considered

Option Verdict
One <group> per lane, members overriding what differs Chosen: simple, one rule
Members give full <position> (x and y) and the group shares nothing but the rest Simpler to explain, but repeats the row in every member
Group position as an origin the member offsets from Avoids repeating anything, but reads worse and makes wrap starts (a member starting off-screen at a negative x) less obvious
Groups nested, inner inheriting from outer Bigger saving (the repeated wrap block), needs merge rules for a sprite; a follow-up
Extend <grid> with per-row velocity and a list of positions Fixes lanes only, and only for one shape per grid; groups also cover the hedges, pads and homes
A reusable named template that objects refer to A different idea (see 27); groups need no second definition to point at
Members able to override collisions, actions and variables Not allowed: a member that acts differently is not “orchestrated the same way”; write it as an <object>

Naming note

The flag under <collisions> that makes the cells of a <grid> move as one block used to be called <group>. It is now <lockstep>, which is what it does, and <group> means only this tag.

Open points

See also 21 for the game these lanes belong to.