Search Unity

Problems with input manager + new UI

Discussion in 'Scripting' started by Valasty, May 27, 2015.

  1. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    Hello guys!

    So, I'm having a problem I can solve, but not quickly/easily, and the quick solution is what I'm looking for.

    I'm building a JRPG 2D style battle, and I'm trying to put all input solely on keyboard, so I expected to press Z on Attack action, the target should appear over enemies, and I should be able to select a target by pressing Z again.
    Problem is, I don't get a chance to select the target, seems like Z is still being recognized after input, selecting automatically the first target.

    The scripting is basically this:

    Code (CSharp):
    1. public void AttackButtonPress(){ //Attack button has it on On Click () as Runtime only
    2.                 selectingTarget = true;
    3.             }
    4.  
    5.             void Update(){
    6.                 if (selectingTarget){
    7.                     if (Input.GetKeyDown(KeyCode.Z)){
    8.                         //target select confirmation
    9.                     }
    10.                 }
    11.             }
    And this is how my game and input manager looks like (sample graphics, please don't judge me :p):



    I've already tried tweeking several fields, but nothing seems to resolve the issue. I can think about a few solutions:
    1) Writting my own input controller (to replace Unity's input manager).
    2) Writting my own standalone input module (not sure if this is same as #1).
    3) Adding a small delay after attack button press before enabling the target select.
    4) Changing Update to FixedUpdate (LateUpdate also doesn't work).

    Solution #1 and #2 would take a lot of time to do, so this is only last resort.
    Solution #3 would be by much easier (30 secs coding), but doesn't seem very pretty.
    Solution #4 works, but I rather not use it as I've already built the entire battle system on top of Update.

    Is there an alternate solution inside the EventSystem/InputManager itself without having to use one of the solutions above? Some specific field to tweek and make inputs last less frames? I could work with solution #4 depending on your feedback, I'm just not very confident on my knowlege of Fixed Update x Update.

    Thank you advance!
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Input.GetKeyDown returns true if you're within the first frame where the Z button was pressed. Since your UI button calls the function immediately (still in the first frame), so Input.GetButtonDown is still returning true. It seems like you've figured this out already. Even though it's not pretty, what I would do in this situation is your solution #3: just wait a frame before setting selectingTarget to true. Or something like this, which (as it happens) is designed to be dropped in as a direct replacement for your current selectingTarget member, and will only return true the next frame:
    Code (csharp):
    1.  
    2. bool selectingTarget {
    3. get {
    4. if (selectingTargetAfter == -1f) return false;
    5. return Time.time > selectingTargetAfter;
    6. }
    7. set {
    8. if (value) selectingTargetAfter = Time.time;
    9. else selectingTargetAfter = -1f;
    10. }
    11. }
    12. float selectingTargetAfter = -1f;
    13.  
     
  3. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    I see, thank you for the input.
    Just for curiosity, what about the FixedUpdate solution? Is that viable?
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    I wouldn't use FixedUpdate. FU runs an unreliable number of times for each normal frame - it might run once, it might run 12 times, it usually runs once or twice, but it could even run zero times. So using FixedUpdate would give you unpredictable result (and could introduce unreproducible bugs). Using input in FixedUpdate is a bad idea is any case, since Input.GetButtonDown et al can return true for multiple FixedUpdate frames in a row (they are only updated between visual frames).

    I actually have been thinking and there's another option that might be less ugly - a global "action button lock-out" timer (say, 0.1 or 0.05 seconds). Every time you press the action button, it locks out for that amount of time. Every single function that is triggered by the action button both checks against this timer before executing, and sets the timer to Time.time + interval. It's "less ugly" than the current proposed solution, while doing more or less the same thing. And it also solves this sort of problem for the remainder of your project, as well, without having to re-implement it later on.
     
  5. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    It's actually what I wanted to do in input manager, but I don't think this is possible without messing with it's script. I will definitely implement this solution, thank you sir!
     
  6. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    Just to update, the solution was harder than I thought.

    I tried implementing a global cooldown for any key press, but that didn't work very well, since some inputs are controlled by the script, and some others from EventsSystems of the UI. Here's what I end up implementing, and worked very well:

    Code (CSharp):
    1. public void AttackButtonPress(){ //Attack button has it on On Click () as Runtime only
    2.             inputCooldown = true;
    3.             selectingTarget = true;
    4.         }
    5.    
    6.         void Update(){
    7.             if (selectingTarget){
    8.                 if (Input.GetKeyDown(KeyCode.Z) && KeyCooldownChecker()){
    9.                     //target select confirmation
    10.                 }
    11.             }
    12.         }
    13.  
    14.         bool KeyCooldownChecker(){
    15.  
    16.             if (inputCooldown){
    17.                 inputCooldown = false;
    18.                 return false;
    19.             }
    20.             else
    21.                 return true;      
    22.         }
    For some reason, the Z press to confirm UI selection is also recognized by the target select even before selectingTarget becomes true (not sure why...), so I added a second check to it. In the first check it won't be recognized, but inputCooldown will become false and allow it to work in a second check.

    Thank you Star for the support :)