407 lines
15 KiB
C#
407 lines
15 KiB
C#
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Linq;
|
||
|
|
using UnityEngine.InputSystem;
|
||
|
|
using UnityEngine.InputSystem.EnhancedTouch;
|
||
|
|
using UnityEngine.InputSystem.LowLevel;
|
||
|
|
using UnityEngine.InputSystem.Users;
|
||
|
|
using UE = UnityEngine;
|
||
|
|
|
||
|
|
namespace Unity.RenderStreaming.Samples
|
||
|
|
{
|
||
|
|
using UnityInputSystem = UnityEngine.InputSystem.InputSystem;
|
||
|
|
|
||
|
|
enum KeyboardEventType
|
||
|
|
{
|
||
|
|
KeyUp = 0,
|
||
|
|
KeyDown = 1,
|
||
|
|
}
|
||
|
|
enum EventType
|
||
|
|
{
|
||
|
|
Keyboard = 0,
|
||
|
|
Mouse = 1,
|
||
|
|
MouseWheel = 2,
|
||
|
|
Touch = 3,
|
||
|
|
ButtonClick = 4,
|
||
|
|
Gamepad = 5,
|
||
|
|
}
|
||
|
|
|
||
|
|
enum GamepadEventType
|
||
|
|
{
|
||
|
|
ButtonUp = 0,
|
||
|
|
ButtonDown = 1,
|
||
|
|
ButtonPressed = 2,
|
||
|
|
Axis = 3
|
||
|
|
}
|
||
|
|
|
||
|
|
enum GamepadKeyCode
|
||
|
|
{
|
||
|
|
Button0 = 0,
|
||
|
|
Button1,
|
||
|
|
Button2,
|
||
|
|
Button3,
|
||
|
|
Button4,
|
||
|
|
Button5,
|
||
|
|
Button6,
|
||
|
|
Button7,
|
||
|
|
Button8,
|
||
|
|
Button9,
|
||
|
|
Axis0Button,
|
||
|
|
Axis1Button,
|
||
|
|
DpadUp,
|
||
|
|
DpadDown,
|
||
|
|
DpadLeft,
|
||
|
|
DpadRight,
|
||
|
|
Axis0 = 100,
|
||
|
|
Axis1
|
||
|
|
}
|
||
|
|
|
||
|
|
internal static class RemoteInputReceiver
|
||
|
|
{
|
||
|
|
private static readonly Dictionary<RemoteInput, uint> s_mapRemoteInputAndInputUserId;
|
||
|
|
private static readonly List<RemoteInput> s_listRemoteInput;
|
||
|
|
|
||
|
|
static RemoteInputReceiver()
|
||
|
|
{
|
||
|
|
s_mapRemoteInputAndInputUserId = new Dictionary<RemoteInput, uint>();
|
||
|
|
s_listRemoteInput = new List<RemoteInput>();
|
||
|
|
}
|
||
|
|
|
||
|
|
internal static void Dispose()
|
||
|
|
{
|
||
|
|
s_mapRemoteInputAndInputUserId.Clear();
|
||
|
|
s_listRemoteInput.Clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
public static IReadOnlyList<RemoteInput> All()
|
||
|
|
{
|
||
|
|
return s_listRemoteInput;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static RemoteInput Create()
|
||
|
|
{
|
||
|
|
InputUser user = InputUser.CreateUserWithoutPairedDevices();
|
||
|
|
user = InputUser.PerformPairingWithDevice(UnityInputSystem.AddDevice<Mouse>(), user);
|
||
|
|
user = InputUser.PerformPairingWithDevice(UnityInputSystem.AddDevice<Keyboard>(), user);
|
||
|
|
user = InputUser.PerformPairingWithDevice(UnityInputSystem.AddDevice<Gamepad>(), user);
|
||
|
|
user = InputUser.PerformPairingWithDevice(UnityInputSystem.AddDevice<Touchscreen>(), user);
|
||
|
|
RemoteInput remoteInput = new RemoteInput(ref user);
|
||
|
|
s_mapRemoteInputAndInputUserId.Add(remoteInput, user.id);
|
||
|
|
s_listRemoteInput.Add(remoteInput);
|
||
|
|
|
||
|
|
return remoteInput;
|
||
|
|
}
|
||
|
|
|
||
|
|
internal static void Delete(RemoteInput remoteInput)
|
||
|
|
{
|
||
|
|
if (remoteInput == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("The instance of argument is null");
|
||
|
|
}
|
||
|
|
bool found = s_mapRemoteInputAndInputUserId.TryGetValue(remoteInput, out uint userId);
|
||
|
|
if (!found)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("The instance of argument is not found");
|
||
|
|
}
|
||
|
|
InputUser user = InputUser.all.First(_user => _user.id == userId);
|
||
|
|
var arrayDeviceId = user.pairedDevices.Select(device => device.deviceId).ToArray();
|
||
|
|
user.UnpairDevicesAndRemoveUser();
|
||
|
|
foreach (var deviceId in arrayDeviceId)
|
||
|
|
{
|
||
|
|
UnityInputSystem.RemoveDevice(UnityInputSystem.GetDeviceById(deviceId));
|
||
|
|
}
|
||
|
|
s_mapRemoteInputAndInputUserId.Remove(remoteInput);
|
||
|
|
s_listRemoteInput.Remove(remoteInput);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public interface IInput
|
||
|
|
{
|
||
|
|
Mouse RemoteMouse { get; }
|
||
|
|
Keyboard RemoteKeyboard { get; }
|
||
|
|
Touchscreen RemoteTouchscreen { get; }
|
||
|
|
Gamepad RemoteGamepad { get; }
|
||
|
|
}
|
||
|
|
|
||
|
|
internal class RemoteInput : IInput, IDisposable
|
||
|
|
{
|
||
|
|
private GamepadState m_gamepadState;
|
||
|
|
|
||
|
|
public Mouse RemoteMouse { get; }
|
||
|
|
public Keyboard RemoteKeyboard { get; }
|
||
|
|
public Touchscreen RemoteTouchscreen { get; }
|
||
|
|
public Gamepad RemoteGamepad { get; }
|
||
|
|
|
||
|
|
public Action<int> ActionButtonClick;
|
||
|
|
|
||
|
|
private UnityEngine.Vector2Int m_prevMousePos;
|
||
|
|
private KeyboardState m_keyboardState = new KeyboardState();
|
||
|
|
private bool disposed;
|
||
|
|
|
||
|
|
internal RemoteInput(ref InputUser user)
|
||
|
|
{
|
||
|
|
RemoteMouse = user.pairedDevices.FirstOrDefault(device => device is Mouse) as Mouse;
|
||
|
|
RemoteKeyboard = user.pairedDevices.FirstOrDefault(device => device is Keyboard) as Keyboard;
|
||
|
|
RemoteTouchscreen = user.pairedDevices.FirstOrDefault(device => device is Touchscreen) as Touchscreen;
|
||
|
|
RemoteGamepad = user.pairedDevices.FirstOrDefault(device => device is Gamepad) as Gamepad;
|
||
|
|
}
|
||
|
|
|
||
|
|
~RemoteInput()
|
||
|
|
{
|
||
|
|
Dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
if (this.disposed)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
RemoteInputReceiver.Delete(this);
|
||
|
|
|
||
|
|
this.disposed = true;
|
||
|
|
GC.SuppressFinalize(this);
|
||
|
|
}
|
||
|
|
|
||
|
|
//---------------------------------------------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
public void ProcessInput(byte[] bytes)
|
||
|
|
{
|
||
|
|
if (bytes == null)
|
||
|
|
throw new ArgumentNullException();
|
||
|
|
if (bytes.Length == 0)
|
||
|
|
throw new ArgumentException("byte length is zero");
|
||
|
|
|
||
|
|
switch ((EventType)bytes[0])
|
||
|
|
{
|
||
|
|
case EventType.Keyboard:
|
||
|
|
var type = (KeyboardEventType)bytes[1];
|
||
|
|
var repeat = bytes[2] == 1;
|
||
|
|
var key = bytes[3];
|
||
|
|
var character = (char)bytes[4];
|
||
|
|
ProcessKeyEvent(type, repeat, key, character);
|
||
|
|
break;
|
||
|
|
case EventType.Mouse:
|
||
|
|
var deltaX = BitConverter.ToInt16(bytes, 1);
|
||
|
|
var deltaY = BitConverter.ToInt16(bytes, 3);
|
||
|
|
var button = bytes[5];
|
||
|
|
ProcessMouseMoveEvent(deltaX, deltaY, button);
|
||
|
|
break;
|
||
|
|
case EventType.MouseWheel:
|
||
|
|
var scrollX = BitConverter.ToSingle(bytes, 1);
|
||
|
|
var scrollY = BitConverter.ToSingle(bytes, 5);
|
||
|
|
ProcessMouseWheelEvent(scrollX, scrollY);
|
||
|
|
break;
|
||
|
|
case EventType.Touch:
|
||
|
|
{
|
||
|
|
var length = bytes[1];
|
||
|
|
var index = 2;
|
||
|
|
var touches = new TouchState[length];
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
const int INPUTSYSTEM_ZERO_ID_GUARD = 128; //ID 0 is reserved by inputsystem
|
||
|
|
int identifier = BitConverter.ToInt32(bytes, index) + INPUTSYSTEM_ZERO_ID_GUARD;
|
||
|
|
index += 4;
|
||
|
|
var phase = (UnityEngine.InputSystem.TouchPhase)bytes[index];
|
||
|
|
index += 1;
|
||
|
|
var pageX = BitConverter.ToInt16(bytes, index);
|
||
|
|
index += 2;
|
||
|
|
var pageY = BitConverter.ToInt16(bytes, index);
|
||
|
|
index += 2;
|
||
|
|
var force = BitConverter.ToSingle(bytes, index);
|
||
|
|
index += 4;
|
||
|
|
touches[i] = new TouchState
|
||
|
|
{
|
||
|
|
touchId = identifier,
|
||
|
|
phase = phase,
|
||
|
|
position = new UnityEngine.Vector2Int(pageX, pageY),
|
||
|
|
pressure = force
|
||
|
|
};
|
||
|
|
}
|
||
|
|
ProcessTouchMoveEvent(touches);
|
||
|
|
if (Touch.activeTouches.Count > length)
|
||
|
|
{
|
||
|
|
ChangeEndStateUnusedTouches(touches);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case EventType.ButtonClick:
|
||
|
|
var elementId = BitConverter.ToInt16(bytes, 1);
|
||
|
|
ProcessButtonClickEvent(elementId);
|
||
|
|
break;
|
||
|
|
case EventType.Gamepad:
|
||
|
|
{
|
||
|
|
GamepadEventType gamepadEventType = (GamepadEventType)bytes[1];
|
||
|
|
|
||
|
|
switch (gamepadEventType)
|
||
|
|
{
|
||
|
|
case GamepadEventType.ButtonDown:
|
||
|
|
case GamepadEventType.ButtonUp:
|
||
|
|
case GamepadEventType.ButtonPressed:
|
||
|
|
{
|
||
|
|
var buttonIndex = bytes[2];
|
||
|
|
var value = BitConverter.ToDouble(bytes, 3);
|
||
|
|
ProcessGamepadButtonEvent(gamepadEventType, (GamepadKeyCode)buttonIndex, value);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case GamepadEventType.Axis:
|
||
|
|
{
|
||
|
|
var buttonIndex = bytes[2];
|
||
|
|
var x = BitConverter.ToDouble(bytes, 3);
|
||
|
|
var y = BitConverter.ToDouble(bytes, 11);
|
||
|
|
ProcessGamepadAxisEvent(x, y, (GamepadKeyCode)buttonIndex);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
UnityInputSystem.QueueStateEvent(RemoteGamepad, m_gamepadState);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#region Gamepads Events
|
||
|
|
void ProcessGamepadButtonEvent(GamepadEventType state, GamepadKeyCode buttonIndex, double value)
|
||
|
|
{
|
||
|
|
GamepadButton buttonToUpdate = GamepadButton.DpadUp;
|
||
|
|
GamepadState gamepadState = m_gamepadState;
|
||
|
|
switch (buttonIndex)
|
||
|
|
{
|
||
|
|
case GamepadKeyCode.DpadUp:
|
||
|
|
buttonToUpdate = GamepadButton.DpadUp;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.DpadDown:
|
||
|
|
buttonToUpdate = GamepadButton.DpadDown;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.DpadLeft:
|
||
|
|
buttonToUpdate = GamepadButton.DpadLeft;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.DpadRight:
|
||
|
|
buttonToUpdate = GamepadButton.DpadRight;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Button0:
|
||
|
|
buttonToUpdate = GamepadButton.B;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Button1:
|
||
|
|
buttonToUpdate = GamepadButton.A;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Button2:
|
||
|
|
buttonToUpdate = GamepadButton.Y;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Button3:
|
||
|
|
buttonToUpdate = GamepadButton.X;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Button4:
|
||
|
|
buttonToUpdate = GamepadButton.LeftShoulder;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Button5:
|
||
|
|
buttonToUpdate = GamepadButton.RightShoulder;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Button6:
|
||
|
|
buttonToUpdate = GamepadButton.LeftTrigger;
|
||
|
|
gamepadState.leftTrigger = (float)value;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Button7:
|
||
|
|
buttonToUpdate = GamepadButton.RightTrigger;
|
||
|
|
gamepadState.rightTrigger = (float)value;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Axis0Button:
|
||
|
|
buttonToUpdate = GamepadButton.LeftStick;
|
||
|
|
break;
|
||
|
|
case GamepadKeyCode.Axis1Button:
|
||
|
|
buttonToUpdate = GamepadButton.RightStick;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
UE.Debug.Log("Unmapped button code: " + buttonIndex);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
m_gamepadState = gamepadState.WithButton(buttonToUpdate, GamepadEventType.ButtonDown == state || GamepadEventType.ButtonPressed == state);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProcessGamepadAxisEvent(double x, double y, GamepadKeyCode axisKeyCode)
|
||
|
|
{
|
||
|
|
GamepadState gamepadState = m_gamepadState;
|
||
|
|
if (axisKeyCode == GamepadKeyCode.Axis0)
|
||
|
|
gamepadState.leftStick = new UE.Vector2((float)x, (float)y);
|
||
|
|
if (axisKeyCode == GamepadKeyCode.Axis1)
|
||
|
|
gamepadState.rightStick = new UE.Vector2((float)x, (float)y);
|
||
|
|
|
||
|
|
m_gamepadState = gamepadState;
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
void ProcessKeyEvent(KeyboardEventType state, bool repeat, byte keyCode, char character)
|
||
|
|
{
|
||
|
|
switch (state)
|
||
|
|
{
|
||
|
|
case KeyboardEventType.KeyDown:
|
||
|
|
if (!repeat)
|
||
|
|
{
|
||
|
|
m_keyboardState.Set((Key)keyCode, true);
|
||
|
|
UnityInputSystem.QueueStateEvent(RemoteKeyboard, m_keyboardState);
|
||
|
|
}
|
||
|
|
if (character != 0)
|
||
|
|
{
|
||
|
|
UnityInputSystem.QueueTextEvent(RemoteKeyboard, character);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case KeyboardEventType.KeyUp:
|
||
|
|
m_keyboardState.Set((Key)keyCode, false);
|
||
|
|
UnityInputSystem.QueueStateEvent(RemoteKeyboard, m_keyboardState);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProcessMouseMoveEvent(short x, short y, byte button)
|
||
|
|
{
|
||
|
|
UnityEngine.Vector2Int pos = new UnityEngine.Vector2Int(x, y);
|
||
|
|
UnityEngine.Vector2Int delta = pos - m_prevMousePos;
|
||
|
|
UnityInputSystem.QueueStateEvent(RemoteMouse, new MouseState
|
||
|
|
{
|
||
|
|
position = pos,
|
||
|
|
delta = delta,
|
||
|
|
buttons = button
|
||
|
|
});
|
||
|
|
m_prevMousePos = pos;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProcessMouseWheelEvent(float scrollX, float scrollY)
|
||
|
|
{
|
||
|
|
UnityInputSystem.QueueStateEvent(RemoteMouse, new MouseState { scroll = new UnityEngine.Vector2(scrollX, scrollY) });
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProcessTouchMoveEvent(TouchState[] touches)
|
||
|
|
{
|
||
|
|
for (var i = 0; i < touches.Length; i++)
|
||
|
|
{
|
||
|
|
UnityInputSystem.QueueStateEvent(RemoteTouchscreen, touches[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
void ChangeEndStateUnusedTouches(TouchState[] touches)
|
||
|
|
{
|
||
|
|
int touchCount = Touch.activeTouches.Count;
|
||
|
|
for (var i = 0; i < touchCount; i++)
|
||
|
|
{
|
||
|
|
int touchId = Touch.activeTouches[i].touchId;
|
||
|
|
if (!Array.Exists(touches, v => v.touchId == touchId))
|
||
|
|
{
|
||
|
|
if (Touch.activeTouches[i].phase == TouchPhase.Ended)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
UnityInputSystem.QueueStateEvent(RemoteTouchscreen, new TouchState
|
||
|
|
{
|
||
|
|
touchId = touchId,
|
||
|
|
phase = TouchPhase.Ended,
|
||
|
|
position = Touch.activeTouches[i].screenPosition
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProcessButtonClickEvent(int elementId)
|
||
|
|
{
|
||
|
|
ActionButtonClick?.Invoke(elementId);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|