godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.16k stars 97 forks source link

Make it possible to use move_and_slide in user implemented character controllers, not only in premade one in Godot 4, and maybe some improvements for more advanced use #5282

Closed vgnod closed 2 years ago

vgnod commented 2 years ago

Describe the project you are working on

Quake - like FPS controller (not a game yet), with features such as de-snap and retain vertical velocity, auto climb stairs or similar things, but implementation details is crucial for my gameplay (strafe-jump like mechanics, wall runs and such), mostly in low detail collision environment.

Describe the problem or limitation you are having in your project

For now, in 3.5 my controller works just fine, however in 4.0 with the new CharacterBody3d node it wouldn't be.

In general, there is a lot of games in different genres that uses low detail collision environment (not actual graphics, just colliding platforms and walls), and implements complicated movement mechanics like acrobatics, platforming, etc. On the other hand, there is high detail collision environments like terrains, or there are developers who like the ability to traverse their environments recklessly, or there is NPC characters. So, while the ability to snap to high poly terrains or various curved objects is important, it should not be the only functionality, it should not be in the way of developers who want more defined behavior of their movement controllers.

So, one major issue - control over snap vector is removed. Now I use move_and_slide_with_snap just to prevent move_and_slide from losing floor, and use snap vector to get desired de-snap conditions. Having snap vector based on up_direction is proven to be insufficient in my tests. Changing up_direction will compromise floor wall and ceiling information that is important part of my algorithm.

Why snap is important - move_and_slide without snap along normal tend to lose the floor, while move_and_collide, moving along the normal of previous collision, tend to stumble on seemingly flat surface, so some kind of snap or hover is important even if game developer provides proper normal aligned movement vectors. This is, of course, due to limited accuracy of normal detection and movement, but something less coarse than vector-based snap (especially now with vector adjustments removed) should be implemented to fix this.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Make another version of KinematicBody, modified to fix its own limitations (inconsistent movement along normals), and separate it from CharacterBody. Make test version of move_and_slide more functional. Also, made somewhat physical-based snap (described below) in addition to current geometry-based one.

The general idea here - to use KinematicBody as a mid-level tool, not as premade character controller, game developers must provide desired movement vector, not the general direction. Gravity and other force integration also should be made in user code, at proper conditions, and things like slide on slopes because of gravity pressing down should not be corrected. However, move_and_slide's own limitations may be.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Vector3 move_and_slide (delta-velocity, up-direction, max_slides, floor_max_angle, floor_slide_friction, wall_slide_friction, friction_cutoff, infitie_inertia):

Vector3 move_and_slide_and_anchor (delta-velocity, up-direction, anchor_vector (negative to floor normal by default), floor_slide_friction, wall_slide_friction, friction_cutoff, max_slides, floor_max_angle, infitie_inertia): A semi-snap feature and fix for existing move_and_slide problem - inconsistent floor detection when moving along normals. If movement starts from is_on_floor = true position, attempt to "drop anchor" and make small movement towards ground to stand on floor firmly at the end of movement. If floor is not found at the extent of anchor length, behave similar to usual move_and_slide. This is, in my opinion, a very solid snap solution for low detail collision environment with full control of movement. Absence of this feature force novice developers to press on floor with gravity (which is sort of intuitive thing to do), which leads to sliding on slope, which leads to another workarounds. With this, there is even no slide as there is no need to apply gravity on ground, and anchoring motion causes no slide as it is pointed at surface (negative to normal).

Vector3 move_and_slide_with_velocity_diference _snap (delta-velocity, up-direction, float: max_snap_difference, floor_slide_friction, wall_slide_friction, friction_cutoff, max_slides, floor_max_angle, infitie_inertia):

This is snap based on how much snapped movement would be different from unsnapped movement(length of difference vector). It behaves like limited ability to resist centrifugal force. This should cover high poly collision objects like terrain, but should work on mixed low detail - high detail collision environments as well. By calculating max difference as a function of speed and other parameters (by game developer, not by build-in functionality), a desired behavior can be achieved. Vector based snapping can discard any amount of vertical inertia; this method is not. Not only there is no need to make exeptions for jumps, but also natural, realistic, good feeling movement with velocity-dependent desnap on cliffs, ramps, any convex or spherical surfaces can be achived rather easily if needed. While making snap vector small to achieve similar cliff desnapping can compromise snap stability, increasing difference snap’s parameter to make it more snappy should work just fine.

Vector3 move_and_slide_with _snap (delta-velocity, up-direction, snap_vector, max_slides, floor_max_angle, infitie_inertia): The old snap vector, at least, should not be removed.

Vector3 test_and_slide (delta-velocity, up-direction, max_slides, floor_max_angle, floor_slide_friction, wall_slide_friction, friction_cutoff, infitie_inertia): Test version of move_and_slide, returns not just true or false, but array of all slide collisions and final point, so it can be possible to get or calculate resulting start-to-end velocity, trajectory, etc. Test versions to other methods can be added. Test version alone should be enough to implement most of stated above features, though may be more expensive.

