Advanced graphics for Inform 7

Note: In contrast to the earlier tutorial on the Basic Floorplan Toolkit, this tutorial is mostly code-based. It is intended for newcomers to Glimmr who have a basic level of familiarity with Inform 7. While the tutorial touches on the creation of graphics, no real detail is provided and the user’s familiarity with graphics software is assumed. You may want to download the source code, images, and design files before beginning. The images and design files are in the public domain and may be freely used or modified for use in any project; please let me know if you use them!

One of the most common, and most enduring, symbols of interactive fiction has been the compass rose. It provides the logo for the annual IFComp, the IF Archive, and even the loading progress graphic for the javascript Quixe interpreter. As a symbol, the compass rose suggests traditional IF’s focus on maps and exploration, but of course it also literally represents the medium’s standard means of navigating by means of compass direction. Over the last 10 or 15 years, games such as Arrival and Bronze have been exploiting the form of the compass rose to provide an exit lister–an at-a-glance display of the exits available from the player’s current location. For obvious reasons, most of these compass rose/exit listers have been text-based (see the mini-screenshot below), using characters arranged in a grid. But there have been graphical implementations as well, such as the one in Emily Short’s City of Secrets.

The text-based compass rose from Emily Short's BronzeThe text-based compass rose/exit lister from Emily Short’s Bronze. Red letters indicate that the player has not yet visited the room in that direction.

In this tutorial, we’ll be creating a hybrid compass rose and exit lister using Glimmr Canvas-Based Drawing. Here’s what our exit lister will do: It will show, on a compass rose, the directions available to the player from the current location, and it will also indicate (via a yellow halo or highlight) whether the room in a given direction has yet to be visited. For completeness, there will also be a supplementary rosette to handle the non-compass directions (up, down, in, and out). Finally, and because it requires virtually no additional effort, we will also allow the player to click on any valid exit on the compass to issue the appropriate movement command.

The complete compass rose

The end result of this tutorial, running in Gargoyle.

Preparing the Graphics Files for the Compass

Following this brief specification, we can see that each of the compass’s directional indicators can be in one of three states:

inactive: no exit in the indicated direction
active: there is an exit the indicated direction
active and unvisited: there is an exit, and the player has not been to that room yet.

The easiest way to build a compass rose in Glimmr is to overlay sprites, representing the active exit directions, on a background image of a compass rose. (Sprites are just individual images that are displayed at particular coordinates on screen.) If we incorporate the inactive state of each direction into the background image of the compass rose, we can simply not display any sprite at all for an inactive directional indicator. That leaves us with two states that need to be implemented for each directional sprite, either active or active + unvisited. We will handle that by creating a separate image for each state of each directional indicator, then let our game decide which image is appropriate for a given direction and display that on top of the base image:

Schematic of the construction of the compass rose, with base image at left, sprites in the center, and the composite rendering of base image plus sprites at right. (Dotted lines show invisible outlines of sprite images that have transparent backgrounds.)

Does this mean that we need two sprites for each direction? No, because Glimmr Canvas-Based Drawing implements sprites as objects, storing the image file associated with a sprite in that sprite’s image-ID property. So, we can change the image-ID during play, and thus we can implement the compass with only 8 sprite objects, rather than 16, by simply changing the image-ID as needed (more on this below).

We want the images for each pair of sprite states to be exactly the same size so that we can switch seamlessly between them. We can create the image files using any high-quality graphics editor, such as Photoshop, GIMP, or Inkscape. A detailed discussion of the creation of the graphics is really beyond the scope of this tutorial, but one way to approach it is to design the entire composition as a unit, placing each different type of entity on a different layer in the graphics editor: in other words, the compass rose on one layer, the “active” sprites on another layer, and the “active unvisited” sprites on yet another layer. (I did this step in Adobe Illustrator, and then exported my layered drawing to Photoshop.) When you are ready to extract the sprite images, select the sprite area for the first indicator pair and crop to the size you want the sprite to be. Your graphics editor should tell you the coordinates of the upper left corner of the cropped area–write these down, you will need them later!

 

