Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move_and_slide_with_snap steep slops problem #30310

Closed
Tracked by #45333
Janders1800 opened this issue Jul 4, 2019 · 6 comments · Fixed by #52889
Closed
Tracked by #45333

Move_and_slide_with_snap steep slops problem #30310

Janders1800 opened this issue Jul 4, 2019 · 6 comments · Fixed by #52889

Comments

@Janders1800
Copy link

Janders1800 commented Jul 4, 2019

Godot version:
3.1.1

OS/device including version:
Zorin OS 12 Core (Ubuntu 16.04 LTS)

Issue description:
Wen a character moves to a slope too steep gets sent flying.

https://www.youtube.com/watch?v=sMNf2ZbLixQ

Steps to reproduce:
Move a KinematicBody with move_and_slide_with_snap to a steep slope.

@Janders1800 Janders1800 changed the title Move_and_slide_with_slap steep slops problem Move_and_slide_with_snap steep slops problem Jul 4, 2019
@Janders1800
Copy link
Author

Janders1800 commented Aug 25, 2019

I fixed it, but I'll leave this open because the fix requires a lot of GDscript code. Maybe it can be addressed easily otherwise.

@BenjaminNavarro
Copy link
Contributor

Can you please share how you fixed it?

@Calinou
Copy link
Member

Calinou commented Aug 17, 2020

@Janders1800 Can you (or anyone else) still reproduce this bug in Godot 3.2.2 or any later release?

If yes, please ensure that an up-to-date Minimal Reproduction Project (MRP) is included in this report (a MRP is a zipped Godot project with the minimal elements necessary to reliably trigger the bug). You can upload ZIP files in an issue comment with a drag and drop.

@Janders1800
Copy link
Author

Yes, it still happens on stable and on 3.2.3-rc3
Here a minimal reproduction project:
Kinema.zip

The red wall is just a little bit rotated and the "player" gets launched on air (I suppose it should prevent it from climbing and force it to stay on floor), the green wall is completely vertical.
Screenshot from 2020-08-18 22-04-39

@e344fde6bf
Copy link
Contributor

e344fde6bf commented Mar 20, 2021

I think move_and_slide() is working as intended here (even if it's not useful behaviour). What seems to be happening is the players' speed is overpowering the effects of gravity. The player controller sets it's horizontal velocity to a fixed value each frame vel.z = speed. Now when the player collides with the slanted wall, move_and_slide() will slide against the wall redirecting its horizontal velocity based on the walls collision normal. Then this code vel = move_and_slide(vel, Vector3.UP, true) overwrites the players vel vector and hence any acceleration due to gravity is canceled out.

You can mostly work around this by storing gravity velocity in a separate vector:

	gravity.y -= 9.8 * delta
	var out_vel = move_and_slide_with_snap(vel + gravity, Vector3.DOWN, Vector3.UP, true)
	if is_on_floor():
		gravity = Vector3()

This way the velocity due to gravity is not overridden.

However, if you want to use this approach without snapping with move_and_slide then with this approach you'll still "hop" when you slide against the wall as it takes several frames for gravity to overpower the players horizontal motion sliding up the wall. A workaround to this new problem is to do something like:

More complicated workaround (just example, need to make it more robust)
	gravity.y -= 9.8 * delta
	var start_pos = global_transform.origin
	was_on_floor = is_on_floor()
	var in_vel = player_vel + gravity
	var out_vel = move_and_slide(in_vel, Vector3.UP, true)
	
	if is_on_floor():
		gravity = Vector3()
		
	if was_on_floor and is_on_wall() and not is_on_floor() and out_vel.y > 0:
		print("sliding up wall %d, first collision %s" % [Engine.get_physics_frames(), get_slide_collision(0).collider.name] )
		self.global_transform.origin = start_pos
		# TODO: can't assume first collision was with a wall, it might have been with the floor
		# if it's not, then we have to iteratively advance our position and check the next collision
		var wall_normal = get_slide_collision(0).normal
		# we want to slide against the slopped wall as if it was a vertical wall
		wall_normal.y = 0
		wall_normal = wall_normal.normalized()
		var new_slide = in_vel.slide(wall_normal)
		new_slide.y = 0 + gravity.y
		out_vel = move_and_slide(new_slide + gravity, Vector3.UP, true)

I suppose it should prevent it from climbing and force it to stay on floor

It can't do this internally without providing an extra argument to control this, because sliding up slanted walls is sometimes wanted behaviour, for example when you jump.

Really, I think half the problem is the get_slide_collision() part of the API is not very user friendly, so it's hard to work around problems like this without essentially rewriting your own move_and_slide() function.

@Janders1800
Copy link
Author

Janders1800 commented Mar 22, 2021

The problem happens with move_and_slide_with_snap.

Your first solution doesn't work, the KinematicBody still gets launched.

The problem in the function is the corruption of the Y axis, a simpler "fix" and a way to see the problem is using this code.

extends KinematicBody

var vel:Vector3 = Vector3()

func _physics_process(delta):
	vel.x = 0
	vel.z = 0
	
	if(Input.is_action_pressed("ui_left")): vel.z = -5
	if(Input.is_action_pressed("ui_right")): vel.z = 5
	
	vel.y -= 9.8 * delta
	
	var prevPosY = global_transform.origin.y
	vel = move_and_slide_with_snap(vel, Vector3.DOWN, Vector3.UP, true)
	if is_on_wall() and not is_on_floor():
		global_transform.origin.y = prevPosY
		vel.y = 0 # Break point here, the y axis goes to +-20

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants