Projectile only being instanced when player is not moving

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

Hi all,

I am trying to create an entity that predictively looks for where a player will be based on their position and velocity and shoots for that location. I’m mostly basing this on the GDC Video ‘Math for Game Programmers: Predictable Projectiles’.

Overall my logic seems to work decently well, but I am running into an issue where the projectile only seems to be spawning / being shot when the player is in a static state / not moving. Here’s the code for my ‘turret’ class, which aims and shoots at the player:

extends StaticBody2D

var target: Tank
var PROJECTILE_SPEED = 100
var pos

const proj = preload("res://Projectile.tscn")

# Called when the node enters the scene tree for the first time.
func _ready():
	$Timer.start()
	print("I am ready")
	pos = $Position.global_position

func _process(delta):
	pass


func track():
	
	pass
	var t_vel = target.velocity
	var t_pos = target.global_position
	
	var a = t_vel.dot(t_vel) - PROJECTILE_SPEED
	var b = 2*(t_pos - pos).dot(t_vel)
	var c = (t_pos - pos).dot((t_pos - pos))
	
	if pow(b, 2) > 4*a*c and a != 0:
		var t1 = (-b + sqrt(pow(b, 2) - 4*a*c))/(2*a)
		var t2 = (-b - sqrt(pow(b, 2) - 4*a*c))/(2*a)
		var t = min(t1, t2)
		
		var predicted_pos = t_pos + t_vel*t
		var dir = (predicted_pos - pos).normalized()
		var final_vel = dir*PROJECTILE_SPEED
		
		var projectile = proj.instance()
		self.get_parent().add_child(projectile)
		projectile.global_position = pos
		print(final_vel)
		projectile.VELOCITY = final_vel

func _on_Timer_timeout():
	print("Firing")
	track()

Sorry if it is a bit messy, this is still very in-progress. The class has a timer node attached for now which should be timing out and then firing every second, but it does not if the player (a ‘tank’ object) is moving.

Here is the code for my basic projectile:

extends Area2D

class_name TempProjectile

var VELOCITY: Vector2 = Vector2(0, 0)

func _physics_process(delta):
	translate(VELOCITY*delta)

Any help or advice would be very appreciated

Does it keep on printing the “Firing”?
Then the problem might be lying in the if clause.
Print out the values of the comparisons in the clause in the line before it and check if (and why) the “if” may not be true.

wombatstampede | 2019-11-08 07:58

Yes it does. After looking at it again with fresh eyes, I noted that the ‘pow(b, 2) > 4ac’ was never evaluating to true while I was moving. That’s because the ‘a’ coefficient term should use pow(PROJECTILE_SPEED, 2) instead of PROJECTILE_SPEED, so ‘a’ was always too large.

I also for whatever reason needed to change the target velocity to the opposite sign to get the correct prediction. Not sure yet if this is an error on my part using the coordinate system or what. Without inverting the velocity, it will always fire behind the target instead of ahead.

Regardless, it’s working now which is great! Thanks for the suggestion.

andrew.s | 2019-11-10 00:46