Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Simple But Effective AI Script Explained

Discussion in 'Made With Unity' started by SomeGuy22, Jun 5, 2011.

  1. SomeGuy22

    SomeGuy22

    Joined:
    Jun 3, 2011
    Posts:
    722
    Hello! I've come up with a script for artificial intelligence that could be of some use to anyone who is trying to attempt this type of script.

    There are two scripts: EnemyMood and EnemyTarget. EnemyTarget handles close-range firing and the firing details themselves. EnemyMood determines whether or not to go to cover, fire from cover, or fire from current position. Both scripts go together on the same game object. The object itself needs two children: an empty to spawn shoot particles, and a particle system for hit particles.

    Enemy Target:

    Code (csharp):
    1.  
    2. var fps : Transform; //this is your target
    3. var shootDistanceF = 5.0; //shoot distance forward
    4. var shootDistanceS = 2.0; //shoot distance sideways
    5.  
    6. var fireRate = 1;
    7. var gravityForce = 6.0;
    8.  
    9. var hitParticles : ParticleEmitter; //this will be a child of the game object with this script
    10. var shootParticles : Transform;
    11.  
    12. var attemptShoot = false;
    13. var shootingWall = false;
    14.  
    15. var damage = 9;
    16. var range = 50;
    17. var force = 60;
    18. var reloadTime = 3.0;
    19.  
    20. var bulletsPerClip = 12;
    21.  
    22. var damping = 6.0;
    23.  
    24. var weaponspawn : Transform; //where shoot particles spawn
    25.  
    26. var bulletsLeft : int = 0;
    27.  
    28. private var nextFireTime = 0.0;
    29. public var controller : CharacterController; //the controller that controls your enemy character
    30.  
    31. function Start()
    32. {
    33.     hitParticles.emit = false;
    34.     bulletsLeft = bulletsPerClip;
    35. }
    36. function Update () {
    37.        
    38. //get the distance
    39.     if (fps.transform.localPosition.z - transform.localPosition.z <= shootDistanceF  GetComponent("EnemyMood").Alerted == true || fps.transform.localPosition.x - transform.localPosition.x <= shootDistanceS  GetComponent("EnemyMood").Alerted == true)
    40.     {
    41.     var rotation = Quaternion.LookRotation(fps.position - transform.position);
    42.     transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
    43.     attemptShoot = true;
    44.     }
    45.     else if (fps.transform.localPosition.z - transform.localPosition.z >= shootDistanceF  GetComponent("EnemyMood").Alerted == true)
    46.     {
    47.         attemptShoot = false;
    48.     }
    49.    
    50.    
    51.     if (attemptShoot  !shootingWall || GetComponent("EnemyMood").AttemptCoverFire  !shootingWall)
    52.     {
    53.         EnemyFireStart();
    54.     }
    55.    
    56.     controller.Move( Vector3(0, controller.velocity.y - gravityForce * Time.deltaTime, 0) * Time.deltaTime);
    57.    
    58. }
    59.  
    60. function EnemyFireStart()
    61. {
    62.     if (Time.time - fireRate > nextFireTime)
    63.         nextFireTime = Time.time - Time.deltaTime;
    64.        
    65.        
    66.     while(nextFireTime < Time.time  bulletsLeft != 0) {
    67.             EnemyFire();
    68.             nextFireTime += fireRate;
    69.     }
    70. }
    71. function EnemyFire()
    72. {
    73.     var direction = weaponspawn.transform.TransformDirection(Vector3.forward);
    74.     var hit : RaycastHit;
    75.    
    76.     // Did we hit anything?
    77.     if (Physics.Raycast (transform.position, direction, hit, range)) {
    78.         // Apply a force to the rigidbody we hit
    79.         if (hit.rigidbody)
    80.             hit.rigidbody.AddForceAtPosition(force * direction, hit.point);
    81.            
    82.         if (hit.rigidbody.gameObject.tag == "Wall")
    83.         {
    84.         shootingWall = true;
    85.         }
    86.         else
    87.         {
    88.             shootingWall = false;
    89.         }
    90.        
    91.         // Place the particle system for spawning out of place where we hit the surface!
    92.         // And spawn a couple of particles
    93.         if (hitParticles) {
    94.             hitParticles.transform.position = hit.point;
    95.             hitParticles.transform.rotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
    96.             hitParticles.Emit();
    97.         }
    98.  
    99.         // Send a damage message to the hit object
    100.         hit.collider.SendMessageUpwards("ApplyDamage", damage, SendMessageOptions.DontRequireReceiver);
    101.     }
    102.    
    103.     bulletsLeft--;
    104.  
    105.     Instantiate(shootParticles, weaponspawn.transform.position, weaponspawn.transform.rotation);
    106.    
    107.     if (bulletsLeft == 0)
    108.         Reload();  
    109.  
    110. }
    111.  
    112. function Reload () {
    113.    
    114.     // Wait for reload time first - then add more bullets!
    115.     yield WaitForSeconds(reloadTime);
    116.        
    117.         bulletsLeft = bulletsPerClip;
    118. }
    119.  
    And Enemy Mood:

    Code (csharp):
    1.  
    2.  
    3. var seeDistF = 15.0; //how far can he see forwards?
    4. var seeDistS = 10.0; //how far sideways?
    5. var fps : Transform; //your target
    6.  
    7. var coverFire : Transform; //where will he go to fire from cover?
    8. var cover : Transform; //where will he go for cover? (represented by empties)
    9. var damping = 6.0; //speed that he turns
    10. var stunnedTime = 3.0; //how long is he stunned
    11. var coveryield = 4.0;
    12. var speed = 6.0;
    13. var angle = 70;
    14.  
    15. var CanSee = false;
    16. var Alerted = false;
    17. var InCover = false;
    18. var AttemptCoverFire = false;
    19.  
    20. function Update () {
    21.     var dist = Vector3.Distance(coverFire.transform.position, transform.position);
    22.     if (GetComponent("EnemyTarget").attemptShoot == false  Alerted)
    23.     {
    24.         CanSee = true;
    25.     }
    26.     if (fps.transform.localPosition.z - transform.localPosition.z <= seeDistF || fps.transform.localPosition.x - transform.localPosition.x <= seeDistS  Alerted)
    27.     {
    28.         CanSee = true;
    29.     }
    30.     else if (fps.transform.localPosition.z - transform.localPosition.z >= seeDistF || fps.transform.localPosition.x - transform.localPosition.x >= seeDistS  Alerted)
    31.     {
    32.         CanSee = false;
    33.     }
    34.     if (CanSee  GetComponent("EnemyTarget").attemptShoot == false  Alerted)
    35.     {
    36.         var rotation = Quaternion.LookRotation(fps.position - transform.position);
    37.         transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
    38.         MoveToCover ();
    39.     }
    40.    
    41.     if (InCover)
    42.     {
    43.         GetComponent("EnemyTarget").Reload();
    44.         FireFromCover();
    45.     }
    46.     //when we reload and away from cover
    47.     if (GetComponent("EnemyTarget").bulletsLeft == 0  AttemptCoverFire)
    48.     {
    49.         AttemptCoverFire = false;
    50.         MoveToCover();
    51.     }
    52.     if (dist <= .3)
    53.     {
    54.         AttemptCoverFire = true;
    55.     }
    56.     if((Vector3.Angle(fps.transform.position - transform.position, transform.forward)) < angle  fps.transform.localPosition.z - transform.localPosition.z <= seeDistF)
    57.     {
    58.         Alerted = true;
    59.     }
    60. }
    61.  
    62. function ApplyDamage (damage : float) {
    63.     Alerted = true;
    64. }
    65.  
    66. function MoveToCover ()
    67. {
    68.     yield WaitForSeconds (stunnedTime);
    69.     var direction = cover.transform.position - transform.position;
    70.     direction.y = 0;
    71.         if (direction.magnitude < 0.5) {
    72.             return;
    73.         }
    74.         if (!CanSee)
    75.         {
    76.         transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), damping * Time.deltaTime);
    77.         transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
    78.         }
    79.  
    80.     // Modify speed so we slow down when we are not facing the target
    81.     var forward = transform.TransformDirection(Vector3.forward);
    82.     var speedModifier = Vector3.Dot(forward, direction.normalized);
    83.     speedModifier = Mathf.Clamp01(speedModifier);
    84.  
    85.     // Move the character
    86.     direction = forward * speed * speedModifier;
    87.     GetComponent (CharacterController).SimpleMove(direction);
    88.     InCover = true;
    89. }
    90.  
    91. function FireFromCover ()
    92. {
    93.     yield WaitForSeconds (coveryield);
    94.     var direction = coverFire.transform.position - transform.position;
    95.     direction.y = 0;
    96.         if (direction.magnitude < 0.5) {
    97.             return;
    98.         }
    99.         if (!CanSee)
    100.         {
    101.         transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), damping * Time.deltaTime);
    102.         transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
    103.         }
    104.  
    105.     // Modify speed so we slow down when we are not facing the target
    106.     var forward = transform.TransformDirection(Vector3.forward);
    107.     var speedModifier = Vector3.Dot(forward, direction.normalized);
    108.     speedModifier = Mathf.Clamp01(speedModifier);
    109.  
    110.     // Move the character
    111.     direction = forward * speed * speedModifier;
    112.     GetComponent (CharacterController).SimpleMove(direction);
    113.     InCover = false;
    114. }
    115.  
    The way this works is that he's not alerted until you shoot him or walk in his view. Then he turns to kill you. If you go out of range, he moves to his designated cover area and attempts to shoot from cover. That's why this works best in a linear game. He doesn't take damage, though. That's another script. :)

    P.S. It works with a simple cube with a character controller.
     
  2. ivanzu

    ivanzu

    Joined:
    Nov 25, 2010
    Posts:
    2,065
    Very good thanks
     
  3. EpicTwist

    EpicTwist

    Joined:
    Apr 19, 2011
    Posts:
    83
    I'd like to use something like this, but I'm too new to even understand a particle system.
     
  4. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Just keep at it and unity will "click", its one of those things that doesn't make any sense at all first then suddenly you hit your head on the wall and go "doh" at how simple it is.
     
  5. Pedro Afonso

    Pedro Afonso

    Joined:
    Mar 13, 2011
    Posts:
    240
    wow, thank you!
     
  6. hike1

    hike1

    Joined:
    Sep 6, 2009
    Posts:
    401
    I can't seem to track down Rob productions, he wrote this script and also 'Invasion of the Trivials'

    http://www.ludumdare.com/compo/ludum-dare-23/?action=preview&uid=7405

    Anyhoo, I took the player from 'Trivials' and put it in a new project, then made an enemy with the enemymood and enemytarget.js


    UnassignedReferenceException: The variable hitParticles of 'enemytarget' has not been assigned.
    You probably need to assign the hitParticles variable of the enemytarget script in the inspector.
    enemytarget.Start () (at Assets/Scripts/Mobs/enemytarget.js:35)

    I'm not sure how to stick the 'hit' particles prefab (or any other prefab) onto the script, there's nothing available when I try to load it from the menu,
    I dragged the prefab into the scene, can't drag it onto the Variable. How is this done anyway?

    Thanks, Rob Productions, hope you don't mind me copy-pasting, this is one of the few games I can understand at all.

    http://dl.dropbox.com/u/102638093/invasion_player.unitypackage
    4.2 MB

    You'll need 'Character Controllers' and 'Particles' packages added from Standard Assets
     
  7. nancy30

    nancy30

    Joined:
    Mar 7, 2013
    Posts:
    17
    exceptional work thanks will helpful while development