Bastion-like 2d isometric collision detection and position implementation?

I recently quit my job to pursue my dream of making an indie game. Part of the game involves procedurally generated tilemaps with enemies, the player, and any game objects (projectiles, traps, events).

Originally I was going to go simple ala Stardew Valley with four main directions and a view that matched. But I wanted the combat to feel better and I wanted the player to go in any direction they want - and it really just feels like isometrics views in games like Bastion, Path of Exile, Diablo, and Hades lend themselves to better combat.

I thought that it would be simple to use a basic non-isometric rectangle on the backend to track object positions and collision. It would allow me to easily calculate the tile a given object was located in based on origin by doing a simple integer divide on the width/height of a tile. Then when it came to drawing, I would just translate the location in the backend interpretation of the gameplay area into the actual screen position and then draw it.

But when I went to implement it, I realized that it would require that I do some calculation like:

double hyp = Math.Sqrt(Game.playerX^2 + Game.playerY^2);
float transX = (float)(Math.Sin(45) * hyp);
float transY = (float)(Math.Cos(45) * hyp);

I’d like to be able to scale to having hundreds of movable game objects in play at once, and calculating the above on each draw seems inefficient.

Should I explore ways to implement an isometric tilemap with collision detection on the backend, or is there a smarter way for me to do this? I did consider that stationary objects would only need to calculate their ‘translated’ X/Y once. Maybe I should have movable game objects store both their conceptual backend location and their translated position on initialization, update their position in update on the backend and do collision detection, and then if there’s a change in position update their draw position based on a simpler calculation? Like store both vectors, and if they conceptually in the backend implementation move 30 degrees below the x-axis down-left, and they move 0.2f, then update their draw vector based on a 45 degree clockwise shift which would be 15 degrees above the x-axis up-right?

Or is there like a super simple solution to this already and I’m just not seeing it?

There is no reason for the engine’s view of the game not to be a regular 2D tile map.

The draw position can be set with the transform matrix, but how exactly that looks will depend on exactly what isometric view you want to go for. Games typically go for dimetric, which gives a nice 2:1 ratio between the horizontal and vertical diagonals of the tiles on screen which makes for nicer pixel art.
Using the transform matrix, there is no real extra translation effort needing to be done by the engine per-entity.

So you’re saying the engine views it as a purely top-down tile map and also draws it that way, and then I use a transform matrix in the sprinteBatch.Begin to rotate?

How would I draw the sprites? I assumed I’d need to draw them in the diamond shape they’d end up on the screen. Otherwise if the engine somehow transformed them it would also transform my character art which I wouldn’t want.

I’m specifically going for this type of isometric art (I realize they’re using 3d models - I won’t be):

Yeah, those games tend to use 3D models to get what they need there.

if you want to use 2D sprites, it’s possible to have actual geometry that you UV-map a texture onto in a way that doesn’t distort it(that’s what I ended up doing for terrain in my isometric engine), and for moving entities you can prob get away with billboards(quads that are oriented towards the camera).

There are ways of doing it all in 2D with getting the draw order correct, but that gets real complicated real quick. Setting up geometry so you can project hand-drawn art onto it directly(which is what I think Bastion does) is a much easier path to walk.

Repo link the description