I dont know how to word the title, but to explain:
I have a player controller, and it has a Node3D which is always in front of the camera. I then have interactibles, which are of type RigidBody3D. I am trying to script some kind of holding of them. I know this would be easily done with just freezing the body and setting its position to be the position of the Node3D thats in front of the camera. BUT, i want it to be physically accurate.
Let me set an example;
if the Node3D is underground, i dont want the RigidBody3D to be also underground, but try to get to the position, while still taking into acount the collisions.
This also would mean that if the held RigidBody3D would be smashed into an object, the object would take the forces and for example break
I am asking here just for the part on getting the RigidBody3D to behave physically accurate, not how to make something break and etc…
where: item_interaction is the currently held interactible interactible_pos is the position that it needs to go to
And i made it when the pos and the interactible are further apart, means the interactible got stuck somewhere, it applies a force in the direction to the position it was supposed to be and reseting gravity
if item_interaction.global_position.distance_to(interactible_pos.global_position) > 2:
item_interaction.apply_impulse(dir_to)
item_interaction.gravity_scale = 1
This aproach i made is very janky and has its own flaws, for example getting stuck on smaller colliders where it could just slide (As CharacterBody3D, can slide, but Rigidbody3D doesnt have that function, it only has move_and_collide).
This could be improved, i know that, i just dont have the knowledge on how to improve it.
I dont think this solves my problem tho, i am just sharing what is atleast 50% working.
@nutlike4693 would you mind telling me how exactly i could do this? i managed to make the interactible as node_b, but else than that it doesnt work, or better said, i dont know how it works xd
OK. Made a few tweaks to make the behaviour better. Item now snaps into place if close enough. Oscillations are dampened at a distance and canceled up close. Lemme know if this works:
extends Node3D
# Exported variables for easy configuration in the Godot editor
@export var item_to_pickup: RigidBody3D # The object to be attracted
@export var attract_force := 10.0 # Strength of the attraction force
@export var attraction_enabled := true # Toggle for the attraction behavior
@export var disable_gravity := true # Option to disable gravity on the attracted object
@export var damping_factor := 2 # Factor to dampen the object's movement
@export var pickup_radius := 0.05 # Radius within which the object snaps to the attractor
@export var dead_zone_radius := 0.001 # Radius within which the object matches velocity with the attractor
func _ready():
# Initial setup when the node enters the scene tree
if item_to_pickup:
if disable_gravity:
item_to_pickup.gravity_scale = 0 # Disable gravity if specified
func _physics_process(_delta):
# Called every physics frame. 'delta' is the elapsed time since the previous frame.
if item_to_pickup and item_to_pickup.is_inside_tree() and attraction_enabled:
# Calculate distance between attractor and object
var distance = global_position.distance_to(item_to_pickup.global_position)
# Apply different behaviors based on the distance
if distance <= dead_zone_radius:
apply_velocity_matching() # Match velocity when very close
elif distance <= pickup_radius:
apply_centering_impulse() # Snap to center when within pickup radius
else:
apply_attraction_force() # Apply attraction force when far
func apply_attraction_force():
# Calculate and apply the attraction force with damping
var direction = global_position - item_to_pickup.global_position
direction = direction.normalized()
var force = direction * attract_force
var velocity = item_to_pickup.linear_velocity
var damping_force = -velocity * damping_factor
var total_force = force + damping_force
item_to_pickup.apply_central_force(total_force)
func apply_centering_impulse():
# Calculate and apply an impulse to center the object in one frame
var displacement = global_position - item_to_pickup.global_position
var required_velocity = displacement / get_physics_process_delta_time()
var velocity_change = required_velocity - item_to_pickup.linear_velocity
var impulse = velocity_change * item_to_pickup.mass
item_to_pickup.apply_central_impulse(impulse)
func apply_velocity_matching():
# Calculate and apply an impulse to match the object's velocity to the attractor
var velocity_difference = -item_to_pickup.linear_velocity # Assuming attractor is stationary
var impulse = velocity_difference * item_to_pickup.mass
item_to_pickup.apply_central_impulse(impulse)
func set_attraction(enabled: bool):
# Toggle the attraction behavior
attraction_enabled = enabled
func set_damping(value: float):
# Set the damping factor and update the object's linear damping
damping_factor = value
func set_pickup_radius(value: float):
# Set the pickup radius
pickup_radius = value
func set_dead_zone_radius(value: float):
# Set the dead zone radius
dead_zone_radius = value
@nutlike4693
hey, sorry for late reply, i did need to modify it a little bit, but nothing major, just swapping some values with what i have, as i also have interactions with other stuff, such as vehicles and etc. but else than that the code works flawlesly, big thanks man!