• Godot Help
  • How to resolve forces on a KinematicBody?

I'm trying to implement a sliding block in a 3D game. I've tried using a RigidBody, but I'm finding the physics engine is giving me strange results so I thought it might be better to use a KinematicBody instead. I'm trying to figure out how to tell when a force is being applied to the object (such as my player pushing into it) so that I can then call a move_and_slide to move it. How can I find the applied physics forces in a frame?

RigidBody is usually the right choice for interactive objects, unless it is a puzzle game that needs perfect accuracy (like you are sliding on a grid and can't move off it, etc.). KinematicBody is meant for players and enemies, that are controller either by a user or by AI. They can affect the physics in the game, but are not intended to be affect by physics (e.g. gravity is not applied) though their movement can be stopped by a StaticBody. Honestly, it may be a good idea to try to solve the issue you are having with RigidBody, rather than reinvent the wheel.

However, there some possible options you have. When you call move_and_slide() on the player, there will be KinematicCollision object detected. You can get this collision by calling get_last_slide_collision().

https://docs.godotengine.org/en/stable/classes/class_kinematicbody.html#class-kinematicbody-method-get-last-slide-collision

From there, you can check if the collider is the object you are looking for, and then apply movement to it, using the paramters of the KinematicCollision (such as collider_velocity or normal). In practice, though, I don't think it will work well. Since what if the box hits a wall? Then the box will stop (due to the physic engine) but the player won't. So you have to have two-way collision between the box and the player (or multiple boxes, multiple walls, etc.) to the point where I think it will just jitter and eventually fail.

The other option would be to use a RigidBody for the box, but override the function _integrate_forces(). This gives you the full physics state of the object, and you can do anything you want it it. You can make it move faster or slower, pause time, restrict to certain axes, etc. This is still sort of a hack, but at least it will work with the existing physics engine.

https://docs.godotengine.org/en/stable/classes/class_rigidbody.html#class-rigidbody-method-integrate-forces

However, I still think it best to make sure you have explored all the options and parameters in the editor, to get the physics working before you try any sort of custom hack type of code.

    cybereality Well, the problem I'm having with the RigidBody right now is that if I place my player character directly over it, it pushes it into the floor. It can also push it into walls. I'm not sure how to correct for this since my code seems to be working for static and kinematic bodies.

    extends KinematicBody
    
    
    export var jump_impulse:float = 20
    export var walk_impulse:float = 200
    export var walk_max_speed:float = 20
    var velocity: Vector3 = Vector3.ZERO
    export var gravity_accel:float = 100
    export var linear_damp:float = .2
    
    func _ready():
    	pass # Replace with function body.
    
    func _physics_process(delta):
    	if is_on_floor():
    		velocity = Vector3.ZERO
    	else:
    		velocity += Vector3.DOWN * gravity_accel * delta
    	
    	var x = global_transform.basis.x
    	var z = global_transform.basis.z
    		
    	var move_impulse:Vector3 = Vector3.ZERO
    	if Input.is_action_pressed("ui_up"):
    		move_impulse = -z * walk_impulse * delta
    	if Input.is_action_pressed("ui_down"):
    		move_impulse = z * walk_impulse * delta
    	if Input.is_action_pressed("ui_left"):
    		move_impulse = -x * walk_impulse * delta
    	if Input.is_action_pressed("ui_right"):
    		move_impulse = x * walk_impulse * delta
    	if Input.is_action_just_pressed("ui_accept"):
    		var overlap = $FloorCheck.get_overlapping_bodies()
    		
    		if overlap.size() > 0:
    			velocity += Vector3.UP * jump_impulse
    		
    	var new_vel = velocity + move_impulse
    	var new_vel_y = new_vel.project(Vector3.UP)
    	var new_vel_xz = new_vel - new_vel_y
    	if new_vel_xz.length() < walk_max_speed:
    		velocity = new_vel
    
    	velocity -= velocity * linear_damp * delta
    
    	move_and_slide_with_snap(velocity, Vector3.DOWN * 10, Vector3.UP, true)

    In that case, the issue is the player and not the box. The first method I explained (using get_last_slide_collision()) can help with this. For example, if the player touches the box from the top (if the normal is mostly facing up and/or the player velocity is moving down, then stop applying gravity to the player). I tried to do this on a 2D demo and almost got it working. It is a little bit of a hack, but it seems like the most promising path.