- Home /
Enemy AI Movement Decision Making
Hi, everyone,
I'm working on an enemy AI movement system for a Unity platformer game that will allow the enemy to make one of three decisions on a constant basis: idle, move to the right, or move to the left. I want the enemy to be able to pick any of these decisions, even if the decision it just picked will be the same as its next decision (i.e. it can choose "move right" twice in a row, or as many more times as it wants). The script below has no errors, but when I test the game it causes my enemy to stutter around. Sometimes it moves right for a split second, then moves to the left, etc. I feel like the inherent logic of my code is somewhat correct, but the way in which it is implemented needs some work. I appreciate any help you can give me.
By the way, if I put the "MakeMovementDecision" function in the "Start" function the enemy moves .07 units right or left, or is seemingly idle, but never makes another decision about movement again.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AIMovement : MonoBehaviour {
// References the enemy's Rigidbody2D component
private Rigidbody2D enemyRigidbody;
// Sets the enemy's movement speed
[SerializeField]
private int movementSpeed;
// Checks if the enemy is moving (to be used with animations)
private bool isMoving;
// The direction in which the enemy will move
private Vector2 directionToMove;
// The random decision (0, 1 or 2) that represents which movement function the enemy will perform
private int decisionValue;
// The time remaining before the enemy chooses which movement function to perform again
private float timeTilNextDecision;
// The random float that will be used to determine for how long the enemy remains idle
private float idleTime;
// The random float that will be used to determine for how long the enemy moves left or right
private float moveTime;
// Use this for initialization
void Start () {
// Accesses the enemy's Rigidbody2D component
enemyRigidbody = GetComponent<Rigidbody2D>();
}
void FixedUpdate () {
MakeMovementDecision();
}
/// <summary>
/// Generates the decision for which type of movement the enemy will perform
/// </summary>
private void MakeMovementDecision ()
{
// Chooses a value upon which the movement decision will be based
decisionValue = Random.Range(0, 3);
switch (decisionValue)
{
// Keeps the enemy standing still
case 0:
Idle();
break;
// Moves the enemy to the right
case 1:
MoveRight();
break;
// Moves the enemy to the left
case 2:
MoveLeft();
break;
}
}
/// <summary>
/// Causes the enemy to stand still with idle animations
/// </summary>
private void Idle ()
{
// Sets the idle stance duration
idleTime = Random.Range(5.0f, 10.0f);
// Calculates the time until the enemy may decide to change its movement
timeTilNextDecision = idleTime - Time.deltaTime;
// Sets the movement bool to false to play the idle animations
isMoving = false;
// Stops the enemy's movement
enemyRigidbody.velocity = Vector2.zero;
// Checks if the enemy should make a decision on its next movement
if (timeTilNextDecision < 0)
{
MakeMovementDecision();
}
}
private void MoveRight()
{
moveTime = Random.Range(2.0f, 5.01f);
timeTilNextDecision = moveTime - Time.deltaTime;
isMoving = true;
directionToMove = Vector2.right;
transform.Translate(directionToMove * (movementSpeed * Time.deltaTime));
if (timeTilNextDecision < 0)
{
MakeMovementDecision();
}
}
private void MoveLeft()
{
moveTime = Random.Range(2.0f, 5.01f);
timeTilNextDecision = moveTime - Time.deltaTime;
isMoving = true;
directionToMove = Vector2.left;
transform.Translate(directionToMove * (movementSpeed * Time.deltaTime));
if (timeTilNextDecision < 0)
{
MakeMovementDecision();
}
}
Answer by TheSOULDev · Sep 25, 2017 at 11:30 PM
You have no loop that reduces your timeTilNextDecision. Also, you need coroutines. I rewrote your script:
public enum AIState
{
idle = 0,
walk_l = 1,
walk_r = 2
}
public class AIMovement : MonoBehaviour
{
public Vector2 idleRange; //Add your constant delays here.
public Vector2 leftRange;
public Vector2 rightRange;
Rigidbody2D enemyRigidbody;
//Add your other stuff here...
void Start()
{
enemyRigidbody = GetComponent<Rigidbody2D>();
DecideMovement();
}
void DecideMovement()
{
StartCoroutine(Execute((AIState)(Random.Range(0, 99998) / 33333)));
}
public IEnumerator Execute(AIState state)
{
if(state == AIState.idle)
{
isMoving = false;
enemyRigidbody.velocity = Vector2.zero;
yield return new WaitForSeconds(Random.Range(idleRange.x, idleRange.y))
}
else
{
Vector2 dir;
float Duration;
isMoving = true;
if(state == AIState.walk_l)
{
dir = Vector2.left;
Duration = Random.Range(leftRange.x, leftRange.y);
}
else if(state == AIState.walk_r)
{
dir = Vector2.right;
Duration = Random.Range(rightRange.x, rightRange.y)
}
while(Duration > 0f)
{
Duration -= Time.deltaTime;
transform.Translate(dir * (movementSpeed * Time.deltaTime));
yield return null;
}
}
DecideMovement();
}
}
Comment if you still have problems.
Why are you doing Random.Range(0, 99998) / 33333))
ins$$anonymous$$d of simply do Random.Range(0, 3)
?
Thanks for the example, it helped me with something similar.
Your answer
Follow this Question
Related Questions
Enemy AI With changing Player 0 Answers
If I duplicate enemies, will their scripts mess each other up? 2 Answers
Unity AI/Enemy won't move towards target 0 Answers
Enemy controller.move script slows down and speeds up. How to get it consistent? 0 Answers
How to Add Knockback Force Based on What Rotation it Came From 2 Answers