Crop to each sprite area in turn.

After cropping the image, turn off all of the layers but the “active unvisited” layer, then save the cropped image as a transparent PNG, with a name like “East Unvisited.png”. Then turn off all layers but the “active” layer and save that as “East.png” (these instructions apply to Photoshop; details may be different with other editors). Next, undo until you return to the full size image (before cropping). Move on to the next indicator pair and repeat the cropping process. In a couple of minutes, you will have a nice set of perfectly sized visited/unvisited image pairs, along with their origin coordinates.

Note: To streamline the tutorial, I am ignoring the supplementary rosette that contains the in/out and up/down directional indicators. The rosette is part of the same background image as the compass rose (it is placed beneath the compass), and the sprites are handled in the same way.

Moving the Images into Your Inform 7 Project

We will now have a number of PNG files, including both the compass background and two versions of each directional indicator, and we are now ready to get these files into our Inform project. If you haven’t already done so, download the Glimmr release package and install each of the extensions; you will also need to download and install the Flexible Windows and Fixed Point Maths extensions if you don’t already have them. Then, create a new Inform project and include the Glimmr Canvas-Based Drawing and Glimmr Graphic Hyperlinks extensions, in that order. Next, navigate to the the project directory and create the Materials and Figures folders, if they don’t already exist. (The Figures folder goes inside the Materials folder). Move the image files you created into the Figures folder. See the Inform manual on external image storage if this file management stuff seems too foreign.

The before and after of the file name regular expression replacement, shown in RegExr

The before and after of the file name regular expression replacement, shown in RegExr

Before you can refer to these images in your game, you will need to declare them (see the Inform manual for more information). This can be tedious, so here’s a tip: if you copy the list of file names (on a Mac, select all of the image files in the Finder, then hit Command + C to copy the file names), you can use regular expressions to derive the list of figure declarations directly from the filenames using an advanced text editor, or online at a site like RegExer. Paste the list of filenames into your chosen tool, then search for the expression

(.*)\.png

… and replace it with

Figure of $1 is the file "$1.png".

This will give you a list of figure declarations that you can copy and paste directly into Inform (see the before and after image at right).  So far, our source text should look something like this:

"Compass Rose Tutorial"

Include Glimmr Canvas-Based Drawing by Erik Temple.
Include Glimmr Graphic Hyperlinks by Erik Temple.

Figure of Compass Rose is the file "Compass.png".
Figure of North is the file "North.png".
Figure of Northwest is the file "Northwest.png".
Figure of West is the file "West.png".
Figure of South is the file "South.png".
Figure of Southwest is the file "Southwest.png".
Figure of Southeast is the file "Southeast.png".
Figure of East is the file "East.png".
Figure of Northeast is the file "Northeast.png".
Figure of Up is the file "Up.png".
Figure of Down is the file "Down.png".
Figure of Inside is the file "Inside.png".
Figure of Outside is the file "Outside.png".
Figure of North Unvisited is the file "North Unvisited.png".
Figure of Northwest Unvisited is the file "Northwest Unvisited.png".
Figure of West Unvisited is the file "West Unvisited.png".
Figure of South Unvisited is the file "South Unvisited.png".
Figure of Southwest Unvisited is the file "Southwest Unvisited.png".
Figure of Southeast Unvisited is the file "Southeast Unvisited.png".
Figure of East Unvisited is the file "East Unvisited.png".
Figure of Northeast Unvisited is the file "Northeast Unvisited.png".
Figure of Up Unvisited is the file "Up Unvisited.png".
Figure of Down Unvisited is the file "Down Unvisited.png".
Figure of Inside Unvisited is the file "Inside Unvisited.png".
Figure of Outside Unvisited is the file "Outside Unvisited.png".

