Journeys with Cerberus - "Undaunted"

Arantor

New Member
Joined
May 27, 2020
As I mentioned, I'm pretty new to Cerberus, I figured I'd share my journey so far.

I've always loved the 2D "iso" thing (yes yes I know it's technically dimetric due to foreshortening on the one axis but everyone calls it isometric) and I'm finally getting around to trying to make a game I've wanted to do on some level for a while, it's gone through a few forms and shifts in my head.

The first step was relatively painless, get some tiles drawn, have a Tile class that bothered with knowing which image to draw and its own position, then a class for an IsoMap with a Tile[][][] property.

So far so good, drawing out tiles with mojo isn't especially hard and even I can manage that in a couple of evenings :p

I did decide to have a twist on it, and render ~6 tiles in each direction from the player, and I got something like so out of it:

unknown.png


Again, no big deal, good with all that (ignore the off-by-one of the character's height, that was later fixed). (The character sprite is... borrowed... from Unity's isometric assets pack until I can actually get a real isometric character sprite sorted, but I have plenty to do first.)

And then I start messing around with Tiled (which I don't have import for yet, but no worries, I can copy/paste for now!)

1591212236712.png


And suddenly I'm starting to have problems. I know Tiled is rendering my 2x height bricks wrong, but I'm not worried about Tiled getting it wrong.

So let me relate that journey - I will come back to solve it when I'm not being hammered with major integration development work at, well, work.

So, I started out asking here for advice, and got a really interesting pointer to using a shader, and I made the requisite mask images that would do what I think I need to do. The next challenge was porting to mojo2 - in hindsight a simple enough task, but without deep familiarity, it felt daunting but only really took an evening or two to hammer out.

I haven't yet gotten round to understanding how to implement the shader in the way I need to, but mostly because I have to solve something a bit deeper first.

If you've ever tried to implement iso before, you've probably encountered some variant of the Painter's Algorithm problem, making sure that you draw things in the correct order, and drawing the static terrain is relatively straightforward, that's already mapped out in tiles in a convenient grid, so iterating over it in the correct order is easy enough.

Buuuut, it's a bit more tricky when starting to figure out things like 2x high cubes. Now I could split this out into two cubes and insert both into the map data as such, but this doesn't solve some of the other challenges - either cubes that cross boundaries by their dimensions, or cubes being placed not on an actual boundary (like the obelisk which is conceptually a 4x high object placed, in this case, at .5 into a tile)

So I'm currently thinking about how to solve this. The current plan, when I can actually get into the right headspace is to make a single pass when the level is first loaded, to build all of the static terrain into a sort of 3D array structure, where I divide it broadly into cells aligned along the xyz axes, and use that to collect each of the contents of each volume along the axes, which can be used to collect knowledge of which objects (including the character), could inhabit that cell which means collisions can be handled more efficiently, by doing it somewhat locally and can be used to draw things in the correct order.

The problem is that in my head, this feels like an awful lot of computation to be done each frame, hence trying to start by computing all the static geometry at level load, cloning that as a base, then running each of the level entities' update routines to calculate updated positions for that frame, and as part of that, applying it into the 3D grid so it can be iterated over correctly and rendered.

But I'm probably over-worrying and over-fretting because this seems way more daunting to execute, but I'm pretty sure right now that issue is mostly because my professional world is... complicated... right now.

So that's my journey so far. I'll try to remember to write more as I solve it, but right now I hear PHP + MySQL calling...
 

Holzchopf

Moderator
3rd Party Module Dev
Joined
Jul 31, 2017
Location
Bern, Switzerland
Looks good!

Regarding the z-ordering: Since mojo2 builds on OpenGL, it should be possible to extend it with some depth-buffer capabilities, meaning you could outsource the z-ordering to the graphics chip. I can't provide an example right now, maybe later. Also, this will only work if your tiles have no semi transparent pixels.

Another approach would be: For every ground plane (let's assume ground plane is X/Y) tile you have a stack, containing the tiles on that ground tile sorted according to height (Z). Every block or object has it's attachment point in the near lower corner (the bottom corner of the edge you look at). And as long as this attachment point is inside a certain X/Y-Tile coordinate, you sort it in its tile stack. And only when an object moves, you check whether it crossed X/Y-tile boundaries, and only if it did so, you remove it from the current stack, move it the neighbour's stack, reorder that one and you're done. The stacks internal render order is quite simple: bottom up. While rendering the single stacks in correct order will be a little trickier: they should be rendered furthest to nearest. I did this for my game Verlassen , although the only tile not 1x1x1 is the player sprite, but it still works. Even with semi transparent pixels.
Ah crap! Wait sorry, I only just realised you have "ceiling" tiles =/ I guess the rendering order I described won't work then. My first (untested) guess is then: the attachment point is top/near. Instead of one ground plane you have a X/Y plane per height level. Render planes from bottom to top, each plane itself rendered far to near.
 

Arantor

New Member
Joined
May 27, 2020
Right now no tiles have semi-transparent pixels and that's absolutely the intent to remain that way because I'm going for the pixel aesthetic with a limited palette.

More than just having ceiling tiles, I also plan on trying interesting things where blocks very much cross interesting boundaries - for example I have thoughts of doing a variable of a few scenes from Indiana Jones inside my world: that scene from Last Crusade where Indy jumps from tile to tile and treading on the wrong tile collapses the floor, I envisage something like that, with blocks falling out as you move on them.

Conceptually in my world this is a set of objects with collision (rather than tiles in the tile map, this is how I'm notionally splitting the world, between static and dynamic objects if you will), which implies some kind of 3D collision map in any event.

The issue isn't even really the tiles in the map, I can iterate over the layers of the map on map load and assign either actual tile objects for real tiles, or dummy objects that point to existing objects with offsets. At some point after this is done I'll draw out with diagrams how this works, my initial plan even solved the concept of having partially transparent pixels by drawing 'the front of a cell' only if a given cell had another thing above it, and drawing 'the full cell' if there was nothing above it (e.g. bottom of the obelisk vs top of the obelisk) but all the time I have fully opaque or fully transparent, this isn't actually an issue. From a purely rendering perspective, the net effect is to emulate the world being 1x1x1 unit cubes with trickery, and then it's just a sorting problem.

Really my mental model problem here is solving how to have something I can use as a collision map (and by extension a rendering map) given the conceptual model of treating each cube in the map as its own object, which to my mind seems awfully expensive, coming as I am from a very different mental background. (I'm professionally a PHP/web dev that specialises in APIs and integrations where the most expensive parts of my day are string slicing a thousand ways and database calls.)

I might just give it a go at the weekend to experiment with what I've been thinking about and just see what happens - maybe I'm just prematurely optimising ;)
 

Holzchopf

Moderator
3rd Party Module Dev
Joined
Jul 31, 2017
Location
Bern, Switzerland
I might just give it a go at the weekend to experiment with what I've been thinking about and just see what happens - maybe I'm just prematurely optimising ;)
Absolutely! In my experience, prematurely optimising is #1 reason for slow game-dev'ing. And experimenting around will never not teach you something new
 
Top Bottom