Rotating Tail when moving in a Snake Game

Question

I’m trying to make a snake game, but with a bullet train. It’s mostly done, but I can’t figure how to make the body of the train face the direction it’s moving. The head is able to rotate to the proper direction when I push a button, but I can’t figure out how to do the same with the body/tail parts.

So how can I have my code check what direction the tail/body part is currently moving and then rotate the part to face horizontal or vertical based on the direction?

Determine Movement Direction:
You’ll need to determine the direction of movement for each body part. If your train moves in a grid-based manner (up, down, left, right), you can use a vector to represent the movement direction.

Rotate Each Body Part:
For each body part of the train, rotate it to face the direction of movement. You’ll need to calculate the angle based on the movement direction and apply the rotation.

Here’s a basic example of how you could implement this in Godot:

Script for the Train

extends Node2D

# Assuming you have an array to store all body parts
var body_parts = []

# Direction vectors
var direction = Vector2.RIGHT

func _ready():
    # Initialize the body_parts array with the train parts
    # For example, body_parts.append($BodyPart1)
    pass

func _process(delta):
    # Update the direction based on input or movement
    update_direction()

    # Rotate each body part to face the direction of movement
    for part in body_parts:
        rotate_body_part(part)

func update_direction():
    # Example to determine direction based on input or movement
    if Input.is_action_pressed("ui_right"):
        direction = Vector2.RIGHT
    elif Input.is_action_pressed("ui_left"):
        direction = Vector2.LEFT
    elif Input.is_action_pressed("ui_up"):
        direction = Vector2.UP
    elif Input.is_action_pressed("ui_down"):
        direction = Vector2.DOWN

func rotate_body_part(part):
    # Rotate the part to face the direction
    if direction == Vector2.RIGHT:
        part.rotation = 0
    elif direction == Vector2.LEFT:
        part.rotation = PI
    elif direction == Vector2.UP:
        part.rotation = -PI / 2
    elif direction == Vector2.DOWN:
        part.rotation = PI / 2

Ok so I tried to do what you said and all it did was stop my head from being able to rotate.

Here is how my code is looking so far, with some of the stuff you suggested added.

class_name Gameplay extends Node2D

const gameover_scene:PackedScene = preload("res://menus/game_over.tscn")
const pausemenu_scene:PackedScene = preload("res://menus/pause_menu.tscn")




@onready var head: Head = %Head as Head
@onready var bounds: Bounds = %Bounds as Bounds
@onready var spawner: Spawner = $Spawner as Spawner
@onready var HUD = %HUD as HUD
@onready var tail: Tail = %Tail as Tail

var direction = Vector2.RIGHT
var gameover_menu:GameOver
var pause_menu:Pause_Menu
var time_between_moves:float = 1000.0
var time_since_last_move:float = 0
var speed:float = 10000.0
var move_dir:Vector2 = Vector2.RIGHT
var snake_parts:Array[SnakePart] = []
var score:int:
	get:
		return score
	set(value):
		score = value
		HUD.update_score(value)



func _ready() -> void:
	head.food_eaten.connect(_on_food_eaten)
	head.collided_with_tail.connect(_on_tail_collided)
	spawner.tail_added.connect(_on_tail_added)
	spawner.spawn_food()
	
	time_since_last_move = time_between_moves
	snake_parts.push_back(head)


@warning_ignore("unused_parameter")
func _process(delta: float) -> void:
	@warning_ignore("unused_variable")
	var new_dir:Vector2 = Vector2.ZERO
	
	update_direction()
	
	
	if Input.is_action_just_pressed("ui_cancel"):
		pause_game()
	
	
	



func _physics_process(delta: float) -> void:
	time_since_last_move += delta * speed
	if time_since_last_move >= time_between_moves:
		update_snake()
		time_since_last_move = 0
	
	
	
	



func update_snake():
	#print("Move the Snake")
	var new_pos:Vector2 = head.position + move_dir * Global.GRID_SIZE
	new_pos = bounds.wrap_vector(new_pos)
	head.move_to(new_pos)
	
	for i in range(1,snake_parts.size(),1):
		snake_parts[i].move_to(snake_parts[i-1].last_position)
	
	
	
	
func _on_food_eaten():
	#print("food eaten")
	
	spawner.call_deferred("spawn_food")
	
	spawner.call_deferred("spawn_tail",snake_parts[snake_parts.size()-1].last_position)
	
	speed += 500.0
	
	
	score += 1
	#HUD.update_score(score)
	

func _on_tail_added(tail:Tail):
	snake_parts.push_back(tail)
	
	

func _on_tail_collided():
	#print("Game Over")
	if not gameover_menu:
		gameover_menu = gameover_scene.instantiate() as GameOver
		add_child(gameover_menu)
		gameover_menu.set_score(score)



func _notification(what):
	if what == NOTIFICATION_WM_WINDOW_FOCUS_OUT:
		pause_game()
	
	


func pause_game():
	if not pause_menu:
		pause_menu = pausemenu_scene.instantiate()
		add_child(pause_menu)


func update_direction():
	if Input.is_action_pressed("ui_up"):
		move_dir = Vector2.UP # (0,-1)
		head.rotation_degrees = 0
	if Input.is_action_pressed("ui_right"):
		move_dir = Vector2.RIGHT # (1, 0)
		head.rotation_degrees = 90
	if Input.is_action_pressed("ui_down"):
		move_dir = Vector2.DOWN # (0, 1)
		head.rotation_degrees = 180
	if Input.is_action_pressed("ui_left"):
		move_dir = Vector2.LEFT # (-1, 0)
		head.rotation_degrees = -90


func rotate_body_part(part):
	if direction == Vector2.RIGHT:
		part.rotation = 0
	elif direction == Vector2.LEFT:
		part.rotation = PI
	elif direction == Vector2.UP:
		part.rotation = -PI / 2
	elif direction == Vector2.DOWN:
		part.rotation = PI / 2

So how do I properly use the code example you have given me?