Hi all,
Extremely new to Godot and having some issues with rotating objects.
I am trying to create a first person camera that can pick up and drop objects in the environment. When I pick up the object I want to lock its current rotation along the Y axis relative to the camera. When I move the mouse, I want the object to rotate in global space to remain directly in front of the camera, but its rotation about the y-axis relative to the camera should remain the same, ie: the item should always remain upright but stay facing the camera at the same XZ orientation that it was picked up at.
My character is structured like:
- Player
-- PlayerPOV
--- RayCast3D
--- HeldItemPosition
The only times that the player.gd code interacts with the carryable object are:
- If the user left-clicks while not carrying anything, it gets the first collider along the RayCast3D and if it’s Carryable it calls pick_up() on the collider.
- If the user right-clicks while carrying something, it calls drop() on the carried object.
if Input.is_action_just_pressed("interact"): if held_object == null: var looking_at = raycast.get_collider() if looking_at is Carryable: held_object = looking_at held_object.pick_up($PlayerPOV/HeldItemPosition) if Input.is_action_just_pressed("drop"): if held_object: held_object.drop() held_object = null
Other than that all of the logic is contained within the carryable scene. What I’m trying to do is:
- When the item is picked up disable all of its collision physics and get the initial angle in global space between the object’s and carrier’s rotation vectors on the XZ plane.
- When the item is dropped, restore all of its collision physics.
- On every physics processing tick, a) get the carrier’s global transform origin and store it as the object’s global transform origin, and b) get the camera’s global transform basis, set the y-vector to Vector3.UP to keep it upright, rotate by the angle recorded when the object was picked up, and store it as the object’s global transform basis.
Desired outcome:
- When moving the mouse in any direction, the carried object always remains upright. - WORKING AS DESIRED
- When moving the mouse left and right, the object should stay in the same rotation relative to the camera - WORKING AS DESIRED
- When first picking up the item it should retain its existing orientation relative to the camera - NOT WORKING
Instead, the object snaps to a seemingly-arbitrary angle about the y axis upon pickup and I can’t figure out why. Here is the code for carryable.gd
extends RigidBody3D
class_name Carryable
var _carrier: Node3D
var _rotation_offset : float
var carried: bool = false
@onready var original_collision_layer = collision_layer
@onready var original_collision_mask = collision_mask
func pick_up(carrier: Node3D) -> void:
# stop physics interactions while object is carried
freeze = true
collision_mask = 0
collision_layer = 0
# capture carrier information
carried = true
_carrier = carrier
# get the angle between the direction of the camera and the object on the xz plane
_rotation_offset = _get_xz_rotation(_carrier.global_rotation, global_rotation)
func _get_xz_rotation(a: Vector3, b: Vector3):
# normalize a and b and project them onto the xz plane
a = Plane.PLANE_XZ.project(a).normalized()
b = Plane.PLANE_XZ.project(b).normalized()
var theta = a.angle_to(b)
return theta
func drop() -> void:
# restore physics
freeze = false
collision_mask = original_collision_mask
collision_layer = original_collision_layer
carried = false
_carrier = null
func _physics_process(delta: float):
if carried:
# get the carrier's position in global space and snap self to it
var target_origin = _carrier.global_transform.origin
global_transform.origin = target_origin
# get the global rotation info for the carrier but maintain the y axis as
# straight up to keep the object upright
var target_basis: Basis = Basis(
_carrier.global_transform.basis.x,
Vector3.UP,
_carrier.global_transform.basis.z).orthonormalized()
# rotate the target basis by the initial angle between the object and the carrier
target_basis = target_basis.rotated(Vector3.UP, _rotation_offset)
# store as the new basis
global_transform.basis = target_basis
Here's a video of what is happening:
As you can see when I pick up the item it initially rotates to a new orientation (bad) which it then maintains (good).
My guess is that _rotation_offset isn’t being calculated properly but I can’t quite figure out why not unless some function isn’t doing what I expect. I’m very new to godot so I might be missing something obvious.
Thanks in advance please let me know if any extra information is helpful.