I'm writing a game with similar mechanics to baseball, where the player can redirect the ball based on the direction held in the right stick of the controller. However, I'm having trouble applying the rotation to the impulse vector based on the player's position in relation to the ball.
I'll leave an image with the player and the ball, and the arrows indicate which direction corresponds to the direction held in the right stick of the controller.

The intended behavior would be that the direction the impulse is nudged towards is dependent on the player's direction. So for this next example, if the player moves the right stick left, the ball would go towards the opponents half; if they move it right, it should go towards their own goal. However, the player should never be able to send the ball behind him, so the R-stick down input should never nudge the impulse.

When hitting without applying any direction (right stick centered) it works very well, the ball always flies out from the front of the player's position, in a straight line forwards. However, when I try to apply any direction, it seems almost random. NPCs also have a lot of trouble directing the ball correctly (they're always aiming towards the goal, but they never nudge in that direction when they can).
Code looks like this. WeaponComponent is a child of the model's arm bone:
extends Node3D
class_name WeaponComponent
@export var baseImpulse: Vector3
@export var weapon_model: PackedScene
@export var maximumRotation: float
@export var force: float = 2
@onready var hitbox: Area3D = %WeaponHitboxComponent
var x_angle
var y_angle
var nudgedImpulse: Vector3
var direction: Vector2
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
func get_nudged_impulse(object, direction) -> Vector3:
var nudgedImpulse:Vector3 = global_transform.basis.z * force * -1
if direction == Vector2.ZERO:
direction = Input.get_vector("aim_left", "aim_right", "aim_up", "aim_down")
print(direction)
x_angle = maximumRotation * direction.x
y_angle = maximumRotation * direction.y
nudgedImpulse = nudgedImpulse.rotated(Vector3(1,0,0), x_angle)
nudgedImpulse = nudgedImpulse.rotated(Vector3(0,0,1), y_angle)
return nudgedImpulse
func hit(direction = Vector2(0,0)):
var objects_to_hit = hitbox.get_overlapping_bodies()
for object in objects_to_hit:
if object.has_method('get_hit'):
nudgedImpulse = get_nudged_impulse(object, direction)
object.get_hit(nudgedImpulse)
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
Keep in mind that the ball has Linear and Angular locks on the Y axis (it's not affected by gravity), and only moves through the X and Z axes. That's why the impulse for the Y direction is rotated on the Z axis.