Menu

SkiaSharp Adventures

June 22, 2019 - .NET, C#, Programming

For some time now, I’ve occasionally created a relatively simple game and typically I’m not bothered to get into fancy “game engines” or using special rendering. usually I just have a Windows Form Application, a Game loop, and paint routines working with the System.Drawing.Graphics canvas. “BASeTris”, a Tetris clone, was my latest effort using this technique.

While much maligned, it is indeed possible to make that work and maintain fairly high framerates; one has to be careful what and when things get drawn, and eliminate unnecessary operations. By way of example, within my Tetris implementation, the blocks that are “set” on the field are drawn onto a separate bitmap only when they change; For the main paint routine, that bitmap get’s drawn in one go, instead of individually drawing each block, which involves bitmap scaling and such each time. Effectively I attack the problem by using separate “layers” which get rendered to individually and then those layers are painted unscaled each “frame”.

Nonetheless, it is a rather outdated approach. Because of that I decided I’d give SkiaSharp a go. SkiaSharp is a cross-platform implementation that is a wrapper around the Skia Graphics Library. This is used in many programs, such as Google Chrome. For the most part, the featureset is very similar conceptually to GDI+, though it tends to be more powerful, reliable, and, of course, portable, since it runs across different systems as well as other languages. It’s also hardware accelerated which is a nice-to-have.

The first problem, of course, was that much of the project was tightly coupled to GDI+. For example, elements that appear within the game will typically have a routine to Perform a Frame of animation and a routine that is capable of drawing to a System.Drawing.Graphics. Now, it would be possible to amend the interface such that there is an added Draw routine for each implementation, But this would clog up a lot of the internals of the logic classes.

Render Providers

I hit upon the idea, which is obviously not original, to separate the rendering logic into separate classes. I came up with this basic interface for those definitions:

The idea being that implementations would implement the appropriate generic interface for the class they can draw, the “Canvas” object they are able to draw onto (the target) and additional information which can vary based on said implementation. I also expanded things to create an interface specific to “Game States”; The game, of course, would be in one state at a time, which is represented by an abstract class implementation for the Menu, the gameplay itself, the pause screen, as well as the pause transitions and so on.

Even at this point I can already observe many issues with the design. The biggest one is that all the details of drawing each object and each state would effectively need to be duplicated. The alternative it seems would be to construct a “wrapper” that is for example able to handle various operations, in a generic but still powerful way, to paint on both SKCanvas as well as System.Drawing.Graphics. I’ve decided against this approach because realistically once a SkiaSharp implementation is working, GDI+ is pretty much just legacy stuff that I could arguably remove altogether anyway. Furthermore, that sort of abstraction would prevent or at least make more difficult utilization of features specific to one implementation or another within the client code doing the drawing, and would just mean that now the drawing logic is coupled to whatever abstraction I created.

There is still the problem of Game Elements using data types such as PointF or RectangleF and so forth, and particularly Image and Bitmap to represent positions, bounds, and loaded images, so I suspect things outside the game “engine” will require modification, but it has provided a scaffolding upon which I can build the new implementations. Seeing working code, I find, tends to motivate further changes. Sort of a tame form of Test Driven Development, I suppose.

I have managed to implement some basic handlers so hopefully I can get a SkiaSharp implementation utilizing a SKControl as the drawing surface sorted out. I decided to implement this stuff before for example trying to create a title screen menu because that would be yet another state and drawing code I’d need to port over.

Some of the direct translations were interesting. They also gave peripheral exposure to what looks like very powerful features that are available in SkiaSharp that would give a lot of power in terms of drawing special effects compared to GDI+. For example, using BlendFilters, it appears it would be fairly straightforward to apply a blur effect to the play field while the game is paused, which I think would look pretty cool.

Have something to say about this post? Comment!

Tags: , ,