SG: Display Markup Language

Page content

SkeletonGame’s display markup language:

SkeletonGame exists to make it easier for you to get your game going. The YAML based display layer markup syntax was intended to make it “easier” to describe complex layer compositions for display on the PyProcGameHD framework, without requiring the programmer to generate code to do so. It was intended just to provide a short hand for a few Layer types. I use it in the attract mode, and it obviously has uses elsewhere too.

It was never intended to become an interface to the entirety of the display power of the framework … but there has been feature creep.

Can I use this in mode code??

Absolutely. Your mode has a layer. That layer is accessible via self.layer –the yaml parser is in the game’s dmdHelper so code such as:

self.layer = self.game.dmdHelper.generateLayerFromYaml(yaml_struct) 

will work, provided yaml_struct contains a valid format, as described below.

The YAML format

Let’s start by taking the canonical example of where this is used and likely to be first encountered, the attract.yaml file. This file starts with a Sequence tag that indicates what follows is a list of layer types that are to be played in, get this: a sequence!

What makes the sequence type “special” is that it augments each of the layer that it contains with three additional tags: * [Required] duration: –a number of seconds that this layer should be shown. * [Optional]lampshow: –the name of a lampshow to be played when this part of the sequence is shown on the display. * [Optional] sound: –the name of a sound to be played when this part of the sequence is shown on the display.

The values for lampshow and sound (if present) should correspond to keys that can be found in the asset_list.yaml file if you expect it to work. Again, every list entry in the Sequence should define a duration

The Sequence contains a list of layers. The following documents the syntax for the layers:

Types of Layers:

Combo: a combination of simple text, shown in front of an image or animation

The Combo layer is the simplest way to get text on the screen The line (or lines) given to the combo layer will be centered on the display. If multiple lines are specified, then they will be centered vertically.

Example:

Combo:
    Text:
        - "My Game"
        - ""
        - "v1.0"
    Font: med
    # FontStyle: None # this line is commented out so it will use default style
    Animation: dark_chrome

