Smart Enemy

Renato Figueiredo
5 min readDec 26, 2023

Objective: Create a new type of enemy that is facing sideways, and shoots an horizontal laser when sees the player.

Sprite for our new enemy.

Since this enemy will be facing sideways, I’ll be placing his turbos on both sides, to give an idea he is using it to stabilize himself.

Our enemy with both turbos.

The idea will be, he will just drop down slowly, and when our player is behind him horizontally, he will shoot a horizontal laser.

private enum EnemyType
{
Default,
Zigzag,
Aggressive,
Smart
}

We have to update our enemy for enemy types, so we can set the proper type in the inspector.

Now we are going to update our laser. Currently we have our laser being a prefab that we instantiate, and use it. Now we are going to fire it sideways both lasers (different than before). To make this easier to calculate in our movement, we are going to create a new object, and use our lasers as sprites for our object.

Our Horizontal Laser with all its components.

Our lasers are just a visual representation of what our laser is, and our laser script is in our blank object. This makes it easier for us, since our lasers are rotated, which means that their movement would be different than intended.

[SerializeField] private bool _isHorizontalLaser = false;
private int _direction = 1;
private void CalculateMovement()
{
if (_isPlayerLaser)
transform.Translate(_laserSpeed * Time.deltaTime * Vector3.up);
else
{
if (!_isHorizontalLaser)
transform.Translate(_laserSpeed * Time.deltaTime * Vector3.down);
else
{
Vector3 horizontalDirection = Vector3.right * _direction;
transform.Translate(_laserSpeed * Time.deltaTime * horizontalDirection);
}
}
}
public void SetDirection(int direction)
{
_direction = direction;
}

With this updated CalculateMovement method, now we added the option of an enemy Laser being horizontal, which is can be set on the Inspector by our _isHorizontalLaser variable. In case it is, it now just move sideways according to the direction is facing.

We also added a method that can change the direction of our laser, which can be used when they are Instantiated, we can pass the direction we want them to go.

Now we move to our enemy script. Before we know when to fire, we are going to update our FireLaser method, to include the option of instantiating our horizontal laser.

private void FireLaser()
{
if(_type == EnemyType.Aggressive)
_fireRate = Random.Range(1.5f, 3f);
else
_fireRate = Random.Range(3f, 7f);

_canFire = Time.time + _fireRate;
if (_type != EnemyType.Smart)
{
_laserOffset.Set(0f, -1.4f, 0f);
Instantiate(_laserPrefab, transform.position + _laserOffset, Quaternion.identity);
}
else
{
if(_direction > 0)
_laserOffset.Set(1.35f, 0f, 0f);
else
_laserOffset.Set(-1.35f, 0f, 0f);
GameObject laserObj = Instantiate(_laserPrefab, transform.position + _laserOffset, Quaternion.identity);
LaserBehaviour laser = laserObj.GetComponent<LaserBehavior>();
if (laser != null)
laser.SetDirection(_direction);
}
}

So what are we doing in our firing method ? Well, we check the type of enemy that is firing, and when its not smart, we just fire it normally.
When it is our Smart enemy, we change the offset of the laser.
When instantiating our laser, we are assigning it to a variable laserObj then we get the LaserBehavior component from it. After null checking it, we just pass our direction, so the laser moves to the proper direction.

private void RotateEnemy()
{
if(_type == EnemyType.Smart)
{
if (_direction > 0)
{
_direction = 1;
_spriteTransform.rotation = Quaternion.Euler(0f, 180f, 0f);
}
else
{
_direction = -1;
_spriteTransform.rotation = Quaternion.Euler(0f, 0f, 0f);
}
}
}

This rotate method, makes sure the sprite is facing the correct side by using Quaternion.Euler, and we always set the direction, to make sure its the reverse of where we are. Meaning if we are facing left, our direction is positive, so the laser goes right. In case we are facing right, we need it to be negative, so our laser goes left.

Now we have to check if our player is behind our enemy, how would that work ? Lets see the changes we made in our Update method.

private void Update()
{
CalculateMovement();

if (Time.time >= _canFire && _type != EnemyType.Zigzag)
{
if (_type != EnemyType.Smart)
FireLaser();
else
{
Vector2 toPlayer = _player.transform.position - transform.position;
float angle = Vector2.SignedAngle(-transform.right, toPlayer.normalized);
float alignmentThreshold = 5f;
float horizontalAlignment = Mathf.Abs(toPlayer.y) / Mathf.Abs(toPlayer.x);

if (Mathf.Abs(angle) < alignmentThreshold && horizontalAlignment < 1f)
FireLaser();
}
}
}

First we start with toPlayer, which calculates the vector from the enemy to the player. It subtracts the enemy’s position from the player’s position, creating a vector pointing from the enemy to the player.
Then we have our angle which computes the signed angle between the negative right direction of the enemy (-transform.right) and the normalized direction vector to the player (toPlayer.normalized). The negative right direction is used because you want to check if the player is behind the enemy.
Now to our alignmentThreshold, this sets a threshold for the angle. This is how we are going to know if our player is behind the enemy.
Then we have our horizontalAlignment, which calculates the horizontal alignment of our player, by taking the absolute values of the y and x components of toPlayer and dividing them. This represents how vertically aligned the player is relative to the enemy.
And finally we have our if statement. Our if statement checks if the absolute value of our angle is lower than our alignmentThreshold, and also if our horizontalAlignment is less than one.

What does that mean ? Well, if the absolute value of our angle is lower than our alignmentThreshold, it means that our player is behind our enemy.
Then we check if our horizontalAlignment is less than one, meaning that the player is vertically aligned with the enemy and behind it. So our enemy will only fire the gun, if his gun is capable of hitting our player.

We can see that our enemy only fires his gun, when the player is behind him, and aligned with him.

Now with these changes, we now have an enemy that can annoy our player in a sideways manner, meaning that it will go down, and if the player doesn’t pay attention to it, it will fire a horizontal laser against 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