Loading... Search articles

Search for articles

Sorry, but we couldn't find any matches...

But perhaps we can interest you in one of our more popular articles?
Flaming Stacks Part 2: Sashimi

Flaming Stacks Part 2: Sashimi

May 5, 2023

This article is written by Maksim Lin

Welcome to the latest edition of #FlutterFunFriday. This is the another instalment in a new series of posts where we’ll be spending some time to have a bit of fun with Flutter on a Friday. So grab a beverage of your choice, fire up your favourite IDE and lets have some fun!

Recap

Those of you who have used the Flutter-based Flame game engine to build a game or have looked into it would already know that, like Flutter itself, Flame is limited to 2D. So while true 3D is not supported, all is not lost as you can still get a 3D-like, “2.5D” experience for your game idea by using a technique called “Sprite Stacking”. For those that may be new to Flame game development, you can get up to speed with the basics from a previous article.

Worlds without end

While in the previous article on sprite stacking we looked into how to use the technique for single objects in your Flame game, the technique really comes into it’s own when used for the entire (or almost the entire) game world. When used for multiple objects in a game sprite-stacking has a neat trick to ensure that depth and perspective is handled properly, but this technique will not work with the SpriteBatch approach we used in the previous article.

Luckily, this is where @wolfenrain’s fantastic new Sashimi package comes in, as it can handle “whole game” sprite stacking and we’ll look at how to use it in this article.

Sashimi

Making use of Sashimi in a Flame-based game involves the usual process of adding the package to the Flutter project:

flutter pub add sashimi

along with Flame itself, if you have not already done so:

flutter pub add flame

Making it horizontal

The Goxel app that I use to make the sprite sheets for game objects, lays out the sprite “slices” in a horizontal fashion, while Sashimi expects them to be in layed out vertically, as is done by the Magica Voxel tool for example. So in order to be able to use my existing sprite sheets, I made a small patch to Sashimi to support horizontally layed out sprite sheets. This patch is not yet (my PR is pending) included in the published versionof Sashimi, so for now you will need to make use of my fork by using this in your pubspec.yaml instead of the above command:

sashimi:
    git:
      url: https://github.com/maks/sashimi
      ref: horizontal-spritesheets

Using Sashimi

Making use of Sashimi in your Flame app consists of first creating a new engine class that inherits from SashimiGame:

class ExampleGame extends SashimiGame {
    ...
}

and then for example in the game classes onLoad() method we create a model and add it to the game:

@override
Future<void> onLoad() async {
    final position = Vector3(10, 10, 0);

    final car = Model(
      position: position,
      sliceSize: Vector2.all(16),
      size: Vector3(16, 16, 1),
      angle: 45 * degrees2Radians,
      image: await images.load('BlueCar.png'),
      horizontalSlices: true,
    );

    await addAll(car);
    return super.onLoad();
}

We can also add in the update() method, user input handling, such as rotating the camera view point based on keyboard input in this case:

  @override
  void update(double dt) {
    final rotateLeft = _keysPressed.contains(LogicalKeyboardKey.arrowLeft);
    final rotateRight = _keysPressed.contains(LogicalKeyboardKey.arrowRight);
    final rotation = rotateLeft ? 1 : (rotateRight ? -1 : 0);
    kamera.rotation += rotation * dt;

    super.update(dt);
  }

Which gives us something that looks like this: screen capture of rotating car

Keeping things in perspective

The above result is pretty much the same as what we achieved in the first article on using sprite stacking. But this is for a single element. Where Sashimi really comes into it’s own is by handling for us any number of sprite stacked elements that we may wish to add to our game world. So if we now add several more models using the same approach as before:

final tree = Model(
  position: Vector3(20, 20, 0),
  image: await images.load('tree1.png'),
  ...
);
final tree2 = Model(
  position: Vector3(10, 0, 0),
  image: await images.load('tree1.png'),  
  ...
);
final house = Model(
  position: Vector3(-15, -10, 0),
  image: await images.load('house1.png'),
  ...
);

await addAll([car, tree, tree2, house]);

and then if we again rotate the camera view (in the demo code I did this using arrow keys for ease of testing on the desktop platform I was developing on), we can see that Sashimi maintains the correct perspective relationship between the different elements, with objects in the foreground (closest to the users view point or “camera”) blocking the view to objects behind them, just as we would expect from a real 3D view engine:

screen capture of rotating perspective

The explanation behind how this is possible to achieve with stacked sprites without any complex depth buffer management is very well explained in this video.

Billboards

While the primary goal of using Sashimi is to draw all the elements of our game world consistently with the correct perspective (as demonstrated above) sometimes we may in fact want to just draw normal “flat” sprites into our game. The term for this from the 3D games world is “billboarding” and Sashimi provides a simple means to do this with a BillboardSprite class which we can use in the same way we did with Model’s, for example:

await add(
  BillboardSprite(
    position: Vector3(0, 0, 140),
    size: Vector2.all(64),
    scale: Vector2.all(2),
    sprite: await Sprite.load('house.png', images: images),
  ),
);

But wait there’s more

Sashimi itself is a new package that is still being actively developed and one thing to look out for in the future is the current work in progress by its developer @wolfenrain of functionality to allow “tilting” the camera view angle of the sprite stacked world, which will open up many new creative possibilities for using Sashimi in your games.

So long and thanks for all the fish

I hope you have enjoyed the articles so far in the #FlutterFunFriday series. For now, the series is going on a short hiatus, so until next time, have fun stacking those sprites!

Related articles

Latest articles

Show more posts