Lock on feature not working

Godot Version

Godot 4.6

Question

I’m trying to make a lock on feature for my game project however it won’t lock on even though I followed the tutorial step by step

The tutorial I used was https://www.youtube.com/watch?v=pq3q3EZZ-PU By DevDrache

#For the Player
extends CharacterBody3D

@export var enemy_select_range := 100.0

@onready var ksd_rotat: Node3D = $KSD_rotation
@onready var ksd_skele: Skeleton3D = $"KSD_rotation/KSD Skeleton"
@onready var ksd_model: Node3D = $"KSD_rotation/KSD Skeleton/KSD Mesh"
@onready var Overall: CollisionShape3D = $Overall
@onready var camera_pivot: Node3D = $"Camera Pivot"
@onready var camera_controller: SpringArm3D = $"Camera Pivot/Camera Controller"
@onready var camera : Camera3D = $"Camera Pivot/Camera Controller/Camera"
@onready var enemy_ray_cast: RayCast3D = $RayCast3D
@onready var health = $HUD/Health
@onready var psi = $HUD/Psi
@onready var stamina = $HUD/Stamina
@onready var gauge_image: TextureRect = $"HUD/HUD control/Gauge Image"
@onready var gauge_timer: Timer = $"Gauge Timer"

#Stats and Moveset
const HEALTH = 1000.0
const ATTACK = 145.0
const DEFENSE = 80.0
const WALK_SPEED = 10.0
const AGILE_SPEED = 30.0
const FIGHT_SPEED = 80.0
const MACH_SPEED = 150.0
const ACCEL = 40.0
const TURN_SPEED = 12.0
const LOOK_SPEED = 8.0
const SHORT_JUMP = 30.0
const HIGH_JUMP = 80.0
const GRAVITY = 10.0
const GAUGE = 1000.0
const STAMINA = 1000.0
const WEAKNESS = ["Curse", "PSI"]
const RESISTANCE = ["Slash", "Fire", "Electric", "Bless"]
const NEW_MOVESET = ["Blast Reign", "Flame Core", "Furalumin", "Armour Burst"]
const ADV_MOVESET = ["God Swipe", "Altar Flare", "Mega E-Go", "Palladia", "Magnus Burst", "Aymrian"]
const PRO_MOVESET = ["Dragon Reign", "Light Monarch", "Platinum Flare", "Virtuous E-Go"]
var camera_input_dir := Vector2.ZERO
var last_dir = Vector3.BACK
var double_jump = false
var gauge = 1
var gauge_ready = true
var gauge_lock = false
var input_buffer_list = []
var selected_enemy: Node3D
var locked_on_enemy: Node3D

func _ready() -> void:
	health.init_health(HEALTH)
	psi.init_health(GAUGE)
	stamina.init_health(STAMINA)
	
	_set_ray_cast_selection()

func _set_ray_cast_selection():
	enemy_ray_cast.add_exception(self)
	enemy_ray_cast.target_position = Vector3(0, 0, enemy_select_range)

func _input(_event: InputEvent) -> void:
	enemy_ray_cast.rotation.x = camera_pivot.rotation.x
	enemy_ray_cast.rotation.y = camera_pivot.rotation.y
	
	_check_current_enemy()
	
	if Input.is_action_just_pressed("Lock On Enemy") and not locked_on_enemy and selected_enemy:
		locked_on_enemy = selected_enemy
		locked_on_enemy.locked_on(true)
	elif Input.is_action_just_pressed("Lock On Enemy") and locked_on_enemy:
		locked_on_enemy.locked_on(false)
		locked_on_enemy = null
	
func _physics_process(delta: float) -> void:
	#Lock on enemy
	if locked_on_enemy: _set_lock_on_camera()
	else: _camera_control()
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta * GRAVITY
	if is_on_floor():
		double_jump = false
	# Handle jump.
	if Input.is_action_just_pressed("Jump-Accept") and is_on_floor():
		if gauge == 1:
			velocity.y = SHORT_JUMP
		else:
			velocity.y = HIGH_JUMP
		if Input.is_action_just_pressed("Jump-Accept") and not is_on_floor() and double_jump == false:
			if gauge == 1:
				velocity.y = SHORT_JUMP
			else:
				velocity.y = HIGH_JUMP
			double_jump = true

	#Movement including Gauge controller and Camera controller
	#Movement inputs
	var input_dir := Input.get_vector("Move Left", "Move Right", "Move Forward", "Move Back")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	var forward := camera.global_basis.z
	var right := camera.global_basis.x
	var move_dir := forward * input_dir.y + right * input_dir.x
	move_dir.y = 0.0
	move_dir = move_dir.normalized()
	direction = direction.rotated(Vector3.UP, camera.global_rotation.y).normalized()
	
	#Gauge Controller
	if Input.is_action_just_pressed("Change Gear") and gauge < 4 and gauge_ready == true and gauge_lock == false:
		gauge_ready = false
		gauge_timer.start()
		gauge += 1
		gauge_image.rotation_degrees -= 90
	elif Input.is_action_just_pressed("Change Gear") and gauge == 4 and gauge_ready == true and gauge_lock == false:
		gauge_ready = false
		gauge_timer.start()
		gauge = 1
		gauge_image.rotation_degrees -= 90
	elif Input.is_action_just_pressed("Change Gear") and gauge == 1 and gauge_ready == true and gauge_lock == true:
		gauge_ready = false
		gauge_timer.start()
		gauge += 1
		gauge_image.rotation_degrees -= 90
	elif Input.is_action_just_pressed("Change Gear") and gauge <= 2 and gauge_ready == true and gauge_lock == true:
		gauge_ready = false
		gauge_timer.start()
		gauge = 1
		gauge_image.rotation_degrees = 0
	if Input.is_action_just_pressed("Change Gear") and gauge < 4 and gauge_ready == true and gauge_lock == false:
		gauge_ready = false
		gauge_timer.start()
		gauge += 1
		gauge_image.rotation_degrees -= 90
	if gauge == 1:
		#Neutral Gear
		camera_controller.position.y = lerp(camera_controller.position.y, 1.5, delta)
		camera_controller.position.z = lerp(camera_controller.position.z, 0.0, delta)
		velocity = velocity.move_toward(move_dir * WALK_SPEED, ACCEL * delta)
	elif gauge == 2:
		#Agile Gear
		velocity = velocity.move_toward(move_dir * AGILE_SPEED, ACCEL * delta)
	elif gauge == 3:
		#Fight Gear
		camera_controller.position.x = lerp(camera_controller.position.x, 0.5, delta * 2)
		camera_controller.position.y = lerp(camera_controller.position.y, 1.5, delta)
		velocity = velocity.move_toward(move_dir * FIGHT_SPEED, ACCEL * delta)
	elif gauge == 4:
		#Speed Gear
		camera_controller.position.x = lerp(camera_controller.position.x, 0.0, delta)
		camera_controller.position.y = lerp(camera_controller.position.y, 3.0, delta * 2)
		camera_controller.position.z = lerp(camera_controller.position.z, 5.0, delta)
		velocity = velocity.move_toward(move_dir * MACH_SPEED, ACCEL * delta)
	
	move_and_slide()
	
	if move_dir.length() > 0.2:
		last_dir = move_dir
	var target_dir = Vector3.BACK.signed_angle_to(last_dir, Vector3.UP)
	ksd_rotat.rotation.y = lerp_angle(ksd_rotat.rotation.y, target_dir, TURN_SPEED * delta)
	
	
func _camera_control():
	#Camera Rotation for controller Inputs
	var axis_dir := Input.get_vector("Look Left", "Look Right", "Look Up", "Look Down")
	if axis_dir.length() >= 0.2:
		camera_pivot.rotate_y(deg_to_rad(-axis_dir.x * LOOK_SPEED))
		
		camera_controller.rotate_x(deg_to_rad(-axis_dir.y * LOOK_SPEED))
		camera_controller.rotation.x = clamp(camera_controller.rotation.x, deg_to_rad(-80.0), deg_to_rad(30.0))
		
		#Stop Z rotation of camera
		camera.rotation.z = clamp(camera.rotation.z, 0.0, 0.0)
		camera_input_dir = Vector2.ZERO
	
