Advertiser Content

Lyzo

Back from vacation

Age 24
Male
The Netherlands
Seen 2 Weeks Ago
Posted July 2nd, 2019
263 posts
12.6 Years
Hi everyone. Following my comment on Cranium's post asking about more information on animated tiles. I decided to create a tutorial on my experience with tile inserting and animation. I am by no means an expert. I only just started using disassembly. If you have any additional information on tile insertion or animation, please feel free to add in the comments below, and I'll make sure to update this post!

Contents:
  1. Basic tile insertion - DONE
    • Preparing your tiles
    • Creating your palette
    • Inserting your palette
    • Inserting your prepared tiles
    • Creating your tiles in porymap
    • Notes and limitations
  2. Tile animation - DONE
    • Preparing your tile animation
    • Inserting your tile animation
    • Defining your tile animation
    • Linking tiles to your animation
    • Notes and limitations
  3. Event object animation (eg. wild grass) - TO BE ADDED
    • Preparing your event object animation
    • Palettes for event object animation
    • Inserting your event object animation
    • Notes and limitations

1. Basic tile insertion
In this part of the tutorial, we will be replacing the original tree tiles with a new tree tiles from this public tileset.

Before & after:
Spoiler:
Before:

After:


Preparing your tiles
First things first, get GraphicsGale. It's a great tool that will help you immensely with sprite editing & tile inserting, as it allows you to easily work with and create pallettes.

I've decided to insert this tree for this tutorial:
Spoiler:

What we first need to do, is cut it out of the tilesheet and paste it into it's own file. We need to make sure that it fits within 16x16 pixel squares (as this is the tilesize in Pokemon games).

You can use the custom tile grid functionality in GraphicsGale to easily fit the tree within 16x16 tiles.

