- Home /
Smarter Enemy (take cover(SIMPLE))
Hi again!
As I am trying to get my bullet tracer working... I decided to try something else as I wait for more suggestions. I have an enemy script below that makes the enemy shoot at the player when in range and all that good stuff. But I want more. I want the enemy to take cover but very simply. I want them to take cover when they go into combat (get shot, player comes into range, ect...) I was thinking raycasting the enemy to find the closest object (Mabey with a certain tag) and go and hide behinde it (Mabey an empty gameobject attected to the wall) or go to that empty gameobject. Then, every 2 secs. or whatever they poke out to fire for 2 secs. then hide again and stop firing. The more simpler the better because I would like to make it myself where they go to a different gameobject every 15 secs. I dont want you guys having all the fun! :) Any suggestions, tips, or even scripts will help! Thanks in advance! You guys at unity answers have been awsome to me!
As far as finding a point, make sure that your AI not only is able to find cover spots, but that they're able to find cover spots that the player can't see (imagine them trying to take cover behind something that the player is also behind... would be a bit of a disaster!). So, you have the AI start cycling through considering hiding spots, and then fire a ray from the player to that spot and see if the player can see it or not. As far as the enemy AI peeking out and firing, that sounds a bit more like drudge work and there shouldn't be any tricks to it.
Thank you for the responce! Good idea with the whole taking cover if player isnt there thing. I think all I would need there is to check the distance from the player to the cover and see if its all good... Do you have any other tips on how to start coding this? Ik you want I can post my enemy script here
Answer by Berenger · Jan 28, 2012 at 02:04 AM
Use Physics.OverlapSphere instead of Raycast. Except that, if you have a good working state machine, it shouldn't too difficult.
I see where your getting this idea. But theres one problem...I have no idea on how to even start scripting this. If you could just point me in the right direction...thats all I need! Thanks
Sure. Basically, the function return an array of collider inside a sphere at a given pos and radius. Look the do (http://unity3d.com/support/documentation/ScriptReference/Physics.OverlapSphere.html), or quicly in your enemy's script : Collider[] duckSpots = Physics.OverlapSphere( transform.position, 5 ); and then a GetComponent if there some duck class. Those empty game objects will need a collider though.
Thanks for the responce! This is what im getting so far...
function TakeCover () {
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(duckSpots.position - myTransform.position), rotationSpeed*Time.deltaTime);
//move towards the Cover
myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;
yield WaitForSeconds(2);
FireOneShot();
yield WaitForSeconds(2);
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(duckSpots.position - myTransform.position), rotationSpeed*Time.deltaTime);
//move towards the Cover
myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;
}
any ideas? Thanks!!!
From what I see, the transformation to move towards the cover happens only once every 4 secondes, I doubt the your vector is enough to reach the spot. If you don't have a state machine, you need at least to replace that line by a boolean (like cover = true) and then inside Update() if( cover ) you move towards the spot every frame.
Ohhhh. I see ill give that a go and get back to you. Thanks!
EDIT: Ok this is what I have now...
if(chaseThreshold == 999) { // How close the player needs to be to attack
takecover = true;
} if(takecover == true) { // Go to cover
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(duckSpots.position - myTransform.position), rotationSpeed*Time.deltaTime);
//going to cover
myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;
} All of this is in the update but the enemy still goes to player... Any ideas? BTW thanks you so much for putting up with me.. Im really am a unity noob! :)
Answer by Komak57 · Dec 01, 2013 at 07:31 PM
I'm working on zombie AI at the moment and came across this looking for ways to scan for my different triggers. Not sure what the two have related. Anyways, my AI is based off of behaviors that cycle under different circumstances, rather than solid loop code. The global behavior is always scanning for targets, and general animations. By default, the AI is also in IDLE behavior. If it obtains a target while in this behavior, it jumps to chase behavior to path to the closest target. If it gets close enough, it moves into attack behavior and swipes at the player. If I were to modify this code for a behavior on looking for cover, it would be based on weather or not the AI took much damage while in chase. I would then create empty-objects within the map that are adequate hiding spots. Once this was done, the cover behavior would sphere scan all ducking positions in proximity, match each to the player with some relative formula about the safety of each (something along the lines of distance to travel, player line of sight on cover, etc), sort for best choice and move, then swap to ranged attack behavior or something.
If your maps are designed on-the-go, you would add these locations to the objects (rather than the level) and delete lost locations (due to debris).
[AI.cs] - to be attached to object that needs intelligence
BehaviorManager Behavior = new BehaviorManager();
public List<Transform> targets = new List<Transform>();
// Use this for initialization
void Start () {
// load profiles
Behavior = new BehaviorManager();
Behavior.AddState("Idle", new Idle(Behavior, this));
Behavior.AddState("Chase", new Chase(Behavior, this));
Behavior.AddState("Attack1", new AttackPatern1(Behavior, this));
Behavior.ChangeState("Idle");
}
void Update () {
Behavior.onUpdate();
}
// Loop for passive actions
void LateUpdate() {
Behavior.onLateUpdate();
}
// Loop for important actions
void FixedUpdate() {
Behavior.onFixedUpdate();
}
[BehaviorManager.cs] - manages behavior states
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class BehaviorManager {
Dictionary<string, Behavior> _states = new Dictionary<string, Behavior>();
string _currentStateID;
Behavior _currentState;// = null;
bool Loaded;// = false;
// Use this for initialization
void Start () {
Behavior _currentState = null;
bool Loaded = false;
}
public void onLoad() {
try
{
if (_currentState == null)
return; //nothing to load
if (!Loaded)
{
_currentState.onLoad();
Loaded = true;
}
}
catch (Exception e)
{
Debug.Log("onLoad() Error! " + e.ToString() + ".");
}
}
public void onUpdate()
{
try
{
if (_currentState == null)
return;//nothing to update
_currentState.onUpdate();
}
catch (Exception e)
{
Debug.Log("onUpdate() Error! " + e.ToString() + ".");
}
}
public void onLateUpdate()
{
try
{
if (_currentState == null)
return;//nothing to update
_currentState.onLateUpdate();
}
catch (Exception e)
{
Debug.Log("onLateUpdate() Error! " + e.ToString() + ".");
}
}
public void onFixedUpdate()
{
try
{
if (_currentState == null)
return;//nothing to update
_currentState.onFixedUpdate();
}
catch (Exception e)
{
Debug.Log("onLateUpdate() Error! " + e.ToString() + ".");
}
}
public void AddState(string stateId, Behavior state)
{
//System.Diagnostics.Debug.Assert(Exists(stateId) == false);
if (_states.ContainsKey(stateId)) {
_states.Remove(stateId);
Debug.Log (stateId + " being replaced.");
}
_states.Add(stateId, state);
//Debug.Log (stateId + " added to States");
}
public void ChangeState(string stateId)
{
// prevent same state reload
if (stateId == _currentStateID)
return;
//System.Diagnostics.Debug.Assert(Exists(stateId));
if (!_states.ContainsKey(stateId)) {
Debug.Log (stateId + " state not found.");
return;
}
Loaded = false;
_currentState = _states[stateId];
_currentStateID = stateId;
onLoad();
Debug.Log("Changed to "+stateId);
}
public Behavior current() {
return _currentState;
}
public string GetStateID() {
return _currentStateID;
}
}
[Behavior.cs] - creates an event-like behavior
using UnityEngine;
using System.Collections;
public interface Behavior
{
void onLateUpdate();
void onUpdate();
void onFixedUpdate();
void onLoad();
}
[Idle.cs] - example of Idle Behavior
using UnityEngine;
using System.Collections;
public class Idle : Behavior {
BehaviorManager _state;
ZombieAI_1 parent;
public Idle(BehaviorManager state, ZombieAI_1 parent) {
_state = state;
this.parent = parent;
}
// Use this for initialization
public void onLoad () {
}
public void onLateUpdate()
{
}
// Update is called once per frame
public void onUpdate () {
if (parent.targets.Count > 0) {
_state.ChangeState("Chase");
}
// pick pathing coordinates
// done
}
public void onFixedUpdate() {
}
public void ChangeTarget(Transform trgt) {
}
}
Should be everything you need to shuffle away from solid-logistic coding, and move towards circumstantially event-driven behavior.
Lemme know if you find anything on registerring sounds given a location, I'd like to avoid creating temporary objects whenever I want to play a sound that might alert my AI.
Answer by WASIL_MOHD · Oct 12, 2017 at 02:14 PM
You can use Physics.OverlapSphere as previously said by Berenger, of X units radius, add all the colliders present inside the X unit area, randomly select any colliders(thats how I did it), and set the destination of the enemy to that collider. Somethng like this:
if(state == "hideSpot") { Debug.Log("state is hideSpot" );
var colliders : Collider[] = Physics.OverlapSphere(transform.position, 10f) ;
var colliderLength : int = colliders.Length ;
var range : int = Random.Range(0, colliderLength) ;
var i : int ;
for( i = 0 ; i <= colliderLength ; i++)
{
Debug.Log("colliders of "+ colliders[i]) ;
if((colliders[i].gameObject.tag == "Wall"))
{
if(i == (range))
{
nav.Resume() ;
Debug.Log(colliders[i].gameObject.tag) ;
Debug.Log(" it is near the gameObject"+colliders[i]) ;
nav.destination = colliders[i].gameObject.transform.position ;
state = "hide" ;
}
}
}
}
The project code can be accessed from here(with animations, music, etc): https://github.com/MohammadWasil/Unity-Soldier-AI-With-Animations-.git