Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[C#] Attempting to "Dumb Down" AI, using Invoke() function to delay, only works on first call

Discussion in 'Scripting' started by SwaggyMcChicken, Sep 25, 2015.

  1. SwaggyMcChicken

    SwaggyMcChicken

    Joined:
    Apr 13, 2015
    Posts:
    108
    I'm currently developing a 3D fighter with my friends and we're having the AI act against the player depending on their moves, such as attacking when he isn't blocking, blocking when he's attacking, etc. My first step was to make the AI a little slower, because right now, as soon as the AI gets close he just lets loose on his sword like there's no tomorrow, and he can never check in time if the player is blocking, making him both an effective AI and a fairly stupid one. So, I read up on the "Invoke()" command and I thought this would work fine with my game, so I put it in, and everything still functions the way it would. I put a two second delay, for testing purposes, and the AI follows these rules when first, but after the first go, he does the exact same thing as before. Here's my code

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class AIPlayerController : MonoBehaviour {
    5.    
    6.     //Created on September 07, 2015 by
    7.     //Purpose: Enables the AI to move, rotate, and make decisions
    8.    
    9.     //Edited on September 21, 2015 by
    10.     //Purpose: Overhauled code to simplify it, cleaned up comments, added comments
    11.    
    12.     //Edited on September 23, 2015 by
    13.     //Purpose: Allowing AI to attack, attempted to delay decision making, setup foundation for further additions
    14.    
    15.     //Edited on September 24, 2015 by
    16.     //Purpose: Allowing AI to block, added comments, attempted to delay decision making, adjusted comments to edited sections
    17.    
    18.     //Bugs
    19.     // - 1. AI only has "Delay" at the beginning of the script
    20.     // -- Suspects it has something to do with the Invoke() command
    21.    
    22.     //NOTE "Player" refers to the actual Player and NOT the AI, AI is referred to as either Bot or AI.
    23.    
    24.    
    25.    
    26.    
    27.     //BLOCK 0: Defines any variables/classes used in the script
    28.    
    29.     //Classes Used
    30.     public CharacterController CC;        //Referencings the CharacterController component for movement
    31.     public CharacterAnimator PlayerCA;    //References the CharacterAnimator script to check what animation's Player is using
    32.     public Transform Player;            //References the Transform component of the Player for rotations
    33.    
    34.     //Reguarding Movements
    35.     public float JumpHeight = 10f;        //(Currently Not In Use) Allows player to jump with a certain strength
    36.     public float Speed = 1f;            //Multiplied with the CC.Move() to make movement quicker
    37.     public float Gravity = 1f;            //Simulates gravity so falling occurs
    38.     public Vector3 MoveDirection;        //Direction used in Move() which is decided by the AI
    39.    
    40.     //Reguarding Rotations
    41.     Vector3 Target;                        //Finds the "Target" to use in the LookRotation function
    42.     Quaternion NewRotation;                //New rotation to rotate to
    43.    
    44.     //Reguarding Decision Making
    45.     public bool PromptAttack;            //Bool used to prompt the AICharacterAnimator to attack
    46.     public bool PromptBlock;            //Bool used to prompt the AICharacterAnimator to block
    47.     int MoveX = 0;                        //(Currently Not In Use) Tells the AI when to move side to side
    48.     int MoveY = 0;                        //(Currently Not In Use) Tells the AI when to move up or down, aka jump
    49.     int MoveZ = 0;                        //Tells the AI when to move forwards or backwards
    50.     float PlayerDistance;                //Tells the AI how far away the Player is
    51.    
    52.    
    53.    
    54.    
    55.     void Update(){
    56.        
    57.         //BLOCK 1: This block of code manages the object rotation
    58.  
    59.         //Finds position between Player and self
    60.         Target = Player.position - transform.position;
    61.        
    62.         //Finds Rotation to rotate to using LookRotation, modifies NewRotation to only rotate along the Y-axis
    63.         NewRotation = Quaternion.LookRotation (Target);
    64.         NewRotation.eulerAngles = new Vector3(0,NewRotation.eulerAngles.y,0);
    65.        
    66.         //Sets the rotation
    67.         transform.rotation = NewRotation;
    68.        
    69.         //Invoke meant to delay the AI in decision making
    70.         //Reference the DecideStuff() function for explanation
    71.         Invoke ("AIMain",2f * Time.deltaTime);
    72.        
    73.                        
    74.         //Compiles all data gathered into a Vector3
    75.         MoveDirection = new Vector3(MoveX,MoveY,MoveZ);
    76.         //Makes MoveDirection relative to world cordinates rather than local
    77.         MoveDirection = transform.TransformDirection(MoveDirection);
    78.         //Multiplies the X, Y, and Z of MoveDirection by Speed to give the player more speed
    79.         MoveDirection *= Speed;
    80.        
    81.        
    82.         //Applies Gravity to the MoveDirection variable
    83.         MoveDirection.y -= Gravity;
    84.  
    85.     } //Closes Update
    86.    
    87.    
    88.     void FixedUpdate(){
    89.        
    90.         //BLOCK 3: Puts the compliled data of BLOCK 2 to use and moves player
    91.        
    92.         //Moves Player
    93.         CC.Move(MoveDirection * Time.deltaTime);
    94.  
    95.  
    96.     } //Closes FixedUpdate
    97.  
    98.  
    99.  
    100.    
    101.    
    102.    
    103.    
    104.     //Function meant to act as the centeralized function of all the decision making functions
    105.     void AIMain(){
    106.    
    107.         //BLOCK 2: This block of code manages the decision making of the AI
    108.        
    109.         //Resets Prompt variables to "clear"
    110.         PromptAttack = false;
    111.         PromptBlock = false;
    112.        
    113.         //Invokes AIMove to delay decision making
    114.         Invoke ("AIMove",2f * Time.deltaTime);
    115.    
    116.     } //Closes AIMain function
    117.    
    118.    
    119.    
    120.    
    121.     //Function tests whether or not the player is attacking/blocking and acts accordingly
    122.     void AICombat(){
    123.        
    124.         //If Player is blocking, do not attack
    125.         if(PlayerCA.Blocking == true){
    126.            
    127.             //Sets PromptAttack to true to tell AICharacterAnimator not to attack
    128.             PromptAttack = false;
    129.            
    130.         }
    131.        
    132.         //Checks to see if player is attacking, if true, stops AI and blocks
    133.         else if(PlayerCA.Attacking == true){
    134.            
    135.             //Doesn't allow AI to move
    136.             MoveZ = 0;
    137.            
    138.         }
    139.        
    140.         //If Player isn't blocking, attack
    141.         else{
    142.            
    143.             //Sets PromptAttack to true to tell AICharacterAnimator to attack
    144.             PromptAttack = true;
    145.            
    146.         }
    147.  
    148.     } //Closes AICombat function
    149.    
    150.    
    151.    
    152.    
    153.     //Function adjusts the variables used in deciding what to do based on distance
    154.     void AIMove(){
    155.        
    156.        
    157.         //Calculates distance, stores in a float
    158.         PlayerDistance = Vector3.Distance(transform.position,Player.position);
    159.        
    160.         //Resets MoveZ so AI doesn't continually walk if no conditions are met
    161.         MoveZ = 0;
    162.        
    163.         //If player is too far away to attack, move forward
    164.         if(PlayerDistance > 3){
    165.            
    166.             MoveZ = 1;
    167.            
    168.         }
    169.        
    170.        
    171.         //If player is too close, move away
    172.         else if(PlayerDistance < 2){
    173.            
    174.             MoveZ = -1;
    175.            
    176.         }
    177.        
    178.         //If either conditions aren't met, Invoke the AICombat Function
    179.         else{
    180.            
    181.             //Check the function for comments
    182.             Invoke("AICombat",2f * Time.deltaTime);
    183.            
    184.         }
    185.    
    186.     } //Closes AIMove function
    187.    
    188.    
    189.    
    190. } //Closes Class
    Sorry if the excessive comments get in the way. Any help would be greatly appreciated! This game is only going to be played by a single player, so the AI is fairly important. Lol
     
  2. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You are calling Invoke every frame in update. That means AIMain will get called every frame, making things go crazy.

    You could call invoke from Start instead. Or you could investigate coroutines.

    FSMs are also worth looking into for this type of work.
     
  3. ketura

    ketura

    Joined:
    Oct 2, 2013
    Posts:
    29
    Unrelated, but those "//Edited on September 21, 2015 by" comments make me cringe. Seriously look into using version control instead, such as git or mercurial. All changes will be automatically tracked, and rolling back changes or branching becomes a possibility.
     
    Kiwasi likes this.
  4. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Yup. Having more lines of comments then there are of code is a bad sign. Comments should be reserved for things not said by the code. This script is a very good example of abusing comments.
     
  5. SwaggyMcChicken

    SwaggyMcChicken

    Joined:
    Apr 13, 2015
    Posts:
    108
    I'm only using the Edited By in the top to just keep track of whatever is going on, I'm working on this project with my friends for a competition, it's my first real Unity game, and I'm just starting to use comments. Could you lead me in the right direction, as to how to use them?

    Thanks for your insight.
     
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    On comments in general:

    Assume the reader is a coder too. They can understand what the code does. Comments like 'adds on to the life' really are unneeded.

    Comments are great for saying why code choices were made. But not what the code does. Keeping comments in sync with code is hard work, you don't want to have to do work that doesn't give you any benefit.

    Make your code as self documenting as possible. With auto complete there is no real issue with making method and variable names long and descriptive.

    On version control:

    Use it. You'll love it and never go back
     
  7. Strategos

    Strategos

    Joined:
    Aug 24, 2012
    Posts:
    255
    I agree on the Finite State Machine recommendation.

    Also instead of using an invoke I would add some logic to the AI to allow you to alter it's ability and add some variation.

    So firstly something like "Reaction time" - a value to dictate how fast the AI can act and react. This will allow you to easily up the difficulty.

    You could also add some fuzzy logic by adding some additional variables. Like Desperation, Aggressiveness and defensiveness for example .Then check these values against other factors to decide what the AI will do. So for instance if they have high defensiveness they are more likely to block, but as their health goes down they may become more desperate and start attacking more. By changing the values for each AI you can add a lot of variety to the game play.
     
    Kiwasi likes this.
  8. SwaggyMcChicken

    SwaggyMcChicken

    Joined:
    Apr 13, 2015
    Posts:
    108
    Yeah I was going to add some kind of chance for the AI to mess up in the script, like not block when it's supposed to. But the "Personality" (I guess) variables would be a pretty good idea too. You said that instead of using an Invoke for this, how would I go about implementing this? Could I not just add it to my function's that are being used in Invoke? Thanks for your input.

    Ah, yeah, sorry. Right now, in my group, the other three don't know much programming, especially for Unity. I decided to screw around with Unity a little over the Summer, so I'm much more advanced than they are, I wouldn't want them to get confused. I also looked up something that MIT posted, talking about commenting, and that every 1-4 lines would be a good habit to keep, so I just took that to heart and did that
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    There are as many different ways to comment as there are coders. I personally don't comment much (read at all). But I work mostly on my own.
     
  10. Strategos

    Strategos

    Joined:
    Aug 24, 2012
    Posts:
    255
    Well if you have a Finite State Machine that decides what the AI does you could for instance have a state transition function - this is just off the top of my head. Or a decision function which contain timers that hold the actual state change until reaction time is reached.

    you could do these timers in a coroutine or in your update function depending on your preference.