Following Character AI

Godot Version

4.3

Question

I am trying to create a two-player system where you switch between characters by pressing a button, but I am having trouble figuring out an AI to make the non-active player follow the active player. I have tried using limboAI but failed. I have been looking for solutions for a while now and was wondering if any of you professionals could help.

1 Like

Try to create your own AI, it’s easy than using Limbo AI.

But I need more information about this so I could help you.

Ok, so I have a system where multiple playable and controllable characters can be turned on and off with variables, and I am trying to figure out how to code an AI that turns on when the other player is being controlled. Hence, the one that is not being controlled follows the one that is being controlled. It’s kind of like the Lego Star Wars game. if that makes sense. The code I have right now looks something like this:

if IsPlayer.is_player == 1:
var speed: float = 0.01
look_at(player.global_position)
self.position = lerp(self.position,player.global_position,speed)

the variable is_player is how I control which player is being controlled at that time. I also need the player that is following to still be controllable when it is not following.

hello?

1 Like

You might want to look into navigation: Navigation — Godot Engine (stable) documentation in English, to use this to give the AI character a path to the controlled character.

1 Like

I have no idea were to start on the docs. I am very new and it confuses me very much.

Please can you tell me the problem with your codes?

I dont really know which is why im reaching out. I just cant figure it out.

You have given very little information. I soooort of have some idea of what you want the code to do, but no idea how far you are with achieving it. What happens when you run your game? Can you show a little more of the relevant code? Is the problem mainly with the logic behind switching “modes”, or is it with the code that makes one character follow the other?

2 Likes

its the code that makes one character follow the other. I’ve not worked on it for a while so I don’t have any more code to show but there is a movement system above that in the physics process. When I launch the game I can switch between characters and move around as all of them but the ai just doesn’t happen ig.

Surely, there must be more code than those four lines? I would very much like to help, but with the information you’ve provided, I would basically have to reinvent your game from scratch, which is a bit much when you say you already have code that lets you switch between which character you’re controlling, for example.

1 Like

hey, sorry it took me so long to reply. I have tried a couple more AIs that I found in tutorials, but they all seem to either break or just not work. I would love to know why you think you would need to reinvent my game so I can see if I can fix it to make it easier for you to help!

Why don’t you use two cameras on each player, and when a specific hotkey is pressed, make the non-active player’s camera enabled and the active player’s camera disabled? I believe this would work and the viewport of the game will follow the active player.

I believe the answer isn’t AI, as it is very unstable.

The only reason I would need to reinvent your game is that you have only shown me 4 lines of code. If I want to show you an example of how I would do it, I would need to decide on my own, for example, how to switch between the different players, how the currently selected player would move, and so on. I would much rather build on the work you have already done, but for some reason refuse to show us. That is, I would rather make suggestions for how to fix up your attempt at an solution, instead of showing you an entirely new solution that I came up with.

I don’t need the entire project, but the code related to moving the player characters and switching between them would certainly be helpful.

1 Like

okay, sorry I should have just shown you.
This is my script for one of the players (its basically the same with a few differences for the other players):

extends CharacterBody2D

class_name Player

@onready var animated_sprite_2d: AnimationController = $AnimatedSprite2D

const  SPEED = 5000.0

func _physics_process(delta: float) -> void:
	var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
	
	if IsPlayer.is_player == 1:
		if direction:
			velocity = direction * SPEED * delta
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED * delta)
			velocity.y = move_toward(velocity.y, 0, SPEED * delta)

		if velocity != Vector2.ZERO:
			animated_sprite_2d.play_movement_animation(velocity)
		else:
			animated_sprite_2d.play_idle_animation()
	if IsPlayer.is_player != 1:
		velocity.x = 0
		velocity.y = 0
	move_and_slide()

This is the code for the switching system:

extends Node

@export var is_player = 1

func _input(ev):
	if Input.is_action_just_pressed("swap"):
		if is_player == 1:
			is_player = 2
		elif is_player == 2:
			is_player = 3
		elif is_player == 3:
			is_player = 4
		elif is_player == 4:
			is_player = 1
		print(is_player)
	elif Input.is_action_just_pressed("1"):
		is_player = 1
	elif Input.is_action_just_pressed("2"):
		is_player = 2
	elif Input.is_action_just_pressed("3"):
		is_player = 3
	elif Input.is_action_just_pressed("4"):
		is_player = 4

