Homing Projectile

Renato Figueiredo
3 min readJan 1, 2024

--

Objective: Learn how to implement a homing projectile style weapon.

So our game has a lot of powerups for utility, but only one weapon powerup, that being our Triple Shot. How about we create a Homing Missile, making it a bit easier for our player to kill enemies for a short duration ?

private enum PowerUpType
{
TripleShot,
Speed,
Shield,
Ammo,
Life,
Special,
Slow,
Homing
}

First we make sure we have our new powerup included in our enum. Now we can select and make changes to each projectile with their typing.

Now we have to make sure our Player has access to it. Lets make the changes in our player script.

[SerializeField] private GameObject _homingMissilePrefab;
private bool _isHomingActive = false;
public void ActivateHomingMissilePowerUp()
{
StartCoroutine(EnableHomingMissileRoutine());
}
private IEnumerator EnableHomingMissileRoutine()
{
_isHomingActive = true;
yield return new WaitForSeconds(5f);
_isHomingActive = false;
}

Now our player has access to our new missile, has a variable for its control, and has both a public method that our Powerup can access, and also a Coroutine to time its duration.

Now we allow our Powerup script to call the method when collected.

private void ActivatePowerUp(Player player)
{
AudioSource.PlayClipAtPoint(_powerUpClip, transform.position);
switch (_type)
{
//other cases
case PowerUpType.Homing:
player.ActivateHomingMissilePowerUp();
break;
default:
break;
}
}

Now we need to make sure our player is going to fire our missile instead of the laser when its active. Lets make that change.

private void FireHomingMissile()
{
_canFire = Time.time + _fireDelay;
_laserOffset.Set(transform.position.x, transform.position.y + _laserOffsetY, 0);
_ = Instantiate(_homingMissilePrefab, transform.position, Quaternion.identity, SpawnManager.Instance.PlayerLaserContainer.transform);
}

Now we just make the changes into our player update, to call this new method when we have our Homing Missile powerup activated.

private void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && _canFire <= Time.time)
{
if(_ammoCount > 0)
{
_ammoCount--;
UIManager.Instance.UpdateAmmoAmount(_ammoCount);
if (_isHomingActive)
FireHomingMissile();
else
FireLaser();
}
}

//rest of the update method
}

Now we just go to our LaserBehavior script, and make sure to implement the changes that are going to allow our missiles to target our enemies.

private GameObject _closetEnemy;
private void CalculateMovement()
{
if (_isPlayerLaser)
{
switch (_type)
{
case ProjectileType.VerticalLaser:
transform.Translate(_laserSpeed * Time.deltaTime * Vector3.up);
break;
case ProjectileType.Homing:
List<GameObject> enemies = SpawnManager.Instance.ActiveEnemies;
float distanceTreshold = 5f;
float closestDistance = 0f;

foreach (GameObject enemy in enemies)
{
Vector3 difference = transform.position - enemy.transform.position;
float distance = difference.magnitude;

if (distance < closestDistance || closestDistance == 0)
{
closestDistance = distance;
_closetEnemy = enemy;
}
}

if (_closetEnemy != null && _closetEnemy.activeSelf && closestDistance < distanceTreshold)
{
Vector3 enemyPosition = _closetEnemy.transform.position;
transform.position = Vector3.MoveTowards(transform.position, enemyPosition, _laserSpeed * Time.deltaTime);
}
else
{
transform.Translate(_laserSpeed * Time.deltaTime * Vector3.up);
}
break;
}
}
}

So, what are we doing that makes our missiles a homing missile ?
First we get a list that contains all enemies, which is held by our SpawnManager.
Then we create two variables, one is going to check if the enemy is within the distance threshold and the other is going to contain the closest distance found.

Then we loop through all enemies in our list. First we calculate the distance from our position to the enemy’s position. Then we calculate the scalar distance, using the magnitude of the difference vector.

Then we check if our distance is lower than our closetDistance, meaning that the enemy is the closest enemy, or if our closestDistance is still zero, which means this is our first enemy being check.
If that is true, we just assign the closestDistance to the distance, and our closestEnemy to that enemy.

Then we break out of our loop, and we are going to check how to move our missile. If our closestEnemy is not null (so we have an enemy assigned), if he’s active and the distance to him is within our threshold, we create a Vector3 with his position, and simply move towards him.

If that is not the case, meaning we either don’t have an enemy, he’s not active or he’s not within our threshold, which means he is farther than our missile is capable of checking, we simply move the missile upwards by default.

Our homing missile in action!

And as you can see, when we fire our missile, if the enemy is close to our missile, our missile locks into our enemy and destroys it!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response