NMLTutorial/Road vehicle graphics

From TTWiki
Jump to navigationJump to search

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:

Empty flatbed truck sprites and flatbed trucks sprites loaded with a container

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]
}

The identifiers spriteset_flatbed_truck_1_goods_empty and spriteset_flatbed_truck_1_goods_full are chosen not without reason (read: with a reason). It lets you know that it's a spriteset, that it's for the flatbed truck 1, that the graphics are for goods cargo and that one of the spritesets is for an empty truck and one for a fully loaded truck.

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