Struggling to transfer arc targeting feature from scene-level to player-level

Godot Version

4.3

Question



player
scene

[I’m a coding noob and more of a designer so please excuse any ridiculous code]

I am attempting to transfer an arc targeting feature, in which my player (a boat) uses the target to launch cannonballs at enemies and crates. Originally, the arc existed in the scene, but I want to transition the game to multiplayer, meaning that multiple players need their own arcs, and the node should instead be attached to the player. In my attempt to transfer, the arc has gotten distorted (before and after shown in photos), and since I had a friend of mine write the original arcing logic, I don’t have a great idea of how to fix it. I’ll include the relevant code blocks and be responsive with answering questions/ providing more if needed, thanks!

main_camera.gd

extends Camera3D

## The boat to follow (assign this in the editor or through code)
#@export var boat: Node3D
#
## Fixed height and distance from the boat
#@export var CAMERA_HEIGHT = 5.0   # Height above the boat
#@export var CAMERA_DISTANCE = 10.0 # Distance behind the boat
#
## Smooth following speed
#@export var FOLLOW_SPEED = 5.0    # Adjust this to make the camera follow faster or slower
#
##func _process(delta):
	##if boat:
		### Get the boat's current position
		##var boat_position = boat.global_transform.origin
##
		### Calculate the desired camera position
		##var target_camera_position = boat_position + Vector3(-CAMERA_DISTANCE, CAMERA_HEIGHT, 0)
##
		### Smoothly interpolate the camera position towards the target position
		##global_transform.origin = global_transform.origin.lerp(target_camera_position, FOLLOW_SPEED * delta)
##
		### Optionally, make the camera look at the boat
		###look_at(boat_position, Vector3.UP)

@onready var player = get_node("/root/Main/NewBoat")  # Assign your player in the editor or dynamically
var margin: float = 400.0  # Distance from the screen edge to start moving the camera
var camera_speed: float = 8.0  # Speed at which the camera moves

