- Home /
How do I make an enemy lead his shots?
Hi, I am making a fps from the fps tutorial and have come across a problem. My robots fire at you but you can easily doge by just moving slightly to the side or to keep moving. I want them to lead their shots so they will hit you where your going. Can anyone tell me what to change in my script? Thanks.
AI.js
var attackRange = 30.0;
var shootAngle = 4.0;
var dontComeCloserRange = 5.0;
var delayShootTime = 0.35;
var pickNextWaypointDistance = 2.0;
var target : Transform;
private var lastShot = -10.0;
// Make sure there is always a character controller
@script RequireComponent (CharacterController)
function Start () {
// Auto setup player as target through tags
if (target == null && GameObject.FindWithTag("Player"))
target = GameObject.FindWithTag("Player").transform;
Patrol();
}
function Patrol () {
var curWayPoint = AutoWayPoint.FindClosest(transform.position);
while (true) {
var waypointPosition = curWayPoint.transform.position;
// Are we close to a waypoint? -> pick the next one!
if (Vector3.Distance(waypointPosition, transform.position) < pickNextWaypointDistance)
curWayPoint = PickNextWaypoint (curWayPoint);
// Attack the player and wait until
// - player is killed
// - player is out of sight
if (CanSeeTarget ())
yield StartCoroutine("AttackPlayer");
// Move towards our target
MoveTowards(waypointPosition);
yield;
}
}
function CanSeeTarget () : boolean {
if (Vector3.Distance(transform.position, target.position) > attackRange)
return false;
var hit : RaycastHit;
if (Physics.Linecast (transform.position, target.position, hit))
return hit.transform == target;
return false;
}
function Shoot () {
// Start shoot animation
animation.CrossFade("shoot", 0.3);
// Wait until half the animation has played
yield WaitForSeconds(delayShootTime);
// Fire gun
BroadcastMessage("Fire");
// Wait for the rest of the animation to finish
yield WaitForSeconds(animation["shoot"].length - delayShootTime);
}
function AttackPlayer () {
var lastVisiblePlayerPosition = target.position;
while (true) {
if (CanSeeTarget ()) {
// Target is dead - stop hunting
if (target == null)
return;
// Target is too far away - give up
var distance = Vector3.Distance(transform.position, target.position);
if (distance > shootRange * 3)
return;
lastVisiblePlayerPosition = target.position;
if (distance > dontComeCloserRange)
MoveTowards (lastVisiblePlayerPosition);
else
RotateTowards(lastVisiblePlayerPosition);
var forward = transform.TransformDirection(Vector3.forward);
var targetDirection = lastVisiblePlayerPosition - transform.position;
targetDirection.y = 0;
var angle = Vector3.Angle(targetDirection, forward);
// Start shooting if close and play is in sight
if (distance < shootRange && angle < shootAngle)
yield StartCoroutine("Shoot");
} else {
yield StartCoroutine("SearchPlayer", lastVisiblePlayerPosition);
// Player not visible anymore - stop attacking
if (!CanSeeTarget ())
return;
}
yield;
}
}
function SearchPlayer (position : Vector3) {
// Run towards the player but after 3 seconds timeout and go back to Patroling
var timeout = 3.0;
while (timeout > 0.0) {
MoveTowards(position);
// We found the player
if (CanSeeTarget ())
return;
timeout -= Time.deltaTime;
yield;
}
}
function RotateTowards (position : Vector3) {
SendMessage("SetSpeed", 0.0);
var direction = position - transform.position;
direction.y = 0;
if (direction.magnitude < 0.1)
return;
// Rotate towards the target
transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
}
function MoveTowards (position : Vector3) {
var direction = position - transform.position;
direction.y = 0;
if (direction.magnitude < 0.5) {
SendMessage("SetSpeed", 0.0);
return;
}
// Rotate towards the target
transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
// Modify speed so we slow down when we are not facing the target
var forward = transform.TransformDirection(Vector3.forward);
var speedModifier = Vector3.Dot(forward, direction.normalized);
speedModifier = Mathf.Clamp01(speedModifier);
// Move the character
direction = forward * speed * speedModifier;
GetComponent (CharacterController).SimpleMove(direction);
SendMessage("SetSpeed", speed * speedModifier, SendMessageOptions.DontRequireReceiver);
}
function PickNextWaypoint (currentWaypoint : AutoWayPoint) {
// We want to find the waypoint where the character has to turn the least
// The direction in which we are walking
var forward = transform.TransformDirection(Vector3.forward);
// The closer two vectors, the larger the dot product will be.
var best = currentWaypoint;
var bestDot = -10.0;
for (var cur : AutoWayPoint in currentWaypoint.connected) {
var direction = Vector3.Normalize(cur.transform.position - transform.position);
var dot = Vector3.Dot(direction, forward);
if (dot > bestDot && cur != currentWaypoint) {
bestDot = dot;
best = cur;
}
}
return best;
}
I don't see the code that aims and shoot the projectile here. In order to lead your character, you will need the velocity of the character. How you get that will depend on how the character is moved: CharacterController, Rigidbody, or Transform.
Sorry, here is the firing script.
var projectile : Rigidbody;
var initialSpeed = 20.0;
var reloadTime = 0.5;
var ammoCount = 20;
private var lastShot = -10.0;
function Fire () {
// Did the time exceed the reload time?
if (Time.time > reloadTime + lastShot && ammoCount > 0) {
// create a new projectile, use the same position and rotation as the Launcher.
var instantiatedProjectile : Rigidbody = Instantiate (projectile, transform.position, transform.rotation);
// Give it an initial forward velocity. The direction is along the z-axis of the missile launcher's transform.
instantiatedProjectile.velocity = transform.TransformDirection(Vector3 (0, 0, initialSpeed));
// Ignore collisions between the missile and the character controller
Physics.IgnoreCollision(instantiatedProjectile.collider, transform.root.collider);
lastShot = Time.time;
ammoCount--;
}
}
Is this a 2D or 3D game? Does your character use a Character Controller or a Rigidbody to move?
$$anonymous$$y character uses a character controller. It is a 3d fps game. The movement script for the player is the standard Character $$anonymous$$otor. I also have a sprint and crouch script that shares with the Character $$anonymous$$otor.
Advanced $$anonymous$$otor Add-on.js
var walkSpeed: float = 7; // regular speed
var crchSpeed: float = 3; // crouching speed
var runSpeed: float = 20; // run speed
private var ch$$anonymous$$otor: Character$$anonymous$$otor;
private var ch: CharacterController;
private var tr: Transform;
private var height: float; // initial height
function Start(){
ch$$anonymous$$otor = GetComponent(Character$$anonymous$$otor);
tr = transform;
ch = GetComponent(CharacterController);
height = ch.height;
}
function Update(){
var h = height;
var speed = walkSpeed;
if (ch.isGrounded && Input.Get$$anonymous$$ey("left shift") || Input.Get$$anonymous$$ey("right shift")){
speed = runSpeed;
}
if (Input.Get$$anonymous$$ey("c")){ // press C to crouch
h = 0.5 * height;
speed = crchSpeed; // slow down when crouching
}
ch$$anonymous$$otor.movement.maxForwardSpeed = speed; // set max speed
var lastHeight = ch.height; // crouch/stand up smoothly
ch.height = $$anonymous$$athf.Lerp(ch.height, h, 5*Time.deltaTime);
tr.position.y += (ch.height-lastHeight)/2; // fix vertical position
}
Answer by robertbu · Aug 03, 2013 at 02:01 AM
The basic idea is to get the velocity of the character and calculate the time of flight of the projectile to their current position. Using the flight time, you can make a rough projection about where the character will be by the time the missile arrives. You then aim at the new position. What I've done here is a bit simplified and imperfect, but you are not looking for perfect. And it is untested. It assumes the character is named "Player". Change as needed.
#pragma strict
var projectile : Rigidbody;
var initialSpeed = 20.0;
var reloadTime = 0.5;
var ammoCount = 20;
private var lastShot = -10.0;
private var cc : CharacterController;
private var player : Transform;
function Start() {
var go = GameObject.Find("Player");
player = go.transform;
cc = go.GetComponent(CharacterController);
}
function Fire () {
// Did the time exceed the reload time?
if (Time.time > reloadTime + lastShot && ammoCount > 0) {
// create a new projectile, use the same position and rotation as the Launcher.
var instantiatedProjectile : Rigidbody = Instantiate (projectile, transform.position, transform.rotation);
// Time of flight to reach player
var t = (player.position - transform.position).magnitude / initialSpeed;
// Project future position
var futurePos = player.position + cc.velocity * t;
// Aim and fire at that future position
var aim = (futurePos - transform.position).normalized;
instantiatedProjectile.transform.rotation = Quaternion.LookRotation(aim);
instantiatedProjectile.velocity = aim * initialSpeed;
// Ignore collisions between the missile and the character controller
Physics.IgnoreCollision(instantiatedProjectile.collider, transform.root.collider);
lastShot = Time.time;
ammoCount--;
}
}
This is superb, except that the rocket mesh is now not facing forwards. It probably has to do with it not moving orthodoxly. Could you tell me how to make them point towards their current direction.
I added line 32 in the script above to aim the projectile at the new position.
I, mean object, not mesh. The rocket object is facing the direction the robot is facing not were it's going. $$anonymous$$y apologies.
Reading back through your comments, it is the missile/rocket that is not pointing in the correct direction or the launcher? The line of code I added should have fixed the problem:
instantiatedProjectile.transform.rotation = Quaternion.LookRotation(aim);
This points the forward of the missile towards the new target.
If you go back and take a look at my original answer, you will find the line already added. I edited the code right after you indicated you had a problem with the aim.
Answer by Ian094 · Aug 03, 2013 at 01:05 AM
You can try using transform.LookAt heres an example:
var Player : Transform;
function Update(){
transform.LookAt(Player);
}
Just attach this to your AI and they might never miss again.
This is very useful, but I don't want to have a bunch of snipers with rocket launchers. I'll work on it. I kinda want the m to hit most of the time but when I sprint I want it to miss because i'm moving too fast. Thanks anyway.