Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

What is the best way to make an enemy chase a player in a multiplayer game?

Discussion in 'Editor & General Support' started by MinhocaNice, May 25, 2020.

  1. MinhocaNice

    MinhocaNice

    Joined:
    May 3, 2020
    Posts:
    249
    Hello, I have been using Nav Mesh to make the zombies for my FPS game, and I plan on making this game multiplayer. However, the tutorial I consulted for this doesn't take multiplayer into account, so I improvised my own way for this.

    Basically, the enemy has an AI script for it so when a player is within range it pursues them. Simple enough. But, to keep track of that player, I need a reference to it, so in the tutorial I mentioned they made another script called Player Manager and referenced the variable from that script into the Start() method for the AI script. Here is the Player Manager, it is very simple and small:
    Code (CSharp):
    1. public class PlayerManager : MonoBehaviour
    2. {
    3.     #region Singleton
    4.  
    5.     public static PlayerManager Instance;
    6.     void Awake()
    7.     {
    8.         Instance = this;
    9.     }
    10.  
    11.     #endregion
    12.  
    13.     [Header("Main References")]
    14.     public GameObject Player;
    15. }
    16.  
    So, to make sure the Player Manager keeps track of all players in the game, I turned the GameObject Player into a list:
    public GameObject Player;
    ->
    public List<GameObject> PlayerList = new List<GameObject>();


    But if it is a list, then the AI script has to reference a list instead of a variable, and then keep track of all the elements in the list so it can find the actual players. I used for() statements and an integer that kept track of the number of players, so it did everything based on how many players are in the game. Here is the code(this one is big):

    Code (CSharp):
    1.    void Start ()
    2.     {
    3.         InPursuit = false;
    4.  
    5.         Agent = gameObject.GetComponent<NavMeshAgent>();
    6.  
    7.         PlayerNum = PlayerManager.Instance.PlayerList.Capacity;
    8.         Player.Capacity = PlayerNum;
    9.  
    10.         for (int i = 0; i < PlayerNum; i++)
    11.         {
    12.             Player.Insert(i, PlayerManager.Instance.PlayerList[i].transform);
    13.         }
    14.     }
    15.  
    16.     void Update ()
    17.     {
    18.         Search();
    19.     }
    20.  
    21.     void Search ()
    22.     {
    23.         for (int i = 0; i < PlayerNum; i++)
    24.         {
    25.             float DistanceFromPlayer = Vector3.Distance(Player[i].position, transform.position);
    26.  
    27.             if (DistanceFromPlayer <= RawDetection && !InPursuit)
    28.             {
    29.                 ChaseTarget(i);
    30.             }
    31.         }
    32.     }
    33.  
    34.     void ChaseTarget (int TargetNum)
    35.     {
    36.         InPursuit = true;
    37.         Agent.SetDestination(Player[TargetNum].position);
    38.  
    39.         Vector3 Direction = (Player[TargetNum].position - transform.position).normalized;
    40.         Quaternion LookRotation = Quaternion.LookRotation(new Vector3(Direction.x, 0, Direction.z));
    41.         transform.rotation = Quaternion.Slerp(transform.rotation, LookRotation, Time.deltaTime * 5f);
    42.     }
    But I feel like this isn't ideal. The Search method is called every single frame, and for every single player. So if there are 8 players online, that for() statement inside Search() function runs 8 times per frame! FOR EVERY SINGLE ZOMBIE IN THE GAME!! This is going to destroy the game and blow up a computer that is running it.

    What would be the best way to do this? I heard arrays are faster, but I gotta use lists otherwise I can't change their size. I thought about tags but they also seem to be slow, and it would have to call them each frame anyway. Is there a way for the enemy knowing the player is there without cluttering the Update() function?

    Also, I couldn't test this because I didn't implement multiplayer yet, but I am pretty sure the way this script works the enemies will keep cycling between players instead of focusing on a single one. I kind of fixed that by making the InPursuit boolean, but now enemies will always follow the same player, even after it stopped chasing them. Any ideas how to fix this?
     
  2. Nivbot

    Nivbot

    Joined:
    Mar 21, 2015
    Posts:
    65
    First of all, you can change the size of an array. It takes some time (minuscule obviously) but you can just do it when ever someone enters.

    I also don't see how it is going to bog anything down. It's just a simple math calculation done for however many players there are. You've already got the reference to the players through the manager, so you don't have to use any resources for that. You're not doing any big calls. I think you'd be fine with hundreds of zombies searching.
     
    MinhocaNice likes this.
  3. MinhocaNice

    MinhocaNice

    Joined:
    May 3, 2020
    Posts:
    249
    Thanks for the reply. I firstly tried using arrays, but I wasn't able to change their size in the code, as Array.Lenght is read only. I have no idea what crazy witchery would be required to change the size of an array without using the Inspector.

    But wouldn't using tags or some other way of referencing the players be more efficient? I mean I believe not, but I want to make sure I am doing the right way so I don't have to change in the future. I feel like 8 calls for a gameobject in a list is still more than a single comparetag call.
    Well, if not then I guess I got it right the first time? But is my code really that neat right away? It looks super spaghetti to me.
     
  4. Nivbot

    Nivbot

    Joined:
    Mar 21, 2015
    Posts:
    65
    Why would you use tags when you have the players already referenced in your manager? Also, to resize an array you use System.array.resize or something along those lines. I forget off the top of my head
     
  5. MinhocaNice

    MinhocaNice

    Joined:
    May 3, 2020
    Posts:
    249
    Because it is faster and requires less work for the processor? Or maybe not?
    Also, at least for now the only reason I have a player manager is because of the enemy movement script.
     
  6. Nivbot

    Nivbot

    Joined:
    Mar 21, 2015
    Posts:
    65
    Searching for tags would use much more resources. Your manager should be a “Singleton” that has a reference to each player. Basically have the array in the manager. Change it accordingly when a player joins.
    The player is already in memory because it’s in the manager. So, in order to reference it you need only call Manager.Players(number)

    like
    Code (CSharp):
    1. for(int i = 0; i < Manager.Players.length; I++)
    2. {
    3. EnemyLookFor(Manager.Player(i);
    4. }
    that’s all you’d need to do
     
    MinhocaNice likes this.
  7. Nivbot

    Nivbot

    Joined:
    Mar 21, 2015
    Posts:
    65
    not exactly of course but something along those lines
     
    MinhocaNice likes this.