Godot Version
v4.7.stable.official [5b4e0cb0f]
Question
Hello everyone! I stumbled upon an interesting bug and I want to know why this happens, and if there is a better fix.
So normally, when using apply_force() function, the force position must be in local coordinates of the RigidBody3D, that’s nice and easy. However, things became somewhat weird with local coordinates when I made a script for moving RigidBody3Ds with mouse.
The script simply grabs a rigidbody from the position you click on it, then moves it towards the mouse cursor by using apply_force() every physics frame. Here’s the breakdown of how I’m doing it, and where it goes weird.
- I cast a ray from the camera in order to get the position.
if event is InputEventMouseButton and event.pressed and event.button_index == 1:
## Cast a ray from the screen to detect what is being clicked on.
var from = camera.project_ray_origin(event.position)
var to = from + camera.project_ray_normal(event.position) * raycast_length
var raycast_data = Global.cast_ray(camera, from, to, 1)
## If the clicked object is a RigidBody, set necessary variables.
if raycast_data.has("collider") and raycast_data["collider"] is RigidBody3D:
rigid_body = raycast_data["collider"]
force_position = raycast_data["position"]
localized_force_position = rigid_body.to_local(force_position)
Raycast returns a global coordinate with ["position’] key, I then convert it to RigidBody3D’s local coordinates and store it so the point you grab the RigidBody doesn’t change even if it moves.
- I then convert that local coordinate back to global in order to compare it to the mouse position and apply the necessary force to move the RigidBody towards the mouse.
#### This code runs in _physics_process() ####
if rigid_body:
var grab_point: Vector3 = rigid_body.to_global(localized_force_position)
## This time, instead of casting a ray, I'm just using the camera.project_ray_normal() function
## to have a fixed position from camera. It works well, no bugs here.
var from = camera.project_ray_origin(mouse_position)
var to = from + camera.project_ray_normal(mouse_position) * pick_distance
var force: Vector3 = grab_point.direction_to(to) * grab_point.distance_to(to) * spring_stiffness * rigid_body.mass
## RigidBody3D.apply_force() takes in local vector values, but somehow
## you got to rotate it too when applying force to a rigidbody from a different node.
## Weird but this is the current fix I could come up with.
var rotated_position: Vector3 = localized_force_position.rotated(rigid_body.global_basis.y, rigid_body.rotation.y)
var velocity: Vector3 = Global.get_point_velocity(rigid_body, localized_force_position)
var damping_force: Vector3 = rigid_body.linear_velocity * rigid_body.mass * damping
rigid_body.apply_force(force - damping_force , rotated_position)
Now here is where it gets weird. In order for apply_force() to get the correct position, I got to rotate the local coordinate (var rotated_position). If I dont rotate the localized_position variable based on the RigidBody3Ds current rotation, local coordinate behaves as if the RigidBody3D is always facing the -z axis.
Let me show it with a video.
I’m curious about the reason behind this behaviour. I may be doing something wrong, and I’d like to know the reason. Using apply_force() never caused an issue like this before for me, and I always used it for my custom car physics without any issues.