Hello,
I’ve been experimenting with 2D Enemy AI movement, and I got something I’m pretty happy with. However, there are just a lot of checks (whether they are next to something, should jump, etc.) and my code is just very long. I’m worried it might cause problems later on.
I don’t know if simple AI scripts are typically very long, but I was just wondering if there was anything I could simplify down.
Just for background, I used scriptable objects to determine the move sets for the enemies. That way I can make different types of enemies with just one script.
Anyways, here’s the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public List<AIMoveEvent> moveEvents;
string chosenMoveName;
int AIMoveEvent_Number;
public Rigidbody2D rb;
public float health;
public float speed;
public bool isGrounded;
public bool isOnTopOfAnotherEnemy;
bool AIMoveCoroutine_isRunning;
RaycastHit2D hitDown, hitDownLeft, hitDownRight;
RaycastHit2D hitRight;
RaycastHit2D hitLeft;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
StartCoroutine("AIMove_Event");
}
// Update is called once per frame
void Update()
{
GroundedChecks();
hitDown = Physics2D.Raycast(new Vector2(transform.position.x, transform.position.y - 0.7f), Vector2.down, 0.3f);
Debug.DrawRay(new Vector2(transform.position.x, transform.position.y - 0.7f), Vector2.down, Color.green);
hitDownRight = Physics2D.Raycast(new Vector2(transform.position.x + 0.2f, transform.position.y - 0.7f), Vector2.down, 0.3f);
Debug.DrawRay(new Vector2(transform.position.x + 0.2f, transform.position.y - 0.7f), Vector2.down, Color.green);
hitDownLeft = Physics2D.Raycast(new Vector2(transform.position.x - 0.2f, transform.position.y - 0.7f), Vector2.down, 0.3f);
Debug.DrawRay(new Vector2(transform.position.x - 0.2f, transform.position.y - 0.7f), Vector2.down, Color.green);
hitRight = Physics2D.Raycast(new Vector2(transform.position.x, transform.position.y + 0.7f), Vector2.right, 0.5f);
Debug.DrawRay(new Vector2(transform.position.x + 0.7f, transform.position.y), Vector2.right, Color.red);
hitLeft = Physics2D.Raycast(new Vector2(transform.position.x - 0.7f, transform.position.y), Vector2.left, 0.5f);
Debug.DrawRay(new Vector2(transform.position.x - 0.7f, transform.position.y), Vector2.left, Color.red);
AIMoveEvent chosenMove = moveEvents[AIMoveEvent_Number];
chosenMoveName = chosenMove.name;
print(chosenMove);
if(chosenMoveName == "MoveLeft")
{
MoveLeft();
}
if(chosenMoveName == "MoveRight")
{
MoveRight();
}
if(health <= 0)
{
Destroy(gameObject);
}
}
void Jump()
{
if (!AIMoveCoroutine_isRunning)
{
StartCoroutine("AIMove_Event");
}
if (!isOnTopOfAnotherEnemy)
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
rb.AddForce(Vector2.up * 2.5f, ForceMode2D.Impulse);
}
}
void MoveLeft()
{
if (!AIMoveCoroutine_isRunning)
{
StartCoroutine("AIMove_Event");
}
if (hitLeft.collider != null && isGrounded)
{
int choice = Random.Range(1, 3);
if (choice == 1 && !isOnTopOfAnotherEnemy )
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
Jump();
}
if (choice == 2 || isOnTopOfAnotherEnemy)
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
chosenMoveName = "MoveRight";
}
}
else if (hitLeft.collider != null && !isGrounded)
{
chosenMoveName = "MoveRight";
}
else if (hitLeft.collider != null && hitLeft.collider.CompareTag("Enemy"))
{
int choice = Random.Range(1, 3);
if (choice == 1)
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
Jump();
}
if (choice == 2)
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
chosenMoveName = "MoveRight";
}
}
else
{
transform.Translate(Vector2.left * speed * Time.deltaTime);
}
}
void MoveRight()
{
if (!AIMoveCoroutine_isRunning)
{
StartCoroutine("AIMove_Event");
}
if (hitRight.collider != null && isGrounded)
{
int choice = Random.Range(1, 3);
if(choice == 1 && !isOnTopOfAnotherEnemy)
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
Jump();
}
if(choice == 2 || isOnTopOfAnotherEnemy)
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
chosenMoveName = "MoveLeft";
}
}
else if(hitRight.collider != null && !isGrounded)
{
chosenMoveName = "MoveLeft";
}
else if(hitRight.collider != null && hitRight.collider.CompareTag("Enemy"))
{
int choice = Random.Range(1, 3);
if (choice == 1)
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
Jump();
}
if (choice == 2)
{
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
chosenMoveName = "MoveLeft";
}
}
else
{
transform.Translate(Vector2.right * speed * Time.deltaTime);
}
}
IEnumerator AIMove_Event()
{
yield return new WaitForEndOfFrame();
AIMoveCoroutine_isRunning = true;
StopCoroutine("AIMove_Timer");
AIMoveEvent_Number = Random.Range(0, moveEvents.Capacity);
print(AIMoveEvent_Number);
StartCoroutine("AIMove_Timer");
}
IEnumerator AIMove_Timer()
{
yield return new WaitForEndOfFrame();
int timerValue = Random.Range(1, 5);
yield return new WaitForSeconds(timerValue);
StopCoroutine("AIMove_Event");
AIMoveCoroutine_isRunning = false;
StartCoroutine("AIMove_Event");
}
void GroundedChecks()
{
if (hitDown.collider != null)
{
isGrounded = true;
if (!hitDown.collider.gameObject.CompareTag("Enemy"))
{
isOnTopOfAnotherEnemy = false;
}
if (hitDown.collider.gameObject.CompareTag("Enemy"))
{
isOnTopOfAnotherEnemy = true;
}
}
if (hitDownRight.collider != null)
{
isGrounded = true;
if (!hitDownRight.collider.gameObject.CompareTag("Enemy"))
{
isOnTopOfAnotherEnemy = false;
}
if (hitDownRight.collider.gameObject.CompareTag("Enemy"))
{
isOnTopOfAnotherEnemy = true;
}
}
if (hitDownLeft.collider != null)
{
isGrounded = true;
if (!hitDownLeft.collider.gameObject.CompareTag("Enemy"))
{
isOnTopOfAnotherEnemy = false;
}
if (hitDownLeft.collider.gameObject.CompareTag("Enemy"))
{
isOnTopOfAnotherEnemy = true;
}
}
else
{
isGrounded = false;
isOnTopOfAnotherEnemy = false;
}
if (isOnTopOfAnotherEnemy)
{
int choice = Random.Range(1, 3);
if (choice == 1)
{
chosenMoveName = "MoveRight";
}
if (choice == 2)
{
chosenMoveName = "MoveLeft";
}
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.name == "GroundPoundTrigger")
{
health = health - 5;
}
}
}
Thanks in advance for any feedback!