Setting up the Window and Background Image

We will be displaying the compass in its own graphics window. (Note for the perplexed: Though the term “window is used by convention, a better term might be “pane”, since all of the so-called windows in a Glulx game are divisions of the root window.)  We will open up this graphics window to the right of the main game window, and request that it always occupy the same proportion of the full window width. To balance things out, we will also open an inventory window–a text window that lists the objects that the player is carrying–immediately below the compass window. The following code block sets up the two windows and also provides some rules for updating the inventory window; see the Flexible Windows extension’s documentation for more on windows.

The inventory-window is a text-buffer g-window spawned by the main-window.
The measurement of the inventory-window is 40.

The graphics-window is a graphics g-window spawned by the inventory-window.
The position is g-placeabove. The measurement is 60.

Window-drawing rule for the inventory-window (this is the construct inventory rule):
	move focus to inventory-window, clearing the window;
	try taking inventory;
	return to main screen.

Every turn when the inventory-window is g-present:
	follow the window-drawing rules for the inventory-window.

Now, the Glulx virtual machine does not allow an author to control the exact dimensions of a graphics window, so, by default, Glimmr Canvas-Based Drawing automatically scales the images and centers them within the available space. As such, we may have a margin on two sides of the compass image, and so we’ll tell Inform to flood the whole graphics window with a color that matches our image. This is done using the “back-colour” property (again, see Flexible Windows). While we’re at it, we’ll also set the back-colour of the inventory window to a pleasing complement. Before setting the windows’ background colors, though, we need to define new named colors, by extending the Table of Common Color Values defined by the built-in Glulx Text Effects extension. We’ll thus be adding something like this to our project:

The window layout after creating the graphic and inventory windows

The window layout after creating the graphic and inventory windows.

Table of Common Color Values (continued)
glulx color value	assigned number
g-SubtleBlue	13755114
g-Creme	16249576

The back-colour of the graphics-window is g-SubtleBlue.
The back-colour of the inventory-window is g-Creme.

So far none of the code we have written is Glimmr-specific; we’ve imply outlined a multi-window layout using Flexible Windows code. See the image at right for the result thus far (click to enlarge).

Let’s add the compass rose background image. First, though, a little explanation. Glimmr Canvas-Based Drawing is so named because it introduces the idea of a “canvas” (borrowing from the HTML5 element of the same name). To define a canvas, we describe its dimensions and assign drawing elements (such as sprites, rectangles, or lines) to it. We can then display that canvas in one or more graphics windows and, if need be, the canvas will be scaled and centered to fit into the window (as will any elements assigned to it). The most basic attributes of a canvas are its dimensions, which we can define either by specifying them directly, or by giving the canvas a “background image”. When a background image is assigned to a canvas, the canvas’s dimensions are automatically set to those of the image. This is especially useful if we have previously defined sprites based on the background image’s coordinate system. Since this is exactly what we did do above, we will define our canvas using the compass rose background. This is quite simple:

The graphics-canvas is a g-canvas.
The background image of the graphics-canvas is the Figure of Compass Rose.

We also need to assign this new canvas to our graphics window, so that Inform will know where to display. Since our game has only one graphics window, it’s also convenient if we just tell the extension that all graphics elements should be associated with our canvas:

The associated canvas of the graphics-window is the graphics-canvas.
The associated canvas of a g-element is usually the graphics-canvas.

Here is what our final layout looks like. The compass doesn’t do anything yet, of course, but we’ll rectify that soon.

Final setup, with background image shown in the graphics window.

Final setup, with background image shown in the graphics window.

Defining the Directional Indicator Sprites

