0 votes

Hello!

I'm developing a 2d top-down game and I'm having a problem with position, movement and vectors. I created 2 images to illustrate my problem. The player can move and rotate freely and shoot bullets forward. The enemy always rotates to face the player and moves freely regardless of rotation. What I want to do is move the enemy perpendicular to the bullet (when a bullet is near the enemy). I don't have good math skills so until now I made only enemy movement, rotation and area2d to detect bullet collision. With the current code the enemy sometimes move correctly and sometimes he doesn't.

Image 1

Image 2

My code for the enemy movement (only the parts needed to illustrate my problem):

func _fixed_process(delta):

    _rotate_to_target(delta, _player1.get_global_pos())

    if _override_direction:

        if not _direction_sign_assigned:

            # here is my stupid trying to make it works
            _direction = Vector2(_area_pos.y, -_area_pos.x)

            var right = globals.angle_left_right_or_aligned_to(get_global_pos(), _area_pos)

            if right > 0:
                _direction_sign = 1
            elif right < 0:
                _direction_sign = -1
            else:
                _direction_sign = -1
            _direction *= _direction_sign

            _direction_sign_assigned = true
            velocity = _direction.normalized() * self.max_acceleration
    else:

        velocity = (target_position - my_pos).normalized() * self.max_acceleration

    _acceleration = velocity

    _acceleration = _acceleration.clamped(self.max_acceleration)

    current_velocity += _acceleration / mass
    current_velocity -= current_velocity * friction

    current_velocity = current_velocity.clamped(self.max_velocity)

    var old_pos = get_pos()
    var new_pos = get_pos()

    new_pos += current_velocity

    set_pos(old_pos.linear_interpolate(new_pos, delta))

# Timer to set movement back to normal behaviour
func _timer_flee_on_timeout():
    _override_direction = false
    _direction_sign_assigned = false
    _timer_flee.stop()

Code for enemy rotation (got from godot answers):

func _rotate_to_target(p_delta, p_target_pos):
    var ang = get_angle_to(p_target_pos)
    var s = sign(ang)
    ang = abs(ang)
    var ang_deg = rad2deg(ang)
    ang_deg += 10
    ang = deg2rad(ang_deg)
    rotate(min(ang, rotation_velocity * p_delta) * s)

Code for area2d bullet signal:

func _on_enemy_ship_emergency_flee(p_area):
    if not _override_direction:
        _area_pos = p_area.get_global_pos()
        _enemy_shot = p_area
        _override_direction = true
        _timer_flee.start()

Code for the function angleleftrightoraligned_to (obtained from the web, I don't remember the source):

# Return: 
# negative number if B is left of A 
# positive if right of A
# 0 if they are aligned.
func angle_left_right_or_aligned_to(p_va, p_vb):
    return -p_va.x * p_vb.y + p_va.y * p_vb.x

PS 1: I'm using Godot 2.1.4.
PS 2: I did a LOT of google search but I couldn't understand how to solve the problem.

Edit: fixed a bad indentation only in the code here, not in the actual code.

in Engine by (96 points)
edited by

I don't know about GDscript, but in C# Vector2 has a function called Tangent() which is what you want.

Weird. I tested before using that function but it was not working. I must have made a mistake. I rewrote the code as below and it's working now. Thank you aaronfranke and Warlaan.

var to_bullet = e_shot.get_global_pos() - get_global_pos() 
var to_bullet_dir = to_bullet.tangent().normalized()
_direction = to_bullet_dir
velocity = _direction * self.max_acceleration

1 Answer

0 votes
Best answer

Let's break it down:

You want the enemy to move into a direction that is perpendicular to the direction towards the bullet.
You get the direction towards the bullet by substracting the global position of the bullet from the global position of the enemy.
You get a perpendicular direction in 2D by replacing x with y and y with -x.

So the code you need is simply this:

var to_bullet = _enemy_shot.get_global_pos() - get_global_pos() 
var to_bullet_dir = toBullet.normalized()
var evasion_dir = Vector2(to_bullet_dir.y, -to_bullet_dir.x)

That should give you a global space vector that points in the correct direction.

by (1,120 points)
selected by

Thank you! It worked.

But I noticed that if the enemy shot is close to enemy in one side, the enemy moves to wrong side, i.e. if the bullet is close to right side of the enemy, the enemy moves right instead left. How can I fix it?

If the vector is always pointing in the wrong direction then you just need to multiply it with -1.

If it's wrong only sometimes then you need to check your code, because I don't see how that could happen with the formula above. That's something that happens often when you use sin() and cos().

Sorry for my bad English but I will try to explain better. The enemy is always moving perpendicular to the bullet. But if the bullet hits out of the center and close to down direction, the enemy moves to down but he should move to up. It happens only to the down side. He uses the longer way. I made a drawing to show how it is happening.

situation

I used the following code:

# code in enemy movement:
func _update_velocity():

    (...)

    if _override_direction:
        if _enemy_shot != null:
            var e_shot = _enemy_shot.get_ref()
            if e_shot:
                var to_bullet = e_shot.get_global_pos() - get_global_pos() 
                var to_bullet_dir = to_bullet.normalized()
                _direction = Vector2(to_bullet_dir.y, -to_bullet_dir.x)
                velocity = _direction * self.max_acceleration
            else:
                _override_direction = false

    _acceleration += velocity

    _acceleration = _acceleration.clamped(self.max_acceleration.length())

    current_velocity += _acceleration / mass
    current_velocity -= current_velocity * friction

    current_velocity = current_velocity.clamped(self.max_velocity.length())

# when enemy see the bullet
func _on_emergency_flee(p_area):
    if not _override_direction:
        _enemy_shot = weakref(p_area)
        _override_direction = true
        _timer_flee.start()

I recommend that you write down in plain english what the enemy is supposed to do. Try to break it down to the simplest sentences you can come up with, and make sure that you don't miss something.
Once you have done that it's often almost trivial to translate it to code.

The code I gave you does none of the things you describe. It instead draws an imaginary line from the position of the bullet to the position of the enemy, and then it rotates that line by 90 degrees, and that's the direction it's moving into. The movement direction of the bullet doesn't appear anywhere in my formula, so it can't factor that in.

In which directions can the enemies move? From your last schematics it looks like they were always moving up, down, left or right. If that's the case then there are of course much easier solutions.

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 Frequently asked questions and 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.