/// <summary> /// Reference for Predictive Aiming: /// https://www.gamasutra.com/blogs/KainShin/20090515/83954/Predictive_Aim_Mathematics_for_AI_Targeting.php /// </summary> protected Vector3 GetPredictiveAimDirection(NpcController npc) { if (npc == null) { return(new Vector3()); } var bulletSpeed = HandController.GetProjectileSpeed(); var bulletSpeedSq = bulletSpeed * bulletSpeed; var bulletOrigin = HandController.GetWeaponPosition(); var enemyInitialPosition = npc.transform.root.position; var enemyVelocity = npc.GetVelocity(); var enemySpeed = enemyVelocity.magnitude; var enemySpeedSq = enemySpeed * enemySpeed; var enemyToBullet = bulletOrigin - enemyInitialPosition; var enemyToBulletDistance = enemyToBullet.magnitude; var enemyToBulletDistanceSq = enemyToBulletDistance * enemyToBulletDistance; var enemyToBulletDirection = enemyToBullet.normalized; var enemyVelocityDirection = enemyVelocity.normalized; //Law of Cosines: A*A + B*B - 2*A*B*cos(theta) = C*C //A is distance from bullet to enemy (known value: enemyToBulletDistance) //B is distance traveled by enemy until impact (enemySpeed * t (time)) //C is distance traveled by bullet until impact (bulletSpeed * t (time)) var cosTheta = Vector3.Dot(enemyToBulletDirection, enemyVelocityDirection); var a = bulletSpeedSq - enemySpeedSq; var b = 2.0f * enemyToBulletDistance * enemySpeed * cosTheta; var c = -enemyToBulletDistanceSq; var discriminant = b * b - 4.0f * a * c; var uglyNumber = Mathf.Sqrt(discriminant); var t0 = 0.5f * (-b + uglyNumber) / a; var t1 = 0.5f * (-b - uglyNumber) / a; var time = Mathf.Min(t0, t1); if (time < Mathf.Epsilon) { time = Mathf.Max(t0, t1); } var bulletVelocity = enemyVelocity + -enemyToBullet / time; return(bulletVelocity); }