flixel icon indicating copy to clipboard operation
flixel copied to clipboard

Classic Physics: Slopes & Advanced Platforming

Open nospoone opened this issue 8 years ago • 33 comments

I've been having this problem for a while, and I really think that having this built-in would benefit the community. @JoeCreates raised the issue in Slack, and I figured it was time to have a serious discussion about it.

The Problem

Flixel's non-NAPE physics work well when you have simple level geometry, such as what FlxTilemap offers. Things however get hairy when you try to adding more "advanced" platformer level elements, such as slopes, one-way floors, or even one-way slopes. As we can see from the FlxTilemapExt Demo, it works but doesn't feel as good as it should.

  • When moving up a slope, the object seems to be going incredibly fast, as the distance you're travelling is greater than your horizontal speed.
  • When moving down a slope, the player should "stick" to the floor, and not float off it.
  • Hitting a sloped ceiling behaves weirdly: it shouldn't feel like you're going faster (related to the first point)

What's Expected

@JoeCreates has (long ago, in FlashPunk) created a demo which displays how slopes are expected to behave. The red floating slope is a one-way slope meaning that you can get in from the right side, but colliding properly on the left. Unrelated, but could also be implemented in FlxTilemapExt.

  • Going up the slope, the horizontal velocity should be the same as walking on non-sloped terrain;
  • This has to be implemented at the FlxObject level. (e.g. not only for FlxTilemapExt);
  • Only convex, rectangular hitboxes have to be implemented, as classic Flixel physics don't support any other type of hitboxes (unless I'm mistaken);
  • Although I don't think this is required, rotated hitboxes would also collide properly;
  • All slope angles (from horizontal to vertical) should be supported.

Potential Solution(s)

I am not qualified enough to dive into physics, but @larsiusprime and @Beeblerox have pointed out multiple resources that could potentially help:


Bounty

