Basically I am writing a simple AI movement script and I am using kineticBody2d node with move and slide to move the enemy for while there are no obstructions between the enemy and the player.

What I need to figure out now is a way to limit the movement to 4 directions only somehow

To do that I need the velocity Vector2 value to be one of 4 possible values:
(1,0) or (-1,0) or (0,1) or (0,-1)

But right now because I am getting it like this:
var angle = getangleto(targetPos)
var velocity = Vector2(sin(angle), cos(angle))
I get the full float range of x and y

Any suggestions on how to do this with math ? I tried with a bunch of if statements and its just messy and sometimes halves the speed or stops moving

in Engine

Checking which number is higher(sin or cos) and by that deciding if you should let the `X` be `0` or the `Y` be `0`.
you can also just check for the player's position and for the Enemy's position and manipulate them in order to achieve whatever you need

The function I gave below does that without using angles or trigonometry, but I believe the problem to solve is how the AI moves for some amount of time, because unlike free movement, 4-directional movement must do an angled path, and that function shouldn't be called all over again in `_process()` (or maybe never be used like this in the first place)

One problem about moving to a target using only 4 directions is that there are multiple vectors that lead to the same result.

It may always be in two phases: one going horizontally, then vertically, but the other way around also works. If your AI is as far as the target in both X and Y, which direction should it take first? (1,0) or (0,1)?

Now, if you just want a vector to a point but that uses only horizontal or vertical directions, you can isolate the problem into a function, like this:

``````static func get_direction(from, to):
var diff = to - from
if abs(diff.x) > abs(diff.y):
return Vector2(sign(diff.x), 0)
else:
return Vector2(0, sign(diff.y))
``````

But again, you need to think about the effect of this across time, because applying the same approach as a "float" direction will make your AI oscillate weirdly as soon as it reaches the point where X and Y distances are equal.

by (29,090 points)
edited by

Hi,
Thank you for the function, but it doesnt work still - if you perfectly align the target position and the from, the node starts moving diagonally towards the target rapidly switching between moving vertically and horizontally

The function should work in order to give you an immediate 4-direction vector, but it's not going to solve your problem on its own, you need to think about how your AI behaves across multiple frames. Beware, I also mean this function shouldn't be used every single frame, because it would cause weird oscillations.

Maybe you need to remember in which direction your AI is going.
For example, you might start going upwards because the target is mostly upwards. You continue going up vertically until it gets horizontally aligned with the target. Then, change direction and go horizontally towards the target.
You may want to refine this algorithm if the target moves though.

That's a two-phase movement because you want to constrain to 4 directions, and the only way for an AI to go from A to B is to do an angled path.

Hi I solved it by introducing grid based movement,so direction is changed only per tile.

Btw is there a way to turn your function into an 8 direction one?

Aaah too bad, I came up with something:

``````extends Sprite

var _dir = null

set_process(true)

func _process(delta):
var target = get_global_mouse_pos()
var pos = get_pos()

if target == pos:
return

if _dir == null:
_dir = get_direction(pos, target)

else:
var diff = target - pos
diff = Vector2(floor(diff.x), floor(diff.y))
var is_vertical = _dir.x == 0

if is_vertical:
if sign(diff.y) != _dir.y or diff.y == 0:
_dir = get_direction(pos, target)
else:
if sign(diff.x) != _dir.x or diff.x == 0:
_dir = get_direction(pos, target)

var speed = 100.0
set_pos(pos + _dir * speed * delta)

static func get_direction(from, to):
var diff = to - from
if abs(diff.x) > abs(diff.y):
return Vector2(sign(diff.x), 0)
else:
return Vector2(0, sign(diff.y))
``````

For 8 directions I don't know, unfortunately I don't have time left to investigate an example right now.
Also keep in mind that the "oscillation" still applies on a turn-based grid, it just means it will pick horizontal and vertical alternatively each turn