Difference between revisions of "NMLTutorial/Spriteset and spritegroup"
(Clarify how a spriteset looks with realsprites in it.) |
(compression isn't interesting for someone learning NML, so let's not bother the reader with that) |
||
(4 intermediate revisions by the same user not shown) | |||
Line 9: | Line 9: | ||
The general syntax is as follows: |
The general syntax is as follows: |
||
− | <pre> |
+ | <pre class="pseudo"> |
spriteset (<identifier> [, <graphics_file>]) { |
spriteset (<identifier> [, <graphics_file>]) { |
||
<list_of_realsprites> |
<list_of_realsprites> |
||
Line 15: | Line 15: | ||
</pre> |
</pre> |
||
− | The <code><identifier></code> is again a name for this spriteset you choose yourself. It's useful to precede the name with ''spriteset_'' |
+ | The <code><identifier></code> 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. <code><graphics_file></code> is optional and may be omitted if you set the path and file name for each sprite individually. |
=== <list_of_realsprites> === |
=== <list_of_realsprites> === |
||
The <code><list_of_realsprites></code> 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: |
The <code><list_of_realsprites></code> 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: |
||
− | <pre> |
+ | <pre class="pseudo"> |
[left_x, upper_y, width, height, offset_x, offset_y] |
[left_x, upper_y, width, height, offset_x, offset_y] |
||
− | [left_x, upper_y, width, height, offset_x, offset_y, compression] |
||
[left_x, upper_y, width, height, offset_x, offset_y, filename] |
[left_x, upper_y, width, height, offset_x, offset_y, filename] |
||
− | [left_x, upper_y, width, height, offset_x, offset_y, compression, filename] |
||
[offset_x, offset_y] |
[offset_x, offset_y] |
||
− | [offset_x, offset_y, compression] |
||
[offset_x, offset_y, filename] |
[offset_x, offset_y, filename] |
||
− | [offset_x, offset_y, compression, filename] |
||
</pre> |
</pre> |
||
− | Use |
+ | 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: |
An explanation of the values you need to provide: |
||
Line 38: | Line 34: | ||
* '''width''': width of the sprite in pixels; |
* '''width''': width of the sprite in pixels; |
||
* '''height''': height 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 |
+ | * '''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 |
+ | * '''offset_y''': vertical offset in pixels from the center of the bounding box (see below); |
− | * '''compression''': [http://newgrf-specs.tt-wiki.net/wiki/NML:Realsprites compression] to use for this sprite, omit it if you don't care; |
||
* '''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. |
* '''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: |
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: |
||
− | <pre> |
+ | <pre class="pseudo"> |
spriteset (<identifier>, <graphics_file>) { |
spriteset (<identifier>, <graphics_file>) { |
||
[left_x1, upper_y1, width1, height1, offset_x1, offset_y1] |
[left_x1, upper_y1, width1, height1, offset_x1, offset_y1] |
||
Line 58: | Line 53: | ||
</pre> |
</pre> |
||
− | ==== Bounding boxes ==== |
+ | ==== 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. |
+ | 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 <code>newgrf_developer_tools</code> active). More details on bounding boxes can be found [http://newgrf-specs.tt-wiki.net/wiki/PalettesAndCoordinates#Coordinates here]. |
+ | |||
− | In OpenTTD, you can view these bounding boxes by pressing ctrl+B (you need to have <code>newgrf_developer_tools</code> active). More details on bounding boxes can be found [http://newgrf-specs.tt-wiki.net/wiki/PalettesAndCoordinates#Coordinates 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 ([http://wiki.openttd.org/NewGRF_Debugging OpenTTD] | [http://www.tt-wiki.net/wiki/GRFAuthorHelperWindow TTDPatch]) is a great tool to help you with that. |
||
=== Paths === |
=== Paths === |
||
Line 78: | Line 80: | ||
The (relative) path and filename to be used in the nml file in this case will be <code>gfx/sprites.png</code>. |
The (relative) path and filename to be used in the nml file in this case will be <code>gfx/sprites.png</code>. |
||
− | |||
== Spritegroup == |
== Spritegroup == |
||
Line 85: | Line 86: | ||
If you only have different sprites for vehicles at stations and vehicles travelling, the general syntax is as follows: |
If you only have different sprites for vehicles at stations and vehicles travelling, the general syntax is as follows: |
||
− | <pre> |
+ | <pre class="pseudo"> |
− | spritegroup < |
+ | spritegroup <identifier> { |
− | loading: < |
+ | loading: <spriteset_identifier1>; |
− | loaded: < |
+ | loaded: <spriteset_identifier2>; |
} |
} |
||
</pre> |
</pre> |
||
Line 94: | Line 95: | ||
If you also have different sprites for loaded and unloaded states, each line turns into an array: |
If you also have different sprites for loaded and unloaded states, each line turns into an array: |
||
− | <pre> |
+ | <pre class="pseudo"> |
− | spritegroup < |
+ | spritegroup <identifier> { |
− | loading: [< |
+ | loading: [<spriteset_identifier1empty>, <spriteset_identifier1full>]; |
− | loaded: [< |
+ | loaded: [<spriteset_identifier2empty>, <spriteset_identifier2intermediate>, <spriteset_identifier2full>]; |
} |
} |
||
</pre> |
</pre> |
||
Line 103: | Line 104: | ||
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. |
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 <code>< |
+ | The <code><identifier></code> 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 <code>< |
+ | The <code><spriteset_identifier...></code> refer each to a spriteset block. You may use the same spritesets in <code>loading</code> and <code>loaded</code> 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). |
Latest revision as of 18:58, 8 September 2011
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