NMLTutorial/Road vehicle graphics
The example used here is from the NML source. The code for this was originally written by Terkhen and planetmaker. The graphics used in the example are by DanMack and Zephyris. Code and graphics are both licensed according to the GPL v2 or later. The code has been modified for the purpose of this tutorial
This continues the second part of the road vehicle example. Now that you know how to link the graphics to the NML code, let's do that for the road vehicle and give it some graphics!
Example graphics
This will be the graphics for our road vehicle. All sprites are contained in a single file:
For this example, the graphics file will be stored in a folder named gfx which resides next to the nml file and the graphics file will be named flatbed_truck_1_goods.png. The relative path therefore will be gfx/flatbed_truck_1_goods.png.
Spritesets
Each road vehicle has eight sprites. Because we have two sets of eight sprites (empty and full), we need a spritegroup for each of these sprites. Both sprite groups will contain eight sprite references. Let's name the spritegroups spriteset_flatbed_truck_1_goods_empty and spriteset_flatbed_truck_1_goods_full and have a look at the code:
//graphics definition spriteset(spriteset_flatbed_truck_1_goods_empty, "gfx/flatbed_truck_1_goods.png") { //left_x, upper_y, width, height, offset_x, offset_y [ 0, 0, 8, 18, -3, -10] [ 16, 0, 20, 16, -14, -7] [ 48, 0, 28, 12, -14, -6] [ 96, 0, 20, 16, -6, -7] [ 128, 0, 8, 18, -3, -10] [ 144, 0, 20, 16, -14, -7] [ 176, 0, 28, 12, -14, -6] [ 224, 0, 20, 16, -6, -7] } spriteset(spriteset_flatbed_truck_1_goods_full, "gfx/flatbed_truck_1_goods.png") { //left_x, upper_y, width, height, offset_x, offset_y [ 260, 0, 8, 18, -3, -10] [ 276, 0, 20, 16, -14, -7] [ 308, 0, 28, 12, -14, -6] [ 356, 0, 20, 16, -6, -7] [ 388, 0, 8, 18, -3, -10] [ 404, 0, 20, 16, -14, -7] [ 436, 0, 28, 12, -14, -6] [ 484, 0, 20, 16, -6, -7] }
Spritegroup
With this done, we can combine both in a spritegroup. We have graphics for full and empty, but these are not different in stations than while on the road. So both loaded
and loading
will have the same definition of two spriteset.
And because a spriteset needs to exist be fore it can be referenced, the spritegroup definition goes below the spriteset definitions!
The code:
spritegroup spritegroup_flatbed_truck_1_goods { loaded: [spriteset_flatbed_truck_1_goods_empty, spriteset_flatbed_truck_1_goods_full]; loading: [spriteset_flatbed_truck_1_goods_empty, spriteset_flatbed_truck_1_goods_full]; }
Now if you've paid real good attention, you'll notice that the spritegroup identifier is exactly the same identifier as used in the item definition for the default graphics. If you didn't notice that, you don't have to be ashamed of yourself. Just remember that in order to attach the graphics to the vehicle, the identifier needs to be the same. Indeed similar how to reference spritesets from the spritegroup, this time the spritegroup gets referenced from the item definition!
Putting everything together
Now we can add this to the code we already had. Once more remember that something needs to exist before it can be referenced. As the item references the spritegroup and the spritegroup references the spriteset, you can fill the rest in yourself. Right? Anyways, the correct answer is that the spritesets go above the item and that the spritegroup goes between the spritesets and the item, like this:
//define the grf grf { grfid: "NML\03"; name: string(STR_GRF_NAME); desc: string(STR_GRF_DESC); version: 0; min_compatible_version: 0; } //graphics definition spriteset(spriteset_flatbed_truck_1_goods_empty, "gfx/flatbed_truck_1_goods.png") { //left_x, upper_y, width, height, offset_x, offset_y [ 0, 0, 8, 18, -3, -10] [ 16, 0, 20, 16, -14, -7] [ 48, 0, 28, 12, -14, -6] [ 96, 0, 20, 16, -6, -7] [ 128, 0, 8, 18, -3, -10] [ 144, 0, 20, 16, -14, -7] [ 176, 0, 28, 12, -14, -6] [ 224, 0, 20, 16, -6, -7] } spriteset(spriteset_flatbed_truck_1_goods_full, "gfx/flatbed_truck_1_goods.png") { //left_x, upper_y, width, height, offset_x, offset_y [ 260, 0, 8, 18, -3, -10] [ 276, 0, 20, 16, -14, -7] [ 308, 0, 28, 12, -14, -6] [ 356, 0, 20, 16, -6, -7] [ 388, 0, 8, 18, -3, -10] [ 404, 0, 20, 16, -14, -7] [ 436, 0, 28, 12, -14, -6] [ 484, 0, 20, 16, -6, -7] } spritegroup spritegroup_flatbed_truck_1_goods { loaded: [spriteset_flatbed_truck_1_goods_empty, spriteset_flatbed_truck_1_goods_full]; loading: [spriteset_flatbed_truck_1_goods_empty, spriteset_flatbed_truck_1_goods_full]; } /* Define the road vehicle */ item(FEAT_ROADVEHS, item_flatbed_truck_1) { property { /* Properties common to all vehicle types */ name: string(STR_NAME_FLATBED_TRUCK_1); climates_available: bitmask(CLIMATE_TEMPERATE, CLIMATE_ARCTIC, CLIMATE_TROPICAL); introduction_date: date(1926,01,01); model_life: 65; /* retire_early not set, use default retirement behaviour */ vehicle_life: 15; reliability_decay: 20; refittable_cargo_classes: bitmask(CC_PIECE_GOODS, CC_EXPRESS); non_refittable_cargo_classes: bitmask(CC_PASSENGERS, CC_REFRIGERATED); loading_speed: 5; cost_factor: 108; running_cost_factor: 90; /* cargo_age_period is left at default */ /* RV-specific properties */ sprite_id: SPRITE_ID_NEW_ROADVEH; //enable new graphics speed: 48 km/h; misc_flags: bitmask(ROADVEH_FLAG_2CC); refit_cost: 0; // Refitting is free /* callback_flags are not set, no need to manually enable callbacks */ running_cost_base: RUNNING_COST_ROADVEH; power: 120 hp; weight: 9.5 ton; /* TE and air drag coefficient is left at default */ cargo_capacity: 20; sound_effect: SOUND_BUS_START_PULL_AWAY; /* Visual effect is left at default (no effect) */ } /* Define graphics */ graphics { default: spritegroup_flatbed_truck_1_goods; // use a goods container for a (loaded) flatbed truck } }
If you want, you can now encode this into a working NewGRF!
And if you noticed that the numbers for the sprites in both spritegroups look very much alike, you're right at that. Continue to learn how to avoid this duplication by means of a template.
NML Tutorial: Road vehicle graphics