I know this is a big issue and a lot to think about (and also that I probably don't understand the implications), but I am offering a small 100$ bounty for this issue to get implemented/fixed.

The bounty is payable upon verifying that the classic HaxeFlixel physics work properly with slopes (the What's Expected list works), and that I am satisfied with the results.


(this is mostly a write up of a Slack conversation) cc @JoeCreates @Beeblerox @larsiusprime

nospoone avatar Apr 09 '16 19:04 nospoone

Also, any takers see this twitter convo for tips: https://twitter.com/larsiusprime/status/718825930085371904

This one in particular: https://twitter.com/raiganburns/status/718830593295802369

larsiusprime avatar Apr 09 '16 19:04 larsiusprime

Also for clarification purposes to anyone who takes this: by "physics" we mean flixel's "classic" "arcadey" physics, not actual realistic physics logics, the kind flixel alternatively supports by using full-blown physics libraries like NAPE. So this is just about getting the "classic" collision logic/gamefeel to get to a good place.

larsiusprime avatar Apr 09 '16 19:04 larsiusprime

I've updated the issue to clarify! Thanks!

nospoone avatar Apr 09 '16 19:04 nospoone

I'm very familiar with this, so I'll ask a few clarifying questions:

  • Going up the slope, is your horizontal velocity the same as walking on the ground, and the slope is dealing with your y velocity separately? Or is your total velocity constant, so your x velocity decreases?
  • I had no idea flixel had a raytracer, although I dislike that it's baked into tilemap. Is this just for tilemap ext's implementation of slopes, or is this a general flixel thing? If the latter, the ray tracer should be moved. The way I'm thinking for slopes requires rays to be cast from the corners.
  • Should there be any form of support for round hitboxes? I don't know if those exist in flixel.
  • Are all shapes convex?
  • Are rectangles aligned to the x and y axes, or can they be rotated?
  • Are there defined slope angles (30, 45, 60), or should it support anything between horizontal and vertical?

Now, side note, if moving up and down slopes is what separates classic physics from nape physics, I've done that in nape before. You have to restrict rotation and modify your velocity when you're colliding with something whose normal is not flat (when you're on a slope). But yeah, that's not something a casual user would be able to figure out easily.

MSGhero avatar Apr 09 '16 21:04 MSGhero

In the same order:

  • Ideally we'd have the choice of both, but I'd be okay with only the same as walking on the ground, and Y being dealt with differently.
  • I had in mind that this would be a general Flixel thing. This way, it makes it relevant for any other thing that requires non-rectangular collisions and not only FlxTilemapExt.
  • It should if they exist in Flixel, although I never heard of such things (cc @Gama11)
  • Yes.
  • Ideally they can be rotated.
  • Ideally they would support anything, but I'd be content with 22.5, 45 and 67.5.

It's not only slopes that makes them different. There are many other reasons.

nospoone avatar Apr 09 '16 21:04 nospoone

I understand. Maybe a summer project for me, but not right now.

MSGhero avatar Apr 09 '16 22:04 MSGhero

If you want to implement it by yourself, using nape, check here. just remember replace the code in callback handler, store the normal of contact in callback. and then, use the direction of that "normal" to adjust the moving direction of your character also, you may need to Map different sprites with different physics bodies.

nape API doc

buckle2000 avatar Apr 10 '16 00:04 buckle2000

@buckle2000 Flixel I don't think even has anything to find the normal of contact. Like, there's no such thing as contact to find the normal of in the first place. All of the collision magic is hidden away in the quadtree class, which doesn't give bonus info describing the collision (and which no one understands). The FlxVector class is loaded with physics-y functions, but probably none of them actually get used right now. And callbacks are very limited.

The point is to add all of that by replacing or extending what currently exists. Then we can worry about one-way platforms, which are just a specific arrangement of all of those features.

MSGhero avatar Apr 10 '16 00:04 MSGhero

Sorry, what I mean is, there is a way to solve @nospoone 's problem, using nape. nothing to do with HaxeFlixel.

And callbacks are very limited.

errr, by callbacks I mean callbacks in nape, but not in HaxeFlixel.

Also, you are right, but do NOT expect HaxeFlixel's physics much, it is simple.

If you wish, See here. callbacks are used to determine whether a player is on the ground, check whether the direction of normal is facing "up", then the player can jump.

buckle2000 avatar Apr 10 '16 01:04 buckle2000

OK, if you want to use flixel's "classic" "arcadey" physics, you can put all south-west facing slopes in one FlxGroup, and so on. If fact, HaxeFlixel has done this part for us. Then, check which kind of tile you are on by using FlxG.overlaps. (just before FlxG.collide) And then, move player according to the result.

The moving part of logic, however, should never be put in FlxTilemapExt.

buckle2000 avatar Apr 10 '16 01:04 buckle2000

@buckle2000 I dont think anyone was suggesting moving logic should go in flxtilemapext. This demo simply highlights some classic platformer features we currently lack. On 10 Apr 2016 02:23, "buckle2000" [email protected] wrote:

OK, if you want to use flixel's "classic" "arcadey" physics, you can put all south-west facing slopes in one FlxSlope, and so on. If fact, HaxeFlixel has done this part for us. Then, check which kind of tile you are on by using FlxG.collide with callbacks. And then, move player according to the result.

The moving part of logic, however, should never be put in FlxTilemapExt.

— You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub

JoeCreates avatar Apr 10 '16 01:04 JoeCreates

@JoeCreates FlxSlope is a misspell. So I mean that since @nospoone and others can have it done in update(), this issue isn't really a issue.

buckle2000 avatar Apr 10 '16 01:04 buckle2000

You could make a whole game from scratch in update. That doesn't mean its not useful to have common features ready implemented, especially seeing as these are so related to things flxobject already does.

Its also a pain to make the new physics reusable across many sprites due to flixels inheritence structure. You will find yourself having to reimplement it several times over if you have classes that already extend subclasses of flxobject or flxsprite, because you could not simply create your own subclass of flxsprite with the modified physics. For example, you could make your own ImprovedPhysicsSprite class, but then you realise you want to have that physocs work on a FlxNestedSprite (or any other existing subclass of FlxSprite).

An ideal solution would involve fixing the inheritence structure and using composition for physics and rendering, but thats a huge job and I dont know who is willing to do it. Until then, such features as these belong with the related logic. On 10 Apr 2016 02:40, "buckle2000" [email protected] wrote:

@JoeCreates https://github.com/JoeCreates FlxSlope is a misspell. So I mean that this issue isn't really a issue, @nospoone https://github.com/nospoone can have it done in update().

— You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub https://github.com/HaxeFlixel/flixel/issues/1808#issuecomment-207894995

JoeCreates avatar Apr 10 '16 02:04 JoeCreates

this conversation goes to a point where we understand that inheritance isn't always good approach and we should use composition. But this will require rewriting some parts of the engine which will mean losing backward compatibility (and someone won't like it). I've been thinking about it for some time lately. And my approach is to break FlxObject into components, like in HaxePunk or Unity, or anything else component based engine. So FlxObject will have: transform (which will hold position, angle, scrolling etc. information), graphic (which will load graphical assets and will take position from transform and render on the screen), physics body (which will be update body's transform), and other components like path following or flickering. Plus i wanted to rewrite nested sprite groups through using transfrorm component so they will be similar to kiwi.js groups (this engine is cool looking for me and similar in some aspects to flixel, since it was based on it) When we will rewrite this part of the engine, we will be able to reimplement such classes as FlxSprite (as close as possible), but eventually we'll lose some of its features and lose backward compatibility with the price of better flexibility.

Beeblerox avatar Apr 10 '16 03:04 Beeblerox

and returning to actual topic of this issue: i think that Phaser do have better implementation of this feature - http://phaser.io/examples/v2/ninja-physics/ninja-tilemap So we can ask @photonstorm that he thinks about his implementation of ninja physics (what limitations it has, its strong and weak parts) and his permition to port this stuff/

Beeblerox avatar Apr 10 '16 03:04 Beeblerox

Phaser is licensed under MIT, so we can use its code as a basis (i guess). Let's try this out!

Beeblerox avatar Apr 10 '16 08:04 Beeblerox

When we will rewrite this part of the engine, we will be able to reimplement such classes as FlxSprite (as close as possible), but eventually we'll lose some of its features and lose backward compatibility with the price of better flexibility.

Making huge breaking changes like that right now seems like a bad idea to me.. We just had a major release where we promised API stability. The changes you describe might be the biggest changes to date.

Gama11 avatar Apr 10 '16 08:04 Gama11

@Gama11 i knew that you won't like it :) i know it's my fault that i've abandoned this project for a long time and hadn't made these changes before major 4.0 release. but i just can't see another way to achieve that. i was thinking about project structure for a while (that's the only thing i could do in my free time) and there are two ways:

  1. we don't make major changes and engine will stay as we know it know, with known flaws (the main source of them is overusage of inheritance over composition). we still can fix minor flaws and bugs (for many of them i should be blamed, since i'm selftaught "programmer" and this was my way of learning things), but in the end it will stay the "same", with all of its childhood problems.
  2. we will make major reconstruction of it and it could become much "better" (imho) engine. We can still maintain current architecture, like Phaser developers does. They maintain Phaser, but develop Lazer engine (which is the name of 3.0 version of the engine) at the same time, as they understand what things can be done with current codebase and what can't be done. That's hard decision and it forced by my incompetence in programming area and lack of involvement in engine's development, but this is how i see it right now.

