[illustrated] predicting enemy's postion

Hello, dear community!

I would like to create a script for a bot to be able to shoot not at enemy’s current position, but at the position where it will be in a second or two. I would like to ask for your help in evaluating my logic, whether it is correct. I am attaching 2 paint pictures for a better explanation.


Script steps:

  1. determine enemy’s movement speed (maybe?done by: getting enemy’s velocity.magnitude ?)
  2. determine enemy’s vector (maybe?done by: store enemy’s transform.position from last frame and check it against current position ?)
  3. determine enemy’s distance (done by: reading enemy’s transform.position)
  4. knowing our speed (done)
  5. knowing the speed of our projectile (done)

equation, where “x” - is the point enemy will be in “y” seconds

x = enemy’s speed* enemy’s movement vector * y

equation, where “edist” - is the distance between us and the enemy

edist = x(current) - x(previous)

equation, where “pspeed” - is the speed of our projectile

pspeed = edist/y

because

pspeed = constant say 10 float

y = constant 2 seconds


if (full “edist” distance can be covered in full “y” seconds){

shoot at “x” ?

}

Does it make sense?

What you are describing seems more or less correct. A few notes though:

  1. The movement speed is determined by the method which which you are moving the enemy. If you’re doing movement through a Rigidbody, then the velocity value would work. If you’re modifying the transform based on some speed value, then that speed value would be the one you want to use.
  2. The direction your enemy is facing can also be determined in several ways. If you have a convention where the enemy moves in the direction of its local forward vector, you could transform the enemy’s local forward vector into world space to determine the direction of movement in world space. An easy solution, if you’re using a Rigidbody, might also be to use the normalized velocity of the Rigidbody.
  3. If the projectile is a bullet or something that moves “instantaneously,” you might be able to simplify your logic by ignoring the movement speed and time for the projectile.

Based on all this, predicting the enemy’s position should be doable. If you know how far in the future you want to predict, you can predict the enemy’s position with something like this:

Vector3 futurePosition = currentPosition + (facingDir * (speed * time));

Based on that future position and your current position, AND the speed of the projectile, you should be able to determine the correct time to shoot the projectile in the direction of the future position such that the projectile gets to that position at the same time as the enemy (unless the enemy changes its movement pattern in the meantime).

The problem with this sort of predictiveness is that it could make the bot too good at the game…which isn’t all that fun. Experimenting with ways to give the bot skill, but some degree of error, takes a lot of work.

Thank you very much for a detailed answer and your thoughts!
I’ve written a script and it works as intended!

using UnityEngine;
using System.Collections;

public class botpathfindingsxcript : MonoBehaviour {

	public GameObject[] points;
	public int randomnumber; 
	public NavMeshAgent agent;
	public GameObject enemy;
	public bool shooting;
	public float timer;
	public float fireRate = 0.1f;
	public GameObject instantiatedProjectile;
	public GameObject projectile;
	public Transform pointtofire;
	public float speed = 20f;

	/// <summary>
	public Vector3 enemydestination;
	public Vector3 old_position;
	public Vector3 newVector;

	public float enemyspeed;
	public float enemytime; //time lag



	public float timeprojectiletoreachdestination;
	public float timeenemytoreachdestination;

	public Vector3 Newvector2;
	public float newdistance;



	// Use this for initialization
	void Start () {

		//get all gameobjects with tag "Finish" into the array
		points = GameObject.FindGameObjectsWithTag ("Finish");
		randomnumber = Random.Range (0, points.Length);
		agent = GetComponent<NavMeshAgent>();

		//
		old_position = enemy.transform.position;
		

	}	
	
	// Update is called once per frame
	void Update () {
	

		/////////// if there are some points in our array
		if (points.Length > 0) {
			if (transform.position != points [randomnumber].transform.position) {

				//start moving towards random point
				agent.SetDestination (points [randomnumber].transform.position); 
			} 

			//if our position is the same as position of the point (ignoring vertical aspect)
			if (transform.position.z == points [randomnumber].transform.position.z && transform.position.x == points [randomnumber].transform.position.x) {
				//new random number
				randomnumber = Random.Range (0, points.Length);
			}

		}

	
		//if enemy player is moving
		if(enemy.transform.position != old_position)
		{
			enemyspeed = enemy.GetComponent<Rigidbody> ().velocity.magnitude;
			enemytime = (Vector3.Distance(enemy.transform.position, transform.position))/(instantiatedProjectile.GetComponent<Rigidbody>().velocity.magnitude);


			//the direction + magnitude where enemy is moving
			newVector = enemy.transform.position - old_position;
		
		//	newVector= enemy.GetComponent<Rigidbody>().velocity;
		//	Newvector2 = enemy.transform.position - old_position;

			newdistance = (Vector3.Distance(old_position, enemy.transform.position))/Time.deltaTime;

			//enemy proposed future destination
			//enemydestination = enemy.transform.position + (newVector.normalized * (enemyspeed * enemytime));
			enemydestination = enemy.transform.position + (newVector.normalized * (newdistance * enemytime));

			//in how many seconds our projectiles will reach the calculcated destination
			timeprojectiletoreachdestination = (Vector3.Distance(enemydestination, transform.position))/((instantiatedProjectile.GetComponent<Rigidbody>().velocity.magnitude));

			//in how many seconds enemy will reach his destination point
			timeenemytoreachdestination = (Vector3.Distance(enemy.GetComponent<NavMeshAgent>().destination, enemy.transform.position))/(enemyspeed);
	
			//if time it takes for a projectile to reach the destination, is smaller than 
			//the time it takes for an enemy player, to go to this destination,
			//then set new destination	
	
	
			//remember old position to compare in the next frame (to determine direction)
			old_position = enemy.transform.position;

		
		}



		//if we see enemy
		if (shooting) {
			
			timer += Time.deltaTime;

			//fire delay
			if (timer > fireRate) {

			//shooting at the current position
			//	instantiatedProjectile = (GameObject)Instantiate(projectile, pointtofire.transform.position + pointtofire.transform.forward, transform.rotation);
			//	instantiatedProjectile.GetComponent<Rigidbody>().AddForce ((enemy.transform.position - pointtofire.transform.position).normalized  * speed, ForceMode.Impulse);
			//	Debug.DrawLine(enemy.transform.position, pointtofire.transform.position, Color.red, 3.0f, true); 
			
				instantiatedProjectile = (GameObject)Instantiate(projectile, pointtofire.transform.position + pointtofire.transform.forward, transform.rotation);
				instantiatedProjectile.GetComponent<Rigidbody>().AddForce ((enemydestination - pointtofire.transform.position).normalized  * speed, ForceMode.Impulse);

				Debug.DrawLine(enemydestination, pointtofire.transform.position, Color.blue, 3.0f, true);
				timer = 0;

	
			}
		}
	}

	//if enemy is inside our trigger, start shooting
	void OnTriggerStay(Collider other){

			if (other.gameObject.tag == "enemy") {

			enemy = other.gameObject;
			shooting = true;

			}
		}
	}