Perpendicular movement to evade bullet

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By equus

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 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 angle_left_right_or_aligned_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.

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

anonymous | 2018-04-02 19:57

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

equus | 2018-04-04 15:51

:bust_in_silhouette: Reply From: Warlaan

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.

Thank you! It worked.

equus | 2018-04-01 18:58

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?

equus | 2018-04-01 19:03

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().

Warlaan | 2018-04-02 08:14

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()

equus | 2018-04-02 17:27

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.

Warlaan | 2018-04-02 18:59