Search Unity

Displaying to user what key to press

Discussion in 'Game Design' started by ArachnidAnimal, May 3, 2018.

?

?

  1. 1. Expect user to learn the controls by looking at settings

    0 vote(s)
    0.0%
  2. 2. Tell the user what buttons to press when the action can be performed

    16 vote(s)
    100.0%
  1. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,815
    What is the general approach for when the players needs to press a key to do something?
    The player can go to the game controls settings to see what buttons do.
    For console this is usually displayed on an image of a controller.

    For PC the player can assign a keyboard, mouse, and or controller key to perform an action.
    So there could be 3 different possible inputs to perform an action.

    Should this be shown in-game?

    Or is it acceptable to expect the player to go to the settings to see what the possible actions are and the corresponding buttons?
     
  2. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    If it's something like walking up to an object and pressing an input to interact with it, usually a small icon of the required button would appear over/in front of the object when the player gets within range to interact with it.
     
    ArachnidAnimal likes this.
  3. ShilohGames

    ShilohGames

    Joined:
    Mar 24, 2014
    Posts:
    3,021
    Users love having the option to change settings, but they hate looking at the settings page to figure out what the controls are. These days, users definitely expect to have the game gently teach them how to play during the first level. Not an obvious tutorial level, though.
     
    Socrates and ArachnidAnimal like this.
  4. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    We created a dynamic tutorial system that reads the user configured settings and display correct texts, it could be extended with images too.

     
    Martin_H and ArachnidAnimal like this.
  5. Hyblademin

    Hyblademin

    Joined:
    Oct 14, 2013
    Posts:
    725
    Avoiding any tutorial discussion, I lean toward teaching. If there are minor actions that aren't particularly necessary but are still cool, I sometimes like discovering those in the controls menu, but only if I know most of the controls already. There's no reason it has to be like this though.

    If a game is too complex to be showing button tips all the time, I like when a training mode is included.
     
  6. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I think it depends on who your target market is, how obscure the action is, and how established your control scheme is across other games.

    For example, in many modern FPS games they expect the player to either know or look up the basic movement and fire controls, but for lesser used actions such as taking control of a fixed turret, getting in a vehicle, or opening a parachute you'll typically see the key/button displayed when the action is available.

    Games with more unique controls may have all of their controls taught to the player in a tutorial, as well as games targeting a younger audience. On the other end, more hard core games, like pretty much any grand strategy game from Paradox, just expects you to learn from friends or learn the interface from losing the game several times over your first 30 hours with it.
     
    Doug_B likes this.
  7. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    I agree. It's hard seeing a 'one size fits all' solution here. Plus there is an element of personal preference. My Son hates games that force you though a learning session - he just wants to get on and play regardless.

    For me, I would be happy with a hint icon that (a) is not mandatory, i.e. does not block the game running and (b) appears only the first time this hint type is encountered. As long as this is backed up by either a tutorial demo mode or an instructions page, I think that could be sufficient. But again, that would depend on the overall complexity of the game in question.

    But for complex PC games, I would imagine that the availability somewhere of a keyboard controls sheet would seem a minimum requirement.
     
    ArachnidAnimal likes this.
  8. newjerseyrunner

    newjerseyrunner

    Joined:
    Jul 20, 2017
    Posts:
    966
    I think it depends on how complicated your game is. If you only have one universal "use" button, I would go for forcing them to figure it out once, then they should know from then on. DOOM is a good example. In E1M1, you can not get out of the first room until you figure out that the spacebar opens the door. The door is very obvious, so you'll go right to it, but there is no prompt so you'll either have to figure it out or look it up. This cements it in the mind so you'll never forget later.

    However, if you have a game with lots of buttons doing different things, I'd want a prompt. Halo has example, has a bunch of buttons: flashlight, grenade, reload... so I find it nice to see the "Press E to open door."
     
  9. Martin_H

    Martin_H

    Joined:
    Jul 11, 2015
    Posts:
    4,436
    How is something like that possible with the default input manager? I never could find that in the api. Or do you need a third party solution like rewired for that?
     
  10. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    We use SteamVR it does not use any unity specific stuff at all, on top of that we have our own abstraction for Rift and Vive and in the future other hardware as well.

    Code (CSharp):
    1.         protected abstract HashSet<ButtonInput> GetMappableButtons();
    2.  
    3.         public Button? CheckForPlayerMappableButtonPress(NVRHand[] hands)
    4.         {
    5.             var possibleButtons = GetMappableButtons();
    6.             foreach (var hand in hands)
    7.             {
    8.                 foreach (var input in possibleButtons)
    9.                 {
    10.                     if (VirtualButtons.Contains((int)input.Button))
    11.                     {
    12.                         if (CheckForVirtualButton(hand, input)) return input.Button;
    13.                         continue;
    14.                     }
    15.  
    16.                     if (CheckForSteamVRButton(hand, input)) return input.Button;
    17.                 }
    18.             }
    19.  
    20.             return null;
    21.         }
    GetMappableButtons is a abstract method implemented by Vive and Rift subclasses it returns the possible buttons that the user can map, for Rift it looks like this

    Code (CSharp):
    1.         private static readonly HashSet<ButtonInput> mappableButtons = new HashSet<ButtonInput>(new[] { Button.A, Button.ApplicationMenu, Button.VirtualButtonCenter, Button.VirtualButtonUp, Button.VirtualButtonDown }.Select(btn => new ButtonInput{ Button = btn, Action = ButtonAction.PressDown}));
    2.  
    3.         protected override HashSet<ButtonInput> GetMappableButtons()
    4.         {
    5.             return mappableButtons;
    6.         }
    We also define default configs, here for rift

    Code (CSharp):
    1.         protected override void ApplyDefaultCommandMap(CommandMap commandMap)
    2.         {
    3.             commandMap
    4.                 .Add(Command.BoltRelease, Button.VirtualButtonUp.PressDown())
    5.                 .Add(Command.MagRelease, Button.VirtualButtonDown.PressDown())
    6.                 .Add(Command.ChangeFireMode, Button.VirtualButtonCenter.PressDown())
    7.                 .Add(Command.Calibrate, Button.Trigger.PressDown())
    8.                 .Add(Command.GripItem, Button.Grip.PressDown())
    9.                 .Add(Command.GripWeapon, Button.Grip.PressDown())
    10.                 .Add(Command.GripFrontGrip, Button.Grip.PressDown())
    11.                 .Add(Command.GripInteractable, Button.Trigger.PressDown())
    12.                 .Add(Command.DropItem, Button.Grip.PressDown())
    13.                 .Add(Command.DropWeapon, Button.Grip.PressDown())
    14.                 .Add(Command.DropFrontGrip, Button.Grip.PressDown())
    15.                 .Add(Command.DropInteractable, Button.Grip.PressDown())
    16.                 .Add(Command.PullGrenadeLever, Button.Touchpad.PressDown())
    17.                 .Add(Command.ReleaseAction, Button.Touchpad.IsPressed())
    18.                 .Add(Command.SameHandMagRelease, Button.Touchpad.PressDown())
    19.                 .Add(Command.ShowMenu, Button.ApplicationMenu.PressDown())
    20.                 .Add(Command.DetachAttachment, Button.Grip.PressDown())
    21.                 .Add(Command.StartPhysicalHand, Button.Trigger.PressDown())
    22.                 .Add(Command.StopPhysicalHand, Button.Trigger.PressUp())
    23.                 .Add(Command.Sprint, Button.Touchpad.IsPressed())
    24.                 .Add(Command.ToggleAttachment, Button.Touchpad.PressUp())
    25.                 .Add(Command.ReleaseDrum, Button.VirtualButtonUp.PressDown())
    26.                 .Add(Command.CockHammer, Button.VirtualButtonDown.PressDown())
    27.                 .Add(Command.UncockHammer, Button.VirtualButtonDown.IsPressed());
    28.         }
    Lastly we have a table over mappable commands

    Code (CSharp):
    1. private static readonly Command[] mappableCommands = new[] { Command.BoltRelease, Command.ChangeFireMode, Command.MagRelease, Command.ReleaseAction };
    Now the UI can query the underlying domain and wait for a button to be pressed, we also have something we call commandGrouping, commands that cant share the same button. If the user select the same button the config turns read and he cant save the config.

    upload_2018-5-10_11-34-20.png
    edit: And offcourse the UI isnt hardcoded, the list of command mappings is databound
    Code (CSharp):
    1.         protected virtual void Start()
    2.         {
    3.             EventBus.Instance.Subscribe(this);
    4.  
    5.             var template = GetComponentInChildren<CommandMapping>();
    6.             template.gameObject.SetActive(false);
    7.            
    8.             var invalidColor = GetComponentInParent<MainMenu>().InvalidColor;
    9.  
    10.             maps = mappableCommands
    11.                 .Select(cmd => Instantiate(template).Init(cmd, invalidColor))
    12.                 .ToDictionary(cm => cm.Command, cm => cm);
    13.  
    14.             foreach (var item in maps.Values)
    15.                 item.transform.SetParent(transform, false);
    16.         }
    The missing component now is UI friendly captions both for commands and buttons, for commands we just "Wordify" the enums so Command.BoltRelease becomes Bolt release, you can also completely override the caption if you add the command to a Command > string dictionary. For the buttons we only have a button > string dictionary.

    Code (CSharp):
    1.         private static readonly Dictionary<Button, string> captions = new Dictionary<Button, string>
    2.         {
    3.             { Button.VirtualButtonUp, "Stick up" },
    4.             { Button.VirtualButtonRight, "Stick right" },
    5.             { Button.VirtualButtonDown, "Stick down" },
    6.             { Button.VirtualButtonLeft, "Stick left" },
    7.             { Button.Touchpad, "Stick" },
    8.             { Button.VirtualButtonCenter, "Stick" },
    9.             { Button.A, "A" },
    10.             { Button.ApplicationMenu, "B" },
    11.         };
    So there sadly aint no magic going on that we can call a singel method in some Unity API :D

    Oh then from the Tutorial we do something like this

    Code (CSharp):
    1.     [CreateAssetMenu(menuName = "Tutorial/ReleaseBoltStep")]
    2.     public class ReleaseBoltStep : TutorialStep
    3.     {
    4.         public bool HasBoltRelease;
    5.  
    6.         public override IEnumerator Execute()
    7.         {
    8.             var firearm = Get<Firearm>();
    9.  
    10.             ShowPopup(firearm.Slide.transform, firearm.Slide.Ready || !HasBoltRelease ? string.Format("Grab the slide by {0}, draw the slide and release.", GetInteractionCaption(firearm.Slide.ItemType)) : string.Format("Release the bolt by pressing {0}", GetCommandCaption(Command.BoltRelease)) );
    11.  
    12.             while (firearm.Bullet == null)
    13.                 yield return null;
    14.         }
    15.  
    16.     }
     
  11. Martin_H

    Martin_H

    Joined:
    Jul 11, 2015
    Posts:
    4,436

    Thanks a lot for the detailed response! I was hoping there was something like
    Input.GetPrimaryBindingForButton("Jump")
    in the default API and I'm just too blind to find it. As far as I can tell there is no way to do something like that without also rolling your own custom key rebinding solution like you did.
     
  12. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    For a complex game you might need it anyway? Just the fact that the built int system using Static methods and strings turns me off :D
     
  13. Serinx

    Serinx

    Joined:
    Mar 31, 2014
    Posts:
    788