Day 40 — Improvements: new enemy and negative powerup

Connor Fullarton
4 min readApr 24, 2021

Hey and welcome!

Things are a bit too easy in the game currently so let’s make things harder by introducing a new enemy type and a powerup that negatively affects the player.

Firstly our enemy, this enemy will spawn at the top of the screen in the middle and remain stationary there, however it will rotate between a 180 degree range and occasionally fire out 1 continuous long beam that will be difficult to dodge.

Go ahead and create something similar to the above making sure that it’s two separate objects with your beam object parented to the enemy so that it moves along with it. With that in place, prefab the enemy and create a new script to attach to the enemy, I called mine BeamEnemy script.

First let’s add in some movement logic for the enemy in the update method or it’s own:

bool _isRotatingRight;if (_isRotatingRight == true)
{
transform.Rotate(new Vector3(0, 0, 1) * _speed * Time.deltaTime);
if (transform.localEulerAngles.z >= 90.0f && transform.localEulerAngles.z < 100.0f)
{
_isRotatingRight = false;
}
}
if (_isRotatingRight == false)
{
transform.Rotate(new Vector3(0, 0, -1) *_speed * Time.deltaTime);

if (transform.localEulerAngles.z < 270.0f && transform.localEulerAngles.z > 260.0f)
{
_isRotatingRight = true;
}
}

We’ve got a bool here similar to our other enemy but instead of checking which direction it’s moving in we’re checking which direction the enemy is rotating to. In order to rotate our enemy we use transform.Rotate which works in the same way as transform.Translate and is what we used to rotate our asteroid.

Something new we have here though is transform.localEulerAngles which is the rotation in degrees relative to our enemy’s current rotation. This means that whatever value you use with this can’t be more than 360 degrees or less than 0 degrees which is why you’re seeing 270.0f here instead of -90.0f. We’re using these euler angles in order to tell when the enemy has rotated to a certain point and then we reverse the rotation back the other way, overall there’s only 180 degrees available to the enemy, anymore than that and the enemy would be firing the laser off the top of the screen.

When you’re happy with that create a new coroutine which gets called in the start method:

[SerializeField]
private GameObject _beamLaser;
IEnumerator BeamFiringRoutine()
{
while (_canFire == true)
{
yield return new WaitForSeconds(3.0f);
if (_canFire == true)
{
_beamLaser.SetActive(true);
}
yield return new WaitForSeconds(11.0f);
_beamLaser.SetActive(false);
}
}

_beamLaser is used to store the beam object that we parented to our new enemy earlier on. In this coroutine we’re simply setting the status of the object to true and false in set intervals so that it’s not constantly firing at the player. The rest of the code you need can be gotten from the previous enemy script such as the collider logic and the animation and audio. We’ll only need the laser collision since our player will never be able to collide with the new enemy as it never moves from the top of the screen.

Next set the tag of the laser object in the inspector to EnemyLaser so that it will damage the player whenever it connects and you should have something looking like this:

Negative Powerup

Next is a short coverage on implementing a negative powerup, the plan here is that when the player picks it up their speed will be severely reduced and they will be unable to use the thrusters as well to escape.

So let’s create the powerup itself using the usual method that we’re used to and set the spawn rate to the same as a regular powerup, with all that in place let’s head over to the Player script and add in some new code:

private bool _antiSpeedActive = false;public void EngageAntiSpeed()
{
_antiSpeedActive = true;
StartCoroutine(AntiSpeedTimer());
}
IEnumerator AntiSpeedTimer()
{
yield return new WaitForSeconds(5.0f);
_antiSpeedActive = false;
}

First here is our logic for what happens when the powerup is collected which is to set _antiSpeedActive to true and then use a coroutine to make it last for roughly 5 seconds before setting it to false again.

private float _negativeSpeed = 0.4f;if (_collectedSpeed == false && Input.GetKey(KeyCode.LeftShift) == false)
{
if (_antiSpeedActive == true)
{
transform.Translate(direction * (_playerSpeed * _negativeSpeed) * Time.deltaTime);
}
else
{
_thruster.SetActive(false);
transform.Translate(direction * _playerSpeed * Time.deltaTime);
if (_thrusterTotal > 0 && Time.time > _nextThrust)
{
_nextThrust = Time.time + _thrusterDecelerate;
_thrusterTotal -= 1;
_uiManager.UpdateThrusterText(_thrusterTotal);
}
}
}

This is what our if statement for the regular movement looks like now, if _antiSpeedActive is true then we’re applying negative speed to the player and if it’s false we go ahead and do our usual code that we’ve now put inside the else.

Then in our if statement for the thrusters we have it like this:

if (_antiSpeedActive == false)
{
else
{
transform.Translate(direction * (_playerSpeed * _negativeSpeed) * Time.deltaTime);
}
}

Which is pretty much the same but this time the negative speed is inside the else instead of the if.

It’s a bit difficult to see the powerup above as it blends in with the background a bit too well but you can see its effect on the player when collected.

--

--

Connor Fullarton

Hey and welcome! My name is Connor and my goal here is to put out a daily post for a full year about my game development journey.