RTS C++ Setup – The Inputs Part 2

As stated in the part 1 of this tutorial, this tutorial will be focused on moving around the camera with the mouse.

This tutorial will not go into the input constructs that Lumberyard offers, but will use them; however, if you really want to know how the concepts behind the input system work, you should read through my first input tutorial for an FPS type game. There I go into more detail about InputChannels, InputDevices, and all the other constructs.

If you want to know more about different constructs, check out here, here, and here. (Totally not a nerd, though.)

Enabling the Cursor

You may have noticed that we don’t have a cursor. That’s not very RTS of us. How are we supposed to feed South Korea’s carpal tunnel epidemic if we don’t have a cursor to allow over 9000!! actions per minute (sorry for the mixed reference.)

So we could throw this into System/GameStartup.cpp in the init function. However, this is not very Lumberyard’ish of us, and not the right place (and I totally didn’t do that at first!)

Lumberyard gives us a nice Bus called GameEntityContextEventBus, and a starting system component that we haven’t used yet (in my case, Source/BirdEyeSystemComponent.h).

Lets include the header and inherit the bus.

BirdEyeSystemComponent.h:

#include "AzFramework/Entity/GameEntityContextBus.h"

namespace BirdEye
{
    class BirdEyeSystemComponent
        : public AZ::Component
        , protected EditorGameRequestBus::Handler
        , protected BirdEyeRequestBus::Handler
        , protected AzFramework::GameEntityContextEventBus::Handler
    {
    public:

And then add the function we need to the use from that bus.

BirdEyeSystemComponent.h:

    protected:

        void OnGameEntitiesStarted() override;

Now lets implement the function in the source file

BirdEyeSystemComponent.cpp:

#include "AzFramework/Input/Devices/Mouse/InputDeviceMouse.h"

...
    void BirdEyeSystemComponent::Activate()
    {
        BirdEyeRequestBus::Handler::BusConnect();
        EditorGameRequestBus::Handler::BusConnect();
        AzFramework::GameEntityContextEventBus::Handler::BusConnect();
    }

    void BirdEyeSystemComponent::Deactivate()
    {
        EditorGameRequestBus::Handler::BusConnect();
        BirdEyeRequestBus::Handler::BusDisconnect();
        AzFramework::GameEntityContextEventBus::Handler::BusDisconnect();
    }

    void BirdEyeSystemComponent::OnGameEntitiesStarted()
    {
        EBUS_EVENT(AzFramework::InputSystemCursorRequestBus, SetSystemCursorState, AzFramework::SystemCursorState::ConstrainedAndVisible);
    }

Now if you compile and launch the game, you will see a mouse cursor, and it will be constrained by the window. Sorta like a rat in a cage. Ok, I feel bad about that one… I should really hit the backspace now…

Capturing when the Mouse is on the Edge

Now we need to detect when the mouse is on the edge. We had an empty function on the BECameraComponent called OnMouseEvent. Time to fill it in.

BECameraComponent.cpp:

void BECameraComponent::OnMouseEvent(const InputChannel& inputChannel)
{
    auto input_type = inputChannel.GetInputChannelId();
    if (input_type == InputDeviceMouse::Button::Left || input_type == InputDeviceMouse::Button::Right)
    {
    }
    else if (input_type == InputDeviceMouse::SystemCursorPosition)
    {
        mouseEdgeDown = mouseEdgeLeft = mouseEdgeRight = mouseEdgeUp = false;

        if (auto position_data = inputChannel.GetCustomData<InputChannel::PositionData2D>())
        {
            auto position = position_data->m_normalizedPosition;
            auto x = position.GetX();
            auto y = position.GetY();

            mouseEdgeLeft = x <= .01;
            mouseEdgeRight = x >= .99;
            mouseEdgeUp = y <= .01;
            mouseEdgeDown = y >= .99;
        }
    }
}

You can see we are handling the SystemCursorPosition input channel type. We grab the position, which is normalized between 0 and 1. 0,0 starts on the top left, and 1,1 is the bottom right.

Be sure to add these mouseEdge* bools to the header file.

Compile to make sure everything is kosher.

Moving the Camera

Now lets use dem variables to actually move the camera. In our OnTick function, we use our current movingUp/Down/Right/Left variables. We can tack on our new variables with an ||.

BECameraComponent.cpp:

if (movingUp || mouseEdgeUp)
    y += movementScale * deltaTime;
if (movingDown || mouseEdgeDown)
    y -= movementScale * deltaTime;
if (movingRight || mouseEdgeRight)
    x += movementScale * deltaTime;
if (movingLeft || mouseEdgeLeft)
    x -= movementScale * deltaTime;

BOOM. That should be it. Compile and run the launcher. Now when you move the cursor to the edge of the window, it should move the camera. Yaaaay.

DON’T RUN THIS IN THE EDITOR. I’M PREJUDICE AGAINST IT

Ok, I’m not against the editor, but if you run it, you will notice that the editor does not show the cursor. Sad face. I mean 😦

This is because the editor actually loads your game and level, but then puts the engine in an editor mode, that can be switched to a game mode (by press control + g). However, right now it seems hard-coded to set the state of the mouse to ConstrainedAndHidden whenever you enter game mode.

Preferably, when switching game mode, it would be good to use the in-editor setting:
tut_birdeye2_cursorprop

For now, in the HardwareMouse.cpp file, you can hardcode it to be ConstrainedAndVisible, but even then, the constrained part doesn’t seem to work in the editor. I’ll have to put a forum post about this.

If I find some way to deal with this better, it looks like we will be limited to the launcher for trying out our game. I honestly don’t mind, I ❤ c++ for life, and generally run the game with the launcher anyhoo.

This is the End, my only Friend, the End (of part 2)

If you made it this far without getting some song stuck in your head, then you are a miracle.

In the next part, we are going to handle rotating the camera. There are a lot of choices here, and we’ll go through them. Until then, stay frosty.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s