Make sure to fill the background of the tileset in a completely different color. This will be your transparent color (which won't be visible in the game). I like using pink, but feel free to use whatever you want.
Spoiler:

Creating your palette
Next up, we need to index our tiles and create our custom palette. For those of you who don't know what that means: it means you have to reduce the color palette that the sprite uses to 16 colors max (including your transparent color). You can do this by going to the top menu > All Frames > Color Depth, and then choosing the 4bpp option.
Spoiler:

For the next step it is very important that you copy and paste your entire image of the tiles (CTRL + A & CTRL + C).
You have to switch the order of the palette colors on the right, so that your transparent color is first. Once you do that, you'll notice the image messes up the colors. However if you paste in your previously copied tiles, the colors should fill in correctly automatically (thank you GraphicsGale).
Spoiler:
Note that in my case the palette was already correct, but this just shows you how it works.

Inserting your palette
Next you have to save your palette in GraphicsGale. There's a small arrow near your palette that will allow you to save it.
Spoiler:

Once you've saved your palette you can start up PoryMap and insert your color palette in the tilesheet that you'd like to insert the tiles into. Go to the top bar and click Tools > Tileset Editor. In the new window that opens up, click palette editor.

The first 6 palettes are in use by the main tileset, don't replace those if you plan on using original tiles.

In my case I'll replace palette number 7.
Spoiler:

Note that it's important that all colors in the palette are different. In my case I had 4 empty colors at the end. This will mess up your other tiles later, so always make sure your palette has 16 DIFFERENT colors.
Spoiler:

Inserting your prepared tiles
Once you've inserted the palette, close the palette editor and use the top bar of the Tileset Editor > Tools > Export Primary Tiles Image...
Spoiler:

Open up the exported tileset image in GraphicsGale and paste in your new tree tile, over the old tree tiles. Note that you only need half of the tree. This is because you can flip images in the tiles image in PoryMap, when creating your tiles.
Spoiler:

Load the new image into PoryMap by opening up the Tileset Editor > Tools > Import Primary Tiles Image...
Spoiler:

Creating your tiles in PoryMap
Finally create your tiles using the tileset editor. Note how you can fill in 8x8 blocks twice, with a bottom & a top layer. In this case I'll use grass as a bottom layer and the tree as a top layer.

You can use the X flip checkbox to flip your tiles for the other side of the tree.
Spoiler:

The result is a new tree in the game. The shadow color I chose originally conflicts quite a bit with the older grass tiles. So I adjusted one of our empty colors slightly (112, 168, 120) and recolored the shadow for the 'end result'. Normally something you would replace the entire grass tileset, or you would recolor the tree with the old palette to make it fit better.
Spoiler:

Notes and limitations
Feel free to add any additional notes or limitations that you have figured out related to inserting tiles in the comments. I'll make sure to add them here!
  • You have a limited number of palettes per map (12 I think), although for some reason I do see 15 palette files in some maps. So I could be wrong. I'm just not sure how to use them.
  • It's best practice to add as many tiles to one palette as possible (eg. Tree + grass + logs). Otherwise you'll get conflicting looking tiles (like my tree in this tutorial). See the example below of a 'better' tile preparation with grass tiles included.
    Spoiler:
  • If you want to animate your tiles, you need to split your tile up into 8x8 blocks and place them one after the other. I'll explain more about that in the animated tiles section below.

2. Tile animation
In this part of the tutorial I will show you how you can create your own tile animations relatively easily. In the future I think it would be better if we add this as a function to PoryMap (I'd like to do it, however I've never built or worked on a tool before, so before I can add this I'll have to spend some time digging through the PoryMap codebase and learn how it works).

We will continuing from the previous tile insertion (the tree we added), by adding this little zigzagoon in a tree animation:
Spoiler:

Preparing your tile animation
First you need to create your animation. I like using GraphicsGale for this, as it has layers & frames, which really help with creating animations. But feel free to use any tool you like.

In total, this zigzagoon in a tree animation has 9 frames. This is what my animation looks like in GraphicsGale.
Spoiler:

Each frame needs to be saved separately. Also, note how only a small part of the zigzagoon is animated (see spoiler below). That is because you only need to save the 8x8 animated tiles separately. To save myself work I only animated two of the 8x8 tiles. This is not a limitation, you can animate as many tiles as you'd like. For the sake of this tutorial however, I just kept it at these two.
Spoiler:

Your animation needs to use the same palette that your tiles do. So a quick way to save all the frames separately with the right palette is by:
  1. Exporting your palette from PoryMap (if you don't know how to do this, check part 1 of this tutorial).
  2. Creating a new picture in GraphicsGale with the total height and width of the 8x8 tiles you want to animate. Note: I've noticed that all other animation frame files have a max picture width of 16 (but no height limit). So you can only put 2 8x8 tiles next to each other before you need to put the next on a new row. I'm not sure if this is an actual limitation, but just in case, I always use a width of 16. I have included an example in the spoiler below of animated rock tiles to better show what I mean.
  3. Next load your palette in the newly created 16x8 image.
  4. Finally copy / paste your animated frames into this new picture and save each frame separately.
See the spoiler below for a picture to explain what I mean with the max width of 16
Spoiler:

See the spoiler below of how I quickly saved each frame for the zigzagoon animation.
Spoiler:

The final result should be that you have all your animated tile frames saved separately & indexed.
Spoiler:

Inserting your tile animation
Great, so now we just need to insert the tiles that we want to animate. When inserting these tiles, you have to pay attention to the order that they are in the tilesheet. The animated tiles need to be right next to each other, not above or below. They also need to be in the same order as how you saved your 8x8 frames. See the spoiler below for several examples.
Spoiler:

For a step by step explanation of inserting tiles, please see part 1 of the tutorial.

Defining your tile animation
Next, we need to link these tiles we inserted to our animations. This will require some code work.

First, go to data/tilesets/primary/general/anim and create a new folder for your animation frame files. In my case I'll call it "zig_tree". Copy in your animation frames.
Spoiler:

All tile animations are stored in their respective tileset folders. So if you want to animate indoor tiles, look for the "anim" folder in the "building" tileset.

Next, we need to edit src/tileset_anims.c.

Go past the include & static void section of the code where until you start seeing file references. In my case I started adding code at line 90 or so. First we need to add a few lines to add all our frame files. Note: Make sure to refer to .4bpp files, not your saved .png files. The compiler will automatically create the .4bpp files for you once you compile the game.
const u16 gTilesetAnims_General_Zig_Tree_Frame0[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_0.4bpp");
const u16 gTilesetAnims_General_Zig_Tree_Frame1[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_1.4bpp");
const u16 gTilesetAnims_General_Zig_Tree_Frame2[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_2.4bpp");
const u16 gTilesetAnims_General_Zig_Tree_Frame3[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_3.4bpp");
const u16 gTilesetAnims_General_Zig_Tree_Frame4[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_4.4bpp");
const u16 gTilesetAnims_General_Zig_Tree_Frame5[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_5.4bpp");
const u16 gTilesetAnims_General_Zig_Tree_Frame6[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_6.4bpp");
const u16 gTilesetAnims_General_Zig_Tree_Frame7[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_7.4bpp");
const u16 gTilesetAnims_General_Zig_Tree_Frame8[] = INCBIN_U16("data/tilesets/primary/general/anim/zig_tree/zig_anim_8.4bpp");
Next, we need to define how we want our frames to play. In my case, I want the frames to play one by one from frame 0 -> 8, then to play frame 0 again (closed eyes), and then back again from 8 -> 1. After which it resets to frame 0 and replays. So I added this code below the file definitions:
const u16 *const gTilesetAnims_General_Zig_Tree[] = {
    gTilesetAnims_General_Zig_Tree_Frame0,
    gTilesetAnims_General_Zig_Tree_Frame1,
    gTilesetAnims_General_Zig_Tree_Frame2,
    gTilesetAnims_General_Zig_Tree_Frame3,
    gTilesetAnims_General_Zig_Tree_Frame4,
    gTilesetAnims_General_Zig_Tree_Frame5,
    gTilesetAnims_General_Zig_Tree_Frame6,
    gTilesetAnims_General_Zig_Tree_Frame7,
    gTilesetAnims_General_Zig_Tree_Frame8,
    gTilesetAnims_General_Zig_Tree_Frame0,
    gTilesetAnims_General_Zig_Tree_Frame7,
    gTilesetAnims_General_Zig_Tree_Frame6,
    gTilesetAnims_General_Zig_Tree_Frame5,
    gTilesetAnims_General_Zig_Tree_Frame4,
    gTilesetAnims_General_Zig_Tree_Frame3,
    gTilesetAnims_General_Zig_Tree_Frame2,
    gTilesetAnims_General_Zig_Tree_Frame1
};
Linking tiles to your animation
Alright, so now, how does the game know that these animations belong to the 2 tiles we inserted earlier? We need to tell it how to play our animation. First, go up back to the beginning of the tileset_anims.c file and add an additional animation que static void. I added this on line 51:
static void QueueAnimTiles_General_Zig_Tree(u16);
Next, scroll down to where the code for all the QueueAnimTiles is (around line 685 or so). We're going to need to tell the game three things:
  1. How many frames is our animation? In this case, because we have 9 frames that are played forward, we then switch back to the first frame, and then play the other 8 frames in reverse, we have a total of 17 frames in our animation.
  2. Where in the tileset are the tiles that are animated? The location of tiles is displayed in the Tileset Editor in porymap. I'll show you where to find this in the spoiler below.
  3. How many tiles in our tileset are animated? In this case, we have two 8x8 tiles in our tileset that are animated
Tile location in PoryMap
Spoiler:

Using this information, we can create our QueueAnimTiles void:
static void QueueAnimTiles_General_Zig_Tree(u16 timer)
{
    u16 i = timer % 17; 
    AppendTilesetAnimToBuffer(gTilesetAnims_General_Zig_Tree[i], (u16 *)(BG_VRAM + TILE_OFFSET_4BPP(54)), 0x40);
}
I've bolded the three numbers above, which are the answers to our questions in the list above:
  1. How many frames is our animation? The bolded 17 refers to the 17 frames in our animation, as explained above
  2. Where in the tileset are the tiles that are animated? The 54 refers to the location of tiles is displayed in the Tileset Editor in PoryMap. Note how PoryMap displays it in hex (0x036). You can use a hex translator to convert the hex numbers to decimal numbers. In this case 0x036 is 54 in decimals.
  3. How many tiles in our tileset are animated? The bolded 0x40 refers to the number of 8x8 tiles that are animated. For each animated tile you need to tell the game to reserve 32 bits. So in our case we wanted to animate 2 8x8 tiles, so a total of 64 bits. You then need to translate this number to hex. You can use the hex translator to do so. 64 is 0x40 in hex.

Now that we've told the game how to play our animation, we need to actually tell it to play our animation. For this you need to edit the static void TilesetAnim_General.
static void TilesetAnim_General(u16 timer)
{
    if (timer % 16 == 0)
        QueueAnimTiles_General_Flower(timer >> 4);
    if (timer % 16 == 1)
        QueueAnimTiles_General_Water(timer >> 4);
    if (timer % 16 == 2)
        QueueAnimTiles_General_SandWaterEdge(timer >> 4);
    if (timer % 16 == 3)
        QueueAnimTiles_General_Waterfall(timer >> 4);
    if (timer % 16 == 4)
        QueueAnimTiles_General_LandWaterEdge(timer >> 4);
    if (timer % 16 == 5)
        QueueAnimTiles_General_Zig_Tree(timer >> 4);
}
Once you've added those last two lines to the TilesetAnim_General, you're done! Save the file, use your animated tiles in PoryMap and compile your game to admire your newly animated tiles!
Spoiler:


Notes and limitations:
  • I am not sure if animated tile frames should be saved with a max width of 16 pixels. It just seems that all other animations do this. I also have not tested to see if the game breaks if you save tile animations in a larger width (this can easily be tested though).
  • I am not sure if there is a limit to the number of animations you can add per tileset or the number of frames per tileset animation.
  • I am also not 100% sure about the 3 number definitions I gave above (eg. if the 32 number is really about reserving bits), however using it in this way does work.
  • This tutorial does not tell you how to add animations for new tilesets or tilesets that currently have no animations. But I don't think this is all too difficult, it just requires the creation of a new static void TilesetAnim + referring to this TilesetAnim for the new tileset.

3. Event object animation (eg. wild grass) - TO BE ADDED
Will edit this post with a tutorial on animation for objects soon.


^^ Thank you The Blueprint !!!

Taの境界

Seen September 4th, 2019
Posted September 4th, 2019
22 posts
4.8 Years
Hi, I was just wondering for the How many tiles in our tileset are animated? for the water tiles, it is set at 3C0. But I thought the water tiles were just 4 animated 8x8 tiles, so 4x32 being 0x80 in hex?
Vanilla Water tiles graphic size is 16x120, so base on the theory its size is exactly 0x3c0 which make sense.

Like Lyzo said,
This tutorial does not tell you how to add animations for new tilesets or tilesets that currently have no animations. But I don't think this is all too difficult, it just requires the creation of a new static void TilesetAnim + referring to this TilesetAnim for the new tileset.
While I did some investigation and prove that the statement is right, simply by create the following instances into tileset_anims.c, the rest is about the tutorial above.

static void TilesetAnim_Example(u16 timer)
Spoiler:
{
if (timer % 6 == 0)
QueueAnimsTiles_Example(timer >> 4);
}


static void QueueAnimsTiles_Example(u16 timer)
Spoiler:
{
u16 i = timer % 6;
AppendTilesetAnimToBuffer(gTilesetAnims_Example[i], (u16 *)(BG_VRAM + TILE_OFFSET_4BPP(0x14F)), 0x80);
}


void InitTilesetAnim_Example(void)
Spoiler:
{
sPrimaryTilesetAnimCounter = 0;
sPrimaryTilesetAnimCounterMax = 256;
sPrimaryTilesetAnimCallback = TilesetAnim_Example;
}


And also define the case to your tileset animation header:
Spoiler:

.align 2
gTileset_Example::
.byte TRUE @ is compressed
.byte FALSE @ is secondary
.2byte 0
.4byte gTilesetTiles_Example
.4byte gTilesetPalettes_Example
.4byte gMetatiles_Example
.4byte gMetatileAttributes_Example
.4byte InitTilesetAnim_Example


I am not sure if animated tile frames should be saved with a max width of 16 pixels. It just seems that all other animations do this. I also have not tested to see if the game breaks if you save tile animations in a larger width (this can easily be tested though).
Also the tile frames doesnt really take count max width of 16 pixels, just putting the tiles graphic in correct ascending array.
Advertiser Content