I’ve written an Enemy State Based AI script which seems to work just fine. My only issue is with my collision with a wall. I’m using a Character Controller on my enemy and moving it using Move from Character Controller. This allows for the OnControllerColliderHit to work.
This seems to be functioning just fine and when the enemy strikes a wall on the x axis it rotates 180 degrees and moves in the opposite direction. However, if it hits a wall on the y or z axis it seems the bounce back and forth.
I have included the code. Please I have been racking my brain for some time now.
/*************************************************************************
* *
* File: Enemy.cs *
* *
* Author: Adam Davis *
* *
* Desc: Enemy script AI for Goobs Cube Adventures *
*************************************************************************/
//
using System;
using UnityEngine;
using System.Collections;
public class EnemyAI : MonoBehaviour {
// variable declaration
public float speed = 1.0F; // the speed at which the player moves.
public float gravity = 20.0F; // how much gravitiational pull is on the player.
private Vector3 moveDirection = Vector3.zero; // this is the vector that stores the players movement information.
CharacterController controller ;
GameObject player;
public GameObject Gun; // used to generate gun object
public Rigidbody projectile; // used to allow various types of projectiles
public float bulletSpeed = 1000f; // speed bullet travels
float myTimer = 0.5f; // used to slow down the rate of fire
public int health;
public int minHealth = 1;
public int maxHealth = 3;
public Rigidbody goldCoins;
public enum ENEMY_STATE{
STATE_WANDER,
STATE_CHASE,
STATE_RETURN
};
public enum STATE_TRIGGERS{
SIGHT_PLAYER,
LOSE_PLAYER,
KILLEDBY_PLAYER
};
public ENEMY_STATE currentState;
public STATE_TRIGGERS currentTrigger = STATE_TRIGGERS.LOSE_PLAYER;
/********* TRIGGER_BEHAVIOR(int currentGenericEnemyAIState, int currentGenericEnemyAITrigger) *******/
/* */
/* DESCRIPTION: Generic Enemy in-game behavior */
/* */
/* PASSED PARAMETERS: */
/* currentGenericEnemyAIState - State the Generic Enemy AI is in */
/* currentGenericEnemyAITrigger - The trigger modifying the Generic Enemy State */
/* */
/* NOTES: */
/* */
/* HISTORY: */
/* 05-22-13 - first cut, atd */
/* */
/****************************************************************************************************/
void TRIGGER_BEHAVIOR(STATE_TRIGGERS currentTrigger)
{
switch(currentTrigger) // Trigger causes state
{
case STATE_TRIGGERS.SIGHT_PLAYER: // Generic Enemy Enters Chase Mode
Debug.Log(currentTrigger.ToString()); // Display it in UI
STATE_BEHAVIOR(ENEMY_STATE.STATE_CHASE); // Call Routine to handle States
break;
case STATE_TRIGGERS.LOSE_PLAYER: // Generic Enemy Loses sight of PLAYER
Debug.Log(currentTrigger.ToString()); // Display it in UI
STATE_BEHAVIOR(ENEMY_STATE.STATE_WANDER); // Call Routine to handle States
break;
case STATE_TRIGGERS.KILLEDBY_PLAYER: // Generic Enemy is killed by PLAYER
Debug.Log(currentTrigger.ToString()); // Display it in UI
STATE_BEHAVIOR(ENEMY_STATE.STATE_RETURN); // Call Routine to handle States
break;
}
}
/********* STATE_BEHAVIOR(int currentGenericEnemyAIState, int currentGenericEnemyAITrigger) *********/
/* */
/* DESCRIPTION: Generic Enemy in-game behavior */
/* */
/* PASSED PARAMETERS: */
/* currentGenericEnemyAIState - State the Generic Enemy AI is in */
/* currentGenericEnemyAITrigger - The trigger modifying the Generic Enemy State */
/* */
/* NOTES: */
/* Handles Actions of Generic Enemy when in individual modes */
/* such as tracking PLAYER, etc. */
/* */
/* HISTORY: */
/* 05-22-13 - first cut, atd */
/* */
/****************************************************************************************************/
void STATE_BEHAVIOR(ENEMY_STATE currentState)
{
switch(currentState)
{
case ENEMY_STATE.STATE_WANDER:
/*********************************************************************************/
/* */
/* Code for Generic Enemy pathing behavior is called from here */
/* */
/*********************************************************************************/
animation.Play("GenericEnemyWalking");
moveDirection = Vector3.forward;
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
//Apply gavity and make the player move
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
break;
case ENEMY_STATE.STATE_CHASE:
/*********************************************************************************/
/* */
/* Code for Generic Enemy pathing behavior to chase PLAYER */
/* goes in here. */
/* */
/*********************************************************************************/
Debug.Log(currentState.ToString()); // Display it in UI
// Look at including x and z leaning
transform.LookAt(player.transform);
// Euler angles are easier to deal with. You could use Quaternions here also
// C# requires you to set the entire rotation variable. You can't set the individual x and z (UnityScript can), so you make a temp Vec3 and set it back
Vector3 eulerAngles = transform.rotation.eulerAngles;
eulerAngles.x = 0;
eulerAngles.z = 0;
// Set the altered rotation back
transform.rotation = Quaternion.Euler(eulerAngles);
// slowing down the fire rate
if(myTimer > 0){
myTimer -=Time.deltaTime;
}else if(myTimer<=0){
animation.Play("GenericEnemyWalkingThrow");
fireGun();
myTimer = 0.5f;
}
break;
case ENEMY_STATE.STATE_RETURN:
/*********************************************************************************/
/* */
/* Code to return Generic Enemy to base goes in here. */
/* */
/*********************************************************************************/
break;
}
}
// function to fire the gun
void fireGun(){
Rigidbody clone; // used to clone bullets
clone = Instantiate(projectile, Gun.transform.position,
transform.localRotation) as Rigidbody; // cloning the bullet object
// direction to propel the bullet;
if((transform.position.x - player.transform.position.x) > 0){
clone.rigidbody.AddForce(Vector3.left * bulletSpeed);
}
else if((transform.position.x - player.transform.position.x) < 0){
clone.rigidbody.AddForce(Vector3.right * bulletSpeed);
}
}
void OnControllerColliderHit ( ControllerColliderHit hit )
{
if (hit.collider.gameObject.tag == "Wall"){
//preventing the player from moving on the x and z axis
Vector3 eulerAngles = transform.rotation.eulerAngles;
eulerAngles.x = 0;
// Set the altered rotation back
transform.rotation = Quaternion.Euler(eulerAngles * -1);
Debug.Log ("HIT WALL - ROTATING!"); // Display it in UI
}
}
void Start(){
// assigning enemy health
health = UnityEngine.Random.Range(minHealth, maxHealth);
controller = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update () {
player = GameObject.Find("Player");
Vector3 sight = player.transform.position - transform.position; // declaring the enemies forward line of sight
sight.y = 0;
float dot = Vector3.Dot(sight, transform.right);
float forwardLineOfSight = 2f;
if (dot < forwardLineOfSight dot > -forwardLineOfSight) // setting conditions for enemy to see player
{
RaycastHit hit; // preparing Raycast
// checking to see if Raycast hit the Player
if (Physics.Raycast(transform.position, (sight).normalized, out hit, forwardLineOfSight))
{
Debug.DrawRay(transform.position, (sight).normalized, Color.green);
if(hit.collider.name == "Player"){
Debug.DrawRay(transform.position, (sight).normalized, Color.red);
currentTrigger = STATE_TRIGGERS.SIGHT_PLAYER;
//seePlayer = true;
}
else{
//transform.renderer.material.color = Color.green;
currentTrigger = STATE_TRIGGERS.LOSE_PLAYER;
}
}
else{
//transform.renderer.material.color = Color.green;
currentTrigger = STATE_TRIGGERS.LOSE_PLAYER;
}
}
TRIGGER_BEHAVIOR(currentTrigger); // run game simulation
}
}