We are now ready to define the directional indicators. As we mentioned above, these are to be sprites–images that are displayed at specific coordinates on our canvas. A sprite is defined as one of a number of subkinds of “g-element” (the g-element is a kind of thing); other kinds of g-elements include “primitives” and “bitmaps”. We will extend this hierarchy of graphic elements by creating a subkind of sprite that will apply only to our directional indicators: the “direction-sprite”. Declaring direction-sprites as a subkind allows us to define properties that apply only to our new kind, saving some memory and, in a complicated project, perhaps helping us to keep things straight.

We need to define two new properties for our direction-sprites. These two properties, which we can call the “visited image” and the “unvisited image”, will store the figure names of the two possible states (as defined above). We can add this block of code to our project:

A direction-sprite is a kind of sprite. 

A direction-sprite has a figure name called the visited image.
A direction-sprite has a figure name called the unvisited image.

The graphlink status of a direction-sprite is g-active.

The final line of this block enables mouse input for direction-sprites: In other words, Glimmr will detect whether the player has clicked on a sprite; if she has, a command will be inserted. All the author needs to do is assign a command for each sprite.

The easiest way to define more than a handful of sprites is to use a table. I like to build my table in a spreadsheet application, like Excel or Google Docs, then copy and paste it into Inform. The tabs that delimit entries are pasted into Inform transparently, so this a convenient method that allows you to take advantage of the ability to paste or move columns as units, which isn’t possible in the Inform IDE. Our table, which we’ll call the Table of Direction Sprites, needs five columns: the name of the sprite, the visited image, the unvisited image, the coordinates on the canvas where the sprite should be displayed, and the command that should be fired when the sprite is clicked with the mouse.

As an example, let’s do the the sprite for north. We’ll call it north-sprite, using the hyphen to avoid any potential conflicts with the word “north” elsewhere in our source code. Looking back to our list of figure names, we see that the image for the active visited state is Figure of North, while the active unvisited is Figure of North Unvisited. The coordinates, which we wrote down when we were producing our images, need to refer to the “origin” of these images; that is, the coordinate pair should describe the upper-left corner of the sprite, expressed in reference to the background image. For example, 100 by 10 means that the upper-left corner of the sprite will be placed 100 pixels to the right and 10 pixels down from the upper-left corner of the background image. In Glimmr, coordinates are written in the form of an Inform list; that is, using curly braces, e.g. {100, 100}. So, our origin coordinate for north will be  {448, 103}. Finally, when the player clicks on the north indicator, we want it to produce the command “go north“, so we’ll make that–the “linked replacement-command” property of the sprite–our fifth column.

direction-sprite visited image unvisited image origin linked replacement-command
north-sprite Figure of North Figure of North Unvisited {448, 103} “go north”

(Here is the full table as a Google spreadsheet.)

We also need to relate each directional indicator to the direction itself: in other words, the sprite for north with the direction object for north. There are multiple ways to do this (as with most everything in Inform), but for now, let’s just be sure to list the direction-sprites in our list in the same order as the direction objects are listed in Inform’s Standard Rules:

Some direction-sprites are defined by the Table of Direction Sprites.

Table of Direction Sprites
direction-sprite visited image unvisited image origin linked replacement-command
north-sprite Figure of North Figure of North Unvisited {448, 103} “go north”
northeast-sprite Figure of Northeast Figure of Northeast Unvisited {688, 193} “go northeast”
northwest-sprite Figure of Northwest Figure of Northwest Unvisited {146, 196} “go northwest”
south-sprite Figure of South Figure of South Unvisited {466, 836} “go south”
southeast-sprite Figure of Southeast Figure of Southeast Unvisited {702, 723} “go southeast”
southwest-sprite Figure of Southwest Figure of Southwest Unvisited {150, 719} “go southwest”
east-sprite Figure of East Figure of East Unvisited {827, 465} “go east”
west-sprite Figure of West Figure of West Unvisited {76, 469} “go west”
up-sprite Figure of Up Figure of Up Unvisited {472, 1052} “go up”
down-sprite Figure of Down Figure of Down Unvisited {456, 1221} “go down”
in-sprite Figure of Inside Figure of Inside Unvisited {388, 1126} “go inside”
out-sprite Figure of Outside Figure of Outside Unvisited {558, 1138} “go outside”

