Game Development: 3.Input Handling
Input Handling
Interaction in a game is created
by determining what buttons are pressed or released by the player, where the
cursor is positioned, and performing some action in response to the input. MonoGame provides an interface for reading
player input, being able to report the current state of the keyboard, mouse (and
gamepad). In a game environment, it may be important to have knowledge of the
previous input state, such as determining if a key is being held, or how far
the mouse has moved since the previous frame. I created an InputState class to
capture this information, setting the previous mouse and keyboard state at the
start of each update, then capturing the current player input:
public void Update(bool isMouseVisible) { //Only handles input if the application is currently the active application if (ScreenManager.GetInstance().Game.IsActive) { //Record the states of the keyboard and mouse from the previous update previousKeyboardState = currentKeyboardState; previousMouseState = currentMouseState; //Get the current state of the keyboard and mouse currentKeyboardState = Keyboard.GetState(); currentMouseState = Mouse.GetState(); this.isMouseVisible = isMouseVisible; } }
For menu screens, the
ScreenManager passes this InputState to each screen, which uses it to determine
if the cursor is over a menu element, by checking if the mouse’s position
overlaps the element’s detection box, or if the menu element has been clicked
by checking if the left mouse button has been pressed while overlapping the
detection box, i.e.:
//Button Clicked if(((input.CurrentMouseState.LeftButton == ButtonState.Pressed && input.PreviousMouseState.LeftButton == ButtonState.Released)&& detectionBox.Contains(new Point(input.CurrentMouseState.X, input.CurrentMouseState.Y))))
If this proves to be true, the
button’s OnClick event is fired, providing the primary interaction with the
game’s menus.
Game World Control State
For interaction within the game
environment itself, I have been building a GameWorldControlState class which is
passed the InputState during every engine update, and maps inputs to actions in
the game world. The control state defines an enumeration of all the actions the
player can take in the game world, such as selecting an ability, interacting
with an object, or attacking an enemy. These values are mapped to an input,
with a trigger specifying if the action should happen when the input is pressed
or released:
public enum Actions { SelectAbilityOne, SelectAbilityTwo, SelectAbilityThree, SelectAbilityFour, SelectAbilityFive, SelectAbilitySix, SelectAbilitySeven, SelectAbilityEight, SelectAbilityNine, SelectAbilityTen, PrimaryHandClickAbility, SecondaryHandClickAbility, ToggleShaders //... } public enum Trigger { Pressed, Released } public struct ActionMapping { public Actions action; //Keyboard/mouse etc. public InputType inputType; public Trigger trigger; //Use if keyboard input public Keys keyboardKey; //Use if mouse input public MouseButton mouseButton; } protected List<ActionMapping> actionMappings = new List<ActionMapping>(); public bool ActionMappingTriggered(ActionMapping actionMapping, InputState inputState) { bool result = false; if (actionMapping.trigger == Trigger.Pressed) { if (actionMapping.inputType == InputType.Keyboard) { if (inputState.CurrentKeyboardState.IsKeyDown(actionMapping.keyboardKey) && inputState.PreviousKeyboardState.IsKeyUp(actionMapping.keyboardKey)) { result = true; } } //... } //... return result }
When the control state is passed
the InputState, it checks the state of each of these action mappings, and marks
the action for activation if the trigger terms are true. Once all the actions
to trigger have been determined, the control state fires the corresponding
event for each action. As the GameWorldControlState class is a singleton, it is
globally accessible, enabling any object in the game world to obtain a
reference to any of its events, tie an event handler to them, and perform some
action when the event is triggered in the control state, i.e.:
//In the player class, tie the event handler for performing the primary hand’s active ability to the corresponding action event in the control state GameWorldControlState.GetInstance().PrimaryHandAbilityOnClickEvent += _primaryHandSlot.ActiveAbilityOnClick;
Mouse Ray Casting
For determining if the player has
clicked on an object in the game world, I have written a raycasting function
that converts the mouse’s screen space coordinates to world space and builds a
Ray using the active camera’s projection and view matrices:
public Ray? GetMouseRay(Camera activeCamera) { Ray? ray = null; if (activeCamera!= null) { //Get the mouse's screen position Vector2 mousePoint = new Vector2(CurrentMouseState.X, CurrentMouseState.Y); Vector3 nearSource = new Vector3((float)mousePoint.X, (float)mousePoint.Y, 0f); Vector3 farSource = new Vector3((float)mousePoint.X, (float)mousePoint.Y, 1f); //Unproject the screen position to world space using the active camera Vector3 nearPoint = ScreenManager.GetInstance().GraphicsDevice.Viewport.Unproject(nearSource, activeCamera.CameraProjectionMatrix, activeCamera.CameraViewMatrix, Matrix.Identity); Vector3 farPoint = ScreenManager.GetInstance().GraphicsDevice.Viewport.Unproject(farSource, activeCamera.CameraProjectionMatrix, activeCamera.CameraViewMatrix, Matrix.Identity); // Create a ray from the near clip plane to the far clip plane. Vector3 direction = farPoint - nearPoint; direction.Normalize(); // Create a ray. ray = new Ray(nearPoint, direction); } return ray; }
Testing the objects for collision
with this ray allows us to determine which objects lie under the mouse cursor,
and by comparing the collision points along the ray, we can discover which of
these objects is nearest to the screen. Efficiently partitioning the objects in
your scene to reduce the search space for raycast tests is a topic I won’t
cover in this post, save for briefly mentioning that I have implemented a
voxel-based traversal algorithm, the original source of which can be found here:
(http://www.cse.yorku.ca/~amana/research/grid.pdf).
Subscribe to:
Post Comments
(
Atom
)
No comments :
Post a Comment