top of page
  • Writer's pictureDan Hatch

IS IT HARD TO WRITE A GAME ENGINE? (PT 5)

Updated: Jan 27, 2021

(Part 5: Getting the key value)


Last time…

In my last post, I’d added a very basic event system to allow the subsystems to communicate with each other. I now had a game engine that could create a window, render some stuff and even end the application. Wow, such functionality!

Keyboard only I’m a keyboard kinda guy rather than a mouse waggler— I just want to hit the escape key and get right back to my coding. I don’t want to be messing about reaching ALL the way to a mouse! To achieve this, I’ll need to read some input. The game engine will need input anyway as any games made with it won’t be much fun without, so why not start the basic framework now? I’d already decided that I’ll be using OIS for reading input, so all I need to be doing here is recognise that the escape key has been pressed, and post a “QUIT” event, just like we did when the window was closed. The engine will then exit just as if we’d closed the window. To get OIS reading input, I found that I need to be extending OIS::KeyListener for keyboard input and OIS::MouseListener for mouse input. Then when OIS is all up and running it’s just a matter of implementing the relevant methods — for example keyPressed and keyReleased. Sounds nice and easy to me, just the way I like it :) It also looks like implementing joysticks would be a similar process to this, but that will be for a future post. I hope to implement some kind of input mapping system at some time in the future :) Most of the work with this basic class is actually in getting OIS initialised and hooked up. I created a class that extends the aforementioned classes, and also implements ISubSystem as it’s another of our subsystems. Then I plumbed in some code to the Initialise method.

void OisInputSubSystem::Initialise() {
    OIS::ParamList pl;

    pl.insert(std::make_pair(std::string("WINDOW"),
        windowHandle_));

    inputManager_ = OIS::InputManager::createInputSystem(pl);

    keyboard_ = static_cast<OIS::Keyboard*>
        (inputManager_->createInputObject(OIS::OISKeyboard,
        true));

    mouse_ = static_cast<OIS::Mouse*>
        (inputManager_->createInputObject(OIS::OISMouse, true));

    mouse_->setEventCallback(this);
    keyboard_->setEventCallback(this);
}

In this snippet, it creates the overall input system, then creates both a keyboard and mouse input object. In this method, you’ll notice there is a windowHandle_ member variable used. This is a handle to our main ogre window so that OIS knows which window to read input for. Getting our OgreRenderSubSystem to provide this was easy to do as it’s a matter of just lifting it from the tutorials (hurrah for software communities!)

std::string OgreRenderSubSystem::GetWindowHandle() {
    size_t windowHnd = 0;
    std::ostringstream windowHndStr;
    window_->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    return windowHndStr.str();
}

We have a little work to do to shut down and clean up after OIS too, so I put this code into the Terminate method:

void OisInputSubSystem::Terminate() {
    if (inputManager_) {
        inputManager_->destroyInputObject(mouse_);
        inputManager_->destroyInputObject(keyboard_);
        OIS::InputManager::destroyInputSystem(inputManager_);
        inputManager_ = 0;
    }
}

Then to actually get OIS to read the input, the capture() method of the input objects must be invoked. Input needs to be up to date for each frame of the game, so I put the capture calls in the Update() method.

void OisInputSubSystem::Update() {
    keyboard_->capture();
    mouse_->capture();
}

I think that’s pretty much it in terms of housework code for OIS. Now as mentioned before it’s a matter of using the keyReleased() method to read when the escape key has been pressed, and post a “QUIT” event like this:

bool OisInputSubSystem::keyReleased(const OIS::KeyEvent &arg) {
    if (arg.key == OIS::KC_ESCAPE) {
        eventManager_.Post(new Event("QUIT"));
    }
}

I then just hooked up the new subsystem into our main loop and was good to go. It seems to work, but I spotted a really annoying thing that this introduces. It’s especially annoying when you’re debugging and need to switch your working window to the debugger. OIS will grab the mouse for exclusive input (or it certainly does on Linux). Which makes it tricky to switch windows.

The answer was found on the Ogre website, where this annoyance has been raised before and it was a simple cut and paste job to correct it. When initialising, you can pass in additional parameters…

// make the mouse non-exclusive
#if defined OIS_WIN32_PLATFORM
    pl.insert(std::make_pair(std::string("w32_mouse"),
        std::string("DISCL_FOREGROUND")));
    pl.insert(std::make_pair(std::string("w32_mouse"),
        std::string("DISCL_NONEXCLUSIVE")));
    pl.insert(std::make_pair(std::string("w32_keyboard"),
        std::string("DISCL_FOREGROUND")));
    pl.insert(std::make_pair(std::string("w32_keyboard"),
        std::string("DISCL_NONEXCLUSIVE")));
#elif defined OIS_LINUX_PLATFORM
    pl.insert(std::make_pair(std::string("x11_mouse_grab"),
        std::string("false")));
    pl.insert(std::make_pair(std::string("x11_mouse_hide"),
        std::string("false")));
    pl.insert(std::make_pair(std::string("x11_keyboard_grab"),
        std::string("false")));
    pl.insert(std::make_pair(std::string("XAutoRepeatOn"),
        std::string("true")));
#endif

With that code in place, the application only reads the mouse when it’s over the window. Much nicer to work with.


Next time…

I’ll be looking at getting some simple scripting working to get our 3D thing moving.



4 views0 comments
bottom of page