Turret Range targetting revisited

Hey guys,
There is an old thread concerning this issue, but I hope to bump this thread up for a better solution. >.<

The link to the old thread is below.
http://forum.unity3d.com/viewtopic.php?t=5036&postdays=0&postorder=asc&start=0&sid=f6023c77a47c561a09edbdbd4388bc0c

I’m trying to make a shameless clone of a tower defense game, a wave of instanced mobs will attempt to cross from point A to B, with turrets placed along the instance points. The problem lies in the turret’s tracking code.

I used the modified sentrygun script from the old thread, unfortuantely I couldn’t get Targos’s GetHim() script version to work. Only the version with the targeting “player” tag moved to update() instead of start().

Here is the script on the turret.

var attackRange = 30.0;
var shootAngleDistance = 10.0;
var target : Transform;

function Update () {
   if (target == null  GameObject.FindWithTag("player"))
      target = GameObject.FindWithTag("player").transform;

   
   if (!CanSeeTarget ())
      return;
   
   // Rotate towards target   
   var targetPoint = target.position;
   var targetRotation = Quaternion.LookRotation (targetPoint - transform.position, Vector3.up);
   transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 2.0);

   // If we are almost rotated towards target - fire one clip of ammo
   var forward = transform.TransformDirection(Vector3.forward);
   var targetDir = target.position - transform.position;
   if (Vector3.Angle(forward, targetDir) < shootAngleDistance)
      SendMessage("Fire");

   if (target == null  GameObject.FindWithTag("player"))
      target = GameObject.FindWithTag("player").transform;

}


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;

}

If I have more than one mob spawn point, I noticed all the turrents irregardless of their range will target the same specific mob(from the same spawn point) and ignore the other spawn point around each turret.

Also, unless the targetted mob is destroyed, the turrets will not switch targets, even if they were out of range.

I feel the original sentry script is inedequate to switch targets based on range. Here’s a short list of what the turrets should do.

  1. Whenever a mob(closest) comes within range of a turrent it will set that mob as the target and fire.
  2. Once out of range the turret will reset the closest available mob as its target and fire.
  3. If none is found clear target var and update to check mobs available?

I think the key lies on the script to identify more than one mob at a time. Currently using FindWithTag is constricting for more than one object.

Or is there a ‘FindAllObjectsWithTag()’ component? XD

Thanks!

 GameObject.FindGameObjectsWithTag("mySweetTag");

:sweat_smile: omg it exists.

var attackRange = 30.0;
var shootAngleDistance = 10.0;
var target : GameObject;

	function Update(){
	target = FindClosestEnemy();

//counter check if enemy is in range
	if (Vector3.Distance(transform.position, target.transform.position) > attackRange)	
{
	return;
}

else{
		// Rotate towards target	
	var targetPoint = target.transform.position;
	var targetRotation = Quaternion.LookRotation (targetPoint - transform.position, Vector3.up);
	transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5.0);

	// If we are almost rotated towards target - fire one clip of ammo
	var forward = transform.TransformDirection(Vector3.forward);
	var targetDir = target.transform.position - transform.position;
	if (Vector3.Angle(forward, targetDir) < shootAngleDistance)
		SendMessage("Fire");
}
}


function FindClosestEnemy (): GameObject {
	var moblist : GameObject[];
	moblist = GameObject.FindGameObjectsWithTag("Enemy");
	var closest: GameObject;
	var distance = Mathf.Infinity;
	var position = transform.position;
	
	for (var mobcheck : GameObject in moblist) {
		var diff = (mobcheck.transform.position - position);
		var curDistance = diff.sqrMagnitude;
		if (curDistance < distance) {
			closest = mobcheck;
			distance = curDistance;
		}
	}
	
	return closest;
}

Thanks so much for the heads up Yog.
This script will set the target for the closest, and then counter check if its within range of the turret in update().

You don’t need the “return” just check to see if it IS in range and if it is, rotate and fire. If it is not in range, it will be ignored.

function Update(){ 
     target = FindClosestEnemy(); 

//counter check if enemy is in range 
   if (Vector3.Distance(transform.position, target.transform.position) <= attackRange)    
   { 
   // Rotate towards target    
   var targetPoint = target.transform.position; 
   var targetRotation = Quaternion.LookRotation (targetPoint - transform.position, Vector3.up); 
   transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5.0); 

   // If we are almost rotated towards target - fire one clip of ammo 
   var forward = transform.TransformDirection(Vector3.forward); 
   var targetDir = target.transform.position - transform.position; 
   if (Vector3.Angle(forward, targetDir) < shootAngleDistance) 
      SendMessage("Fire"); 
   } 
}

[/quote]

yup thx for the advice seon. :slight_smile:

So been playing with this script for many hours. Not getting it to do what i want.

basically my Turret is made up of several sections, but lets simplify it to a base, and a gun.

I want to restrict the Base to only rotate in the y axis, and the gun to only rotate in the x axis. but obviously to point towards the target.

I have tried setting up joints, i have tried using only y components of the quaternion, tried converting say vector.up to a quaternion and multiplying it to my rotation quaternion in the hope to knock out 2 axis of rotation. None of these methods work, so clearly i don’t understand quaternions well enough.

Can anyone give me some pointers in the right direction?

Er… use eulers :slight_smile:

Here’s my AI turret code from Manta.

instantAim is for launchers that just shoot in the direction they like (i.e. don’t need to rotate to aim).

The script attaches to the turret base, you then assign the gun barrel to the appropriate variable and it gets elevated. If gunBarrel isn’t set the the whole turret will be rotated (think the spherical “ion cannon” turrets in Empire Strikes Back)

Click on a turret when debugging and stuff like its range, firing arcs, current target, and so forth will be displayed. (Very useful when you’re placing turrets all over a battlestation and don’t want them to blow off bits of the battlestation.)

I don’t think I implemented “lead”, and the turret is intended to be used by a separate AI (which will decide whether or not to fire a turret based on its internal state and whether the turret “hasShot()”).

/*
  Automatic Turret Script
  Written by Tonio Loewald ©2008
  You are free to modify and use with attribution.
*/

public var target : Transform;
public var instantAim : boolean = false;
public var yRotationRate : float = 2;
public var xRotationRate : float = 2;
public var restrictYRotation : boolean = false;
public var minYRotation : float = -180;
public var maxYRotation : float = 180;
public var maxUpAngle : float = 45;
public var maxDownAngle : float = 5;
public var lead : boolean = false;		// aim AHEAD of the target
public var aimPrecision : float = 4.0;	// how close do we have to be to take a shot?
public var maxTrackingRange = 100;
public var gunBarrel : Transform;
public var debugInfo : String;
public var ignoreObstacles : boolean = false;

// debugging
public var hasShot : boolean;

function Start (){
	if( !gunBarrel ){
		gunBarrel = transform;
	}
	var launchers = transform.GetComponentsInChildren(Launcher);
	for( var launcher : Launcher in launchers ){
		launcher.turret = this;
	}
}
function HasShot () : boolean {
	if( !target ){
		debugInfo = "No target.";
		hasShot = false;
		return false;
	}
	var p : Vector3 = gunBarrel.InverseTransformPoint(target.position);
	
	if( !ignoreObstacles ){
		var d = target.position - gunBarrel.position;
		var hits = Physics.RaycastAll( transform.position, d, d.magnitude );
		var s : String = "";
		for( var h in hits ){
			s = s + h.transform.name + " ";
			if( h.collider != target.collider ){
				// print( s );
				return false;
			}
		}
	}
	
	if( p.magnitude > maxTrackingRange ){
		debugInfo = "Target out of range.";
		hasShot = false;
		return false;
	} else {
		var a : float = Vector3.Angle( p, Vector3.forward );
		debugInfo = "Target offset angle: " + a;
		hasShot = a < aimPrecision;
		return hasShot;
	}
}

function Update() {
	if( !target ){
		return;
	}
	
	var d = target.position - transform.position;
	
	if( d.magnitude > maxTrackingRange ){
		return;
	}
	
	var p = gunBarrel.InverseTransformPoint(target.position);
	p.Normalize();
	var e : Vector3 = Vector3.zero;
	
	if( instantAim ){
		transform.LookAt(target);
		e = transform.localEulerAngles;
	} else {
		e.y = transform.localEulerAngles.y;
		e.x = gunBarrel.localEulerAngles.x;
		e.y += yRotationRate * p.x * Time.deltaTime;
		e.x -= xRotationRate * p.y * Time.deltaTime;
	}
	
	if( restrictYRotation ){
		e.y = ClampAngle( e.y, minYRotation, maxYRotation );
	}
	e.x = ClampAngle( e.x, 360 - maxUpAngle, maxDownAngle );
	
	transform.localEulerAngles.y = e.y;
	transform.localEulerAngles.x = 0;
	gunBarrel.localEulerAngles.x = e.x;
}

function ClampAngle( a : float, min : float, max : float ) : float {
	while( max < min ){
		max += 360;
	}
	while( a > max ){
		a -= 360;
	}
	while( a < min ){
		a += 360;
	}
	if( a > max ){
		if( a - (max + min) * 0.5 < 180 ){
			return max;
		} else {
			return min;
		}
	} else {
		return a;
	}
}

function OnDrawGizmosSelected(){
	var p1 : Vector3;
	var p2 : Vector3;
	var p3 : Vector3;
	var p4 : Vector3;
	var p5 : Vector3;
	var yIncrement : float;
	if( restrictYRotation ){
		yIncrement = ( maxYRotation - minYRotation ) / 12;
	} else {
		yIncrement = 30;
	}
	var r : float;
	var s : float;
	var a : float;
	var p : Vector3 = transform.position;
	var t : Transform = transform.parent;
	if( !t ){
		// only occurs during testing...
		return;
	}
	Gizmos.color = Color(0, 0, 1, 0.4);
	Gizmos.DrawWireSphere( transform.position, maxTrackingRange );
	var arcSize : float = 10;
	for( var y = 0; y < 12; y++ ){
		p1.x = Mathf.Sin( (minYRotation + y * yIncrement) * Mathf.Deg2Rad);
		p1.z = Mathf.Cos( (minYRotation + y * yIncrement) * Mathf.Deg2Rad);
		p2.x = Mathf.Sin( (minYRotation + (y+1) * yIncrement) * Mathf.Deg2Rad);
		p2.z = Mathf.Cos( (minYRotation + (y+1) * yIncrement) * Mathf.Deg2Rad);
		for( var x = 0; x < 6; x++ ){
			a = -maxDownAngle + (maxUpAngle + maxDownAngle) * x / 5;
			r = arcSize * Mathf.Cos(a * Mathf.Deg2Rad);
			s = arcSize * Mathf.Sin(a * Mathf.Deg2Rad);
			p3 = p + t.TransformDirection(p1 * r + Vector3.up * s);
			p4 = p + t.TransformDirection(p2 * r + Vector3.up * s);
			
			if( x < 5 ){
				a = -maxDownAngle + (maxUpAngle + maxDownAngle) * ( x + 1 ) / 5;
				r = arcSize * Mathf.Cos(a * Mathf.Deg2Rad);
				s = arcSize * Mathf.Sin(a * Mathf.Deg2Rad);
			}
			Gizmos.color = Color(1, 0.5, 0, 0.4);
			Gizmos.DrawLine( p3, p4 );
			if( x == 0 ){
				Gizmos.color = Color(1, 0, 0, 0.4);
				Gizmos.DrawLine( p3, transform.position );
			} else if( x == 5  maxUpAngle < 90 ){
				Gizmos.color = Color(1, 1, 0, 0.4);
				Gizmos.DrawLine( p3, transform.position );
			}
			if( restrictYRotation ){
				if( y == 0 ) {
					Gizmos.color = Color(0, 1, 0, 0.4);
					Gizmos.DrawLine( p3, transform.position );
					p5 = p + t.TransformDirection(p1 * r + Vector3.up * s);
					Gizmos.DrawLine( p3, p5 );
				} else if ( y == 11 ){
					Gizmos.color = Color(0, 1, 0, 0.4);
					Gizmos.DrawLine( p4, transform.position );
					p5 = p + t.TransformDirection(p2 * r + Vector3.up * s);
					Gizmos.DrawLine( p4, p5 );
				}
			}
		}
	}
}

Many thanks Podperson, that was great.

Still got masses to learn about dealing with 3D space but that has given me lots to think about.

Jim

Oh if you use my script you’ll need to implement a Launcher script or rip out the stuff involving Launchers. Again in my script a Launcher is anything that fires missiles, bullets, etc., and turrets automatically inform launchers if they’re owned by a turret.

dear guys.
I edited the two first codes a bit and I have good homing missile now :slight_smile:
thank you very much for posting.

but I have a problem if there is not an enemy ingame.
how can I avoid the Unassignement error?