How to make an enemy script that copies the player's input by a slight delay?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By SpillProff

I’m attempting to make an enemy entity that will copy the player’s input and movement by a slight delay. I can’t really describe it better than that.
A good example if you want to know what i want is a section in the game Celeste where you are chased by doppelgangers that can copy your movements but follow you behind by a second.

The video for those who never played the game: https://youtu.be/eFuUojykT9w

Below is some basic platformer code which I’m using as a basis. I want to know what I need to make the code function as indented.

EDIT: I’ve tried using yield(get_tree().create_timer(2), "timeout") however, the enemy sometimes lags behind for a tiny bit causing it to separate from the player’s position.

:bust_in_silhouette: Reply From: Wakatta

One way would be to use timed Signals

Basically you add the time of the input + the delay to an Array then consume those stored events in the process function

CopycatEnemy.gd

var movement = Array()
var delta_time = 0

func _ready():
    $Player_node.connect("follow", self, "copy_input")

func copy_input (direction, time, speed)
    var move = Dictionary()
    move.direction = direction
    move.time = time
    move.speed = speed
    movement.append(move)

func _physics_process(delta):
    motion.y += 10

    if not movement.empty():
        delta_time += delta
        var move = movement.front()
        if delta_time >= move.time:
            if move.direction in ["left", "right"]:
                motion.x = move.speed
            elif move.direction in ["up", "down"]:
                motion.y = move.speed
            movement.pop_front()
            delta_time = 0

    motion = move_and_slide(motion, UP)

Player.gd

Signal follow(direction, time, speed)

func _physics_process(delta):
    if Input.is_action_pressed("ui_right"):
        motion.x = 150
        emit_signal("follow", "right", 2, 150)
   Etc. . . . . . .

Alright so I’ve tinkered around with the code for a bit. I’ve tried making the signals seperate and then put them back as you had stated, however, the copycat decides that it’ll take the signal and constantly do it.

Basically, if I tap left and don’t do anything, it’ll keep going left until it hits a wall.
However, if I tap right after it hits a wall, it won’t move.

When I signal it to jump, it jumps normally.
If I signal it to jump up and move left in the air, the copycat will jump and wait for itself to land, then it’ll start moving left.

I’ve tried making an idle signal but when I do that, it won’t move at all and doesn’t take any inputs. I don’t know, maybe I didn’t put the code in right or something.

SpillProff | 2021-08-08 18:34

This part of your code

motion.y += 10

Is kind of odd as it always ensures that there is motion regardless of input also make sure the move direction and speed are correct

func copy_input (direction, time, speed):
    #if direction is "ui_right"
        #speed must be a positive number for motion.x
    #if direction is "ui_left"
        #speed must be a negative number for motion.x
    #if direction is "ui_down"
        #speed must be a positive number for motion.y
    #if direction is "ui_up"
        #speed must be a negative number for motion.y
    pass

Wakatta | 2021-08-08 18:56

I forgot to mention, the reason why I have motion.y += 10 is because it’s a platformer; its included for gravity purposes.

SpillProff | 2021-08-08 20:43

Understood.

The only other thing that comes to mind is to completely copy the players movement script and lerp the motion ‍♂️

Wakatta | 2021-08-09 13:17