Now let’s associate each of Inform’s direction objects with the appropriate direction-sprite. We do this to make it easier to decide which direction-sprites to activate at any given moment: once these are linked, we will be able to simply iterate through the directions and, wherever the direction is a valid exit from the current location, we activate the linked direction-sprite.

To set up the linkage, we assign each direction object a property, the “dir-sprite” property, that will point to a given direction-sprite. Then we exploit the fact that our Table of Direction Sprites list the direction-sprites in the same order as the direction 0bjects: we just iterate through the directions and assign the sprite in the matching row of the table to that direction. We also take this opportunity to initialize the sprite’s “image-ID” property to the sprite’s “visited image”.

We do all this at the beginning of the game, right before we open the graphics window, because the compass will be drawn as soon as the graphics window is opened. (Glimmr Canvas-Based Drawing automatically draws whatever is on the canvas as soon as the window is opened.)

A direction has a direction-sprite called the dir-sprite.

To link sprites with directions:
	let count be 1;
	repeat with heading running through directions:
		choose row (count) in the Table of Direction Sprites;
		now the dir-sprite of the heading is the direction-sprite entry;
		now image-ID of direction-sprite entry is visited image entry;
		increment count.

When play begins:
	link sprites with directions;
	if glulx graphics is supported:
		open up the graphics-window.

The Exit Lister Functionality

The final step in creating our graphical exit lister is to write the routine that will select and display the directional indicators. But when in the turn seqence should we update the compass? In Inform, the looking action is probably the most reliable place to reset the compass, because it is triggered automatically each time the player enters a new location, so that the room description can be printed. So, we’ll hook into the looking action for our graphics window updates.

The logic we’ll use for our compass follows that in Emily Short’s treatment of the textual compass rose in Bronze, though of course the actual code doesn’t resemble the latter very strongly. First, we assume that the player will be aware of an exit if either the room he is in or the place he wants to go is lit; in either case, we can assume he will be aware of a path between the two spaces. We can write a definition to capture this:

Definition: A room is compass-ready:
	if it is lit, yes;
	if in darkness, no;
	[otherwise] yes.

We can now ask if a room is “compass-ready” to determine whether it should be activated or not.

The final bit of code is a rule added to the looking action, and will fire each time the player looks in any room. This rule does four things:

  1. Iterate through the directions, checking each to see whether a compass-ready room lies that way;
  2. If there is a compass-ready room in a given direction, choose the appropriate sprite (based on whether the room is visited or unvisited) and assign it as the image-ID of the direction-sprite associated with that direction;
  3. Make the sprite visible if there was a compass-ready room; otherwise, make the sprite invisible.
  4. After all of the directions have been checked in this way, redraw the graphics window to reflect our updates.

Here’s the code:

Carry out looking (this is the compass rose updating rule):
	repeat with heading running through directions:
		let place be the room heading from the location;
		if place is a room and place is compass-ready:
			if place is unvisited:
				now the image-ID of the dir-sprite of the
				  heading is the unvisited image of the
				  dir-sprite of the heading;
			otherwise:
				now the image-ID of the dir-sprite of the
				  heading is the visited image of the
				  dir-sprite of the heading;
			activate the dir-sprite of the heading;
		otherwise:
			deactivate the dir-sprite of the heading;
	follow the window-drawing rules for the graphics-window.

Now, to actually be able to do anything with the code, we need a map to move around in. Feel free to insert your own, of course, or you may use the following code, which also gives the PC some belongings that will show up in the inventory window.

