Jump to content
  • Advertisement
Sign in to follow this  
Gykonik

C++ Check for multiple keyboardinputs

This topic is 844 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello,

 

In my game I have a input-manager class, that can check for an keyboard input. Sadly at the moment it only works for one input at the time, but I want it to work for multiple (e.g. press STRG + m, then something will happen)

 

I don't know, what changes I have to do on my existing class, or even if it works...

I would highly appreciate it, if you could say that to me :)

 

Here is my Code:

InputManager.h:

#pragma once

#include <unordered_map>
#include <glm/glm.hpp>

namespace Bengine {

// Input manager stores a key map that maps SDL_Keys to booleans.
// If the value in the key map is true, then the key is pressed.
// Otherwise, it is released.
class InputManager
{
public:
    InputManager();
    ~InputManager();

    void update();

    void pressKey(unsigned int keyID);
    void releaseKey(unsigned int keyID);

    void setMouseCoords(float x, float y);

    /// Returns true if the key is held down
    bool isKeyDown(unsigned int keyID);

    /// Returns true if the key was just pressed
    bool isKeyPressed(unsigned int keyID);

    //getters
    glm::vec2 getMouseCoords() const { return _mouseCoords; }
private:
    /// Returns true if the key is held down
    bool wasKeyDown(unsigned int keyID);

    std::unordered_map<unsigned int, bool> _keyMap;
    std::unordered_map<unsigned int, bool> _previousKeyMap;
    glm::vec2 _mouseCoords;
};

}

InputManager.cpp:

#include "InputManager.h"

namespace Bengine {

InputManager::InputManager() : _mouseCoords(0.0f)
{
}


InputManager::~InputManager()
{
}

void InputManager::update() {
    // Loop through _keyMap using a for each loop, and copy it over to _previousKeyMap
    for (auto& it : _keyMap) {
        _previousKeyMap[it.first] = it.second;
    }
}

void InputManager::pressKey(unsigned int keyID) {
    // Here we are treating _keyMap as an associative array.
    // if keyID doesn't already exist in _keyMap, it will get added
    _keyMap[keyID] = true;
}

void InputManager::releaseKey(unsigned int keyID) {
    _keyMap[keyID] = false;
}

void InputManager::setMouseCoords(float x, float y) {
    _mouseCoords.x = x;
    _mouseCoords.y = y;
}

bool InputManager::isKeyDown(unsigned int keyID) {
    // We dont want to use the associative array approach here
    // because we don't want to create a key if it doesnt exist.
    // So we do it manually
    auto it = _keyMap.find(keyID);
    if (it != _keyMap.end()) {
        // Found the key
        return it->second;
    } else {
        // Didn't find the key
        return false;
    }
}

bool InputManager::isKeyPressed(unsigned int keyID) {
    // Check if it is pressed this frame, and wasn't pressed last frame
    if (isKeyDown(keyID) == true && wasKeyDown(keyID) == false) {
        return true;
    }
    return false;
}

bool InputManager::wasKeyDown(unsigned int keyID) {
    // We dont want to use the associative array approach here
    // because we don't want to create a key if it doesnt exist.
    // So we do it manually
    auto it = _previousKeyMap.find(keyID);
    if (it != _previousKeyMap.end()) {
        // Found the key
        return it->second;
    } else {
        // Didn't find the key
        return false;
    }
}

}

Share this post


Link to post
Share on other sites
Advertisement

You shouldn't have to make any changes.

A keyboard is a stateful thing. The operating system will only generate events when the state of a key has actually changed. So it's boolean in nature.

That being said. You simply check for each key's state individually.

Assuming that Cntrl and K are predefined constants....

 

InputManager KeyboardState = new InputManager;

if (KeyboardState.isKeyDown(Cntrl) && KeyboardState.isKeyDown(K)) {
    //Do some code
}

This type of checking is done via polling. Now... to reduce the amount of times you need to make calls to the OS. You can request data for all the important keys ahead of time for game controls. SDL does something like this.

 

Keep in mind that if checks are procedural. When it comes to controls, you'll need your combinations to be checked for success FIRST, and if one is filled, ignore the rest. There could also be better ways. 

 

If you wish for the user to be able to custom assign keys and combinations. You'll probably end up using a completely different method. How I'd approach this is a dynamically adapting if schema.

So:

First lets define the class

 

// This is very basic, but it theoretically works. Edits are needed for real world use.
Class KeyCombination {
    private:
    int length;
    KeyCheck* StrokesArray;

    public:
    bool PushKeyStroke(const KeyCheck Stroke); //Push stroke to the end of the array
    bool CheckKeys();                          //Checks the Keys to make sure this can be triggered.
    int getLength();                           //What ever completed KeyCombination has the longest length should be triggered.
    
  }

Your structure will look something like so.

const enum KeyState {

KEY_UP
KEY_DOWN

}

struct KeyCheck {
KeyState NeededState
int Key

}

As you can see, you run some simple checks on each struct in the array to make sure that the combination requirements are met. On an unrelated note, you'll need some additional logic to ensure that the correct thing is activtated.

On it's own, a keyed combo that uses Ctrl+k+f will activate:

Cntrl
k
f
Cntrl + k
Cntrl + f
Cuntrl + k + f
f+k

Edited by Tangletail

Share this post


Link to post
Share on other sites

InputManager KeyboardState = new InputManager;

if (KeyboardState.isKeyDown(Cntrl) && KeyboardState.isKeyDown(K)) {
//Do some code
}

This one I tried and I don't know why, but it didn't work...

 

 

EDIT:

Now it works, I had a bad mistake in my code :D

Thanks ^^

Edited by Gykonik

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!