func _check_current_enemy():
	if locked_on_enemy: return
	
	if enemy_ray_cast.is_colliding() and "Enemy" in enemy_ray_cast.get_collider().name:
		selected_enemy = enemy_ray_cast.get_collider()
		selected_enemy.selected(true)
	else:
		if selected_enemy:
			selected_enemy.selected(false)
		selected_enemy = null

func _set_lock_on_camera():
	camera_pivot.look_at(locked_on_enemy.global_position)

func _on_gauge_timer_timeout() -> void:
	gauge_ready = true

#For the Enemy
extends CharacterBody3D

@onready var label_3d: Label3D = $Label3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5

func _init() -> void:
	add_to_group("enemy")

func _ready() -> void:
	selected(false)

func _physics_process(delta: float) -> void:
	if not is_on_floor():
		velocity += get_gravity() * delta

	move_and_slide()

func selected(boolean):
	label_3d.modulate = Color.WHITE
	label_3d.visible = boolean

func lock_on(boolean):
	label_3d.modulate = Color.RED
	label_3d.visible = boolean

That’s a lot of code to sift through, can you explain your problem better so we’d have an idea where to look, or reduce the code pasted to just what you believe is relevant/error prone?

sure

#For the Player
extends CharacterBody3D

@onready var enemy_ray_cast: RayCast3D = $RayCast3D

func _ready() -> void:
    _set_ray_cast_selection()

func _set_ray_cast_selection():
	enemy_ray_cast.add_exception(self)
	enemy_ray_cast.target_position = Vector3(0, 0, enemy_select_range)

func _input(_event: InputEvent) -> void:
	enemy_ray_cast.rotation.x = camera_pivot.rotation.x
	enemy_ray_cast.rotation.y = camera_pivot.rotation.y
	
	_check_current_enemy()
	
	if Input.is_action_just_pressed("Lock On Enemy") and not locked_on_enemy and selected_enemy:
		locked_on_enemy = selected_enemy
		locked_on_enemy.locked_on(true)
	elif Input.is_action_just_pressed("Lock On Enemy") and locked_on_enemy:
		locked_on_enemy.locked_on(false)
		locked_on_enemy = null

func _physics_process(delta: float) -> void:
	#Lock on enemy
	if locked_on_enemy: _set_lock_on_camera()
	else: _camera_control()

func _camera_control():
	#Camera Rotation for controller Inputs
	var axis_dir := Input.get_vector("Look Left", "Look Right", "Look Up", "Look Down")
	if axis_dir.length() >= 0.2:
		camera_pivot.rotate_y(deg_to_rad(-axis_dir.x * LOOK_SPEED))
		
		camera_controller.rotate_x(deg_to_rad(-axis_dir.y * LOOK_SPEED))
		camera_controller.rotation.x = clamp(camera_controller.rotation.x, deg_to_rad(-80.0), deg_to_rad(30.0))
		
		#Stop Z rotation of camera
		camera.rotation.z = clamp(camera.rotation.z, 0.0, 0.0)
		camera_input_dir = Vector2.ZERO
	
func _check_current_enemy():
	if locked_on_enemy: return
	
	if enemy_ray_cast.is_colliding() and "Enemy" in enemy_ray_cast.get_collider().name:
		selected_enemy = enemy_ray_cast.get_collider()
		selected_enemy.selected(true)
	else:
		if selected_enemy:
			selected_enemy.selected(false)
		selected_enemy = null

func _set_lock_on_camera():
	camera_pivot.look_at(locked_on_enemy.global_position)

The enemy code is short enough

This is still pretty long, spanning multiple functions, can you explain your problem better? To help we have to know what you expected to happen and what really happens. Maybe you could add some print statements to help follow code flow, find out where it breaks specifically.

Like printing before checking the enemy name

func _check_current_enemy():
	if locked_on_enemy: return
	
	print(enemy_ray_cast.get_collider()) # prints any collision object or <null>
	if enemy_ray_cast.is_colliding() and "Enemy" in enemy_ray_cast.get_collider().name:
		selected_enemy = enemy_ray_cast.get_collider()
		selected_enemy.selected(true)
	else:
		if selected_enemy:
			selected_enemy.selected(false)
		selected_enemy = null