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. Dismiss Notice

Cleaner way to ensure only one NPC uses an object at one time?

Discussion in 'Scripting' started by Sendatsu_Yoshimitsu, Jan 11, 2015.

  1. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    A lot of my AI is based on spreading around interactable objects, and putting the animations, status effects, and all other use-related information on the object itself, so all the AI ever has to do is locate an object, pathfind to it, and call its UseMe function.

    The one big thing I need to avoid is having multiple NPCs all clustering around the same chair/table/computer trying to use it at once. What I'm doing right now is giving the base Interactable class a bool called InUse, and when an NPC tries to find something to do, he iterates through the master list of objects in his room, adds each Interactable where InUse = false to some list AvailableItems, and if AvailableItems.Count >0 the NPC will chose a random index of AvailableItems as its desired interactable, move to it, and call its UseMe.

    This is okay, although iterating through the list of every interactable in the room seems potentially wasteful. Where I'm concerned is how I can curate the value of InUse correctly: right now every NPC has a variable MyItem of type Interactable that gets set to the item they want to use, the first line of UseMe sets InUse=true, and when one of the many, many things that could change an NPC's AI state occurs, they set MyItem.InUse=false and move on.

    What I'm wondering is if there's an obvious, better way to approach this problem? My implementation works, but it seems clumsy at several stages, especially when it comes to checking which items are potentially useable, and the need to manually set and unset InUse in every single state change seems very unwieldy, especially since it requires adding a custom variable to the main NPC class for an incredibly specific use case.
     
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,523
    What about using a manager script?

    In OnEnable, the interactable registers itself with the manager. In OnDisable it unregisters itself. The manager keeps two lists: reserved interactables and available interactables.

    When an NPC needs something to do, it asks the manager for a random interactable from its available list. The manager can move this interactable to the reserved list. When the NPC is done, or if it decides to do something else, it tells the manager to move the interactable back to the available list.

    The manager can just grab the first interactable from the available list. When you return an interactable to the available list, insert it at a random index.

    You could move the current user information to the interactable or the manager instead of the NPC, but ultimately it needs to be somewhere, so you're not going to get rid of it entirely.
     
  3. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Oh that's a great idea Tony, thank you! Using a manager is a great idea, but I'm curious, how would you recommend keeping track of which NPC is reserving which object, if I move the user information out of the NPC class? I've been trying to keep NPC data extremely trim as it's one of the only classes that I do lots of search/sort on, but the more I think about it the more it seems worth taking the hit to store the reference on the NPC: if it's on the NPC proper I could hand the manager a reference to it along with the request to re-register it in the active objects, but if all the NPC sends is a reference to itself, I'd be adding an extra search to match NPC with object no matter where that data was actually stored.
     
  4. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    You could use a Dictionary<NPC, Interactable> for the manager's in-use items instead of just a List<Interactable>. Then looking up what item an npc is using will be quick and not require searching through a list.

    http://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx
     
  5. Random_Civilian

    Random_Civilian

    Joined:
    Nov 5, 2014
    Posts:
    55
    Keep in mind, Dictionaries can only have unique keys so if an NPC is able to reserve/use multiple objects, you might want to do Dictionary<NPC, List<Interactable>>
     
  6. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Unique keys is actually really useful, as I want to restrict NPCs to one object at a time. I had completely forgotten about dictionaries, but that was a hugely useful idea- after tinkering I managed to get a manager working that only ever needs a reference to the NPC, thank you for the suggestion!!
     
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Does the interactable/manager really need to know which NPC has an object reserved, or just that it is reserved? Something to consider. You may be able to cut down on dependencies this way.
     
  8. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    That's an interesting point. The interactable/manager don't need the information, but it's still useful to keep around: I chunk the map very aggressively for optimization, and maintaining a record of the relationship makes loading chunks much easier, as it can spawn anybody who's using an interactable by that interactable.