func _process(delta):
	if not player:
		return

	# Get player's position in screen space (2D coordinates)
	var player_screen_pos = unproject_position(player.global_transform.origin)

	# Check if the player is near the left or right edges of the screen
	if player_screen_pos.x < margin:
		# Move camera left if player is near the left edge
		global_transform.origin.x -= camera_speed * delta
	elif player_screen_pos.x > (get_viewport().size.x - margin):
		# Move camera right if player is near the right edge
		global_transform.origin.x += camera_speed * delta
		
		
	global_transform.origin.z = player.global_transform.origin.z + 5```

cannonballfire.gd

extends Node3D

@onready var projectile_scene = preload(“res://Cannonball/Projectile.tscn”)
@onready var camera = get_node(“…/Camera_Controller/Camera_Target/MainCamera”)
@onready var reticle = $Reticle
@onready var arc_mesh_instance = $Arc # Reference to the PathMeshInstance node
@onready var hit_ball = $HitBall

var fixed_y = 1
var projectile_y_offset = 1

Circular plane properties

var circle_radius = 5.0
var circle_segments = 32

var target_position = Vector3()
var launch_speed = 20.0 # Declare launch_speed as a class variable
var time_of_flight = 0.0
var gravity = ProjectSettings.get_setting(“physics/3d/default_gravity”)
var arc_height = 5

var immediate_mesh : ImmediateMesh

@onready var arc_shader: Shader = load(“res://Shaders/arc.gdshader”)
@onready var ui: Crew = get_node(“/root/Main/Crew”)
@export var boat: Boat

var cooldown_timer: Timer
var can_fire := true

func _ready():
immediate_mesh = ImmediateMesh.new()
arc_mesh_instance.mesh = immediate_mesh
arc_mesh_instance.cast_shadow = false

#cooldown_timer = Timer.new()
#cooldown_timer.set_wait_time(1.0/ui.fire_rate)
#cooldown_timer.autostart = false
#cooldown_timer.one_shot = true
#add_child(cooldown_timer) # must be added to tree for timer to work

# Signals
InputController.controller_status_changed.connect(self._on_controller_status_changed)

func _on_controller_status_changed(is_using_controller: bool):
if is_using_controller:
reticle.global_transform.origin = boat.global_transform.origin
reticle.global_transform.origin.y = fixed_y

func _process(delta):
if boat == null:
boat = get_node_or_null(“/root/Main/NewBoat”) # Adjust the path to match your scene tree
if boat == null:
print(“Error: Could not find the Boat node! Stopping execution.”)
return # Stop execution if boat is still null

print("Boat position:", boat.global_transform.origin, "Target:", target_position)	

#cooldown_timer.set_wait_time(1.0/ui.fire_rate)

# Update the plane's position continuously to follow the mouse position
if InputController.is_using_controller:
	reticle.global_transform.origin = InputController.apply_inputs_to_vector(reticle.global_transform.origin, 10*delta)
else:
	var mouse_pos = get_viewport().get_mouse_position()
	reticle.global_transform.origin = get_position_on_y_plane(
		camera.project_ray_origin(mouse_pos),
		camera.project_ray_normal(mouse_pos),
		fixed_y
	)

# Draw the arc from the projectile origin to the target plane
target_position = reticle.global_transform.origin
adjust_launch_speed(boat.global_transform.origin, target_position)
draw_arc(boat.global_transform.origin, target_position)

# Check for mouse click to launch the projectile
if Input.is_action_just_pressed("mouse_click"): 
#and cooldown_timer.is_stopped() and ui.cannon_amount > 0:
		#cooldown_timer.start()
		#ui._add_cannon(-1)
	launch_object(target_position)

Function to calculate the intersection of a ray with a horizontal plane at fixed_y

func get_position_on_y_plane(origin: Vector3, direction: Vector3, fixed_y: float) → Vector3:
if direction.y == 0:
return origin
var t = (fixed_y - origin.y) / direction.y
return origin + direction * t

Function to adjust the launch speed based on distance to the target

func adjust_launch_speed(start_pos: Vector3, end_pos: Vector3):
var horizontal_distance = Vector3(start_pos.x - end_pos.x, 0, start_pos.z - end_pos.z).length()

# Calculate time of flight using the vertical motion equation
var initial_vertical_velocity = arc_height  # Vertical velocity
time_of_flight = (initial_vertical_velocity + sqrt(initial_vertical_velocity**2 + 2 * gravity * (start_pos.y + projectile_y_offset - fixed_y))) / gravity

# Adjust launch_speed to ensure the projectile lands on the plane
launch_speed = horizontal_distance / time_of_flight

func launch_object(target: Vector3):
var modified_boat = boat.global_transform.origin
modified_boat.y += projectile_y_offset

var direction = (target - modified_boat).normalized()
var projectile = projectile_scene.instantiate() as RigidBody3D
projectile.global_transform.origin = modified_boat

add_child(projectile)
var velocity_xz = direction * launch_speed
var velocity_y = arc_height
var velocity = Vector3(velocity_xz.x, velocity_y, velocity_xz.z)
projectile.launch(velocity)

Input.start_joy_vibration(0, 0.15, 0, 0.25)

Function to draw the arc from the origin to the target

func draw_arc(origin: Vector3, target: Vector3):
immediate_mesh.clear_surfaces()
immediate_mesh.surface_begin(Mesh.PRIMITIVE_POINTS)

var boat_y = boat.global_transform.origin.y

var horizontal_distance = Vector3(origin.x - target.x, 0, origin.z - target.z).length()
var num_segments = horizontal_distance * 3

hit_ball.visible = false
for i in range(num_segments + 1):
	var t = float(i) / num_segments
	var arc_point = lerp(origin, target, t)
	
	var lt = lerp(0.0, time_of_flight, t)
	if t > 0.0 and t < 1.0:
		arc_point.y = boat_y + projectile_y_offset + arc_height*lt - (gravity*(lt**2))/2
	immediate_mesh.surface_add_vertex(arc_point)
	
	var collider = _check_arc_point_collision(arc_point)
	if collider != null:
		print(collider)
		hit_ball.global_transform.origin = arc_point
		hit_ball.visible = true
		break
	
immediate_mesh.surface_end()

func _check_arc_point_collision(point: Vector3):
var layer_mask = 1 << 0
var space_state = get_world_3d().direct_space_state
var params = PhysicsPointQueryParameters3D.new() # Create new parameters
params.position = point # Set the position to check for collisions
params.collision_mask = layer_mask # Set the layer mask
var results = space_state.intersect_point(params)

if results.size():
	for result in results:
		var collider = result["collider"]
		if collider.name == "NewBoat" || collider is Cannonball:
			continue
			
		return result["collider"]
return null```