In the example above, this entry will show three lines of text (the middle line being blank) drawn in a Font named med (as defined in the asset_list.yaml). No FontStyle has been specified (notice the line starts with a # which comments out the line). This text will be shown on top of some image or animation named dark_chrome which is presumably defined in the asset_list.yaml.

A FontStyle can be defined in two different ways: one, as a string that maps to a key in the asset_list.yaml –very useful if you plan to use the same style over and over again. The other way is with an in-line definition. That looks something like this:

Combo:
    Text:
        - "My Game"
        - ""
        - "v1.0"
    Font: med
    FontStyle:
        interior_color: [0,0,255]  # R,G,B, each 0..255 --fill the text blue
        line_width: 1              # a one "dot" border will surround the text
        line_color: [255,0,0]      # that border will be red
    Animation: dark_chrome

Animation

Super simple. Show a single frame or animation with a key found in the asset_list. Example:

Animation:
    Name: t800-war

So we expect to find an entry keyed t800-war in the asset_list.

If you want more control/power, see the #animation# object, later in this document.

RandomText

An idea from scottdanesi, this is a Combo layer that allows you to specify multiple options for the text and, at playback, one will be selected at random each time the layer is shown.

Like Combo, it shares Font, FontStyle and Animation tags. What is different is the Header tag which, if present, will add a line of text at the top of each of the options when displayed. The list of options are tagged TextOptions which is a list of Text entries, each of which is a list, of multiple lines of text which might be shown (as in the Combo layer)

RandomText:
    Header: Instructions ## omit line completely to specify 'None'
    TextOptions:
        - Text:
            - ""
            - do not drink
            - gasoline
            - ""
        - Text: ["", "", "do not swim in", "the ocean", ""]
        - Text: don't panic
    Font: small
    Animation: dark_chrome
    FontStyle: weird

This layer has three different options that will be chosen at random, but all would be rendered in front of the dark_chrome asset, in the small Font, in the weird FontStyle.

HighScores

Multiple frames will be generated (a sequence of frames) for each of the system’s recorded high scores. You can control the Font, FontStyle, a background Image/Animation, and the order in which the stats appear on the display. Of note, the duration tag here is required and each of the frames will be shown for the given duration

For example, if there are four high scores and a duration of 4.0, then the entire high score sequence will take 16 seconds.

HighScores:
    Font: tiny
    FontStyle: weird
    Background: dark_chrome
    Order:
        - player
        - category
        - score
    duration: 4.0

LastScores

Multiple frames will be generated (a sequence of frames), one for each of the player’s score from the last game played. You can control the Font, FontStyle, a background Image/Animation. The duration tag again is used to indicate how long each frame is shown. If this is the first launch of the game and there was no immediate prior play, then this frame will not be generated.

LastScores:
    Font: large
    FontStyle: 
        interior_color: [130,230,130]
        line_width: 1
        line_color: [60,60,60]
    Background: dark_chrome
    duration: 2.0

“power” layers

The following layers are actually quite powerful and flexible. They are indicated by lower case layer names and the _layer suffix naming convention.

All of the _layers that follow provide the user with the optional ability to specify the x and y location (relative to the top left corner of the display and the image) as well as a width and height.

These values are specified in a fairly flexible format:

  • if a positive int is specified, the value is taken as-is
  • if a negative int is specified, the value is taken as offset from the relative value (e.g., for x or width, this is the width of the DMD display)
  • if a float is specified the value is taken as a percentage of the relative value (e.g., 0.25 is 25% of the display width, in the case of width or x)
  • if a string value matching the key is specified, the relative value is returned (so specifying width: width has the same effect as specifying 1.0 as 100%)

When width and height are omitted, the framework will try to compute the values on its own. This may be incorrect in certain pathological cases. When something seems amiss and you cannot see the entirety of your layer content, you may wish to set these values as width:width and height:height just to check.

animation_layer

The Name: tag remains from the Animation layer, however the animation_layer provides quite a bit more control over the animation playback.

The following tags are supported to adjust the specific settings for this play-through of the animation:

  • [Optional] hold_last_frame: If True, the last frame will be held if the animation is shown for longer than the time it takes to play all of the frames. The alternative (i.e., False) is that a blank frame is shown. If unspecified, the default value is the opacity set in the asset_list

  • [Optional] opaque: When True the player can see through this frame (not terribly useful unless the animation has transparent pixels and is part of a group). If unspecified, the default value is the opacity set in the asset_list

  • [Optional] frame_list: A list of frame indices to be shown. For example a value of [0,1,2] would only show the first three frames of the named animation. A value of [0] would only show the first frame. A value of [-1] would only show the last frame.

The following example shows only the final frame of the animation found in the asset_list with key intro:

animation_layer:
    Name: intro
    frame_list: [-1]
    hold_last_frame: True

markup_layer

This layer exposes the ‘MarkupLayer’ provided in PyProcGame, however this has been updated for HD functionality. This layer is commonly used as a “Special Thanks To:” layer, or to provide a large amount of text, as it supports multi-line text and some basic formatting (two different font styles, left justify, right justify, and centered text, all on the same layer). This does not support an animation tag for a background. If you want one, you’ll have to provide your own through a group_layer. Similarly, it does not scroll/pan. If you want panning, you will need to enclose this layer in a panning_layer

markup_layer:
    width: 450
    Bold: 
        Font: med
        FontStyle: blueish
    Normal: 
        Font: small
        FontStyle: blueish
    Message: 
        - "#Gerry Stellenberg#"
        - "#Adam Preble"
        - "Josh (Rosh)#"
        - "[Scott Danesi"
        - "Matt Bonemma]"
        - "{some-logo}"

In the example above, notice that two Fonts are defined: Bold and Normal Lines with a [ are rendered in Normal Font & FontStyle, and those with a # are considered Bold. Lines that have style indicators at the start and end of the line of text are centered. An indicator only on the left indicates the line is left justified. An indicator only on the right means the line will be right justified. width can be used to control the width of the rendered frame.

The final entry in the list, is surrounded with curly-braces. The markup layer will replace that element with an inline drawing of the first frame of the animation/image with key matching some-logo in the asset_list.

panning_layer

This layer exposes the PanningLayer provided in PyProcGame/HD. It provides the ability to scroll/pan the layer that it contains.

panning_layer:
    width: 500
    height: 500        
    origin_x: 0
    origin_y: -130 
    scroll_x: 0
    scroll_y: 2
    frames_per_movement: 2 
    bounce: False
    contents:
        markup_layer:
            width: ...

The origin_x and origin_y represent where the layer will start on the display. This allows the layer to “scroll into view” starting from off screen. Similarly scroll_x or scroll_y can be specified as zero to not move or negative to scroll left or up. frames_per_movement is how many frames need to pass (w.r.t. frames per second) before the next scroll_x and scroll_y are added to the layer’s current location. If True, bounce indicates that the layer will start moving in the opposite direction if it hits the edge of the display.

group_layer

This layer allows you to group multiple layers just as the PyProcGame GroupLayer does in code. The contents are a list of other layers (potentially including other group_layers if you really want to)

group_layer:
    width: 500
    height: 500
    contents:
        - animation_layer:
            name: dark_chrome
        - markup_layer:
            width: 450
            Bold: 
                Font: med
                FontStyle: blueish
            Normal: 
                Font: small
                FontStyle: blueish
            Message: 
                - "#Gerry Stellenberg#"
                - "#Adam Preble"
                - "Josh (Rosh)#"

Items in the content list are added “bottom up” so the first item will appear beneath the next item, which appears beneath the next and so on…

text_layer

About as fine-grained as you can get, the text_layer is the mechanism to describe a single line of text for the display. Sure, it supports Font and FontStyle, and there is a slight change in the use of Text to describe a single lines worth of text, however you can also specify relative x, y, v_justify (vertical justification, either top, bottom, or center), h_justify (horizontal justification, either: left, right, or center), width.

In addition, a blink_frames tag can be added to specify a periodicity at which the text will appear/disappear. For example, blink_frames:15 would cause the text to appear for 15 frames then disappear for 15 frames (and that cycle would repeat). At 30fps (depending on your config.yaml) this is half a second on, half a second off.

Examples go a long way:

text_layer:
    x: width
    y: height
    h_justify: right
    v_justify: bottom
    Text: "Look down here"
    Font: med
    duration: 1.0

Another:

text_layer:
    x: 0
    y: 0
    h_justify: left
    v_justify: top
    Text: "Look up here"        
    Font: med
    duration: 1.0

Another:

text_layer:
    x: .50   # 50% of the display width
    y: -5    # 5 "dots" up from the bottom
    h_justify: center
    v_justify: bottom
    Text: "Look over here"        
    Font: med
    duration: 1.0