NMLTutorial/Spriteset and spritegroup

From TTWiki
Jump to navigationJump to search

Spritesets link an actual graphics file to the NML code. Spritegroups combine multiple spritesets to show different graphics for loaded and unloaded vehicles as well as different graphics for vehicles travelling and vehicles in stations.


Spriteset

The spriteset defines where sprites can be found in a graphics file, how big these sprites are, what their offsets should be with respect to the ingame bounding box and optionally what compression to use for the sprite. The location of the graphics file can be set once per spriteset or for each sprite separately.

The general syntax is as follows:

spriteset (<identifier> [, <graphics_file>]) {
	<list_of_realsprites>
}

The <identifier> is again a name for this spriteset you choose yourself. It's useful to precede the name with spriteset_ followed by the name of the item this spriteset belongs to. This way you know this identifier is that of a spriteset for this particular item and helps keeping unique identifiers. <graphics_file> is optional and may be omitted if you set the path and file name for each sprite individually.

<list_of_realsprites>

The <list_of_realsprites> defines the details of each actual sprite. A vehicle typically has eight sprites in its spriteset. Each sprite definition in the spriteset has one of the following formats:

[left_x, upper_y, width, height, offset_x, offset_y]
[left_x, upper_y, width, height, offset_x, offset_y, filename]
[offset_x, offset_y]
[offset_x, offset_y, filename]

Use the first or second format if you have more than one sprite in a single graphics file. The third format can only be used if the spriteset has exactly one sprite and the graphics file contains exactly the one sprite (and no extraneous white around it). The last format can be used if you have each sprite in a separate file.

An explanation of the values you need to provide:

  • left_x: horizontal distance in pixels from the top left of the image file to the top left of the sprite;
  • upper_y: vertical distance in pixels from the top left of the image file to the top left of the sprite;
  • width: width of the sprite in pixels;
  • height: height of the sprite in pixels;
  • offset_x: horizontal offset in pixels from the center of the bounding box (see below);
  • offset_y: vertical offset in pixels from the center of the bounding box (see below);
  • filename: path and file name of the graphics file this sprite is in, if it is different than the one defined in the spriteset itself.

Each realsprite goes on its own line inside the spriteset. You don't end each line with a semicolon as you would do with regular code lines. So for a vehicle with eight views, you'll end up with a spriteset as the following:

spriteset (<identifier>, <graphics_file>) {
	[left_x1, upper_y1, width1, height1, offset_x1, offset_y1]
	[left_x2, upper_y2, width2, height2, offset_x2, offset_y2]
	[left_x3, upper_y3, width3, height3, offset_x3, offset_y3]
	[left_x4, upper_y4, width4, height4, offset_x4, offset_y4]
	[left_x5, upper_y5, width5, height5, offset_x5, offset_y5]
	[left_x6, upper_y6, width6, height6, offset_x6, offset_y6]
	[left_x7, upper_y7, width7, height7, offset_x7, offset_y7]
	[left_x8, upper_y8, width8, height8, offset_x8, offset_y8]
}

Bounding boxes and offsets

Every object with a height has a bounding box in TTD. This bounding box helps the game draw the sprites in the correct order. You can see this bounding box as a cuboid in which the object is drawn. In OpenTTD, you can view these bounding boxes by pressing ctrl+B (you need to have newgrf_developer_tools active). More details on bounding boxes can be found here.


The offsets you set in a spriteset define where the sprite is drawn inside this bounding box.

For vehicles this is relative to the middle of the bounding box. So both offsets set to zero will put the top left corner of the sprite in the middle of the bounding box. A general rule of thumb here is to take half the width/height (rounded down) and then the negative of that for the x/y offset. This places the vehicle roughly in the middle of it's bounding box.

For buildings and other non-vehicle things the sprites are drawn relative to the furthest (lower) corner of the bounding box. In this case with a 0/0 offset the top left of the sprite is in that particular corner. The rule of thumb here is to take negative half the sprite width for the x-offset and minus sprite height plus diagonal bounding box depth for the y-offset.

Even with these rules of thumb nearly every sprite will need minor tweakings. The ingame sprite aligner (OpenTTD | TTDPatch) is a great tool to help you with that.

Paths

The path to a graphics file can be either absolute to your hard drive or relative to the nml file. It's recommended to use relative paths, as this allows you to move the folder with the NML source files without having to edit all paths and it allows others to use your source without too much problems as well.

Let's take a look at the following folder structure for instance:

mygrf
 |- gfx
 |   |- sprites.png
 |- lang
 |   |- english.lng
 |   |- other_language.lng
 |- custom_tags.txt
 |- mygrf.nml

The (relative) path and filename to be used in the nml file in this case will be gfx/sprites.png.

Spritegroup

A spritegroup combines multiple spritesets into a single entity. It is only available for vehicles and is used to show different graphics for loaded and unloaded vehicles as well as different graphics for vehicles travelling and vehicles in stations.

If you only have different sprites for vehicles at stations and vehicles travelling, the general syntax is as follows:

spritegroup <identifier> {
	loading: <spriteset_identifier1>;
	loaded:  <spriteset_identifier2>;
}

If you also have different sprites for loaded and unloaded states, each line turns into an array:

spritegroup <identifier> {
	loading: [<spriteset_identifier1empty>, <spriteset_identifier1full>];
	loaded:  [<spriteset_identifier2empty>, <spriteset_identifier2intermediate>, <spriteset_identifier2full>];
}

It is up to you how many states between empty and full you want to provide. You can just stick with the two (empty and full), or provide any number of intermediate sprites to show a gradual loading effect.

The <identifier> again is a name for the block you choose yourself. This time it's useful to precede it with spritegroup_, for reasons hopefully known to you by now (if not, read back).

The <spriteset_identifier...> refer each to a spriteset block. You may use the same spritesets in loading and loaded if you for instance want to show open doors in a station. If you end up all spritesets in a spritegroup definition being the same, you can best completely leave the spritegroup out and reference the spriteset directly from where you need it (e.g. in case of the purchase menu sprite).


Continue to learn how to apply this new knowledge to the example road vehicle.


NML Tutorial: Spriteset and spritegroup