Additional: Get "real geometry normal" in KinematicCollision object, one that does not change while with the same face, similar to raycast returned normal.

If this enhancement will not be used often, can it be worked around with a few lines of script?

It will be used quite often. However, yes there is one, but ugly, workaround - apply small movement into surface with another move_and_slide after each step with main one. That messes with slides count, is_on_floor/wall/ceiling conditions and requires to retrieve and copy intermediate data in script, each step, which probably can be costly. On the other hand, making similar functionality by the means of move_and_collide and low-level stuff is hard. It can work, but there is too many corner cases, and forcing regular game developer to do that is too much.

Is there a reason why this should be core and not an add-on in the asset library?

Because movement is important, and decreasing fidelity that can be achieved by mid-level and high-level functions looks wrong. It can be in the asset library, or it can be left like it is, but then it can turn a lot of people from Godot. Beginner-friendly should not mean beginner-only, or rather beginners + those who ready to dive at the level of engine developer, understand how move_and_slide works (by the looks of it it's rather complicated) and make their own.

Zireael07 commented 2 years ago

Get "real geometry normal" in KinematicCollision object, one that does not change while with the same face, similar to raycast returned normal.

Are you on Bullet or Godot physics?

For now, in 3.5 my controller works just fine, however in 4.0 with the new CharacterBody3d node it wouldn't be.

Why? I've ported a project using KinematicBodies to CharacterBody3D with zero problems. What are the specific problems you're having? (Problems, not requests for improvements)

Calinou commented 2 years ago

It can be in the asset library, or it can be left like it is, but then it can turn a lot of people from Godot.

If a new user is looking for a turnkey solution to Quake-style character movement, they should probably turn towards the asset library :slightly_smiling_face:

vgnod commented 2 years ago

Why? I've ported a project using KinematicBodies to CharacterBody3D with zero problems. What are the specific problems you're having? (Problems, not requests for improvements)

Snap vector, that's why. Which is already an ugly workaround, but at least works.

If a new user is looking for a turnkey solution to Quake-style character movement, they should probably turn towards the asset library 🙂

Okay, i see, making fun of me. Well, fine.

mieldepoche commented 2 years ago

The proposal is long and I can't read it entirely but

So, one major issue - control over snap vector is removed

Why not using move_and_slide and snapping manually afterwards if needed? It's the matter of a simple ray cast.

vgnod commented 2 years ago

Why not using move_and_slide and snapping manually afterwards if needed? It's the matter of a simple ray cast.

There is a lot of reasons. Messed speed, corner cases like cliffs, standing on small things, standing on spikes or something. I'm using cylinder collider and its also important. So, if there is no point of collision, from where and to where i should use that simple raycast?

vgnod commented 2 years ago

So, if, at minimum, move_and_slide with ability to test and get full data (like move_and_collide test_only) and snap vector not removed is a turnkey solution, what is not then? And what is turnkey solution in that context, exactly? I mean, yes, it sort of is, but what's alternative? I can't write my own muv_and_slide and expect it to be not worse then original. Is the meaning of that phrase, like, "if you cannot made quake - like movement without using move_and_slide, go to asset store"? Well, i already goddamn made it. I just made it using move_and_slide despite it not having full test. But how i made it? I abused snap! And why i abused snap? Because i want desnap based on vertical - speed! If i'm going up the cliff, then i have to sort of launch in the air based on my velocity, not geometry of the cliff. So to do that, i manipulated snap vector based on speed, vertical speed and such. And this works. But why i need to do that in a first place? Because after regular unsnapped move_and_slide on normal it is no longer is_on_floor! Even if i calculate all the vectors, some kind of snap is necessary. So i made another workaround, it works even in 4.0 but it have a lots of side effects. So i decided to write a long post about it and get laughed at.

vgnod commented 2 years ago

I need test_only mode for move_and_slide (not true/false, full data), especially with snap. So i can decide to keep snap or discard it. Is it wrong? What should i do? Do move_and_slide, check updated state, revert it? How this is turnkey solution? With tests move_and_slide is good tool, without i don't know what it is. Some sort of training wheels? Well no, it is not. It's good tool, please don't ruin it for no reason! If you need CharacterBody to be simple premade controller, put move_and_slide in PhysicsBody3D. Okay, my whole "proposal" is bs, but this two things, test and snap vector, is it unreasonable?

vgnod commented 2 years ago

I also can use physics server's body_test_motion. But there is said in the documentation:

Try to never request any information from VisualServer, PhysicsServer or Physics2DServer by calling functions unless you know what you are doing.

And i don't know what i'm doing. So what should i do?

Zireael07 commented 2 years ago

So i decided to write a long post about it and get laughed at.

I can't see anyone laughing at you nor anyone saying the proposal is "bs".

We already have snap vector configurable, and I second the idea of making the test function return full data