Godot Version
4.2
Question
I’m making a JRPG-style game where the party members follow the player’s exact route in an equally spaced manner(similar to Earthbound). I’ve tried different approaches like using AStar2D but ultimately settled on a “bread crumbs” solution, where every character (player and party members) pushes its current position to the front of an array (that keeps track of the past 32 positions). Each character also has a reference to the character ahead of it (the target character), looks at that character’s array of positions, and moves to the least recent point if the target character is a defined distance away from it.
Player Code:
extends CharacterBody2D
const speed: int = 100
@onready var _animated_sprite = $AnimatedSprite2D
var input_direction: Vector2
@export var facingState: int
@export var isMoving: bool
@export var pos_trail: Array
enum MovementState {IDLE, LEFT, RIGHT, UP, DOWN}
func get_input():
input_direction = Input.get_vector("left", "right", "up", "down")
velocity = input_direction * speed
func _ready():
#isMoving = false
pos_trail = [
position, position, position, position, position, position, position, position,
position, position, position, position, position, position, position, position
]
func _physics_process(delta):
get_input()
move_and_slide()
if(facingState != MovementState.IDLE):
pos_trail.push_front(position)
if(pos_trail.size() > 32):
pos_trail.pop_back()
#Animation handling
if input_direction.x < 0:
#Going left
_animated_sprite.play("walk_left")
facingState = MovementState.LEFT
#isMoving = true
elif input_direction.x > 0:
#Going right
_animated_sprite.play("walk_right")
facingState = MovementState.RIGHT
#isMoving = true
elif input_direction.y < 0:
#Going up
_animated_sprite.play("walk_up")
facingState = MovementState.UP
#isMoving = true
elif input_direction.y > 0:
#Going down
_animated_sprite.play("walk_down")
facingState = MovementState.DOWN
#isMoving = true
elif input_direction.x == 0 and input_direction.y == 0:
#Idle
match(facingState):
MovementState.LEFT:
_animated_sprite.play("idle_left")
MovementState.RIGHT:
_animated_sprite.play("idle_right")
MovementState.UP:
_animated_sprite.play("idle_up")
MovementState.DOWN:
_animated_sprite.play("idle_down")
facingState = MovementState.IDLE
Party NPC Code:
extends CharacterBody2D
@onready var _animated_sprite = $AnimatedSprite2D
@onready var nav_agent: NavigationAgent2D = get_node("NavigationAgent2D")
@export var target_char: CharacterBody2D
@export var facingState: int
@export var followDist: int
@export var pos_trail: Array
const movement_speed: int = 100
enum MovementState {IDLE, LEFT, RIGHT, UP, DOWN}
func round_to_dec(num, digit):
return round(num * pow(10.0, digit)) / pow(10.0, digit)
func _ready():
#pos_trail = [position, position, position, position, position, position, position, position, position, position, position, position, position, position, position, position]
pass
func _physics_process(delta):
if(facingState != MovementState.IDLE):
pos_trail.push_front(position)
if(pos_trail.size() > 32):
pos_trail.pop_back()
var direction: Vector2
if(target_char.pos_trail.size() > 0 and target_char.position.distance_to(target_char.pos_trail.back()) > followDist):
#print(target_char.pos_trail.back())
while(round_to_dec(target_char.position.distance_to(position), 0) <= round_to_dec(target_char.position.distance_to(target_char.pos_trail.back()), 0) and facingState != MovementState.IDLE):
#print("Moving away from player")
target_char.pos_trail.pop_back()
direction = target_char.pos_trail.back() - position
#print(direction)
position = position.move_toward(target_char.pos_trail.pop_back(), delta * movement_speed)
else:
#print("IDLE")
direction = Vector2(0, 0)
#Animation handling
#var hor_anim_snap = 25
#var vert_anim_snap = 1
if round_to_dec(direction.x, 1) < 0:
#Going left
_animated_sprite.play("walk_left")
facingState = MovementState.LEFT
elif round_to_dec(direction.x, 1) > 0:
#Going right
_animated_sprite.play("walk_right")
facingState = MovementState.RIGHT
elif direction.y < 0:
#Going up
_animated_sprite.play("walk_up")
facingState = MovementState.UP
elif direction.y > 0:
#Going down
_animated_sprite.play("walk_down")
facingState = MovementState.DOWN
elif direction.x == 0 and direction.y == 0:
#Idle
match(facingState):
MovementState.LEFT:
_animated_sprite.play("idle_left")
MovementState.RIGHT:
_animated_sprite.play("idle_right")
MovementState.UP:
_animated_sprite.play("idle_up")
MovementState.DOWN:
_animated_sprite.play("idle_down")
facingState = MovementState.IDLE
With this code, the party NPCs do in fact follow the player’s exact path but spacing between the characters is not constant. What are ways that I could fix this?
Any feedback is welcome and appreciated. Thanks!