0 votes

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 by (100 points)

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)

1 Answer

0 votes

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 (28,789 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

This is exactly what I warned you about, read my answer again :p
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


func _ready():
    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

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.