0 votes

I want the node to move in a fashion of four arrow keys movement towards set point.
I started with something like this, but this creates obvious problem with twitching, and the node will never achieve "0" in the if statement. Is there a workaround or should I approach it differently?

if Input.is_action_pressed("click"):
    mps = get_global_mouse_pos()
dire = (mps-get_global_pos()).normalized()

if get_global_pos().distance_to(mps) > 10:
    if dire.x>0:
        set_pos(get_pos()+Vector2(1,0)*delta*speed)
    if dire.x<0:
        set_pos(get_pos()+Vector2(-1,0)*delta*speed)
    if dire.y>0:
        set_pos(get_pos()+Vector2(0,1)*delta*speed)
    if dire.y<0:
        set_pos(get_pos()+Vector2(0,-1)*delta*speed)
in Engine by (23 points)

Just to give more info - I'm on a quest to recreate a simple pathfinding in style of Warcraft game, without using navigation nodes. I don't know if it's relevant.

If you need pathfinding and not just simple movement from start to end see A*: https://www.youtube.com/watch?v=Ad6Us73smNs

1 Answer

+1 vote
Best answer

In my opinion you should devide space in grid cells to simulate 8 dir movement, and move from cell to cell (not actual TileMap just virtual grid space for movement, if you already dont use TileMap for level, if you do then things are easier). Cell size is dependent of how granular you want 8dir movement to be (or if you use TileMap in game you should use cell size of TileMap). Devide your start and goal position by cell size so you get start and goal cell positions, and use some of line algorithms ( https://www.redblobgames.com/grids/line-drawing.html ) to store grid points of a line deffned by start and goal in array. Then you need to move from one array index to another (multiply cell positions by cell size to get actual pixel position) until you reach the last one. If you need to land exactly on goal point you should replace last cell position from line array with your goal position and move there. Hope it helps.

by (243 points)
selected by

Two line algorithms:

static func WalkGrid( p0 : Vector2, p1 : Vector2) -> Array:
    var dx = p1.x - p0.x
    var dy = p1.y - p0.y
    var nx = abs(dx)
    var ny = abs(dy)
    var signX = 1 if dx > 0 else -1
    var signY = 1 if dy > 0 else -1
    var p : Vector2 = p0 # Point to walk on grid. set it to Start point
    var points : Array = [p] # Add Start position to array

    var ix : int = 0
    var iy : int = 0

    while ix < nx || iy < ny:
        if ( ( 1 + ( ix << 1) ) * ny < ( 1 + ( iy << 1) ) * nx ):
            p.x += signX
            ix +=1
        else:
            p.y += signY
            iy += 1
        points.append(p)
    return points

static func BresenhamLine(p0 : Vector2, p1 : Vector2):
    var s = Vector2()
    var dx = abs(p1.x-p0.x);  s.x = 1 if p0.x<p1.x else -1
    var dy = abs(p1.y-p0.y);  s.y = 1 if p0.y<p1.y else -1
    var err = (dx if dx>dy else -dy)/3
    var e2
    var points = []

    while true:
        points.append(p0)
        if p0.x == p1.x && p0.y == p1.y:
            break
        e2 = err
        if e2 > -dx:
            err -= dy
            p0.x += s.x
        if e2 < dy:
            err +=dx
            p0.y += s.y
    return points

Thanks for the suggestions!
The TileMap solved half of my problems. With grid on board I was able to smooth the moving.
You and the commenter suggested using line/pathfinding algorithms and that's the thing I want to avoid.
What I came with is:
var targetpos = Vector2()
var unit
pos = Vector2()
var speed = 100

func _input(event):
    if event.is_action_released("rmb"):
        target_pos = get_global_mouse_pos().snapped(Vector2(64,64))
func _process(delta):

    if target_pos.x-unit_pos.x < 0:
        set_pos(get_pos()+Vector2(-1,0)*speed*delta)
        if target_pos.x-unit_pos.x > -1:
            set_pos(get_global_pos().snapped(Vector2(64,0)))
    if target_pos.x-unit_pos.x > 0:
        set_pos(get_pos()+Vector2(1,0)*speed*delta)
        if target_pos.x-unit_pos.x < 1:
            set_pos(get_global_pos().snapped(Vector2(64,0)))
    if target_pos.y-unit_pos.y < 0:
        set_pos(get_pos()+Vector2(0,-1)*speed*delta)
        if target_pos.y-unit_pos.y > -1:
            set_pos(get_global_pos().snapped(Vector2(0,64)))
    if target_pos.y-unit_pos.y > 0:
        set_pos(get_pos()+Vector2(0,1)*speed*delta)
        if target_pos.y-unit_pos.y < 1:
            set_pos(get_global_pos().snapped(Vector2(0,64)))

    if abs(target_pos.x-unit_pos.x) < 1 and abs(target_pos.y-unit_pos.y) < 1:
        set_pos(target_pos) 

    unit_pos = get_global_pos()

Now, if someone could tell if it's Godot-proper?

It seem to be ok, at least your idea. One question, I am curious: why do you need to simulate 8 dir movement? You can just change appropriate sprite animation direction depending of vector of movement and use Godot's Navigation2D/NavigationPolygon for movement of units (even on individual tiles you can set NavigationPolygon).

Warcraft didn't used anything as elaborate as Navigation nodes provides.
It's AI was because of this really bad, and I want to achieve something similar in terms of clunkiness.
Also I don't want to use pre-calculated lines, paths etc, to make it even more stupid.

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.