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

Question Preventing multiple of the same Input.GetKey from overlapping?

Discussion in 'Scripting' started by ResonantWilterUni, Sep 23, 2023.

  1. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    I tried to word the title as best as I could so apologies in advance if its confusing. I have the standard Unity Character Controller component that I use and I use the WASD keys to move like normal, with the W key being forward. Just for simplicity I am temporarily using the standard Controller code in the Unity docs.

    However, I have code to be able to climb objects and for me to be able to climb I need to hold down both the W key and Spacebar at the same time. The reason for using both keys is so that after the climbing is done, I get "pushed" forward since I'm holding down the W key.

    But the problem is that since I use W for standard movement, when I climb the wall the game acts like I am trying to walk forward rather than climbing (since I'm also not using a trigger), so I start climbing the wall diagonally rather than just climbing vertically:


    Code (CSharp):
    1. // Inside Update() and outside of my isGrounded bool
    2. if (Physics.Raycast(transform.position + transform.up * -0.8f, transform.forward, 1f))
    3. {
    4.     if (Physics.Raycast(transform.position + transform.up * .5f, transform.forward, 1f))
    5.     {
    6.         canClimb = true;
    7.     }
    8. }
    9. else
    10. {
    11.     canClimb = false;
    12. }
    13.  
    14. if (canClimb)
    15. {
    16.     if (Input.GetKey(KeyCode.W))
    17.     {
    18.         if (Input.GetKey(KeyCode.Space))
    19.         {
    20.             controller.Move(Vector3.up * Time.deltaTime * climbSpeed);
    21.         }
    22.     }
    23.     else
    24.     {
    25.         canClimb = false;
    26.     }
    27. }
    I have tried putting the two climbing input if statements into the same line like
    if input W && input Space
    and then below them adding an else if statement saying
    else if input W -> canClimb = false;
    but there was no change.
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    You need to separate input and how it's processed, for starters. After which, you can properly direct the flow of logic.

    Naturally, if you're climbing, you don't want to be moving forward. And if you aren't climbing, you're moving forward. It's an either-or (XOR) situation, so you don't want them both to be happening.

    So just, the play
    canClimb
    don't process the logic for moving forwards.
     
    ResonantWilterUni and CodeSmile like this.
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    Exactly! First collect all the input. You have the move Vector3D from WASD and the ClimbAction bool. The move vector axis are x for left/right and z for forward/backward by definition, with y being up/down which is only set by climbing.

    Only if ClimbAction is true, you check if the forward vector z > 0 (W pressed). Then you check if the player can climb. It makes sense to defer this check unless it is also needed for other purposes, for example by providing an input tooltip or making the ladder glow.

    If all of the above is true then you set the forward z axis of the move vector to 0 so there is no forward movement. You also set y to the corresponding up value.

    Note that pressing space to climb a ladder seems rather uncomfortable. Usually you‘d want the player to just approach the ladder and keep moving forward, except that when facing the ladder forward is actually interpreted as moving up.

    Typically at the top of the ladder there is a short transition that is not player controlled to make the stepping off the ladder part seamless. All other cases of unmounting the ladder are simply transitions to the falling state.
     
    ResonantWilterUni likes this.
  4. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Is that short transition simply saying something like
    velocity.y = 2f;
    to kind of push the player up after climbing is done?

    I have been trying to add a short transition myself right before climbing is finished but how do you make the game know that climbing is almost finished and to enter the short transition?

    Edit: Also thanks a lot for reminding me to use the Move Vector rather than focusing on my W input key, I just fixed that particular diagonal movement problem. Also thanks to Spiney for the assistance too
     
    Last edited: Sep 23, 2023
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Well in the ladder example above, you would have set positions that the level designers would set. So, say you were using a state machine, the state for ladder climbing might continually check if it's above the top-most point (or below the bottom most point, for descending, too), and at either point, run the dismounting animation before continuing onto the regular movement state.

    In terms of games with more dynamic climbing elements, such as the recent Zelda games... lots and lots of various checks. Such as a ray cast forward to check the angle of the normal for any terrain in front of the player, along with a raycast down just a bit in front of the player to test how high any terrain is with respect to the player. So, if you find the terrain directly in front of the player is lower than, say, chest height, then that's one condition that could perhaps be met to start the mantling animation. But it goes without saying that a single check usually won't suffice, and you'll be checking for corner cases upon corner cases.

    Needless to say any of this dynamic climbing stuff is seriously advanced territory. Don't be discouraged if you find this about as difficult as it would to climb a sheer wall IRL.

    Nonetheless, to give some pointers, I find it helps to break this into smaller pieces. Input can be it's own component, one that simply polls input and updates some values, exposing them via read-only properties. Data gathered from the surroundings could be it's own component, working same as the input component: running a bunch of checks, updating internal values, and exposing them via properties.

    Then your character controller component can hook into these components and access these values wherever needed.
     
    ResonantWilterUni likes this.
  6. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    I thought I fixed the problem based on the two replies, but unfortunately I only fixed 1/3 of the problem. When I climb the wall I set my move.z to 0f:
    move.z = 0f;
    (inside of my if W & Space) so that my player only climbs vertically, and not diagonally.

    However if I climb the wall from a different angle, since I'm only reducing the z Vector value, I climb diagonally. I'm just not sure how to make the game tell the difference. All I can think of is inside my
    canClimb
    bool, somehow get the players current transform.position? If I climb while facing the green face of the cube below, its perfect and vertical. But when I move to the right of the cube to the red face, that's where I get the diagonal climbing.

    upload_2023-9-23_20-39-13.png

    Also if I rotate the cube by 45 degrees on the y axis, now all 4 sides have diagonal climbing. I believe the fix for this is very easy, it's just a matter of trying to make the game know whether to use
    move.z = 0f;
    or
    move.x = 0f;
    which I cannot figure out.
     
    Last edited: Sep 23, 2023
  7. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Actually I just fixed everything. I spent the last hour or so coming up with different ideas, and then spent some time testing everything I could think of with the code below and it works perfectly. I just added two quick if statements basically saying:

    Code (CSharp):
    1. // if moving even the slightest bit to the left or right when climbing, "freeze" movement from that axis
    2. if (move.z > 0.0001f || move.z < 0.0001f)
    3. {
    4.      move.z = 0f;
    5. }
    And the same for x axis. Not sure if this is a good method of doing it but so far it works on objects that are not rotated diagonally.

    Edit: Solution didn't work sadly
     
    Last edited: Sep 23, 2023