Getting the ai to move toward the player

Godot Version

v4.2.2.stable.official [15073afe3]

Question

I want to have an ai that gets the players position, Pauses, Then charges at the AI, and stops if it hits a wall.
I have tried to code it but i have no absolute idea what I’m doing as it keeps charging straight to the bottom right corner of the screen

MY CODE:
func _physics_process(delta):
if D_end == 0 and killed == 0:
print(delta)
print(‘begin!’)
await lock_on()
if ahead == 1:
charged = 1
D_end = 0
print(“charging!”)
while collision == 0:
charged = 1
ahead = 0
await CHARGE()
D_end = 1
charged = 0
else:
holdVect = Vector2(0,0)
Demo.velocity = Vector2(0,0)
func lock_on():
if charged == 0:
print(‘targeting’)
target = $“…/mc”
D_end = 1
await get_tree().create_timer(3.0).timeout
D_end = 0
ahead = 1
Demo.velocity = (target.global_position)
func CHARGE():
holdVect = Demo.velocity
Demo.move_and_slide()
await get_tree().create_timer(0.01).timeout

Please help

try this

extends Node

@onready var attacker1:CharacterBody2D = $attacker1;
@onready var attacker2:CharacterBody2D = $attacker2;
@onready var attacker3:CharacterBody2D = $attacker3;
@onready var target1:Node2D = $"../target1"
@onready var target2:Node2D = $"../target2"

const IMPACT_DISTANCE = 40.
const TOTAL_LOCKON_TIME = 6.
const TOTAL_CHARGE_FORWARD_TIME = 0.5
const TOTAL_COOLDOWN_TIME = 2.
const TURN_SPEED = 5.
const CHARGE_FORWARD_SPEED = 500.
const BOUNCE_SPEED = 10.
const STRAFE_SPEED = 30.
const CHARGE_DECEL_RATE = 30.
const KNOCKBACK_RECOVER_RATE = 45.

static func reflect_bounce(_node:Node2D, _surfVec:Vector2, _knockback:Vector2):
	_knockback = (_knockback * 0.6).reflect(_surfVec)
	_node.transform.x = _node.transform.x.reflect(_surfVec) 
	_node.transform.y = _node.transform.y.reflect(_surfVec)
	return _knockback

static func move_irregular(delta:float, _node:Node2D):
	var _sintime = sin(Time.get_unix_time_from_system());
	var _forward_move = _node.transform.x * BOUNCE_SPEED;
	var _strafe_move = _node.transform.y * _sintime * STRAFE_SPEED;
	_node.global_position += (_forward_move + _strafe_move) * delta;
	_node.rotate(delta);
		
static func bounce_irregular(delta, _node:Node2D, _knockback:Vector2) -> Vector2:
	move_irregular(delta, _node);
	var _origpos = _node.global_position;
	var _newpos = _origpos.clamp( Vector2.ZERO, Vector2(Engine.get_main_loop().root.size));
	_node.global_position = _newpos;
	if ( !is_equal_approx(_newpos.x, _origpos.x) ):
		_knockback = reflect_bounce(_node, Vector2(0., 1.), _knockback)
	if ( !is_equal_approx(_newpos.y, _origpos.y) ):
		_knockback = reflect_bounce(_node, Vector2(1., 0.), _knockback)
	return _knockback;

static func apply_knockback(_delta:float, _node:Node2D, _knockback:Vector2)-> Vector2: 
	if (!_knockback.is_zero_approx()):
		_node.global_position += (_knockback * _delta)
		_knockback = _knockback.move_toward(Vector2.ZERO, _delta * KNOCKBACK_RECOVER_RATE);
		return _knockback;
	return Vector2.ZERO;

var collision:int = 0;

class demo_attacker:
	var _locking_on_time_left:float
	var _charging_forward_time_left:float
	var _cooling_down_time_left:float
	var killed:bool
	var lockon_while_recovering:bool
	
	func _init():
		_locking_on_time_left = 1.
		_charging_forward_time_left = 0.
		_cooling_down_time_left = 0.
		killed = false
		lockon_while_recovering = false

	func _dec(_value, delta) -> float:
		return max(0., _value - delta);

	var in_cooldown:bool: 
		get:
			return _cooling_down_time_left > 0.

	var is_charging_forward:bool:
		get:
			return _charging_forward_time_left > 0.

	var is_locking_on:bool:
		get:
			return _locking_on_time_left > 0.

	var cooldown_norm_progress:float:
		get:
			return _cooling_down_time_left / TOTAL_COOLDOWN_TIME;

	var charge_forward_norm_progress: float:
		get:
			return _charging_forward_time_left / TOTAL_COOLDOWN_TIME;

	func cooldown( delta) -> bool:
		_cooling_down_time_left = _dec( _cooling_down_time_left, delta);
		return in_cooldown

	func charge_forward( delta):
		_charging_forward_time_left	= _dec( _charging_forward_time_left, delta);

	func lock_on( delta):
		_locking_on_time_left = _dec( _locking_on_time_left, delta);
		
	func reset_lockon():
		_locking_on_time_left = TOTAL_LOCKON_TIME
		_charging_forward_time_left = 0.; _cooling_down_time_left = 0.

	func reset_charging_forward():
		_charging_forward_time_left = TOTAL_CHARGE_FORWARD_TIME
		_locking_on_time_left = 0.; _cooling_down_time_left = 0.

	func reset_cooldown():
		_cooling_down_time_left = TOTAL_COOLDOWN_TIME
		_locking_on_time_left = 0.; _charging_forward_time_left = 0.

@onready var all_attackers:Array[CharacterBody2D] = [ attacker1, attacker2, attacker3 ]
@onready var all_targets:Array[Node2D] = [ target1, target2 ]

var knockbacks:Dictionary
var info:Dictionary

func _ready():
	knockbacks = {};
	info = {}
	for t in all_targets:
		knockbacks[t] = Vector2.ZERO;
	for t in all_attackers:
		knockbacks[t] = Vector2.ZERO;
		info[t] = demo_attacker.new()
		info[t].reset_cooldown();
		
func _debug_lockon(_attacker):
	var _dbgCol = Color.WHITE;
	if (info[_attacker].in_cooldown):
		_dbgCol = Color.ORANGE.lerp( Color.WHITE, info[_attacker].cooldown_norm_progress);
	else:
		if (!info[_attacker].killed):
			if (info[_attacker].is_charging_forward):
				_dbgCol = Color.YELLOW.lerp( Color.RED, info[_attacker].charge_forward_norm_progress );
			else:
				_dbgCol = Color.YELLOW;
		else:
			_dbgCol = Color.GRAY;
	_attacker.modulate = _dbgCol

func _physics_process(delta):
	for t in all_targets:
		knockbacks[t] = apply_knockback(delta, t, bounce_irregular( delta, t, knockbacks[t] ));
	for a in all_attackers:
		var _info = info[a]
		knockbacks[a] = apply_knockback(delta, a, knockbacks[a] );
		_debug_lockon(a);
		if _info.in_cooldown:
			if _info.cooldown(delta):
				if _info.lockon_while_recovering:
					LOCKON(delta, a, false)
			else:
				_info.reset_lockon()
		if !( _info.killed || _info.in_cooldown):
			if _info.is_charging_forward:
				CHARGE(delta, a)
			elif _info.is_locking_on:
				LOCKON(delta, a)
		else:
			a.velocity = Vector2(0,0)

func LOCKON(delta, _attacker:Node2D, charge_if_close:bool = true):
	var _angle_towards = 10000.
	for t in all_targets:
		var _ang = _attacker.get_angle_to(t.position);
		if abs(_ang) < abs(_angle_towards):
			_angle_towards = _ang;
	if abs(_angle_towards) > 0.01:
		info[_attacker].lock_on(delta)
		_attacker.rotation = rotate_toward(_attacker.rotation, _attacker.rotation + _angle_towards, delta * TURN_SPEED );
		return
	if (charge_if_close):
		#print(_attacker.name, " got a lock-on, charging forward!")
		info[_attacker].reset_charging_forward()
		_attacker.velocity = _attacker.transform.x * CHARGE_FORWARD_SPEED

func CHARGE(delta, _attacker:Node2D):
	info[_attacker].charge_forward( delta )
	var _made_impact:bool = false;
	for t in all_targets:
		if (( t.global_position - _attacker.global_position ).length() < IMPACT_DISTANCE):
			#print (_attacker.name, ": Got 'em! Gonna take a break for a second. (cooldown reset, delay re-lockon)")
			knockbacks[t] = _attacker.velocity * 2.5;
			knockbacks[_attacker] = -_attacker.velocity * 0.25;
			_made_impact = true;
			info[_attacker].lockon_while_recovering = false
			break;
	if (_made_impact || !info[_attacker].is_charging_forward):
		if (!_made_impact):
			#print (_attacker.name, ": Phew, I'm out of breath from all that charging forward! Where'd he go? (immediate re-lockon)")
			info[_attacker].lockon_while_recovering = true
		info[_attacker].reset_cooldown()
		_attacker.velocity = Vector2.ZERO
	else:
		_attacker.velocity = _attacker.velocity.move_toward(Vector2.ZERO, delta * CHARGE_DECEL_RATE);
	_attacker.move_and_slide()

1 Like

(I took a break after getting frustrated at the code to calm myself)
Would I use this in a parent node which contains all the ai or under the charge ai, as it defines multiple attackers

Okay so after implimenting the code, it works for the most part.
A few issues:

  1. I realized i said that the AI targets other AI, and not the player
  2. The ai overestimates the player (which i most like will keep as it actually reads the player’s direction which will help with the concept)

To give more explanation on my concept for the game (if needed):
Im planning to make a mobile game that gets more chaotic as the player gets more “currency”. When a player collects a certain amount of currency, another ai will be thrown in. Leading to moments where you’re barely dodging ai

How make it so that if the player collides with the AI while its resting, The ai dies?

Still, Thank you so much for giving me something to start off. I’m pretty new to godot so I’m trying to learn how things work

I managed to incorporate death by adding a condition (to a collision box connected to the ai)
func _on_charge_area_body_entered(body):
if body.name == “mc”:
if (info[attacker1].in_cooldown):
var rng = randi_range(1,3)
if rng == 1:
AudioManager.Ddeath1.play()
if rng == 2:
AudioManager.Ddeath2.play()
if rng == 3:
AudioManager.Ddeath3.play()
info[attacker1].killed = true
else:
metadata.death = 1
AudioManager.Dlaugh.play()

#button is reset button
func _on_button_pressed():
info[attacker1].killed = false

1 Like

After A little bit of tweaking to fit the concept, IT WORKS :partying_face: :partying_face: :partying_face:
THANK YOU SO MUCH!

1 Like