Let’s create a mobile game with Xamarin and OpenGL – Part 4 (User input)

In this part we are looking at important aspect of games, handling the user input. Without user interaction a game is not playable. Luckily it’s pretty simple to integrate user input in our project. In this article we will simply show or hide a sprite, whenever the screen is touched.

Creating an input manager

Create a new folder “Abstractions” in project “Engine.Shared” and add a new interface “IInputListener.cs”:

namespace Engine.Shared.Abstractions { public interface IInputListener { bool Down(float x, float y); bool Up(float x, float y); bool Move(float x, float y); } }

This interface will be used for game specific implementations of input handling.

Next we have to create a new class “InputManager.cs” in the existing folder “Managers”:

using Engine.Shared.Abstractions; using System; using System.Collections.Generic; namespace Engine.Shared.Managers { public class InputManager { public List<IInputListener> Listeners { get; private set; } = new List<IInputListener>(); public void Down(float x, float y) { Action((inputListener) => inputListener.Down(x, y)); } public void Up(float x, float y) { Action((inputListener) => inputListener.Up(x, y)); } public void Move(float x, float y) { Action((inputListener) => inputListener.Move(x, y)); } private void Action(Func<IInputListener, bool> action) { foreach (var listener in Listeners) { if (action(listener)) { break; } } } } }

The “InputManager” allows us to add multiple listeners which can react to the users inputs. You might need different kind of input handling. For example you will need a different listener for a menu than for the movement of the character in a game. Currently the “InputManager” will only handle the Down, Up and Move events. You can later extend this class to also handle scaling gestures.

Implement an input listener

Implement method “OnTouchEvent” in the “GameView” class in project “Engine.Android”, which will allow us to catch touch events:

using Android.Util; using System; using OpenTK.Platform.Android; using Android.Content; using OpenTK; using OpenTK.Graphics; using Android.Runtime; using Engine.Shared; using Android.Views; namespace Engine.Android { public class GameView : AndroidGameView { ... public override bool OnTouchEvent(MotionEvent e) { base.OnTouchEvent(e); float x = e.GetX(); float y = MeasuredHeight - e.GetY(); if (e.Action == MotionEventActions.Down) { gameController.Down(x, y); } else if (e.Action == MotionEventActions.Up) { gameController.Up(x, y); } else if (e.Action == MotionEventActions.Move) { gameController.Move(x, y); } return true; } } }

Finally we only need to make a few adjustments in the “GameController.cs” class:

using Engine.Shared.Abstractions; using Engine.Shared.Drawables; using Engine.Shared.Managers; using Engine.Shared.Renderers; using OpenTK; using OpenTK.Graphics.ES30; using System; using System.Reflection; namespace Engine.Shared { public class GameController : IDisposable { ... private readonly InputManager inputManager; ... public bool Show { get; set; } = true; private class SelectionInputListener : IInputListener { private readonly GameController controller; public SelectionInputListener(GameController controller) { this.controller = controller; } public bool Down(float x, float y) { controller.Show = !controller.Show; return true; } public bool Move(float x, float y) { return false; } public bool Up(float x, float y) { return false; } } public GameController(FileManager fileManager, Assembly gameAssembly) { ... this.inputManager = new InputManager(); } public void Load() { ... inputManager.Listeners.Add(new SelectionInputListener(this)); } public void Render() { ... if (Show) { spriteRenderer.BeforeRender(); spriteRenderer.Render(sprite1, new Vector4(0.5f, 0.5f, 1f, 1f), viewProjection); spriteRenderer.AfterRender(); } } ... public void Down(float x, float y) { inputManager.Down(x, y); } public void Up(float x, float y) { inputManager.Up(x, y); } public void Move(float x, float y) { inputManager.Move(x, y); } } }

First we create a new private class “SelectionInputListener” which is an implementation of the “IInputListener” interface. It will simply toggle the value of the property “Show”. In the “Load” method we add an instance of “SelectionInputListener” on the Listeners of “InputManger”. Everytime we touch the screen, the input listener will be notified by the input manager. Then in then Render method we check this property and only render the cross sprite, whenever the value is true.

When you run the application, you will see that the cross disappears when you touch the screen and it will be displayed again on the next touch.

You can download the source code of this article here: