Gravity is not working and CharacterBody3D won't touching floor

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By kzulfazriawan

Hello, I’m trying to create an isometric game with Diablo-like control, but gravity isn’t working. The CharacterBody3D won’t touch the floor, the print(is_on_floor()) always results false.

Here’s my code for PlayerCapsule

extends ActorPlayable3D
class_name PlayerCapsule


var navigationAgent3D

func _ready():
	navigationAgent3D = get_node(navigationAgent)


func _physics_process(delta):
	velocity.y -= gravity * delta
	if navigationAgent3D.is_navigation_finished():
		return
	
	var movement_delta = speed * delta
	var next_path_position: Vector3 = navigationAgent3D.get_next_path_position()
	var current_agent_position: Vector3 = global_position
	var new_velocity: Vector3 = (next_path_position - current_agent_position).normalized() * speed
	if navigationAgent3D.avoidance_enabled:
		navigationAgent3D.set_velocity(new_velocity)
	else:
		_on_navigation_agent_3d_velocity_computed(new_velocity)

func setNavigationAgentToLocation(pos: Vector3):
	navigationAgent3D.set_target_position(pos)


func _on_navigation_agent_3d_velocity_computed(safe_velocity):
	velocity = safe_velocity
	print(safe_velocity)
	print(is_on_floor())
	move_and_slide()

And here’s the ActorPlayable3D class

extends CharacterBody3D
class_name ActorPlayable3D

@export var speed: float = 5.0
@export var navigationAgent: NodePath

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

class Navigation:
	func setNavigationAgent(node: NavigationAgent3D) -> void:
		pass

Here’s the output from the editor:
enter image description here

How can I fix the gravity issue and make the player touch the floor?

Is this a gravity issue? It looks like the provided code reports ‘true’ when the capsule is on the stairs, so what’s different here? Worth checking the colliders for the geometry is as you expect it. move_and_slide should report the collision too. If it’s false, there’s something else going on.

spaceyjase | 2023-06-13 10:48

I don’t know much about 3D physics bodies or navigation, but I don’t think modifying the velocity like that has any effect unless you call move_and_slide(). Also when you calculate the velocity from the navigation data you don’t incorporate the gravity, so it just heads straight for the navigation point.

Also also I don’t think you’re supposed to multiply the velocity by delta because the movement routines already take delta into account. The docs say it is " typically metres per second".

HyperlinkYourHeart | 2023-06-13 11:41

Well, Yes when the CharacterBody3D is moving on the slope surface the collisionshape between the floor and the kinematics, maybe because there’s an overlap between the NavigationRegion3D with the CollisionShape in the mesh instance. But on the flat surface the NavigationRegion3D are layering the floor CollisionShape, it always says is_on_floor() is false.

Here are some picture from different angle in scene

kzulfazriawan | 2023-06-13 11:55

The move_and_slide method is called in the next signal actually after the condition

if navigationAgent3D.avoidance_enabled:
    navigationAgent3D.set_velocity(new_velocity)
else:
    _on_navigation_agent_3d_velocity_computed(new_velocity)

but I have tried to override the velocity.y to calculate pulled into gravity but no works, and could you please share with me the documentation calculating velocity without using delta I’m quite new to changes in Godot 4 after migrating from version 3

kzulfazriawan | 2023-06-13 12:03

Sorry I missed that call to move_and_slide. You updated velocity with the gravity, but it seems to me like you need to incorporate it into new_velocity. You override the velocity with new_velocity, so the change to velocity you made to incorporate the gravity earlier in the function is lost.

Try something like this:

var new_velocity: Vector3 = (next_path_position - current_agent_position).normalized() * speed
new_velocity.y -= gravity * delta
if navigationAgent3D.avoidance_enabled:
    navigationAgent3D.set_velocity(new_velocity)
else:
    _on_navigation_agent_3d_velocity_computed(new_velocity)

The velocity property of CharacterBody3D is described here. The move_and_slide example in the physics tutorial does multiply the gravity by delta, so I guess I was wrong about that - I suppose because it is applying an acceleration rather than setting a velocity - my bad.

HyperlinkYourHeart | 2023-06-13 13:43

:bust_in_silhouette: Reply From: kzulfazriawan

Okay so I have finally figured it out by excluding the Y in path Vector3 after getting the new velocity from the calculation current to the next path navigation, but before that I divide it into 2 methods the path subtraction to direction and normalized multiped with speed. the direction.y will tell the _physics_process is the path going up to slope or down, if the path target is up/higher than the current position override velocity.y to newVelocity otherwise use gravity * delta

Here’s the PlayerCapsule.gd:

extends ActorPlayable3D
class_name PlayerCapsule

var pathScanning
var vel = Vector3.ZERO

func _ready():
	pathScanning = PathScanning.new($".")
	pathScanning.setNavigationAgent($NavigationAgent3D)
	pathScanning.setSpeed(speed)


func _physics_process(delta):
	if not is_on_floor():
		vel.y -= gravity * delta
	print("touch the floor ? ", is_on_floor())
	
	if not $NavigationAgent3D.is_navigation_finished():
		var direction = pathScanning.getSubtractPosition()
		pathScanning.setDirectionFromPosition(direction)
		
		var newVelocity = pathScanning.getNewVelocity()
		vel.x = newVelocity.x
		vel.z = newVelocity.z
		
		if floatWithTail(direction.y, 2) > 0.1:
			print("direction Y up -> ", floatWithTail(direction.y, 2))
			vel.y = newVelocity.y - (gravity * delta)
		
		if not $NavigationAgent3D.avoidance_enabled:
			_on_navigation_agent_3d_velocity_computed(vel)
			
		else:
			$NavigationAgent3D.set_velocity(vel)
	else:
		vel = Vector3(0, vel.y, 0)
		velocity = vel
	move_and_slide()

func setNavigationAgentToLocation(pos: Vector3):
	$NavigationAgent3D.set_target_position(pos)


func _on_navigation_agent_3d_velocity_computed(safe_velocity):
	velocity = safe_velocity

and Here’s the class ActorPlayable3D

extends CharacterBody3D
class_name ActorPlayable3D

@export var speed: float = 5.0
@export var navigationAgent: NodePath

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")


func floatWithTail(num: float, tail: int) -> float:
	return round(num * pow(10.0, tail)) / pow(10.0, tail)


class PathScanning:
	var node: CharacterBody3D
	var navigationAgent3D: NavigationAgent3D
	var speed: float
	var direction: Vector3
	
	func _init(node: CharacterBody3D):
		self.node = node
	
	func setSpeed(speed: float) -> void:
		self.speed = speed
	
	func setNavigationAgent(node: NavigationAgent3D) -> void:
		self.navigationAgent3D = node
	
	func getSubtractPosition() -> Vector3:
		var nextPathPosition:= self.navigationAgent3D.get_next_path_position()
		var currentPosition := self.node.global_position
		return nextPathPosition - currentPosition
	
	func setDirectionFromPosition(pos: Vector3) -> void:
		self.direction = pos
	
	func getNewVelocity() -> Vector3:
		return (self.direction).normalized() * self.speed

Here’s the result
enter image description here