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

Manage input state with chain of responsibility delegates?

Discussion in 'Scripting' started by yamiko, Jun 2, 2015.

  1. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    I am trying to figure out a way to implement a input manager that can be subscribed and unsubscribed to as a chain of responsibility. The subscriptions would have a delegate to handle the input. Each the event would run each link until the input is handled.

    In other words

    The player would be subscribed to the input manager and pass a handle input method that, will move the player with wasd.

    If they press start than the start menu would subscribe to the input manager and pass a similar method. this time wasd navigates the menu. The event should not pass on to the player since it was handled by the start menu.

    Once the menu is closed it unsubscribes to the input manager.

    If the player is over an item the item would subscribe. If they pick it up then it is handled by the item. If they move with wasd. The item cant handle the input so it continues to the player handler which handles the input.

    Hoe can I implement this in C#?
     
  2. Fajlworks

    Fajlworks

    Joined:
    Sep 8, 2014
    Posts:
    344
    Perhaps it would be easier to tackle this problem with FSM, something like Playmaker or your custom solution.

    @Iron-Warrior posted this solution few days ago:
    http://pastebin.com/pQiLmmpq

    And an example:
    http://pastebin.com/WB4sp2p8

    Although, if you prefer your way regardless, you can encapsulate your delegates into classes and put those objects into array. The last object in array will have its event delegate called to all subscribers, so you can push / pop your delegates as you enter / exit various states of your gameplay.
     
  3. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    have a look over this thread about the UnityEvent system brought in with the new UI in 4.6+, it shows how to subscribe/unsubscribe to events via code during runtime.

    http://forum.unity3d.com/threads/unityevent-where-have-you-been-all-my-life.321103/

    some sort of state handling would be good here I think, when you move from "menuManipulation" to "playerManipulation" to "itemManipulation" unsubscribe the existing events and subscribe the new ones.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,201
    The easiest would to have a InputManager interface. Give the class that handles input a list of InputManagers, and send the input to the last element in that list.

    Then the RegisterInputManager class simply adds something to the end of that list. UnRegisterInputManager removes things from that list.

    That'll be pretty robust - whatever asked for input last will get it, and you can unregister the managers without checking if they're currently the one in control.

    You could also let different InputManagers have different priorities, and then use an OrderedQueue.
     
  5. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    I was trying to avoid FSM. My thinking was what I want allows new intractable features and states to be easily added without having to manage states. If a handler cnat handle the input it gets passed to the next one in the chain.

    Another pro is that objects can interact with the player which moves some code out of the player and into the other objects. Bad idea maybe? I am still new to game dev so maybe FSM is the better solution?
     
  6. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    I didnt know about events. Ill read up on it later today. Thanks.
     
  7. dterbeest

    dterbeest

    Joined:
    Mar 23, 2012
    Posts:
    389
    You could have it be an array or a queue of interfaces. Each interface would implement a function returning a bool, if false is returned you don't continue. The trick is in the ordering, but i dont know what needs you have for that
     
  8. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    The typical C# way to let another class subscribe to your output is with an event. (There's also Unity-specific version of events with inspector support.)

    But usually, if two listeners both subscribe to the same event, then they'll both be called when that event is invoked. You're describing a special case where the listeners are called in some priority order, and only until one of the listeners "handles" the event, so you're going to need to do something a bit more complicated.

    So in this case, you probably want to maintain your own collection of delegates so you can explicitly control the order they're called. You'll want a delegate that allows the listener to return a value indicating whether the event was handled, something like:

    Code (CSharp):
    1. public enum Direction { Up, Down, Left, Right }
    2.  
    3. // DirectionKeyListeners should return true if the input was handled and false otherwise
    4. public delegate bool DirectionKeyListener(Direction direction);
    It sounds like you want listeners called in LIFO order (the last-added listener gets the first chance to handle the input), so I'd probably store then in a List<DirectionKeyListener> where newly-added listeners are added to the back. Then, when a key is pressed, you iterate over the list back-to-front, calling each listener in turn until one of them returns true.
     
  9. bloomingdedalus

    bloomingdedalus

    Joined:
    Aug 13, 2012
    Posts:
    139
    You can create pseudoevents with a "Stack" class that will only call the top delegate. That way controls will only be routed to the last subscriber. You will have to keep track of which keys you subscribed, however, and possibly subscribe blank ones if you want to block out controls you're not using. You will then need to "pop" out all the delegates when you're done using that particular state.

    Otherwise you can create a state machine for the input manager which allows each subscriber to select (or be assigned) it's own state designation and then switch the "state" of the input manager according to whichever subscriber needs dominance over the input.

    I would probably do something like this (all-caps represents unknown type or parameters):

    Code (csharp):
    1.  
    2. Dictionary<STATE, Dictionary<KeyCode, KEYCODEDELEGATE>> States =  new Dictionary<STATE, Dictionary<KeyCode, KEYCODEDELEGATE>>();
    3. STATE _state = 0;
    4.  
    5. void Update()
    6. {
    7.   foreach(KeyValuePair<KeyCode, KEYCODEDELEGATE> kvp in States[_state])
    8.    {
    9.      if(Input.GetKey(kvp.key))
    10.      {
    11.        kvp.value(ACTIVATEDELEGATE);
    12.      }
    13.    }
    14. }
    15.  
    16.  
    17.  
     
    Last edited: Jun 2, 2015
  10. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    Initially I was thinking about doing it with a LIFO array but it was giving me a bad feeling that if a case came up where I didn't want to take unsubscribe the last one it would be hard to fix.

    The more I think about this the more I think I should do a state machine as others suggested. I think I was over engineering for a problem that doesn't really exist.