jcornaz / heron

[DISCONTINUED] An ergonomic physics API for bevy games
MIT License
292 stars 44 forks source link

Provide 2d oriented constructors for CollisionShape #72

Open faassen opened 3 years ago

faassen commented 3 years ago

Here are some proposed convenience shapes for 2d that mostly don't exist directly in Rapier but are easy to implement. The argument to have them is because when dealing with 2d, people think in 2d terms. It's weird to refer to a circle as a "sphere", for instance. We can also use Vec2 instead of Vec3 to define them where vecs are involved.

I think we can make them in a special Body2 enum that gets translated into Body underneath.

// alias for Sphere
Body2::Circle {
   radius: f32
}

// like Cuboid, but with Vec2
Body2::Rectangle {
   half_extends: Vec2
}

// Convenience over Body2::Rectangle
Body2::Square {
   size: f32 // size of side. Or should this be half_side?
}

// Parry has a specific triangle shape
Body2::Triangle {
   a: Vec2,
   b: Vec2,
   c: Vec2
}

// Stadium is the geometric name for a 2d capsule. Since this is such an obscure name, the name Capsule still works too
Body2::Stadium {
  half_segment: f32, 
  radius: f32  // radius of the half circles
}

// A convex polygon defined exactly by its points (convex_polyline in Rapier)
Body2::ConvexPolygon {
   points: Vec<Vec2>
}

// This requires some code but it's convenient to have
Body2::RegularPolygon {
  sides: int; // amount of sides
  radius: f32; // distance from center for each point
}
jcornaz commented 3 years ago

Thanks for the proposition.

I think having aliases for creating 2d shapes might indeed be more user-friendly. But I am not so entousiast about having a different component type, because it quickly adds new edge-cases and maintenance costs.

Instead I'd like to propose providing 2d oriented constructors on Body itself. Example:

impl Body {

  #[cfg(feature = "2d")]
  pub fn circle(radius: f32) -> Self {
    Self::Sphere { radius }
  }

  #[cfg(feature = "2d")]
  pub fn rectangle(half_extends: Vec2) -> Self {
    Self::Cuboid { half_extends: half_extends.extend(0.0) }
  }
}

That as the advandage to not require writing/maintaining any special systems. What do you think @faassen?

jcornaz commented 3 years ago

To be honest, I'm also wondering if it is not against the disign principle of "it should look like it is part of Bevy".

After all, Bevy doesn't really have first-class concept of 2d. It is a 3d oriented game engine, and 2d is only achieved by pointing an orthographic camera toward the -z axis. Bevy still forces to define the position of 2d assets using Vec3 and their rotation using Quat.

faassen commented 3 years ago

I think we're confusing API with implementation if we argue Heron shouldn't expose 'Circle' and 'Rectangle' as a concept because 2d graphics are implemented with 3d graphics.

Bevy talks about 2d support right on its homepage. It exposes a Vec2.

Let's look at another library, bevy_prototype_lyon. It's talking about rectangles and circles too. What else could it do? It's a 2d library. It uses Vec2 throughout, which makes sense.

Bevy's breakout example is in 2d, and uses Vec2 a lot, for sprites for instance. It also uses Vec3 with z set to 0, for transforms. That's not an awesome API but it may be a reasonable compromise.

So Heron is in the same position as Bevy: it supports both 2d and 3d. It should expose the best APIs for both. For 2d in Bevy, that's Circles and Rectangles and Vec2 and possibly some compromises.

faassen commented 3 years ago

Concerning using a feature toggle, see also the comments in my PR. I think, it would be the simplest if there were only a Body to worry about, not a Body2.

I only need to figure out a way to convince rust analyzer to give me information about the code for 2d, as it's completely ignored by default.

faassen commented 3 years ago

A possibly relevant discussion: https://github.com/bevyengine/bevy/issues/1275

This hints that the z coordinate is used in graphics to prioritize layers. Though of course it argues that's not an ideal solution.

jcornaz commented 3 years ago

Bevy's breakout example is in 2d, and uses Vec2 a lot, for sprites for instance. It also uses Vec3 with z set to 0, for transforms. That's not an awesome API but it may be a reasonable compromise.

When something is only useful for 2d (like sprites) then it uses 2d terminology and types. But it doesn't duplicate APIs. One of cart's moto is to not provide 2 different ways of doing the same thing. Which is why, by design the transform uses Vec3 and Quat. Because it is used for both 2d and 3d.

Let's look at another library, bevy_prototype_lyon. It's talking about rectangles and circles too. What else could it do? It's a 2d library. It uses Vec2 throughout, which makes sense.

That's still follow the bevy philosophy, because bevy_prototype_lyon only makes sense in 2d, and doesn't support 3d at all.

So Heron is in the same position as Bevy: it supports both 2d and 3d.

Well that's the point indeed. Heron is in the same position as bevy, not of bevy_prototype_lyon. And, as I said above, when something is used for both 2d and 3d, bevy provides only the 3d oriented API.

Of course for shapes that only exist in a 2d space, then heron should provide a 2d-oriented API (like bevy does for sprite). But when it is used for both 2d and 3d (like capsulse, cuboid, sphere, etc.)... well, we can still do whatever we want, because heron is not bevy. But let's at least reckognise that bevy does not provide 2 different APIs when something is used for both 2d and 3d.

Anyway, I think that adding sugars to create 2d shapes maybe fine, if it makes usage of heron more intuitive in 2d. What I am not entousiast about is to add new components and systems for that. Which is why I propose to provide constructory on Body rather than a new component.

I think, it would be the simplest if there were only a Body to worry about, not a Body2.

Yes, I think that's what I suggested in a previous comment. To not introduce a new Body2 type, but instead to add constructors to Body. Or am I misunderstanding your point?

faassen commented 3 years ago

Yes, I agree that adding constructors to Body makes the most sense, also from an implementation perspective.