Friday 26 February 2021

C++ & FLTK // Adding Simple "Sprite" Support

This isn't really a tutorial, more of a "hey, I wanted to add 'sprites' to my FLTK build, how would I do that?"

For those of you new to my blog, I've been working my way through Bjarne Stroustrup's Practice & Principles using C++ in which there are several chapters focused on graphics programming. For this he supplies a couple of helper files that wrap some fltk features in a "higher level bow" so to speak. Over the months I've been editing these quite a bit to create my own shapes and add other functionality.

After these graphics chapters ended, I started to wonder if I could make a 2d engine using fltk and what does every good 2d engine need? Sprite support.

After an hour of tinkering, I had achieved this:

C++ & FLTK - Adding "Sprite" Support

Very exciting. I'd like to note, this did not capture well and it kind of looks like there's a slight stall after a few frames but when running the program it's fine.

So I first went about this by creating a new simple window that supports "ticking". It has a quit button and a tick function that's registered with fltk's callback system to run every 0.1 seconds.

Then I made a new class called Sprite which inherits from the Shape class. I soon learnt that Shape is great for basic stuff, but when you want to start doing event based game stuff, the way shapes are drawn and updated is kind of clunky so I need to re-do shapes. But for now I worked round it.

So a Sprite takes in a point to draw from the top left-hand corner, a file name, frame width, frame height and the number of rows in the spritesheet; kind of reaching for the moment as it can't deal with sprites with multiple rows just yet.

The Sprite itself is just an Fl_Image that holds the png "spritesheet". This was mine:

C++ & FLTK - Adding "Sprite" Support

It's the coin blocks from Super Mario World. When attaching this image to the window, obviously the entire image was displayed. Sprites should be able to only display a certain portion or 'frame' of their spritesheet, so I made a function called drawFrame() that takes in a frame number. This frame number is stored and used in draw_lines() to display a particular part of the image. This is the annoying part of using a Shape; I must override draw_lines() and use that to draw. 

To only display a certain part of the image, you can just use built-in functionality of Fl_Image. The draw() function allows you to specify a part of the image to draw by passing in the top left corner locations and an offset. This essentially "crops" the image to the size given. The width and height is then multiplied by the frame number each frame to advance it to the next image:
C++ & FLTK - Adding "Sprite" Support

C++ & FLTK - Adding "Sprite" Support

Here, my spritesheet is 200x400 pixels with each block being 40x40. Therefore, to play an animation, fltk is told to start at frame 0 and increase by 1 each frame until it hits 3, then go back to 0. This stops it from showing the brown block, which is frame number 4.

So to show the second image block, it would do 40 * 1, which is 40. This gives an x offset of 40. Then the x and y coordinates of the new cropped image are obtained by getting the top left of the full sprite sheet (I set it to 50, 50) then adding on the offset; making the new draw point 90, 50. The draw() function is handy though as it can display the cropped image in the same position as the last through the first two parameters. The Y offset is ignored right now as we don't have multiple rows.

It's also important that post increment is used here. I usually detest using post++, however here it is necessary as the current frame needs to be drawn before it's incremented. If it was ++frame, then the next frame would be drawn as it's incremented before use.

And that's pretty much it for creating the most basic of basic sprite systems for FLTK. I have plans to rewrite the shape class so I have full control over how they're drawn.

You can find the full code for this project here along with a project file:

No comments:

Post a Comment