fishfolk / punchy

A 2.5D side-scroller beatemup, made in Bevy

Home Page:https://fishfolk.github.io/punchy/player/latest

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sort 2D sprites in 3D space

erlend-sh opened this issue · comments

@odecay @64kramsystem @edgarssilva I wanted to open discussion on this topic now because I'm wondering how far we want to go before we figure this out. It seems like it might be pretty important to do early.

Things like throwing items don't really make sense with our current 2D collision system, because if you throw the bottle in an arch it can go and hit fighters that are higher on the screen, even though it should technically be going up into the sky.

We'll need to position everything in 3D, and I think we will need to have 3D collision boxes for everything, including the players. The render position of the sprites on the screen will then be determined by a combination of it's y axis position and it's z axis position. Then we'd use something like the strategy described in @erlend-sh's links above for doing z-sorting of the rendered sprites.

I think fighter collisions will probably be simple enough, because it's just a square, but what will be more complicated is scenery. For example, if we have a car, we have to specify a multi-part 3D collision box for it, so that we have one section for the top of the car and the hood, etc. so you can jump up onto different parts of it. Specifying collision shapes for scenery might be an important role of an editor, and otherwise, we'll just have to deal with hot-reload combined with YAML definitions and debug rendering of the collision boxes.

Since rapier's 3D bevy plugin uses assumes you're using a 3D camera, and we are going to be using a 2D camera, we may also have to make our own rapier debug rendering system so that we can visualize the collision boxes in the space of our 2D rendering. We can do that simply enough with Egui, using the epaint API to render the debug lines.


I'm feeling like we should do this somewhat soon, before we get too stuck coding into a way that we are just going to have to un-do later, and I'd be find taking this on if nobody else wants to, but probably after getting #140 done ( which shouldn't be way to long from now ).

I'm wanting to try to get the foundations of the game nailed down so we don't have as many more PRs like #203 that kind of modify everything while other people are busy working on other parts of the game that are impacted by the big PR. Or at least I'd want to get those over with as soon as possible so people can keep adding feature in parallel.

But totally feel free to tell me what to do or that we have other ideas. Just let me know!

Dropping this feature entirely is also a possibility. We are learning a lot of beatemup fundamentals in the process of building this game, and this feature is quite literally a next-gen addition that came in a much later iteration of the genre.

Should we hold off on it until a potential v2.0 post-publication? LF2 and similar games have plenty of fun to offer without this extra dimension.

Dropping this feature entirely is also a possibility. We are learning a lot of beatemup fundamentals in the process of building this game, and this feature is quite literally a next-gen addition that came in a much later iteration of the genre.

Should we hold off on it until a potential v2.0 post-publication? LF2 and similar games have plenty of fun to offer without this extra dimension.

I think a minimum of 3D (or 2.5, or any term 😄) implementation is necessary. This is because characters move in a 3D space. Without a third dimension, we can't perform correct collision detection, which is a problem already now.