Beeblerox avatar Apr 10 '16 09:04 Beeblerox

We definitely can't just abandon 4.0 right now. And I doubt that any of these restructures help @nospoone, since they will take a long time to develop and he probably isn't willing to rewrite his entire game to fit the new API just for slope physics.

This issue seems to be getting way off-topic. We have issues like "component system for FlxObject" already: #1063

Gama11 avatar Apr 10 '16 09:04 Gama11

I know that mantaining 4.0 is a priority and it should continue to be, but:

  1. It's a great idea to create a new branch and experiment with a new structure imho (composition for example)
  2. A lot of frameworks mantain more than one architecture at a time for big breaking changes, besides Phaser a good example is OpenFL with its legacy and next architecture
  3. The new stuff from openfl (e.g. openf.display.Tilemap, per-sprite shaders, etc) will require flixel to do a major refactor to its rendering part if we want to use it (maybe even Tilesheet would eventually be removed?)
  4. Even HaxeFlixel is used to this modus operandi, before 4.0.0 there were two architectures: 3.3 and dev

Tiago Ling Alexandre Tel: +55 41 8819-3191

2016-04-10 6:38 GMT-03:00 Gama11 [email protected]:

We definitely can't just abandon 4.0 right now. And I doubt that any of these restructures help @nospoone https://github.com/nospoone, since they will take a long time to develop and he probably isn't willing to rewrite his entire game to fit the new API just for slope physics.

This issue seems to be getting way off-topic. We have issues like "component system for FlxObject" already: #1063 https://github.com/HaxeFlixel/flixel/issues/1063

— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub https://github.com/HaxeFlixel/flixel/issues/1808#issuecomment-207950952

Tiago-Ling avatar Apr 10 '16 11:04 Tiago-Ling