The Front Lawn is south of the Porch.
The East Lawn is northeast of the Front Lawn.
The West Lawn is northwest of the Front Lawn.
The Rear Lawn is northwest of the East Lawn.
Southwest of the Rear Lawn is the West Lawn.
The Dining Room is east of the Foyer.
The Living Room is west of the Foyer.
The Kitchen is north of the Dining Room.
Bottom of the Stairs is north of the Living Room.
The Hallway is east of Bottom of the Stairs, north of the Foyer,
   and west of the Kitchen.
Top of the Stairs is up from Bottom of the Stairs.
Master Bedroom is east of Top of the Stairs.
Master Bath is east of the Master Bedroom.
West Hall is south of Top of the Stairs.
East Hall is east of West Hall and south of the Master Bedroom.
East of East Hall is Front Bedroom. 

The front door is a closed door.
It is inside from the porch and outside from the Foyer.
"The front door is [if the front door is open]open[otherwise]closed[end if]."

A tree is scenery in Rear Lawn. "Looks climbable."
Instead of climbing the tree: try going up.

Up from the Rear Lawn is Treetop.
"A tall tree grows here. Large branches scrape against the house."
The description of Treetop is "You could climb into the house
   through the window. Type IN."

A window is an open door. It is not openable. "The window is open."
The window is inside from the Treetop and outside from the Master Bedroom.
The Master Bath is dark.

The player carries a compass.
The player wears a derby hat and a fine coat.

Instead of doing anything with the compass:
	say "You always keep your trusty compass by your side.
	  You would never dream of abandoning, destroying, or concealing it."

And that’s it! You can download all of the files for this tutorial, including the original vector and raster files, the sprite images, and the final gblorb file (use an interpreter such as Gargoyle or WinGlulxe to play), here. The images and design files are in the public domain and may be freely used or modified for use in any project. Enjoy! Please feel free to use the comments section for questions, clarifications, suggestions, or … comments!

Advertisements

Comments on: "Compass Rose / Exit Lister Tutorial" (4)

  1. This just came up because I was copying and pasting, but where you have ‘Figure of $1 is the file “$1.png”‘, you need a period at the end, or Inform will be unhappy.

  2. Thanks, I’ve fixed that.

  3. Patrick said:

    It seems there is an error in the extension Glimmr Graphic Hyperlinks. My complete code:

    Include Glimmr Canvas-Based Drawing by Erik Temple.
    Include Glimmr Graphic Hyperlinks by Erik Temple.

    MyRoom is a room.

    When I try to build the code, I get the following errors:

    Problem. It looks as if ‘Table of Common Color Values (continued)’ is meant to be related to an existing table, but I can’t find one if it is.

    Perhaps you’ve put the new part before the original? The original has to be earlier in the source text.

    ——————————————————————————–
    In Part 6b – Link Management (for use with Glimmr Canvas-Based Drawing by Erik Temple) in the extension Glimmr Graphic Hyperlinks by Erik Temple:

    Problem. You wrote ‘Table of Graphlink Glulx Replacement Commands linkid g-win p-top p-bottom p-left p-right replacement alt action an object a g-window a number a number a number a number some text number stored action with 120 blank rows’ : but this seems to give something a name which is reserved for Inform’s own internal use, so is not allowed. There are only a few of these – ‘storage’, ‘variable’, ‘condition’, ‘phrase’ and ‘action’, for example. But whatever you were making here, you’ll need to call it something else.

    ——————————————————————————–

    Problem. In row 1 of the table ‘Table of Graphlink Glulx Replacement Commands’ , the entry ‘stored action’ won’t fit, because its row is already full. (This entry would be in column 9 and the table has only 8.)

    • Hi Patrick,

      I strongly suspect that you are trying to use Glimmr with the latest version of Inform. Glimmr works only with Inform 6G60. You’ll need to use that version of Inform, and you’ll likely also need to use the older versions of built-in extensions and other key extensions such as Flexible Windows. I’ve ceased developing Glimmr, so this situation isn’t likely to change. Sorry!

      By the way, a better place to post problems would be in the forum at http://www.intfiction.org/forum/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: