Interspersing Sprites and Tiles

I’m creating (well, re-implementing in OpenFL) a game in which the scene is rendered in 3/4 perspective. For those (like myself) unfamiliar with that perspective name:

So far, I’ve gotten by with a single Tilemap and Tileset, allowing me to put down hundreds of identical images (tiles) without a performance hit. I re-sort the tilemap’s tile array whenever the camera rotates around the game area, so that objects “in front of” other objects cover each other appropriately.

However, now I’d like to manually draw some pixels (sprites) on the screen, in the middle of the tile order. Namely, I’d like a health bar to appear over creature tiles wandering in a forest, and for that health bar to be partially obstructed by tree and rock tiles appearing “in between” the player tiles and the camera.

The first solution I came up with (but have not yet implemented) is to create separate images (and tile IDs) for every fill state of the health bar, and render the health bars as tiles like everything else. That would probably work fine for this use case (just round the actual health value to the nearest 25%), but I want a more general solution for rendering arbitrary Sprites within a Tilemap’s display list. Is this desire simply impossible to achieve?

If so, how might I efficiently get the same effect as a Tilemap’s ability to draw rectangles from a larger BitmapData into a display list? I would be willing to ditch Tilemap entirely if it means I have a more elegant solution for interspersing custom-drawn sprites with pre-made bitmap resources.


P.S. If some additional context would help, I’m re-implementing a Java-based Ludum Dare game called “Breaking the Tower” in Haxe and OpenFL. The original source code can be found here. In that implementation, a java.awt.Graphics2D object is passed to a render function implemented by every game object (in sorted order), and the object is responsible for drawing itself onto that under-construction image for every frame.

I’ve been putting my best effort into translating this code into OpenFL’s “passive rendering” strategy, in which DisplayObjects are simply added to the stage on initialization and then the objects’ display properties (position, tile ID, etc) are updated in an event-based manner.

I think what you’re looking for is TileContainer. It behaves like a Tile that contains other tiles.

I am indeed using TileContainer for other “nested tile” requirements, such as smoke puffs appearing above house tiles. Whereas before I would have just a single tile with the house’s ID, now I have a TileContainer containing both the house tile itself and a handful of smoke puff tiles that are updated internally, in the game object’s rendering logic.

This works because smoke puffs are parts of a spritesheet bitmap that I’ve prepared in advance. Now, I want to add “tiles” that don’t have a representation in the Tileset (or any Tileset at all!), but that I can draw on and update like the graphics member of plain old Sprites.

I’ll take some screenshots and add them to this topic, if it would help.

You can use multiple spritesheets in Tilemap, a single bitmap can by a 1 tile tileset and you can add it whenever you want. What you can’t do is intermingle display objects in the same TileMap. This would require using multiple TileMaps. If you draw on the bitmapData being used for a tile, you might have to update its tileset property or redraw/refresh the tile.

1 Like

Thank you for your help so far, @Dysphoric.

From my limited understanding, this solution still seems a bit heavy-handed, especially in terms of memory. Say I have ten creatures, each with a health bar rendered over them at an arbitrary health percentage. Using your suggestion, wouldn’t I need ten BitmapData objects, ten Tileset objects (each reading from a different bitmapData), and then a hook that calls Tile.invalidate() every time a creature’s health is changed (and the corresponding bitmapData is modified)?

On the one hand, when everything is phrased this way, it seems obvious that I should just draw a continuum of health bars in my texture atlas and load them as any other tile. I can (and probably will) do that for this particular instance, but I also feel that there are other cases where drawing every possible bitmap as a texture to load would be infeasible.

Would it ever make sense to ditch OpenFL’s rendering system entirely and pass around a BitmapData to each object for it to draw itself onto, as the old Java code did?

What kinds of sprites do you need to render in the middle of the tiles ?
Because, for health bars, I usually put 2 tiles (one for the background and one for the foreground), and I just change the texture coordinates (UVs) of the foreground tile according to the health ratio.

1 Like

That’s an excellent idea, @tour1st! I will try that and report back if any other unexpected issues arise.

I’m still interested in hearing what other people have done in similar situations, so I won’t mark anything as an answer yet, but these ideas have given me a lot to work on. Thank you, everyone!

Actually I had a pretty similar issue in my project (inserting sprites from another spritesheet into an existing tilemap), and ended up splitting the tilemap in parts.

For example, if I have a tilemap with tiles A,B,C,D,E,F and I want to insert sprite Z after C, I create a new tilemap and move DEF there, so at the end I have 3 display objects :

  • object 1 : tilemap with tiles A,B,C
  • object 2 : sprite Z
  • object 3 : tilemap with tiles D,E,F

Yeah, that’s another alternative I was considering. It’s good to know that someone else had a similar thought!

Did you have to keep moving tiles between the two tilemaps based on their changing relative “depth” on the stage? If so, how efficient was that solution?

In my case, depth wasn’t changing during runtime, so I didn’t bother so much :slight_smile:
I think that switching textures (aka draw calls) is the most expensive task on GPU side (so in my example it renders in 3 draw calls).
So optimizing the number of tilemaps is a key factor for performance (at least in GL targets).
But I think that just moving tiles from a tilemap to another shouldn’t be so expensive.

1 Like

Gotcha. Well for now, your recommendation about just creating two tiles and scaling them “health-bar style” seems to be working excellently. I’m hoping that by mid-January I’ll be able to post my project and code in the #showcase topic, and more high-level code review discussion can happen at that time. Thank you once again for your help!

I followed an approach similar to tour1st’s one for isometric elevated hexagon.
With z order :
• TileContainer/Tilemap for floor/water
• Sprite/TileContainer/Tilemap for dynamical elements (players/icons/pathfinding and so forth…)
• TileContainer/Tilemap for settings like trees or elevated items who may cover the floors/water tiles.

I may be wrong but your view type is called « isometric view » because you’re currently keeping same length for x/y/z axis, just projected in a different way.
That « 3/4 » name is a strange shortcut, who did not reflect the potentials of the isometric mathematicals.
You may find more accurate and usefull explanations of their mathematical laws with « isometric » terms than « 3/4 ». A lot of people have worked on it, had already by the past, seen libraries on it.

Thanks @Stephane! Yes, if I were making a more visually-immersive game, I would definitely consider isometric viewpoints and rotations. For now, I’m just going with a “birthday-candle” approach where the game “world” exists on a flat (two-dimensional) plane, and game objects just stick up from the ground as icons that have no rotational knowledge. (In other words, a tree looks the same from all directions.)

Here’s a GIF of the rendering that I’ve been able to accomplish so far:

Obviously it’s no masterpiece, but I’m also just focusing on learning the fundamentals of OpenFL so that I’m comfortable enough to rapidly prototype other (more original) game ideas in the future.

1 Like

I understand well, I have the same “problem”, delving with difficulty into blender to help me with the vertical elements. ^^’
But the mathematical approach remains the same. An angle of 45° or 60° or 75° Your z axis unit is relative to the trigonometrical ratio, and well represented at the same size, whatever the concerned tile, be it at the rear of the scene or at the front. It is isometry, that was I was trying to say.

Your shortcut is not so bad at the end.
I am wondering how the coast is remaining so undifferents while rotating !
You’re drawing/tilesetting so much and little tiles ?

The island/coastline is actually a separate bitmap (outside the tilemap and rendered behind it), scaled with an affine transformation that only approximates the perspective view of “true” three-dimensional graphics. There is no concept of “diminishing perspective” for more distant objects here. If you’re curious, here’s the actual snippet of code that updates the transformations:

    /**
        @param rot Rotation of the island view in radians
    **/
    function updateTransforms(rot:Float) {
        coordinateTransform.identity();
        coordinateTransform.rotate(rot);
        coordinateTransform.scale(1.5, 0.75);
        coordinateTransform.translate(WIDTH / 2, HEIGHT * 43 / 70);

        islandBitmapTransform.identity();
        islandBitmapTransform.translate(-islandBitmap.bitmapData.width / 2,
            -islandBitmap.bitmapData.height / 2);
        islandBitmapTransform.concat(coordinateTransform);
        islandBitmap.transform.matrix = islandBitmapTransform;
    }

The coordinateTransform matrix is used to update the positions of all the trees, tower, etc., and the islandBitmapTransform matrix is directly applied to the island bitmap (as can be seen in the final line of code).

I hope to put the game into #showcase very soon (along with the source code) for people to enjoy and critique it more comprehensively!

Oh you were cheating with the floor… :stuck_out_tongue:
But how do you project another type of landscape ? You may have to modify your updateTransforms() according to their other shape sizes…
Whatever, I would like to see your project when published.
:+1:

If you’re referring to having mountains or cliffs rather than an island or flat grassland, then yes, my cheap rendering hack would quickly fall apart!

However, for rendering entities with different shapes/origin points, I handle that simply by changing the tile’s originX/Y values when it is loaded (so that its coordinate rotations cause it to travel according to the point that it meets the ground).

Anyway, I’ve also published an alpha version of the game here, along with some discussion of various development decisions. I hope that you find it interesting! :smiley: