Covyne Entertainment
3 min readJan 28, 2021

Android Unity Game Development Blog: The Ultimate Input Controller — A Mouse and Touch Input All In One.

Problem:

I’d like to know when the player has clicked, touched, licked (eyeballed?) an object on the screen, I don’t care if it’s a left mouse click, a finger tap on the screen or some other ligament/appendage of the human body.

Input is input.

I want the ability to have multiple game objects react to the same input.

I want to know the Vector3 location of the input.

I’d also like to test this new code to make sure it works okay.

I’ll probably eventually want two inputs in the future (e.g left and right mouse button, one or two fingers), but not right now.

Solution:

Normally, I avoid abstraction like the plague in favour of composition. But in this case, I think it makes sense to go with abstraction, we have two classes that need to do exactly the same thing, so abstraction it is.

We’re going to create:

  1. An AbstractInputType.cs class to declare common functions. Any new input device will derive from this class.
  2. An example MouseInput.cs MonoBehaviour to capture mouse clicks.
  3. A example TouchInput.cs MonoBehaviour to capture touch events.
  4. A PlayerInput.cs to show us how to use our new AbstractInputType
  5. A PlayerInputControllerTest to check it all works.

Let’s start by creating a simple AbstractInputType.cs with one Action we can call. This action will be the “PrimaryAction” and will provide us with what we need — just the Vector3 location of where the user clicked (licked?) the screen.

Anything that is an input type (Mouse/Finger/Nose) will derive from this new class:

using System;
using UnityEngine;
public class AbstractInputType: MonoBehaviour
{
public Action<Vector3> PrimaryInput;
}

2. Let’s add a “MouseInput” GameObject to our Unity project, with a MouseInput.cs script to match. Let’s make it derive from our AbstractInputType and call the PrimaryInput Action (if it has been set) when the user clicks the primary (left) button:

using UnityEngine;public class MouseInput : AbstractInputType
{
void Update()
{
if (Input.GetMouseButtonDown(0))
{
PrimaryInput?.Invoke(Input.mousePosition);
}
}
}

3. Let’s do the same with our TouchInput GameObject and TouchInput.cs script:

using UnityEngine;public class TouchInput : AbstractInputType
{
void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
PrimaryInput?.Invoke(touch.position);
}
}
}

4. Let’s create a PlayerInput GameObject in Unity, and add a PlayerInput.cs Script to it. In the future, we will add the PlayerInput object to any other GameObject in the game that want to react to input events. The PlayerInput.cs has a few parts:

  • And AbstractInputType List, so we can have multple input types.
  • A Delegate and Event property that other GameObjects can assign their methods to.
  • A function (EnableInput) that cycles the AbstractInputType List and assigns the PrimaryInput abstract field to the PlayerInput.cs delegage method.
using System.Collections.Generic;
using UnityEngine;
public class PlayerInput : MonoBehaviour
{
public List<AbstractInputType> InputTypes; public delegate void PrimaryInput(Vector3 location); public event PrimaryInput OnPrimaryInput; void Awake()
{
//bail early if we have no inputs
if (InputTypes == null)
return;
EnableInput(); } public void EnableInput()
{
InputTypes.ForEach(
input => input.PrimaryInput =
(vector3) => OnPrimaryInput?.Invoke(vector3)
);
}
}

6. Finally, let’s create a test to make sure both events from the MouseInput and TouchInput are fired. Sorry about the formatting, but Medium really sucks with code snippets.

using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
public class PlayerInputControllerTests
{
private GameObject parent = new GameObject("TestRoot");
private PlayerInput pInput;
private TestPlayerInput testInput1;
private TestPlayerInput testInput2;
private List<Vector3> actuals = new List<Vector3>();
private Vector3 testPos1 = Vector3.down;
private Vector3 testPos2 = Vector3.up;
[Test]
public void InputControllerReactsToTwoInputTypes()
{

SetupInputTestObjects();
//capture the inputs
pInput.OnPrimaryInput += (vector3) => actuals.Add(vector3);
//Enable our input (this is done in the Awake method)
pInput.EnableInput();
//fire away!
testInput1.TestPrimaryInput(testPos1);
testInput2.TestPrimaryInput(testPos2);
//Check the results
Vector3[] ActualInputVectors = actuals.ToArray();
Assert.IsTrue(ActualInputVectors.Length == 2);
Assert.IsTrue(ActualInputVectors[0] == testPos1);
Assert.IsTrue(ActualInputVectors[1] == testPos2);
} private void SetupInputTestObjects()
{

pInput = parent.AddComponent<PlayerInput>();
testInput1 = pInput.gameObject.AddComponent<MyPlayerInput>();
testInput2 = pInput.gameObject.AddComponent<MyPlayerInput>();

//this is usually done in the Unity Inpector
pInput.InputTypes = new List<AbstractInputType>()
{ testInput1, testInput2 };
} private class MyPlayerInput : AbstractInputType
{
public void TestPrimaryInput(Vector3 vector3)
{
PrimaryInput?.Invoke(vector3);
}
}
}

I like this video, dude’s a bit goofy, but entertaining. If you want to learn how to setup tests in Unity, this is what I followed:

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response