Hope this helps.

Hi, yes, that helps a lot! So, what I’m noticing here is that your player script does not have a way to see where the other players are, or to know which node belongs to the currently active player. Since you’ve already assigned a number to each player, a simple way to do this would be by using a dictionary. So, in your switching system, I would add a dictionary + a way to function the current player node:

extends Node

@export var is_player = 1
# I suggest a dictionary, where you use
# numbers as the keys, and nodes as the values
var player_nodes = {}

# Look up the current player's node in the dictionary
# and return it. If not found, print an error message
func get_current_player():
	var player = player_nodes[is_player]
	if !player:
		push_error("Player " + str(is_player) + " not found.")
	return player

# The _input function you can leave unchanged, it's perfectly fine

Now, we need some way to actually fill that dictionary. I suggest doing it from the player scripts:

extends CharacterBody2D
class_name Player

# It's nice to have this in a variable, since we'll now be using
# it a couple of times through the code
var player_id = 1

func _ready():
	# Tell the switching system that player 1 means
	# this node
	IsPlayer.player_nodes[player_id] = self

The first half of the _physics_process function is more or less unchanged (I’ve changed some 1’s to use the player_id variable instead). In the second half, I’ve added code that finds the direction towards whatever player is currently active, and moves in that direction. This is of course very simple movement, and you might need something more sophisticated, but I hope it’s a starting point you can work with.

func _physics_process(delta: float) -> void:
	var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
	
	# I changed this line to use player_id
	if IsPlayer.is_player == player_id:
		if direction:
			velocity = direction * SPEED * delta
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED * delta)
			velocity.y = move_toward(velocity.y, 0, SPEED * delta)

		if velocity != Vector2.ZERO:
			animated_sprite_2d.play_movement_animation(velocity)
		else:
			animated_sprite_2d.play_idle_animation()
	# I changed this line to use player_id
	if IsPlayer.is_player != player_id:
		# Now, let's get the direction to the current player,
		# and use that for our movement
		var current_player = IsPlayer.get_current_player()
		if !current_player: # If the player could not be found
			velocity.x = 0
			velocity.y = 0
		else: # Yay, we found the current player! Let's move towards it
			var direction_to_player = current_player.global_position - global_position
			direction_to_player = direction_to_player.normalized()
			velocity = direction_to_player * SPEED * delta
	move_and_slide()

The code above is untested, and may have some errors, but I hope the idea is clear.

Thanks this is going to help a lot! I’m sorry if this is a dumb question. I haven’t used a dictionary and was wondering if you could explain further what goes into it.

Sure! (Also definitely click on the documentation link I put in the post above).

A dictionary is a key-value store (a common term in programming). The idea is that you have keys, each of which has an associated value. For example, here’s a dictionary showing what pets I have:

my_pets = {
    "cats" : 1,
    "dogs" : 0,
    "parrots" : 0
}

If I were to suddenly acquire an iguana, I could do add it to the dictionary like this:

my_pets["iguanas"] = 1

Or, if I get another cat, I can do this to change a value that’s already in there:

my_pets["cats"] += 1

I can use similar syntax to look up how many I have of a certain kind of pet:

print("I have " + str(my_pets["dogs"]) + " dogs")

In this case, the keys of the dictionary are strings - specifically, animal names like "cats", "dogs", and so on. The associated values are numbers. Each key has exactly one associated value (we cannot have 2 cats and at the same time have 3 cats).

In your case, we want the keys to be numbers - specifically, the numbers 1, 2, 3, and 4, for your four characters. The associated values are references to nodes in the scene. We’re being sneaky and making those nodes add themselves to the dictionary as soon as they get created, which is why the dictionary initially gets created as the empty dictionary {}.

Thank you so much I’m going to do some testing and get back to you!

1 Like

do you know how to make animations work with it? because everything else works. Also how could i make them stop pushing the player and getting them stuck in corners?