Simple example: when the flop is executed, if an enemy is a above the player, but still on the ground, it will be hit. This is because the flop is encoded as moving across the ground, rather than up (since the third coordinate doesn't exist).

If LF2 is a beat 'em up with jumps (I need to check 😁), it must handle the third coordinate in some way (even if it fakes it, like disabling collisions while in the air).

It may be simple to implement in a basic way, so it's not necessarily bad, it really depends on how one chooses to implement it.

Yes this seems like the big topic right now, if you want to take this on go ahead. If we want to delay this off as erlend said we could probably get away with working with a virtual y value.

So we have a Y-Jump that gets added to the real y before render and subtracted after render.

That way we can define that collisions only happen in the same y interval. Like intervals of 10y so the fighters don't need to be exactly the same height.

Not sure if that brings any unwanted problems.

A virtual y would probably work easy enough for jumps and throws, but it might get more confusing once we add scenery that logically has a depth. Anyway, I'll check it out once I get the chance and see how an attempt at 3D goes. I don't think it will be way to hard, but I'll have a better idea after some testing.

Maybe @tyt2y3 (F.LF) or @kazzmir (paintown) have some input here.

heya, for my game which was an attempted remake of LF2, I implemented this using the following method (the game used Amethyst, which used specs under the hood):

  • Each object in game has an x, y, z Position, and their hurt box's width, height, depth is offset from that position.
  • The Position is used for collision calculations, which is separate from the Transform type which the renderer used for positioning the sprite. This was the easiest way I could shift the y coordinate on the Transform without messing up collisions.
  • Collisions is done in 3d using the Position values
  • Rendering is still done in "3d" using an ortho camera using the Transform values. The correct x/y position on screen is achieved by tagging the object with PositionZAsY, and then transforming the y coordinate of the objects. Note that the z coordinate is still copied across for z ordering.

By the way, this also means you need to track velocity and acceleration in 3 dimensions as well. I haven't looked at the code if it already does that.

May be relevant, background / playable area are defined as cuboids (x, y, z, and width, height, depth), and I think I had layers that also had z coordinates, because I wanted backgrounds with sprites rendered in front of the objects, e.g. if you want to have trees that render in front of objects to block the player's view.

LF2 and similar games have plenty of fun to offer without this extra dimension.

I'm certainly biased, but I really think this dimension contributes a lot to the fun -- especially if you come to the point of implementing flying characters (imagine having a jetpack in 2.5d).

My comment was a bit unclear because we’re juggling a lot of subtly different dimensions here 😆

I was just saying I’m perfectly fine with whatever LF2 does to achieve ‘2.5D’ vs the additional techniques involved in the RCR: Underground example to allow for 3-dimensional height variations.

The difference from one to the other is unclear to me.

Awesome @azriel91 and thanks for the breakdown! That sounds pretty similar to what I was thinking.

A cool thing about it to is that I think we can totally avoid needing to do any complicated graph sorting like in RCRU with that model, because if we translate sprites down when the Z value goes up, and we use the Z value for depth-sorting, then the player will only pass behind the other sprites once it gets all the way on the other side of it.

If I'm thinking about that right. I'll need to experiment, but I think that will work well for us.

Im still kindof suspicious about switching to 3d physics for attack collisions, I cant think of a scenario where you have an attack or projectile visually overlap with an enemy sprite where you want them to not collide, I feel like that would cause confusion to the player. But the other aspects of this sound nice, enabling jumping/nice aerial attacks/juggle combos would be really fun. We do also need some way to solve for depth of props/level and how it interacts with sprite sorting.

I'm not sure, maybe I'm not thinking about the first bit from the right perspective.

Well, right now, for instance, when you thrown a bottle, you're throwing it "forward" but it has an arch that puts it up on the screen, and if that bottle goes up on the screen and it hits an enemy that isn't actually lined up with you up-and-down on the screen, it doesn't really make sense, because you threw the bottle "forward" ( aka. to the right ) but it hit an enemy that is "back" toward the background instead.

Or even the flop attack. I can hit players futher back toward the background by flopping because my player animation rises slightly, but I should only be able to hit fighters further to the right of me.

I get that but the alternative is you have a scenario where its not clear to players when an attack should and shouldn't be able to hit enemies visually.

That's a good point. I think shadows go a long way for that, actually. My brother had mentioned that when the player flops.

It's hard to tell when the player flops if he is going towards the back of the screen, or whether he is "jumping". If we add shadows to everything it would convey the difference going toward the back of the screen or rising above the floor.

Maybe we could also come up with some other visual cues for flying objects, or just try to avoid long arching projectiles where they might end up confusing.

Hmm +1 to adding shadows in general, It will help with depth perception, but I still dont think it entirely solves this problem. Although if they were in you could track where an item/player should hit by seeing if their shadow collides with something, that seems counterintuitive to what a player will actually be looking at when performing an attack/throwing a projectile.

This seems like a good thing to study in other similar games to see how they handle projectiles, if at all.

If I remember correctly from the old river city ransom on the NES ( that's the only beat-em-up I've played through ), items were only ever thrown like a bullet in a straight line, probably for a similar reason to the issues we're having here.

But as far as dimensions were concerned, there was still jumping, and you wouldn't be able to get hit by enemies further back on the screen when you were in mid-air, I don't think.

But as far as dimensions were concerned, there was still jumping, and you wouldn't be able to get hit by enemies further back on the screen when you were in mid-air, I don't think.

I think that could be achieved just by having the sprite and collider separate, disabling the collider, and throwing the sprite up.

I keep thinking in my head, and that's what made me use the small hitboxes in the poc, is that if we ignore the height in 3D we get just 2D and we could "project" it flat on the screen you would get your hitbox.

Idk if you can get what I mean.

So trying to explain myself a little better if we separate the map into a grid, with way smaller rows than this. We could define a hitbox inside 1 or multiple rows. That way we can know if the player is gonna get hit or not.

hitbox
.

So in this case, if we ignore the height of the player and the item the top one will hit and the bottom not. This way the size of the entities is how "fat" it is or a more visual way how many rows he would occupy.

Not sure if this makes any sense, but been kinda stuck in my head.

Hmm like using the projection on the ground plane instead of projection on screen plane to do collisions?

That seems like it could work.

If we want to lean away from 3D positions, I'm totally fine with that. In my head I was thinking 3D boxes for everything wouldn't be that hard, but it might be a bigger can of worms that I'm thinking.

In summary, I'm not picky if it works. :D

Doing it in rows like that seems to give us similar freedom to 3D, it just breaks the depth into bigger slices, allowing us to do 2D collision detection instead of 3D by grouping things into a small set of layers. Seems like a reasonable proposal. 👍

And visually, we could still have arbitrarily varied depth, it would just effect collisions.

I think I prefer going full 3d over using ground plane projection to determine collisions.
edit: or at least i'm not sold on it yet.

It was mostly as a placeholder suggestion since we were kinda going towards it with the idea of setting the sprites to the bottom center anchor.