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

Impulse forces on keydown. Is there a better way?

Discussion in 'Scripting' started by yamiko, Jan 4, 2015.

  1. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    I just picked up unity a few weeks ago and started working on a platformer for my first game. For jumping I am adding a impulse force on key down. Since it was on key down I did this in update so the event wouldn't be missed and the physics had no issues.

    I moved on to wall jumping today and where the player jumps off of walls like you could in Mario or many other platformers. Since it was on keydown I did this just like I did jumping but I started noticing inconsistent behavior with the physics. Sometimes the force was not applied, sometimes it stopped midway and other times it worked.

    Moving the jump machinics to fixedupdate fixes the physics issue but then keydown events are not always caught in fixed update so I will have to listen to keydown in update and run the physics in fixed update.

    Assuming I will need to do this for other actions what is the best way to track the state of the keys and pass it to fixed update without creating a ton of variables? or is there a better way to do this?
     
  2. Sykoo

    Sykoo

    Joined:
    Jul 25, 2014
    Posts:
    1,394
    Your best bet would probably be to use a boolean to check how many jumps has been made.
    For example:

    Code (CSharp):
    1. public bool jumpedTwice= false;
    2.  
    3. void Update()
    4. {
    5. if(Input.GetKeyDown(KeyCode.Space) && !jumpedTwice)
    6. {
    7.   //jump
    8. if(Input.GetKeyDown(KeyCode.Space)
    9. {
    10. jumpedTwice = true;
    11. //jumping second time
    12. }
    13. }
    14. }
     
  3. Sykoo

    Sykoo

    Joined:
    Jul 25, 2014
    Posts:
    1,394
    Basically, void Update would probably be the best method to catch Input from user. But you should always try to use some variables in order to prevent it from bugging! Hope it was the right answer to your question!
     
  4. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    That's what I am currently doing.

    I am targeting consoles which will have 12+ buttons. Add that to keydown, key and keyup stats and you have a lot of class properties.

    I am hoping there is a better way to pass state from update to fixed update.
     
  5. ErikElgerot

    ErikElgerot

    Joined:
    Nov 24, 2014
    Posts:
    11
    How about handling the input in update (so nothing is missed) and setting a flag that a jump is triggered, then in fixedupdate read this flag and if it is set, do the jump and clear the flag?
     
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    There is not really a better way. bools don't take up that much memory or code space.
     
    Sykoo and ErikElgerot like this.
  7. Sykoo

    Sykoo

    Joined:
    Jul 25, 2014
    Posts:
    1,394
    Yes, bool's will probably be your best bet! :) Dont be afraid of using them.
     
  8. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    By better I am not talking about memory but about maintainability and readability.

    I feel like 300+ lines of code of setting bool values and checks as overkill. If its the only option then I will move it into it's own method.
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You are correct. Input checking in Unity is not overly pleasant. Not being able to configure input to catch it directly in FixedUpdate is one of the issues that crops up. The other is that axes bindings can not be changed at run time.
     
  10. Sykoo

    Sykoo

    Joined:
    Jul 25, 2014
    Posts:
    1,394
    You can achieve the same effect you do with 5 booleans, by using only 2 booleans - depends on how experienced you are.
     
  11. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    Can you give me an example on how I can map keydown, key, and key up states for 8 buttons with less than 16 bools?

    I can reduce 24 checks down to 16 by having two bools per key. wasDown and isDown.
     
  12. Sykoo

    Sykoo

    Joined:
    Jul 25, 2014
    Posts:
    1,394
    Why are you not using the different key inputs? You dont need "isDown". You can just say if(Input.GetKey(KeyCode.E)) and it will check if you are holding down E button. If you say if(Input.GetKeyDown(...)) then it will check if you just click the button. After using Input.GetKey you can use Input.GetKeyUp. Then by these input methods, you can assign variables inside of them. Basically, 8 buttons = 8 bools mostly - depending on how much you need them.
     
  13. Sykoo

    Sykoo

    Joined:
    Jul 25, 2014
    Posts:
    1,394
    For example, you can say:
    Code (CSharp):
    1. public bool isDown;
    2.  
    3. if(Input.GetKey(KeyCode.E))
    4. {
    5. isDown = true;
    6. }
    7.  
    8. if(Input.GetKeyUp(KeyCode.E))
    9. {
    10. isDown = false;
    11. }
    12.  
     
  14. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    For most situations that will do but in some cases it wont. For example in Mario the fire button only fires on key down but can be used to run if held and he wont fire again until you repress the button.

    Sure I could listen to only the key event but then I have to track if it was the first instance of being true. To me it seems easier and more readable to listen to the two different events of keyDown and key.
     
  15. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    it would be simpler to do isDown = Input.GetKey(KeyCode.E) for the use case you are suggesting. your code makes it impossiable to do something soon as the key is lifted.
    I am asking about cases where you want to do a different actions for each of the states KeyDown, Key and KeyUp. I am currently doing this like so

    Code (CSharp):
    1. public bool wasDown = false;
    2. public bool isDown = false;
    3.  
    4. void Update() {
    5.     wasDown = isDown;
    6.     isDown = Input.GetKey(KeyCode.E);
    7. }
    8.  
    9. void FixedUpdate(){
    10.     if(isDown && !wasDown){
    11.         // key down event
    12.     } else if(isDown && wasDown)  {
    13.         // key event
    14.     } else if(wasDown && !isDown) {
    15.         // key up event
    16.     }
    17. }
    18.  
     
  16. Nubz

    Nubz

    Joined:
    Sep 22, 2012
    Posts:
    553
    Then why are you using getkey instead of GetButton?
     
  17. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    im am prototyping for computers while I learn unity and c#. Find and replace is not hard to do when I need to switch.
     
  18. Nubz

    Nubz

    Joined:
    Sep 22, 2012
    Posts:
    553
    Was just thinking you could set the buttons in the input manager naming them jump walk fire.
     
  19. Sykoo

    Sykoo

    Joined:
    Jul 25, 2014
    Posts:
    1,394
    You can use as much as you want in one single key press, and it's pretty usefull to just have one variable that you assign to do some stuff, while the input itself can take care of some other stuff. Obviosuly it's just another method, but I'd suggest you less variables. What you're doing seems like duplicating stuff to me.
     
  20. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    @Sykoo The whole point of the variables is to capture the input inside of Update, then use it inside of FixedUpdate. As these don't run at the same rate you can't simply set the bool to true of false in Update, this would be exactly the same as checking for input in FixedUpdate. At best you need to set a bool true in Update and false in FixedUpdate. This doesn't allow for sophisticated input detection, and ends up with complicated if statements.

    @yamiko Did you ever consider doing something event based, like the old OnGUI system? Its more complex to code, but could potentially make your code look better and tidy up all of those nasty if statements. Create a struct to store input, build a queue. Enqueue each input during Update and Dequeue it during FixedUpdate?

    Not sure how this approach would run in terms of efficiency. But it would eliminate the need to write tonnes of variables to transfer each input across to FixedUpdate.

    Its also worth checking which inputs actually need to go to FixedUpdate, and which can be handled in Update. In general things like physics based jumping and movement should be in FixedUpdate. But shooting, dialogue, menus, regular movement and any other non physics behaviour doesn't actually need its input translated. I'd be surprised if all 12+ of your buttons needed to be read by FixedUpdate.
     
  21. Juice-Tin

    Juice-Tin

    Joined:
    Jul 22, 2012
    Posts:
    233
    yamiko, use bitwise operators to store multiple values into a single variable!

    Code (CSharp):
    1. //Keys
    2. int keys = 0; //<-- variable that holds what keys are pressed
    3. int A = 1;
    4. int B = 2;
    5. int C = 4; //<-- make as many as you want, numbers need to double
    6. int trigger = 8;
    7. int pause = 16;
    8.  
    9. void Update(){
    10. //Add pressed ones to keys variable
    11. if(getkeydown(A)) keys |= A; // keys |= will add to the variable
    12. if(getkeydown(B)) keys |= B;
    13. if(getkeydown(C)) keys |= C;
    14.  
    15. //Remove buttons from keys variable when released
    16. if(getkeyup(B)) keys ^= B; // keys ^= will remove from the variable
    17. if(getkeyup(C)) keys ^= C;
    18. }
    19.  
    20. void FixedUpdate(){
    21. //Check keys contains button, do action, and remove key since it's a single press
    22. if(keys & A){
    23.      //do action
    24.      //remove from key
    25.      keys ^= A;
    26. }
    27.  
    28. if(keys & B){ // keys & will check if it's in the variable
    29.      //do action
    30. }
    31. if(keys & C){
    32.      //do action
    33. }
    34. }
    35.  
    Note: I just quickly threw this together, I left out some basic syntax stuff out of laziness. ;)
     
    Kiwasi likes this.
  22. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    Great suggestion! I think I will track bit positions with a enum and will play with this idea tonight or this weekend. This should reduce the number of variables by a lot. Ill post my code once I figure it out.
     
  23. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Check out the binding flags property for enums too. Might simplify your code slightly.
     
  24. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    The C# book I am going over didn't go into this when talking about enums. Can you point me to something with more info on this subject?
     
  25. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Sorry, I misremembered the name. The attribute is called flags. Here is a link.

    http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx

    This lets you do bitwise operations directly on enums, as opposed to needing to convert them to ints first.

    (I think, I've never actually used this, just saw it a while ago and thought it would be cool)
     
  26. yamiko

    yamiko

    Joined:
    Dec 11, 2014
    Posts:
    47
    Ok I came up with a solution last night. It can capture ButtonDown, Button and ButtonUp events and will still work if two update calls were made vs one fixedUpdate or vice versa and scales nicely.

    What do you guys think?

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. class Controller {
    6.     private BitArray wasDown;
    7.     private BitArray isDown;
    8.     private List<string> buttonMap;
    9.    
    10.     public Controller(List<string> buttonMap){
    11.         this.buttonMap = buttonMap;
    12.         int numberOfButtons = buttonMap.Count;
    13.         wasDown = new BitArray(numberOfButtons);
    14.         isDown = new BitArray(numberOfButtons);
    15.     }
    16.    
    17.     private int getBitPosition(string button){
    18.         return buttonMap.FindIndex(
    19.             delegate(string obj) {
    20.                 return obj == button;
    21.             }
    22.         );
    23.     }
    24.    
    25.     public void update(){
    26.         for(int i = 0; i < this.buttonMap.Count; i++){
    27.             isDown.Set(i, Input.GetButton(buttonMap[i]));          
    28.         }
    29.     }
    30.    
    31.     public void handled(){
    32.         wasDown = (BitArray)isDown.Clone();
    33.         isDown.SetAll(false);
    34.     }
    35.    
    36.     public bool isButtonDown(string button){
    37.         int position = this.getBitPosition(button);      
    38.         return (this.isDown.Get(position) && !this.wasDown.Get(position));
    39.     }
    40.    
    41.     public bool isButton(string button){
    42.         int position = this.getBitPosition(button);
    43.         return this.isDown.Get(position) && this.wasDown.Get(position);
    44.     }
    45.    
    46.     public bool isButtonUp(string button){
    47.         int position = this.getBitPosition(button);
    48.         return !this.isDown.Get(position) && this.wasDown.Get(position);
    49.     }
    50. }
    It can be implemented like so.

    Code (CSharp):
    1.  
    2. private Controller controller;
    3.  
    4. void Start () {
    5.    controller = new Controller(new List<string> { "Jump", "Fire1" });
    6. }
    7.  
    8. void Update () {
    9.    controller.update();
    10. }
    11.  
    12. void FixedUpdate () {
    13.    // jump
    14.    if ( controller.isButtonDown("Jump")) {
    15.      if(!isWalled || isGrounded){
    16.        Jump();
    17.      } else {
    18.        wallJump();
    19.      }
    20.    }
    21.    controller.handled();
    22. }