Caching and Apache
Quick tutorial covering how HTTP headers affect browser caching and configuring Apache for your needs.
Author
Learn how to detect enemies and shoot projectiles at them. A continuation of the tower defense tutorial series.
Learn how to make turrets detect enemies and shoot projectiles at them. A continuation of the tower defense tutorial series.
Go ahead and get started by creating a Sprite in your hierarchy.
This should create a new GameObject with the Transform and Sprite Renderer components
Don't worry too much about changing the position; we're going to be setting that programmatically when the player tries to place a turret on the map. Do, however, add some cool art to the Sprite property of your renderer.
If your art is scattered across multiple files, you may want to consider using our sprite sheet maker tool to pack the images into a single sprite sheet.
Attach a MonoBehavior script and call it Turret.cs.
Attach a Sphere Collider too. Make sure the Is Trigger property is checked. Don't worry about changing values for Center and Radius as we'll be changing these programmatically.
Go ahead and create a prefab by dragging the turret from your scene to a directory in the project tab. Having our turrets as prefabs will give the benefit of being able to instantiate them from code. We can even create multiple prefabs which will allow for different turret types, each with their own stats, special abilities, and artwork.
Open up the Turret.cs attached to your prefab. It should look something like this:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class Turret : MonoBehaviour
{
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
Let's add some fields and properties. Keep in mind the public fields will be accessible in Unity's inspector panel. damage, range, and rateOfFire are all meant to adjusted there on a 0-10 scale. The associated properties AttackDelay and DetectionRadius scale those values to based on the size of the map. DetectionRadius also takes responsibility for changing the size of the attached sphere collider.
// Configurable
public float accuracyError = 2.0f;
public int damage = 10;
public GameObject projectileType;
public int range = 5;
public int rateOfFire = 5;
// Constants
private const float MinAttackDelay = 0.1f;
private const float MaxAttackDelay = 2f;
// Internal
private List myTargets;
private float nextDamageEvent;
private ObjectManager objectManager;
private static readonly object syncRoot = new object ();
// Properties
private float AttackDelay
{
get
{
int inverted = rateOfFire;
if (rateOfFire == 0)
{
return float.MaxValue;
}
else if (rateOfFire < 5)
{
inverted = rateOfFire + 2 * (5 - rateOfFire);
}
else if (rateOfFire > 5)
{
inverted = rateOfFire - 2 * (rateOfFire - 5);
}
return (((float)inverted - 1f) / (10f - 1f)) * (MaxAttackDelay - MinAttackDelay) + .1f;
}
}
public float DetectionRadius
{
get
{
float minRange = Mathf.Min(objectManager.Map.nodeSize.x, objectManager.Map.nodeSize.y) * 1.5f;
float maxRange = minRange * 4f;
float detectionRadius = (((float)range - 1f) / (10f - 1f)) * (maxRange - minRange) + minRange;
detectionRadius = detectionRadius / transform.localScale.x;
return detectionRadius;
}
set
{
float minRange = Mathf.Min(objectManager.Map.nodeSize.x, objectManager.Map.nodeSize.y) * 1.5f;
float maxRange = minRange * 4f;
float detectionRadius = (((float)value - 1f) / (10f - 1f)) * (maxRange - minRange) + minRange;
detectionRadius = detectionRadius / transform.localScale.x;
SphereCollider collider = transform.GetComponent ();
collider.radius = detectionRadius;
}
}
Here we initialize some of those private fields. objectManager is just a singleton we are using to help maintain game state. Matt talks more in-depth about it in some of the earlier videos from the Tower Defense Tutorial Video Series.
// Runs when entity is Instantiated
void Awake()
{
objectManager = ObjectManager.GetInstance();
objectManager.AddEntity(this);
}
// Use this for initialization
void Start ()
{
DetectionRadius = range;
myTargets = new List();
}
These two methods track when enemies enter and exit the attached sphere collider. At any given time, myTargets should now reflect all enemies inside the sphere, the turret's detectable area.
void OnTriggerEnter (Collider other)
{
if (other.gameObject.tag == "enemy") {
myTargets.Add (other.GetComponent());
}
}
void OnTriggerExit (Collider other)
{
lock (syncRoot) {
if (other != null &&
myTargets.Select (t => t!= null && t.gameObject).Contains(other.gameObject)) {
myTargets.Remove (other.GetComponent());
}
}
}
In order to create a projectile, create a new GameObject starting with as a sphere. I ended up adding a Mesh Renderer and a Line Renderer to get it to look like a bullet. Attach a MonoBehavior script called Projectile.cs. Go ahead and make a prefab from this object the same way you did for turrets.
Nothing too crazy going on in this script. It requires a target enemy (and associated location) and just homes in on it until it "hits". We're not doing any collision detection here, but rather checking distance between the projectile and its target. Once the projectile reaches the target, it destroys itself and damages the enemy by subtracting from its health.
using UnityEngine;
using System.Collections;
public class Projectile : MonoBehaviour
{
// Configurable
public float range;
public float speed;
public EnemyBase target;
public Vector3 targetPosition;
public int Damage { get; set; }
// Internal
private float distance;
// Runs when entity is Instantiated
void Awake ()
{
distance = 0;
}
// Update is called once per frame
void Update ()
{
Vector3 moveVector = new Vector3 (transform.position.x - targetPosition.x,
transform.position.y - targetPosition.y,
transform.position.z - targetPosition.z).normalized;
// update the position
transform.position = new Vector3 (transform.position.x - moveVector.x * speed * Time.deltaTime,
transform.position.y - moveVector.y * speed * Time.deltaTime,
transform.position.z - moveVector.z * speed * Time.deltaTime);
distance += Time.deltaTime * speed;
if (distance > range ||
Vector3.Distance (transform.position, new Vector3 (targetPosition.x, targetPosition.y, targetPosition.z)) < 1)
{
Destroy (gameObject);
if (target != null)
{
target.Damage (Damage);
}
}
}
}
Now that projectiles are good to go, drag that new projectile prefab onto the projectileType field (in the inspector when you've got a turret selected). Next, you'll need the following two methods to make the turret "fire" projectiles. All we're doing is setting up a loop where the turret instantiates new projectiles targeted at a random enemy within range.
void Fire (EnemyBase myTarget)
{
var targetPosition = myTarget.transform.position;
var aimError = Random.Range (-accuracyError, accuracyError);
var aimPoint = new Vector3 (targetPosition.x + aimError, targetPosition.y + aimError, targetPosition.z + aimError);
nextDamageEvent = Time.time + AttackDelay;
GameObject projectileObject = Instantiate (projectileType, transform.position, Quaternion.LookRotation (targetPosition)) as GameObject;
Projectile projectile = projectileObject.GetComponent ();
projectile.Damage = damage;
projectile.target = myTarget;
projectile.targetPosition = aimPoint;
}
// Update is called once per frame
void Update ()
{
lock(syncRoot)
{
if (myTargets.Any())
{
EnemyBase myTarget = myTargets.ElementAt(Random.Range(0, myTargets.Count));
if (myTarget != null) {
if (Time.time >= nextDamageEvent)
{
Fire(myTarget);
}
}
else
{
nextDamageEvent = Time.time + AttackDelay;
myTargets.Remove(myTarget);
}
}
}
}
To fill in some of the gaps like placing turrets on the map and spawning enemies, I encourage you to go take a look at some of the earlier videos from the Tower Defense Tutorial Video Series.
We have similar articles. Keep reading!
Quick tutorial covering how HTTP headers affect browser caching and configuring Apache for your needs.
Author
Technical talk from Matt Bauer about A* pathfinding in Nauticus Act III.
Author
Video tutorial about managing enemy health in a rail shooter built with Unity3d. We'll also discuss killing enemies in fiery explosions.
Author