@Nospoone soooooo... If you want to use nape now, you can hit me up for tips on how to change the feel of the engine

MSGhero avatar Apr 10 '16 13:04 MSGhero

Hi all - just to add some thoughts here: I've been looking to implement slopes into Phaser's tilemap system for a while now (so under Arcade Physics, ala Flixel physics). If someone wants to work with me on this we could cross-share the results, so both projects would benefit.

photonstorm avatar Apr 10 '16 21:04 photonstorm

@photonstorm Raigan from metanet (N, N+, N++) is going to send me some code samples, I'll share my findings.

larsiusprime avatar Apr 11 '16 00:04 larsiusprime

@photonstorm i'd really like to participate, but can work only on weekends. Do you have any directions/ideas to start from?

Beeblerox avatar Apr 11 '16 16:04 Beeblerox

@photonstorm the only thing i found about your plan is "I want to get a proper SAT system into Arcade Physics" from http://phaser.io/news/2016/04/phaser-is-3-years-old

Beeblerox avatar Apr 13 '16 02:04 Beeblerox

Nah that's unrelated to this really. I'm just looking at a nice way to handle slopes in tilemaps, so am starting with research into that. There are various methods, but it doesn't require a full-on SAT implementation (that's just something I'm putting in Phaser 2.5)

photonstorm avatar Apr 14 '16 15:04 photonstorm

@photonstorm ok, i think that you are going to implement SAT only for slopes then and stay with AABB for everything else. is this correct?

Beeblerox avatar Apr 14 '16 15:04 Beeblerox

@photonstorm @Beeblerox, So Raigan just sent me the stuff!

Here's the email for context:

hey, Here's the collision code from N v2.0.. it could definitely be improved, but the basic idea is that the world is described as a bunch of line segments and circular arcs, added to a grid to speed up queries. The two queries are distance and raycast.

In hindsight this might not be totally suitable in general since it means dynamic objects must all be circles (i.e since all the queries are distance-from-point-to-geometry). If you get rid of the circular arc geometry, it shouldn't be too hard to add support for dynamic capsule and rectangle shapes -- you just need to write distance-from-shape-to-linesegment.

The other main gotcha is that it's a bit complex to actually generate the surface geometry from a set of tile shapes, this code is in tiles.rar.

I don't know how performant our implementation of a "virtual grid" is.. mostly it's simple/straightforward, just a 1d list of cells which we map to a 2d grid. To look something up from the grid, you transform a worldspace x,y position into the corresponding 2d grid cell u,v coordinates, and then you transform the u,v coordinates into a 1d index into the actual list of cells. If you want to lookup a rectangular region, you get the 2d grid coords for min/max corners and then iterate across those.

Finally, I don't have any good examples of code that actually uses the collision system to perform collision detection+response.. sadly that's buried in a mess of other code and not easy to pull out. The basic idea is that for each dynamic object, you get the nearby geometry and then iteratively do: -find the single closest point on the geometry -push object out of that collision, and repeat

Right now I'm working on a revised collision system which will support more shapes and hopefully be even easier to understand.. no idea when it will be done though, lately there's not much time.

Please let me know if you have any questions.. I might be a couple days replying, stuff is busy :/ But, I would imagine there will be lots of questions since I haven't spent enough time explaining things nicely.. sorry!!

Cheers, Raigan

Here's the source that was generously attached to said email:

collision.rar: https://gist.github.com/larsiusprime/ced7c520203c40591a3937ba84cc8839

tiles.rar: https://gist.github.com/larsiusprime/eeead2179393bc8bd8276f991e94bdef

larsiusprime avatar Apr 21 '16 17:04 larsiusprime

If you're going for SAT you might as well look at luxe's code: https://github.com/underscorediscovery/luxe/blob/master/luxe/collision/sat/SAT2D.hx

It does pretty much what you guys need and it's Haxe code already.

But if I were you I'd also stick a spatial hash grid as the first check to avoid doing all the math each cycle.

tanis2000 avatar Apr 21 '16 17:04 tanis2000

So back in the days I went through the QuadTree code to solve a bug unrelated to this topic, and I can say with full confidence, that implementing slopes with the current system is nigh impossible, and even if you succeeded with something like that, it would probably really buggy (even the current system has it's problems) and break lots of legacy code. On the other side, one-way platforms might be doable, but can't say anything for sure with this.

danim1130 avatar May 30 '16 22:05 danim1130