A charecter stands still after detecting player

Godot Version

4.6

Question

I was trying to implement a charecter in my game that would move around a seted path and when an area named detector will overlap a player, he’ll approache him, but there’s a pronlem. He doesn’t start approaching player when he enters the area. I tried to fix it by reparenting him to the scene’s nody, but he’s just starting to stand stil after he detects player.

Here is the code:

extends CharacterBody2D

var speed:=200.0
@onready var detector: Area2D = $Detector
@onready var new_parent = get_node(“/root/Prison”)

func _physics_process(delta: float) → void:
     detector.body_entered.connect(func(body:Node2D):
     if body is Player:
        print(“a”)
        call_deferred(“reparent”, new_parent)
        speed=350.0
        var direction:=global_position.direction_to(body.global_position)
        velocity=velocity.move_toward(direction,speed*delta))
      move_and_slide()

Here is the code of the path:

extends PathFollow2D

var policeman:CharacterBody2D

func _ready() → void:
     policeman=get_child(0)

func _process(delta: float) → void:
     progress+=policeman.speed * delta

Indent your code properly.

Are there any errors reported in the debugger?

1 Like

is there a reason why you are trying to connect the body_entered signal of the detector inside _physics_process()? this only needs to be done once, not every physics frame, so you maybe want to move this to ready. As @normalized mentioned, without the indents, it’s harder to read the code.
I’m trying to rewrite this so we maybe understand what should happen here:


func _ready() -> void:
	detector.body_entered.connect(_on_detector_body_entered)
	
func _on_detector_body_entered(body: Node2D) -> void:
	if body is Player:
		print("a")
		call_deferred("reparent", new_parent)
		speed=350.0
		var direction: Vector2 = global_position.direction_to(body.global_position)
		velocity = velocity.move_toward(direction, speed*delta)

func _physics_process(delta: float) -> void:
	move_and_slide()

I’ve tried to extract the function in detector.body_entered.connect(…) according to the parentheses, but this does not really make sense (there is also no delta available in the new function).

My guess is that you want to create a flag (= a boolean variable) to check if the player was noticed, and if this flag is set, move in _physics_process()`.

E.g. like this:

var has_detected_player: bool = false
#...

func _on_detector_body_entered(body: Node2D) -> void:
	if body is Player:
		has_detected_player = true
		#...

func _physics_process(delta: float) -> void:
	if has_detected_player:
		speed=350.0
		var direction: Vector2 = global_position.direction_to(body.global_position)
		velocity = velocity.move_toward(direction, speed*delta)
		move_and_slide()

I hope this makes sense.

Also, did you check that the signal is triggered? i.e. was “a” printed? If not, check that the area is monitoring, and compare its collision mask to the player’s collision layer

No, debugger hasn’t show any problems

“a” is printed every time as intended. The thing I noticed is that without reparenting, a charecter simply continues to move around the path, and without reparenting he stands still as mentioned before. “a“ is printed in both situations btw.

move_and_slide() needs to be called every frame.

It does changes every frame. The problem needs to be somewhere else.

How do we know that. It’s impossible to tell from improperly formatted code you posted. Have you tried doing what @substain suggested?

Yeah, that didn’t work either.

Here is properly formated code:

extends CharacterBody2D

var speed:=300.0
@onready var detector: Area2D = $Detector
@onready var new_parent = get_node(“/root/Prison”)

func _ready() → void:
     detector.body_entered.connect(\_on_detector_body_entered)

func _on_detector_body_entered(body: Node2D) → void:
     if body is Player:
        print(“a”)
        call_deferred(“reparent”, new_parent)
        speed=350.0
        var direction: Vector2 = global_position.direction_to(body.global_position)
        velocity = velocity.move_toward(direction, speed)

func _physics_process(delta: float) → void:
     move_and_slide()

> Blockquote

not sure what the \is doing there in the connect function.

Okay, this is only the first half of the comment. Did you try introducing a boolean variable that you can use to check if you should move in move_and_slide (and also do the direction + velocity calculation there)?

Try assigning velocity directly instead of that misused move_toward.

Yes, it doesn’t change anything.
Like I said previously, the only thing I noticed is that with reparanting he stands still and without it he continues to move around the path. Any other change I tried to implement didn’t show any result.

Print the velocity after you do this:

velocity = velocity.move_toward(direction, speed)

Since direction gotten from direction_to() is a normalized unit vector and speed value is 350, this will likely be equivalent to writing velocity = direction, so your velocity magnitude will end up being 1 pixel per second. This might be too small to perceive.

As I said, print the velocity there to confirm this.

So i experimented with velocity and discovered that by using velocity = direction*speed a charecter reacts differently when detecting player. He starts moving into the direction where player was heading when he enterd charecter’s detector area.
Ps. velocity = velocity.move_toward(direction, speed) leads to the charecter’s velocity being less than 1. You were right.

Then you need to re-calculate the direction and velocity every frame. Currently you’re just calculating it once, at the moment the area was entered.