Guys, I’m doing a shmup and I’m trying to make AI of the bosses.
I’m a beginner with game development and I’m using state machine for the first time, but I don’t know if I implemented it right.
There are some sequences that I didn’t know how to do. So I ended up using Coroutines.
For example: after n seconds, the boss enter in weapon2 state, and should:
1 - go to the middle of the screen and idle
2 - wait a second
3 - shoot in different directions
4 - stop firing and wait another second
5 - move again
I’ve managed the flow with Coroutines but it doesn’t seem right. Can you give me ideas on how I could do better?
using System.Collections;
using UnityEngine;
public class Boss1: Enemy
{
[Header("Gun")]
public float g_MinCooldown;
public float g_MaxCooldown;
public int g_ShootsPerBurst;
public float g_TimeBetweenShoots;
float _g_Cooldown;
Transform _gun;
[Header("TeD")]
public float td_MinCooldown;
public float td_MaxCooldown;
public float td_Initial_Delay;
public int td_ShootsPerBurst;
public float td_TimeBetweenShoots;
float _td_Cooldown;
[Header("Bullet Wall")]
public float bw_MinCooldown;
public float bw_MaxCooldown;
public float bw_Initial_Delay;
public int bw_ShootsPerBurst;
public float bw_TimeBetweenShoots;
float _bw_Cooldown;
new void Start()
{
_gun = transform.Find("_gun");
ResetGunCooldown();
ResetTeDCooldown();
ResetBulletWallCooldown();
base.Start();
_sm.SetState(Move);
}
void Move()
{
_g_Cooldown -= Time.deltaTime;
_td_Cooldown -= Time.deltaTime;
_bw_Cooldown -= Time.deltaTime;
if (_g_Cooldown <= 0)
{
ResetGunCooldown();
_sm.SetState(Gun);
}
else if (_td_Cooldown <= 0)
{
ResetTeDCooldown();
_sm.SetState(TeD);
}
else if (_bw_Cooldown <= 0)
{
ResetBulletWallCooldown();
_sm.SetState(BulletWall);
}
}
void Gun()
{
StartCoroutine(Gun_Fire());
_sm.SetState(Move);
}
IEnumerator Gun_Fire()
{
for (int i = 0; i < g_ShootsPerBurst; i++)
{
LookAtPlayer();
var l_direction = _gun.up.normalized;
var laser = Instantiate(Laser, _gun.position, _gun.rotation);
laser.GetComponent<Rigidbody2D>().velocity = l_direction * ProjectileSpeed;
AudioSource.PlayClipAtPoint(projectileSound, Camera.main.transform.position, projectileSoundVolume);
yield return new WaitForSeconds(g_TimeBetweenShoots);
}
}
void TeD()
{
if (!_pathing.IsStopped())
StartCoroutine(TeD_Fire());
}
IEnumerator TeD_Fire()
{
_pathing.Stop(true);
var destination = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.75f));
destination.z = 0f;
while (transform.position != destination)
{
transform.position = Vector3.MoveTowards(transform.position, destination, 2f * Time.deltaTime);
yield return new WaitForEndOfFrame();
}
yield return new WaitForSeconds(td_Initial_Delay);
for (int i = 0; i < td_ShootsPerBurst; i++)
{
LookAtPlayer();
var l_direction = _gun.up.normalized;
var laser = Instantiate(Laser, _gun.position, _gun.rotation);
laser.GetComponent<Rigidbody2D>().velocity = l_direction * ProjectileSpeed;
AudioSource.PlayClipAtPoint(projectileSound, Camera.main.transform.position, projectileSoundVolume);
yield return new WaitForSeconds(td_TimeBetweenShoots);
}
yield return new WaitForSeconds(td_Initial_Delay);
_sm.SetState(Move);
_pathing.Stop(false);
}
void BulletWall()
{
if (!_pathing.IsStopped())
StartCoroutine(BulletWall_Fire());
}
IEnumerator BulletWall_Fire()
{
_pathing.Stop(true);
//prepare Position
var destination = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.75f));
destination.z = 0f;
while (transform.position != destination)
{
transform.position = Vector3.MoveTowards(transform.position, destination, 5f * Time.deltaTime);
yield return new WaitForEndOfFrame();
}
//attack
var turn = false;
var angle = 0;
for (int i = 0; i < bw_ShootsPerBurst; i++)
{
angle = !turn ? Random.Range(-55, -45) : Random.Range(-45, -35);
for (int r = angle; r <= -angle; r += 20)
{
LookAtBottom();
_gun.rotation = _gun.rotation * Quaternion.Euler(0f, 0f, r);
var l_direction = _gun.up.normalized;
var laser = Instantiate(Laser, _gun.position, _gun.rotation);
laser.GetComponent<Rigidbody2D>().velocity = l_direction * ProjectileSpeed;
}
AudioSource.PlayClipAtPoint(projectileSound, Camera.main.transform.position, projectileSoundVolume);
yield return new WaitForSeconds(bw_TimeBetweenShoots);
turn = !turn;
}
_pathing.Stop(false);
_sm.SetState(Move);
}
void LookAtPlayer()
{
if(_player == null) return;
Vector3 diff = _player.transform.position - transform.position;
diff.Normalize();
float rot_z = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
_gun.rotation = Quaternion.Euler(0f, 0f, rot_z - 90);
}
void LookAtBottom()
{
float rot_z = Mathf.Atan2(-1, 0) * Mathf.Rad2Deg;
_gun.rotation = Quaternion.Euler(0f, 0f, rot_z - 90);
}
void ResetGunCooldown()
{
_g_Cooldown = ResetCooldown(g_MinCooldown, g_MaxCooldown);
}
void ResetTeDCooldown()
{
_td_Cooldown = ResetCooldown(td_MinCooldown, td_MaxCooldown);
}
void ResetBulletWallCooldown()
{
_bw_Cooldown = ResetCooldown(bw_MinCooldown, bw_MaxCooldown);
}
float ResetCooldown(float min, float max)
{
return Random.Range(min, max);
}
}