1. Hey! Guest! The 35th GMC Jam will take place between November 28th, 12:00 UTC - December 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

Getting multiple inputs

Discussion in 'Programming' started by inkBot, Apr 6, 2017.

  1. inkBot

    inkBot Member

    Joined:
    Sep 15, 2016
    Posts:
    44
    I'm working on getting the game to recognize a string of inputs in order to do special moves, fighting game style.
    For every input I add to the variable "det_input" which I will later check if there's a viable string there and then execute a script relating to what string it is.

    The trouble I'm having is that my code will add extra characters. I know what is causing it. It's the parts I added that would check if the player presses one direction while another direction is held. This is to detect diagonals.

    So if the player presses down, down+right, right, then the string in det_input would be "236", but it's not. Instead it becomes 2366. It will add 2 first, then 3 and 6 at the same time, and last 6 again.

    Here's my mad mess of a code for this:
    Code:
    //    Detects the input from keyboard and returns a string
    
    //    Attack Buttons
    if keyboard_check_pressed(ord("D")) det_input = det_input + "A";        //    Light Attack
    if keyboard_check_pressed(ord("F")) det_input = det_input + "B";        //    Medium Attack
    if keyboard_check_pressed(ord("G")) det_input = det_input + "C";        //    Heavy Attack
    if keyboard_check_pressed(vk_space) det_input = det_input + "D";        //    Jump
    if keyboard_check_pressed(ord("B")) det_input = det_input + "E";        //    Guard
    if keyboard_check_pressed(ord("H")) det_input = det_input + "F";        //    Pick Up Item/Misc Action
    
    //    Detects when releasing one of two held directions
    if keyboard_check(vk_right) && keyboard_check_released(vk_down) det_input = det_input + "6";
    if keyboard_check(vk_down) && keyboard_check_released(vk_right) det_input = det_input + "2";
    if keyboard_check(vk_right) && keyboard_check_released(vk_up) det_input = det_input + "6";
    if keyboard_check(vk_up) && keyboard_check_released(vk_right) det_input = det_input + "8";
    
    if keyboard_check(vk_left) && keyboard_check_released(vk_down) det_input = det_input + "4";
    if keyboard_check(vk_down) && keyboard_check_released(vk_left) det_input = det_input + "2";
    if keyboard_check(vk_left) && keyboard_check_released(vk_up) det_input = det_input + "4";
    if keyboard_check(vk_up) && keyboard_check_released(vk_left) det_input = det_input + "8";
    
    //    Directionals
    if keyboard_check_pressed(vk_right)
            {
                if keyboard_check(vk_down)
                {
                    det_input = det_input + "3";
                }
               
                if keyboard_check(vk_up)
                {
                    det_input = det_input + "9";
                }
               
                else
                {
                    det_input = det_input + "6";
                }
            }
           
           
    if keyboard_check_pressed(vk_left)
        {
            if keyboard_check(vk_down)
            {
                det_input = det_input + "1";
            }
           
            if keyboard_check(vk_up)
            {
                det_input = det_input + "7";
            }
                       
            else
            {
                det_input = det_input + "4";
            }
        }
    
    
       
    if keyboard_check_pressed(vk_down)
        {
            if keyboard_check(vk_right)
            {
                det_input = det_input + "3";
            }
           
            if keyboard_check(vk_left)
            {
                det_input = det_input + "1";
            }
                   
            else
            {
                det_input = det_input + "2";
            }
        }
    
    
    if keyboard_check_pressed(vk_up)
        {
            if keyboard_check(vk_right)
            {
                det_input = det_input + "9";
            }
           
            if keyboard_check(vk_left)
            {
                det_input = det_input + "7";
            }
                   
            else
            {  
                det_input = det_input + "8";
            }
        }
    
    As I said, I have a fairly good idea of what is causing the issue, but I'm unclear about how to fix it.

    (Edit: There are probably plenty things that I could be doing better with the structure of it all)
     
  2. TheouAegis

    TheouAegis Member

    Joined:
    Jul 3, 2016
    Posts:
    7,007
    Code:
    if keyboard_check_pressed(vk_right)
           {
               if keyboard_check(vk_down)
               {
                   det_input = det_input + "3";
               }
          
               if keyboard_check(vk_up)
               {
                   det_input = det_input + "9";
               }
          
               else
               {
                   det_input = det_input + "6";
               }
           }
    You're missing an else between checking down and checking up.

    Part of the issue is you are performing multiple keyboard checks, any of which could be true at the same time. It is entirely possible (contrary to what I once believed) to have multiple keys pressed and multiple keys released at the same time. So suppose you had left, right and down all pressed at the same time (button masher). Your code registers it as left-down, down-left, right-down, and down-right. So by mashing 3 buttons at the same time, you'd get a four-character string. You need to restrict your code so only one input is considered per step. The easiest way to do that is to put else between ALL of your conditionals.

    Edit: hold on, I think this was only part of your problems.
     
    Last edited: Apr 7, 2017
  3. inkBot

    inkBot Member

    Joined:
    Sep 15, 2016
    Posts:
    44
    Gave that a try and it does work better. You can still mash and get odd strings from it, however.

    Code:
    //    Detects the input from gamepad and returns a string
    
    //    Attack Buttons
    if keyboard_check_pressed(ord("D")) det_input = det_input + "A";        //    Light Attack
    if keyboard_check_pressed(ord("F")) det_input = det_input + "B";        //    Medium Attack
    if keyboard_check_pressed(ord("G")) det_input = det_input + "C";        //    Heavy Attack
    if keyboard_check_pressed(vk_space) det_input = det_input + "D";        //    Jump
    if keyboard_check_pressed(ord("B")) det_input = det_input + "E";        //    Guard
    if keyboard_check_pressed(ord("H")) det_input = det_input + "F";        //    Pick Up Item/Misc Action
    
    //    Detects when releasing one of two held directions
    if keyboard_check(vk_right) && keyboard_check_released(vk_down) det_input = det_input + "6";
    if keyboard_check(vk_down) && keyboard_check_released(vk_right) det_input = det_input + "2";
    if keyboard_check(vk_right) && keyboard_check_released(vk_up) det_input = det_input + "6";
    if keyboard_check(vk_up) && keyboard_check_released(vk_right) det_input = det_input + "8";
    
    if keyboard_check(vk_left) && keyboard_check_released(vk_down) det_input = det_input + "4";
    if keyboard_check(vk_down) && keyboard_check_released(vk_left) det_input = det_input + "2";
    if keyboard_check(vk_left) && keyboard_check_released(vk_up) det_input = det_input + "4";
    if keyboard_check(vk_up) && keyboard_check_released(vk_left) det_input = det_input + "8";
    
    
    //    Directionals
    if keyboard_check_pressed(vk_right)
        {       
            if keyboard_check(vk_down) det_input = det_input + "3";
                        
            else
            if keyboard_check(vk_up) det_input = det_input + "9";
            
            else           
            if keyboard_check(vk_left) det_input = det_input;
                
            else det_input = det_input + "6";           
        }
            
            
    if keyboard_check_pressed(vk_left)
        {
            if keyboard_check(vk_down) det_input = det_input + "1";
            
            else               
            if keyboard_check(vk_up) det_input = det_input + "7";
            
            else               
            if keyboard_check(vk_right)det_input = det_input;
                                
            else det_input = det_input + "4";       
        }
        
    if keyboard_check_pressed(vk_down)
        {
            if keyboard_check(vk_right) det_input = det_input + "3";
            
            else               
            if keyboard_check(vk_left) det_input = det_input + "1";
            
            else               
            if keyboard_check(vk_up)det_input = det_input;
            
            else det_input = det_input + "2";
        }
    
    if keyboard_check_pressed(vk_up)
        {
            if keyboard_check(vk_right) det_input = det_input + "9";
            
            else               
            if keyboard_check(vk_left) det_input = det_input + "7";
            
            else               
            if keyboard_check(vk_down)det_input = det_input;
                            
            else det_input = det_input + "8";
        }
    
     
  4. inkBot

    inkBot Member

    Joined:
    Sep 15, 2016
    Posts:
    44
    @TheouAegis I was reading the solution you gave in the Street Fighter-Style thread. It's very interesting, and I can see the benefit of it, even if I don't quite fully understand how to use it. (I've never messed with bitwise operations before (if that's what it is, I'm not even sure of that)).

    Is the benefit from that method sizeable compared to what I've been doing in this thread, or is it negligible enough that it doesn't really matter?
     
  5. TheouAegis

    TheouAegis Member

    Joined:
    Jul 3, 2016
    Posts:
    7,007
    Well one issue with it is if you are playing on a keyboard, then mashing is easy. The main issue sounds like it's in how you handle the combo validation. Typically button mashing breaks combos in games. So if button mashing makes combos work with my code, it's probably the rest of the code that doesn't work.

    As for your method, it's slow.

    That's 38 keyboard check subroutine calls. For just 10 keys. I think the code I posted in the Street Fighter thread actually used keyboard_check_pressed(), which would have made my code run 20 keyboard check subroutine calls, but I just did that for simplicity for the guy. Properly done, it should only be one call per key per step. Unless they drastically improved upon it in GMS2, keyboard check subroutine calls are slow. You can get by with generating just 4 variables (or 1 if you don't need to worry about presses and/or releases): det_input_hold, det_input_previous, det_input_press, and det_input_release (to use you variable name). The det_input_hold variable is the only one set based on the keyboard check subroutine. The det_input_previous variable is set to the value of det_input_hold at the start of each Begin Step prior to any keyboard check subroutine calls. Then the value of det_input_press is found by the algorithm det_input_press=det_input_hold | det_input_previous ^ det_input_previous, while the value of det_input_release is found by the algorithm det_input_release=det_input_hold | det_input_previous ^ det_input_hold.

    The additional benefit is you don't need to check if one key is pressed while another is already held and you could even throw everything into a giant switch statement if you really, really wanted.
     
  6. inkBot

    inkBot Member

    Joined:
    Sep 15, 2016
    Posts:
    44
    So, if I'm getting this right, the important factor here is minimizing the number of calls for every input. Whether to use the array/byte method or the string/parsing method to check if a valid move has been made is more a matter of preference, then?

    The idea is to play using an arcade stick, so directional mashing should be nil in that circumstance. But not everyone has one of those, so gamepad and keyboard need to be on the table as well. I was just using keyboard initially to test things.

    Considering multiplayer, the issue of amount of calls adds to itself. 2 to maybe 4 players in multiplayer.
     
  7. TheouAegis

    TheouAegis Member

    Joined:
    Jul 3, 2016
    Posts:
    7,007
    Well, working with strings is relatively slower, but it's not too important. So pretty much, yeah the important part is reducing the input calls. even just one keyboard_check() in the step event is slower than using the keyboard events (I've tested this); so when you have 20, 30 or even 40 keyboard checks in a single step event, things start to slow down quickly.

    As for the multiplayer aspect, are you doing multiplayer on a single machine or across the internet? It wouldn't matter as much across the internet because inputs are only handled on the machine, so player 1 would have his inputs, player 2 would have his inputs handled separately, player 3 would have his handled separately, and likewise with player 4. It's only when all 4 players will be handling inputs on the same machine that it would be THAT crippling.

    Also, keep in mind that at least for the keyboard inputs you can use keyboard_set_map() and then just define the default keys in your game. When the player wants to customize the keyboard controls, you just set the map of the key the player wants to use to the default key for that function. But this only works for the keyboard.
     
  8. inkBot

    inkBot Member

    Joined:
    Sep 15, 2016
    Posts:
    44
    To begin with multiplayer is local only. whether the max is 3 or 4 players will have to be determined through testing. To do networking, I'm going to have to get some serious help with that, but that's a bridge to cross at a later time.

    I was thinking that ultimately having inputs be stored as a variable (ex: p1_input_left = vk_left and so on) so that you could set it to either keyboard or gamepad controls (or a mix, if you're insane). From what I gather, there's an issue there with detecting the inputs from a gamepad.
     
  9. titusthreex

    titusthreex Guest

    I don't see any reason where check_released functions are necessary in this case, specially when you're only trying to achieve are circular motions.

    You can simplify a quarter circle motion by doing the next code.
    Code:
        if(keyboard_check(vk_down))
        {
            if(keyboard_check(vk_right)) det_input = det_input + "3";
            else det_input = det_input + "2";
        }
        else if(keyboard_check(vk_right))
        {
            det_input = det_input + "6";
        }
    
    One thing I noticed about your code, is that the timing is very strict. Say, a player holds the down+right keys longer, then the sequence is off. I think Input system should be a bit relax to accommodate players who are not familiar with the piano input, but not too relaxed that a player will not try to improve . The next code provides a timer that gives the player enough time between key inputs, before the var det_input gets reset.
    Code:
    Create Event: and Alarm[0] Event :
    det_input=" ";
    last_input=" ";
    this_input=" ";
    motion=" ";
    
    Step Event:
     if(keyboard_check(vk_down))
        {
            if(keyboard_check(vk_right)) this_input="3";
            else this_input="2";
        }
        else if(keyboard_check(vk_right))
        {
            this_input="6";
        }
    
    // this wont work in dash (double tap right/left) motion, so use different variables and filtering process
    if(last_input!=this_input)
    {
        det_input=det_input+this_input;
        last_input=this_input
        this_input=" ";
        alarm[0]=20; // reset det_input after 20 cycles means user doesn't want to continue with the combo
    }
    
    if(string_count("236", det_input)>0) motion="quarter_circle";
    
    
    Also when you are customizing or assigning keys, be aware of keyboard hardware limitations. Some keyboards won't allow you to press certain keys/characters at the same time.
     
    Last edited by a moderator: Apr 29, 2017
  10. inkBot

    inkBot Member

    Joined:
    Sep 15, 2016
    Posts:
    44
    @titusthreex My intent with the released checks was to avoid getting double inputs from going from say down to down+forward to forward. Obviously it didn't really work like I wanted it to, and your solution is much more elegant than what I came up with, thanks!

    The idea was originally that input leniency would be added later. I wanted to get the game to recognize the inputs first and foremost and then I would add input leniency by adding filler symbols (like - ) at a steady rate. So when inputting 236 you could have, for example:
    -23-6
    2-3-6-
    --23-6
    and each of these would work, parsing out the dashes. But if there were too many, like: "2---3----6" then the motion would fail.

    I tried your method out in a separate project and it works really well. It only covers 1/4 of the input range, but it does work with both quarter circles and dragon punch motions. This is what I came up with to include motions to the left:

    Code:
    if (gamepad_button_check(4,gp_padd))
        {
            if(gamepad_button_check(4,gp_padr)) this_input="3";
            else if(gamepad_button_check(4,gp_padl)) this_input="1";
            else this_input="2";
        }
        else if(gamepad_button_check(4,gp_padr)) this_input="6";
        else if(gamepad_button_check(4,gp_padl)) this_input="4";
    
    I've changed it to gamepad inputs now, since I got a new fightstick and intend to use that for testing. Eventually I want to replace the gamepad checks with variable checks instead, so that the player can customize to play with either gamepad or keyboard (still haven't figured out how to do that).
    I'm going to take the code above and flip it to cover the other half or the range of inputs. (EDIT: Scratch that, just tried it and simply duplicating the if statement and switching out inputs breaks it. Gonna have to think of some other way to do it)

    There are two things i've noticed with this. First, here is no room for sloppiness with the inputs. If you intend to do a dragon punch (623) but end the motion on 6 (6236), you will get a quarter circle forward (236). (Edit: Now I can't seem to reproduce this. Odd.)
    Second, (and this may not even be an issue in the long run, I'm not sure) there is no limit to how long the input chain can be. You can keep moving the stick/dpad and add to det_input for as long as you want.

    But still, this works really well. Thanks to both of you for helping me with this.
     
    Last edited: May 11, 2017
  11. inkBot

    inkBot Member

    Joined:
    Sep 15, 2016
    Posts:
    44
    Solved it. Since I was already getting 4 and 6 from the original code, duplicating the parts adding 4 and 6 was unnecessary.
    Code:
    if (gamepad_button_check(4,gp_padd))
        {
            if(gamepad_button_check(4,gp_padr)) this_input="3";
            else if(gamepad_button_check(4,gp_padl)) this_input="1";
            else this_input="2";
        }
        else if(gamepad_button_check(4,gp_padr)) this_input="6";
        else if(gamepad_button_check(4,gp_padl)) this_input="4";
    
    if (gamepad_button_check(4,gp_padu))
        {
            if(gamepad_button_check(4,gp_padr)) this_input="9";
            else if(gamepad_button_check(4,gp_padl)) this_input="7";
            else this_input="8";
        }
    
    That's a total of 8 checks to account for all input ranges, compared to the 38 checks previously. I find that acceptable.
     
  12. inkBot

    inkBot Member

    Joined:
    Sep 15, 2016
    Posts:
    44
    I came up with a simple solution for limiting how long the det_input string can be:
    Code:
    if string_length(det_input) > 8
        {
         det_input = string_delete(det_input,1,1);
        }
    
    I've started adding the attack buttons to this mix. For now, I'm assuming that at least A and B will be used simultaneously for some moves, so I wrote this:

    Code:
    if (gamepad_button_check_pressed(4,gp_face1) && gamepad_button_check_pressed(4,gp_face4))this_input="EX";
        else if gamepad_button_check_pressed(4,gp_face1)
        {
        this_input="A";
        IsAttacking = true;
        AttackType = "A1";
        state = p1_states.attacking;
        }
     
        else if (gamepad_button_check_pressed(4,gp_face4))this_input="B";
    if (gamepad_button_check_pressed(4,gp_shoulderr))this_input="C";
    
    This is the code for the shoryuken motion:
    Code:
    if ((string_count("623A", det_input))
        xor (string_count("623 A", det_input))
        xor (string_count("6236A", det_input))
        xor (string_count("6236 A", det_input))>0)
     
        {
            motion="Shoryuken";
            IsAttacking = true;
            AttackType = "UP1";
            state = p1_states.attacking;
        }
    
    I've added several xor statements to account for sloppy inputs from the player, and it works. I just don't know if it's an efficient way of doing it.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice