ywmaa / Advanced-Movement-System-Godot

This project is a template for creating advanced Third/First Person movement in GODOT
MIT License
424 stars 37 forks source link

Mantle System #7

Open ywmaa opened 2 years ago

ywmaa commented 2 years ago

Simple System for climbing relatively small obstacles like maximum 1 meter height climbing

TheFunnyBrain commented 1 year ago

I'd like to have a try at this one - I've pulled in and made a branch :)

I can't promise any talent, skills or a good solution, but I guess I can try, if I get anywhere close then offer it as a pull request?

My thoughts so far based on how I've done similar features in Unity:

Random thoughts:

ywmaa commented 1 year ago

I can't promise any talent, skills or a good solution, but I guess I can try, if I get anywhere close then offer it as a pull request?

Yes, any help is good, thanks in advance.

or manually tagged by the developer. I like the idea of it being automatic, then opting objects out as needed, as it would allow terrain to naturally be traversed..

Focus on Automatic system.

Is there any animation earmarked for this? I know mixamo has a couple, but I fear Adobe, so wanted to check! :)

I am already using a mixamo character and animations, so mixamo animations are now the most compatible. Plus the character and the animations are only demos.

Any system should consider that they can be switched.

I think it would be more fitting to make the player's collider move, and the animation follow it - i.e. not have the root motion

The philosophy of the Advanced-Movement-System-Godot Is to never use Root Motion, always move the capsule and try as much as possible to sync the animation to it with stuff like

Motion Matching.

Distance Matching.

Motion Warping.

These techniques are used in unreal engine, and they are what make animation looks really good in unreal.

ywmaa commented 1 year ago

Other tips :

The Advanced-Movement-System-Godot is inspired by

Advanced-Locomotion-System in Unreal Engine : https://youtu.be/ru1--3wP-F8?si=wocLp2z3MI4QuknN

You can see the dynamic mantling system in 3:50

TheFunnyBrain commented 1 year ago

Thank you so much for the outlining - it's incredibly helpful to catch the underlying philosophy and approach to the code before guessing and making a meal of it! :) I'll focus on the detection initially, so that the character movement has a concept of obstacles, height comparison etc. :)

ywmaa commented 1 year ago

I would like to point out, my stair solving solution somehow worked for high obstacles BUG OR FEATURE ? so I made a maximum climbing height, and I think I set it to 0.2 Meters or something.

ywmaa commented 1 year ago

if I would go through the climbing approach, I would do the detection, then use the MotionWarping Node (Available in the template too). the MotionWarping Node can be used to change the player position smoothly (Check Out the test in "fire" input, where the chaarcter starts kicking, in the code, you will see the use of motion warping), and while changing the player position smoothly to match the animation time, it should give the full feel of the character climbing.

ywmaa commented 1 year ago

The MotionWarping Node waits for the animtion to execute the function, in the kicking case I named the function "kick_target", this means I need to go to the kicking animation, and the exact time the kicking hits the target, and execute the function.

Anyway, if you find all of this confusing, you can focus on climbing detection only, and for me plugging these stuff is quite easy.

TheFunnyBrain commented 1 year ago

I would like to point out, my stair solving solution somehow worked for high obstacles BUG OR FEATURE ? so I made a maximum climbing height, and I think I set it to 0.2 Meters or something.

I'm leaning into feature territory, but not sure. 😂 I am tempted to look into the move_and_collide features in godot, at least to try and predict if it's possible for the player to climb up without clipping into things, hitting their head etc.

I'm at the stair solution in the code, and as you say, they're very similar solutions. I'm wondering whether to extend your existing work on the stairs by adding in a high ray (at head height) to assess if the obstacle is higher than stairs, but also not too high to mount?

I've annotated what I think is happening, and where I'm thinking the code could live. :)

CharacterMovementComponent.stair_move() (approx line 435)

    var stair_top_collision = direct_state.intersect_ray(climb_ray_info)
    #If so, the obstacle height is < stair height, so snap to top of stair?
    if stair_top_collision:
        if stair_top_collision.position.y - character_node.global_position.y > 0 and stair_top_collision.position.y - character_node.global_position.y < 0.15:
            movement_state = Global.movement_state.grounded
            is_moving_on_stair = true
            character_node.position.y += stair_top_collision.position.y - character_node.global_position.y
            character_node.global_position += Vector3(0, 0, 0.01).rotated(Vector3.UP,movement_direction)
        else:
            #Object ends above max_stair_climb_height - would this be a good place to add mantle?
            await get_tree().create_timer(0.4).timeout
            is_moving_on_stair = false
    else:
        await get_tree().create_timer(0.4).timeout
        is_moving_on_stair = false
else:
    #There is no obstacle
    await get_tree().create_timer(0.4).timeout
    is_moving_on_stair = false
ywmaa commented 1 year ago

I think you can just use the same script to just check if it is possible to mantle, so here : #Object ends above max_stair_climb_height - would this be a good place to add mantle? just add a variable can_mantle in the whole script, and set it in this part of code to true if possible, else set it to false. also to not rewrite the code for getting where the player should be snapped, you can save that too in another variable like

mantle_position

Then in mantle(): placeholder function that I left there, you implement the execution of mantle, so it works just like jump(). This allows us to call mantle() if player presses Jump and it is possble to mantle, it will mantle instead of jumping. Or while in Air it of course will favour mantling because anyway the player can't jump in Air.

Edit : typos

TheFunnyBrain commented 1 year ago

Thank you :) I'm looking at it now, and hopefully can make some progress - thanks for guiding me through all this, I'm hoping it's useful in the long run :)

Edit: can mantle now reliably gets called; it's a tiny bit unresponsive due to the timers etc, but calls on the white boxes :) Onto adding the mantle position, and a mantle function :)