UI 자동화를 위해 바인딩 기능 구현

- 유니티 에셋 인증 오류로 meta 재생성
This commit is contained in:
2026-01-25 01:31:34 +09:00
parent 2ceb28f55d
commit ce83f21c93
1861 changed files with 377882 additions and 211 deletions

View File

@@ -0,0 +1,228 @@
#if UNITY_EDITOR || UNITY_ANDROID || PACKAGE_DOCS_GENERATION
using System;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEngine.InputSystem.Android.LowLevel;
using UnityEngine.InputSystem.Utilities;
namespace UnityEngine.InputSystem.Android.LowLevel
{
/// <summary>
/// Enum used to identity the axis type in the Android motion input event. See <see cref="AndroidGameControllerState.axis"/>.
/// See https://developer.android.com/reference/android/view/MotionEvent#constants_1 for more details.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags", Justification = "False positive")]
public enum AndroidAxis
{
/// <summary>
/// X axis of a motion event.
/// </summary>
X = 0,
/// <summary>
/// Y axis of a motion event.
/// </summary>
Y = 1,
/// <summary>
/// Pressure axis of a motion event.
/// </summary>
Pressure = 2,
/// <summary>
/// Size axis of a motion event.
/// </summary>
Size = 3,
/// <summary>
/// TouchMajor axis of a motion event.
/// </summary>
TouchMajor = 4,
/// <summary>
/// TouchMinor axis of a motion event.
/// </summary>
TouchMinor = 5,
/// <summary>
/// ToolMajor axis of a motion event.
/// </summary>
ToolMajor = 6,
/// <summary>
/// ToolMinor axis of a motion event.
/// </summary>
ToolMinor = 7,
/// <summary>
/// Orientation axis of a motion event.
/// </summary>
Orientation = 8,
/// <summary>
/// Vertical Scroll of a motion event.
/// </summary>
Vscroll = 9,
/// <summary>
/// Horizontal Scroll axis of a motion event.
/// </summary>
Hscroll = 10,
/// <summary>
/// Z axis of a motion event.
/// </summary>
Z = 11,
/// <summary>
/// X Rotation axis of a motion event.
/// </summary>
Rx = 12,
/// <summary>
/// Y Rotation axis of a motion event.
/// </summary>
Ry = 13,
/// <summary>
/// Z Rotation axis of a motion event.
/// </summary>
Rz = 14,
/// <summary>
/// Hat X axis of a motion event.
/// </summary>
HatX = 15,
/// <summary>
/// Hat Y axis of a motion event.
/// </summary>
HatY = 16,
/// <summary>
/// Left Trigger axis of a motion event.
/// </summary>
Ltrigger = 17,
/// <summary>
/// Right Trigger axis of a motion event.
/// </summary>
Rtrigger = 18,
/// <summary>
/// Throttle axis of a motion event.
/// </summary>
Throttle = 19,
/// <summary>
/// Rudder axis of a motion event.
/// </summary>
Rudder = 20,
/// <summary>
/// Wheel axis of a motion event.
/// </summary>
Wheel = 21,
/// <summary>
/// Gas axis of a motion event.
/// </summary>
Gas = 22,
/// <summary>
/// Break axis of a motion event.
/// </summary>
Brake = 23,
/// <summary>
/// Distance axis of a motion event.
/// </summary>
Distance = 24,
/// <summary>
/// Tilt axis of a motion event.
/// </summary>
Tilt = 25,
/// <summary>
/// Generic 1 axis of a motion event.
/// </summary>
Generic1 = 32,
/// <summary>
/// Generic 2 axis of a motion event.
/// </summary>
Generic2 = 33,
/// <summary>
/// Generic 3 axis of a motion event.
/// </summary>
Generic3 = 34,
/// <summary>
/// Generic 4 axis of a motion event.
/// </summary>
Generic4 = 35,
/// <summary>
/// Generic 5 axis of a motion event.
/// </summary>
Generic5 = 36,
/// <summary>
/// Generic 6 axis of a motion event.
/// </summary>
Generic6 = 37,
/// <summary>
/// Generic 7 axis of a motion event.
/// </summary>
Generic7 = 38,
/// <summary>
/// Generic 8 axis of a motion event.
/// </summary>
Generic8 = 39,
/// <summary>
/// Generic 9 axis of a motion event.
/// </summary>
Generic9 = 40,
/// <summary>
/// Generic 10 axis of a motion event.
/// </summary>
Generic10 = 41,
/// <summary>
/// Generic 11 axis of a motion event.
/// </summary>
Generic11 = 42,
/// <summary>
/// Generic 12 axis of a motion event.
/// </summary>
Generic12 = 43,
/// <summary>
/// Generic 13 axis of a motion event.
/// </summary>
Generic13 = 44,
/// <summary>
/// Generic 14 axis of a motion event.
/// </summary>
Generic14 = 45,
/// <summary>
/// Generic 15 axis of a motion event.
/// </summary>
Generic15 = 46,
/// <summary>
/// Generic 16 axis of a motion event.
/// </summary>
Generic16 = 47,
}
}
#endif // UNITY_EDITOR || UNITY_ANDROID

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15fa4fb6e47739e43b36a70fce024b6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,242 @@
#if UNITY_EDITOR || UNITY_ANDROID || PACKAGE_DOCS_GENERATION
using System;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Android.LowLevel;
using UnityEngine.InputSystem.DualShock;
using UnityEngine.InputSystem.Utilities;
namespace UnityEngine.InputSystem.Android.LowLevel
{
/// <summary>
/// Default state layout for Android game controller.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public unsafe struct AndroidGameControllerState : IInputStateTypeInfo
{
public const int MaxAxes = 48;
public const int MaxButtons = 220;
public class Variants
{
public const string Gamepad = "Gamepad";
public const string Joystick = "Joystick";
public const string DPadAxes = "DpadAxes";
public const string DPadButtons = "DpadButtons";
}
internal const uint kAxisOffset = sizeof(uint) * (uint)((MaxButtons + 31) / 32);
public static FourCC kFormat = new FourCC('A', 'G', 'C', ' ');
[InputControl(name = "dpad", layout = "Dpad", bit = (uint)AndroidKeyCode.DpadUp, sizeInBits = 4, variants = Variants.DPadButtons)]
[InputControl(name = "dpad/up", bit = (uint)AndroidKeyCode.DpadUp, variants = Variants.DPadButtons)]
[InputControl(name = "dpad/down", bit = (uint)AndroidKeyCode.DpadDown, variants = Variants.DPadButtons)]
[InputControl(name = "dpad/left", bit = (uint)AndroidKeyCode.DpadLeft, variants = Variants.DPadButtons)]
[InputControl(name = "dpad/right", bit = (uint)AndroidKeyCode.DpadRight, variants = Variants.DPadButtons)]
[InputControl(name = "buttonSouth", bit = (uint)AndroidKeyCode.ButtonA, variants = Variants.Gamepad)]
[InputControl(name = "buttonWest", bit = (uint)AndroidKeyCode.ButtonX, variants = Variants.Gamepad)]
[InputControl(name = "buttonNorth", bit = (uint)AndroidKeyCode.ButtonY, variants = Variants.Gamepad)]
[InputControl(name = "buttonEast", bit = (uint)AndroidKeyCode.ButtonB, variants = Variants.Gamepad)]
[InputControl(name = "leftStickPress", bit = (uint)AndroidKeyCode.ButtonThumbl, variants = Variants.Gamepad)]
[InputControl(name = "rightStickPress", bit = (uint)AndroidKeyCode.ButtonThumbr, variants = Variants.Gamepad)]
[InputControl(name = "leftShoulder", bit = (uint)AndroidKeyCode.ButtonL1, variants = Variants.Gamepad)]
[InputControl(name = "rightShoulder", bit = (uint)AndroidKeyCode.ButtonR1, variants = Variants.Gamepad)]
[InputControl(name = "start", bit = (uint)AndroidKeyCode.ButtonStart, variants = Variants.Gamepad)]
[InputControl(name = "select", bit = (uint)AndroidKeyCode.ButtonSelect, variants = Variants.Gamepad)]
public fixed uint buttons[(MaxButtons + 31) / 32];
[InputControl(name = "dpad", layout = "Dpad", offset = (uint)AndroidAxis.HatX * sizeof(float) + kAxisOffset, format = "VEC2", sizeInBits = 64, variants = Variants.DPadAxes)]
[InputControl(name = "dpad/right", offset = 0, bit = 0, sizeInBits = 32, format = "FLT", parameters = "clamp=3,clampConstant=0,clampMin=0,clampMax=1", variants = Variants.DPadAxes)]
[InputControl(name = "dpad/left", offset = 0, bit = 0, sizeInBits = 32, format = "FLT", parameters = "clamp=3,clampConstant=0,clampMin=-1,clampMax=0,invert", variants = Variants.DPadAxes)]
[InputControl(name = "dpad/down", offset = ((uint)AndroidAxis.HatY - (uint)AndroidAxis.HatX) * sizeof(float), bit = 0, sizeInBits = 32, format = "FLT", parameters = "clamp=3,clampConstant=0,clampMin=0,clampMax=1", variants = Variants.DPadAxes)]
[InputControl(name = "dpad/up", offset = ((uint)AndroidAxis.HatY - (uint)AndroidAxis.HatX) * sizeof(float), bit = 0, sizeInBits = 32, format = "FLT", parameters = "clamp=3,clampConstant=0,clampMin=-1,clampMax=0,invert", variants = Variants.DPadAxes)]
[InputControl(name = "leftTrigger", offset = (uint)AndroidAxis.Brake * sizeof(float) + kAxisOffset, parameters = "clamp=1,clampMin=0,clampMax=1.0", variants = Variants.Gamepad)]
[InputControl(name = "rightTrigger", offset = (uint)AndroidAxis.Gas * sizeof(float) + kAxisOffset, parameters = "clamp=1,clampMin=0,clampMax=1.0", variants = Variants.Gamepad)]
[InputControl(name = "leftStick", variants = Variants.Gamepad)]
[InputControl(name = "leftStick/y", variants = Variants.Gamepad, parameters = "invert")]
[InputControl(name = "leftStick/up", variants = Variants.Gamepad, parameters = "invert,clamp=1,clampMin=-1.0,clampMax=0.0")]
[InputControl(name = "leftStick/down", variants = Variants.Gamepad, parameters = "invert=false,clamp=1,clampMin=0,clampMax=1.0")]
////FIXME: state for this control is not contiguous
[InputControl(name = "rightStick", offset = (uint)AndroidAxis.Z * sizeof(float) + kAxisOffset, sizeInBits = ((uint)AndroidAxis.Rz - (uint)AndroidAxis.Z + 1) * sizeof(float) * 8, variants = Variants.Gamepad)]
[InputControl(name = "rightStick/x", variants = Variants.Gamepad)]
[InputControl(name = "rightStick/y", offset = ((uint)AndroidAxis.Rz - (uint)AndroidAxis.Z) * sizeof(float), variants = Variants.Gamepad, parameters = "invert")]
[InputControl(name = "rightStick/up", offset = ((uint)AndroidAxis.Rz - (uint)AndroidAxis.Z) * sizeof(float), variants = Variants.Gamepad, parameters = "invert,clamp=1,clampMin=-1.0,clampMax=0.0")]
[InputControl(name = "rightStick/down", offset = ((uint)AndroidAxis.Rz - (uint)AndroidAxis.Z) * sizeof(float), variants = Variants.Gamepad, parameters = "invert=false,clamp=1,clampMin=0,clampMax=1.0")]
public fixed float axis[MaxAxes];
public FourCC format
{
get { return kFormat; }
}
public AndroidGameControllerState WithButton(AndroidKeyCode code, bool value = true)
{
fixed(uint* buttonsPtr = buttons)
{
if (value)
buttonsPtr[(int)code / 32] |= 1U << ((int)code % 32);
else
buttonsPtr[(int)code / 32] &= ~(1U << ((int)code % 32));
}
return this;
}
public AndroidGameControllerState WithAxis(AndroidAxis axis, float value)
{
fixed(float* axisPtr = this.axis)
{
axisPtr[(int)axis] = value;
}
return this;
}
}
// See https://developer.android.com/reference/android/view/InputDevice.html for input source values
internal enum AndroidInputSource
{
Keyboard = 257,
Dpad = 513,
Gamepad = 1025,
Touchscreen = 4098,
Mouse = 8194,
Stylus = 16386,
Trackball = 65540,
Touchpad = 1048584,
Joystick = 16777232
}
[Serializable]
internal struct AndroidDeviceCapabilities
{
public string deviceDescriptor;
public int productId;
public int vendorId;
public bool isVirtual;
public AndroidAxis[] motionAxes;
public AndroidInputSource inputSources;
public string ToJson()
{
return JsonUtility.ToJson(this);
}
public static AndroidDeviceCapabilities FromJson(string json)
{
if (json == null)
throw new ArgumentNullException(nameof(json));
return JsonUtility.FromJson<AndroidDeviceCapabilities>(json);
}
public override string ToString()
{
return
$"deviceDescriptor = {deviceDescriptor}, productId = {productId}, vendorId = {vendorId}, isVirtual = {isVirtual}, motionAxes = {(motionAxes == null ? "<null>" : String.Join(",", motionAxes.Select(i => i.ToString()).ToArray()))}, inputSources = {inputSources}";
}
}
}
namespace UnityEngine.InputSystem.Android
{
/// <summary>
/// Represents a gamepad device on Android, providing unified support for various controller types.
/// </summary>
/// <remarks>
/// This layout covers multiple Android-supported gamepads, including but not limited to:
/// - ELAN PLAYSTATION(R)3 Controller
/// - My-Power CO., LTD. PS(R) Controller Adaptor
/// - Sony Interactive Entertainment Wireless (PS4 DualShock)
/// - Xbox Wireless Controller (Xbox One)
/// - NVIDIA Controller v01.03/v01.04
/// - (More may be added later)
///
/// ### Typical Android Axis and Key Mappings
/// | Control | Mapping |
/// |------------------------|----------------------------------------------------|
/// | Left Stick | AXIS_X(0) / AXIS_Y(1) |
/// | Right Stick | AXIS_Z(11) / AXIS_RZ(14) |
/// | L1 | KEYCODE_BUTTON_L1(102) |
/// | R1 | KEYCODE_BUTTON_R1(103) |
/// | L2 | AXIS_BRAKE(23) |
/// | R2 | AXIS_GAS(22) |
/// | Left Thumb | KEYCODE_BUTTON_THUMBL(106) |
/// | Right Thumb | KEYCODE_BUTTON_THUMBR(107) |
/// | X | KEYCODE_BUTTON_X(99) |
/// | Y | KEYCODE_BUTTON_Y(100) |
/// | B | KEYCODE_BUTTON_B(97) |
/// | A | KEYCODE_BUTTON_A(96) |
/// | DPAD | AXIS_HAT_X(15), AXIS_HAT_Y(16) or KEYCODE_DPAD_* |
///
/// ### Notes
/// - **NVIDIA Shield Console**
/// - The L2 and R2 triggers generate both `AXIS_BRAKE` / `AXIS_GAS` and `AXIS_LTRIGGER` / `AXIS_RTRIGGER` events.
/// - On most Android phones, only `AXIS_BRAKE` and `AXIS_GAS` are reported; `AXIS_LTRIGGER` and `AXIS_RTRIGGER` are not invoked.
/// - For consistency across devices, triggers are mapped exclusively to `AXIS_BRAKE` and `AXIS_GAS`.
/// - The Shield also reports `KEYCODE_BACK` instead of `KEYCODE_BUTTON_SELECT`, causing the **Options** (Xbox), **View** (DualShock), or **Select** buttons to be non-functional.
///
/// - **PS4 Controller Compatibility**
/// - Official PS4 controller support is available starting from **Android 10 and later**
/// (see: https://playstation.com/en-us/support/hardware/ps4-pair-dualshock-4-wireless-with-sony-xperia-and-android).
/// - On older Android versions, driver implementations vary by manufacturer. Some vendors have partially fixed DualShock support in custom drivers, leading to inconsistent mappings.
///
/// - **Driver-Dependent Behavior**
/// - Gamepad mappings may differ even between devices running the *same Android version*.
/// - For example, on **Android 8.0**:
/// - **NVIDIA Shield Console:** buttons map correctly according to `AndroidGameControllerState` (for example, `L1 → ButtonL1`, `R1 → ButtonR1`).
/// - **Samsung Galaxy S9 / S8** and **Xiaomi Mi Note2:** mappings are inconsistent (for example, `L1 → ButtonY`, `R1 → ButtonZ`).
/// - These discrepancies stem from device-specific **driver differences**, not the Android OS itself.
///
/// Because mapping inconsistencies depend on vendor-specific drivers, its impractical to maintain per-device remaps.
/// </remarks>
[InputControlLayout(stateType = typeof(AndroidGameControllerState), variants = AndroidGameControllerState.Variants.Gamepad)]
public class AndroidGamepad : Gamepad
{
}
/// <summary>
/// Generic controller with Dpad axes
/// </summary>
[InputControlLayout(stateType = typeof(AndroidGameControllerState), hideInUI = true,
variants = AndroidGameControllerState.Variants.Gamepad + InputControlLayout.VariantSeparator + AndroidGameControllerState.Variants.DPadAxes)]
public class AndroidGamepadWithDpadAxes : AndroidGamepad
{
}
/// <summary>
/// Generic controller with Dpad buttons
/// </summary>
[InputControlLayout(stateType = typeof(AndroidGameControllerState), hideInUI = true,
variants = AndroidGameControllerState.Variants.Gamepad + InputControlLayout.VariantSeparator + AndroidGameControllerState.Variants.DPadButtons)]
public class AndroidGamepadWithDpadButtons : AndroidGamepad
{
}
/// <summary>
/// Joystick on Android.
/// </summary>
[InputControlLayout(stateType = typeof(AndroidGameControllerState), variants = AndroidGameControllerState.Variants.Joystick)]
public class AndroidJoystick : Joystick
{
}
/// <summary>
/// A PlayStation DualShock 4 controller connected to an Android device.
/// </summary>
[InputControlLayout(stateType = typeof(AndroidGameControllerState), displayName = "Android DualShock 4 Gamepad",
variants = AndroidGameControllerState.Variants.Gamepad + InputControlLayout.VariantSeparator + AndroidGameControllerState.Variants.DPadAxes)]
public class DualShock4GamepadAndroid : DualShockGamepad
{
}
/// <summary>
/// An XboxOne controller connected to an Android device.
/// </summary>
[InputControlLayout(stateType = typeof(AndroidGameControllerState), displayName = "Android Xbox One Controller",
variants = AndroidGameControllerState.Variants.Gamepad + InputControlLayout.VariantSeparator + AndroidGameControllerState.Variants.DPadAxes)]
public class XboxOneGamepadAndroid : XInput.XInputController
{
}
}
#endif // UNITY_EDITOR || UNITY_ANDROID

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7c6472eb810ba445be34c69325e4fea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ede17f8b7e9dfcf42b560545a8102eeb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,293 @@
#if UNITY_EDITOR || UNITY_ANDROID || PACKAGE_DOCS_GENERATION
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using UnityEngine.InputSystem.Android.LowLevel;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Processors;
////TODO: make all the sensor class types internal
namespace UnityEngine.InputSystem.Android.LowLevel
{
internal enum AndroidSensorType
{
None = 0,
Accelerometer = 1,
MagneticField = 2,
Orientation = 3, // Was deprecated in API 8 https://developer.android.com/reference/android/hardware/Sensor#TYPE_ORIENTATION
Gyroscope = 4,
Light = 5,
Pressure = 6,
Temperature = 7, // Was deprecated in API 14 https://developer.android.com/reference/android/hardware/Sensor#TYPE_TEMPERATURE
Proximity = 8,
Gravity = 9,
LinearAcceleration = 10,
RotationVector = 11,
RelativeHumidity = 12,
AmbientTemperature = 13,
MagneticFieldUncalibrated = 14,
GameRotationVector = 15,
GyroscopeUncalibrated = 16,
SignificantMotion = 17,
StepDetector = 18,
StepCounter = 19,
GeomagneticRotationVector = 20,
HeartRate = 21,
Pose6DOF = 28,
StationaryDetect = 29,
MotionDetect = 30,
HeartBeat = 31,
LowLatencyOffBodyDetect = 34,
AccelerometerUncalibrated = 35,
HingeAngle = 36
}
[Serializable]
internal struct AndroidSensorCapabilities
{
public AndroidSensorType sensorType;
public string ToJson()
{
return JsonUtility.ToJson(this);
}
public static AndroidSensorCapabilities FromJson(string json)
{
if (json == null)
throw new ArgumentNullException(nameof(json));
return JsonUtility.FromJson<AndroidSensorCapabilities>(json);
}
public override string ToString()
{
return $"type = {sensorType.ToString()}";
}
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct AndroidSensorState : IInputStateTypeInfo
{
public static FourCC kFormat = new FourCC('A', 'S', 'S', ' ');
////FIXME: Sensors to check if values matches old system
// Accelerometer - OK
// MagneticField - no alternative in old system
// Gyroscope - OK
// Light - no alternative in old system
// Pressure - no alternative in old system
// Proximity - no alternative in old system
// Gravity - OK
// LinearAcceleration - need to check
// RotationVector - OK
// RelativeHumidity - no alternative in old system
// AmbientTemperature - no alternative in old system
// GameRotationVector - no alternative in old system
// StepCounter - no alternative in old system
// GeomagneticRotationVector - no alternative in old system
// HeartRate - no alternative in old system
[InputControl(name = "acceleration", layout = "Vector3", processors = "AndroidCompensateDirection", variants = "Accelerometer")]
[InputControl(name = "magneticField", layout = "Vector3", variants = "MagneticField")]
// Note: Using CompensateDirection instead of AndroidCompensateDirection, because we don't need to normalize velocity
[InputControl(name = "angularVelocity", layout = "Vector3", processors = "CompensateDirection", variants = "Gyroscope")]
[InputControl(name = "lightLevel", layout = "Axis", variants = "Light")]
[InputControl(name = "atmosphericPressure", layout = "Axis", variants = "Pressure")]
[InputControl(name = "distance", layout = "Axis", variants = "Proximity")]
[InputControl(name = "gravity", layout = "Vector3", processors = "AndroidCompensateDirection", variants = "Gravity")]
[InputControl(name = "acceleration", layout = "Vector3", processors = "AndroidCompensateDirection", variants = "LinearAcceleration")]
[InputControl(name = "attitude", layout = "Quaternion", processors = "AndroidCompensateRotation", variants = "RotationVector")]
[InputControl(name = "relativeHumidity", layout = "Axis", variants = "RelativeHumidity")]
[InputControl(name = "ambientTemperature", layout = "Axis", variants = "AmbientTemperature")]
[InputControl(name = "attitude", layout = "Quaternion", processors = "AndroidCompensateRotation", variants = "GameRotationVector")]
[InputControl(name = "stepCounter", layout = "Integer", variants = "StepCounter")]
[InputControl(name = "rotation", layout = "Quaternion", processors = "AndroidCompensateRotation", variants = "GeomagneticRotationVector")]
[InputControl(name = "rate", layout = "Axis", variants = "HeartRate")]
[InputControl(name = "angle", layout = "Axis", variants = nameof(AndroidSensorType.HingeAngle))]
public fixed float data[16];
public AndroidSensorState WithData(params float[] data)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
for (var i = 0; i < data.Length && i < 16; i++)
this.data[i] = data[i];
// Fill the rest with zeroes
for (var i = data.Length; i < 16; i++)
this.data[i] = 0.0f;
return this;
}
public FourCC format => kFormat;
}
[DesignTimeVisible(false)]
internal class AndroidCompensateDirectionProcessor : CompensateDirectionProcessor
{
// Taken from platforms\android-<API>\arch-arm\usr\include\android\sensor.h
private const float kSensorStandardGravity = 9.80665f;
private const float kAccelerationMultiplier = -1.0f / kSensorStandardGravity;
public override Vector3 Process(Vector3 vector, InputControl control)
{
return base.Process(vector * kAccelerationMultiplier, control);
}
}
[DesignTimeVisible(false)]
internal class AndroidCompensateRotationProcessor : CompensateRotationProcessor
{
public override Quaternion Process(Quaternion value, InputControl control)
{
// https://developer.android.com/reference/android/hardware/SensorEvent#values
// "...The rotation vector represents the orientation of the device as a combination of an angle and an axis, in which the device has rotated through an angle theta around an axis <x, y, z>."
// "...The three elements of the rotation vector are < x * sin(theta / 2), y* sin(theta / 2), z* sin(theta / 2)>, such that the magnitude of the rotation vector is equal to sin(theta / 2), and the direction of the rotation vector is equal to the direction of the axis of rotation."
// "...The three elements of the rotation vector are equal to the last three components of a unit quaternion < cos(theta / 2), x* sin(theta/ 2), y* sin(theta / 2), z* sin(theta/ 2)>."
//
// In other words, axis + rotation is combined into Vector3, to recover the quaternion from it, we must compute 4th component as 1 - sqrt(x*x + y*y + z*z)
var sinRho2 = value.x * value.x + value.y * value.y + value.z * value.z;
value.w = (sinRho2 < 1.0f) ? Mathf.Sqrt(1.0f - sinRho2) : 0.0f;
return base.Process(value, control);
}
}
}
namespace UnityEngine.InputSystem.Android
{
/// <summary>
/// Accelerometer device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_ACCELEROMETER"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "Accelerometer", hideInUI = true)]
public class AndroidAccelerometer : Accelerometer
{
}
/// <summary>
/// Magnetic field sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_MAGNETIC_FIELD"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "MagneticField", hideInUI = true)]
public class AndroidMagneticFieldSensor : MagneticFieldSensor
{
}
/// <summary>
/// Gyroscope device on android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_GYROSCOPE"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "Gyroscope", hideInUI = true)]
public class AndroidGyroscope : Gyroscope
{
}
/// <summary>
/// Light sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_LIGHT"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "Light", hideInUI = true)]
public class AndroidLightSensor : LightSensor
{
}
/// <summary>
/// Pressure sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_PRESSURE"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "Pressure", hideInUI = true)]
public class AndroidPressureSensor : PressureSensor
{
}
/// <summary>
/// Proximity sensor type on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_PROXIMITY"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "Proximity", hideInUI = true)]
public class AndroidProximity : ProximitySensor
{
}
/// <summary>
/// Gravity sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_GRAVITY"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "Gravity", hideInUI = true)]
public class AndroidGravitySensor : GravitySensor
{
}
/// <summary>
/// Linear acceleration sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_LINEAR_ACCELERATION"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "LinearAcceleration", hideInUI = true)]
public class AndroidLinearAccelerationSensor : LinearAccelerationSensor
{
}
/// <summary>
/// Rotation vector sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_ROTATION_VECTOR"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "RotationVector", hideInUI = true)]
public class AndroidRotationVector : AttitudeSensor
{
}
/// <summary>
/// Relative humidity sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_RELATIVE_HUMIDITY"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "RelativeHumidity", hideInUI = true)]
public class AndroidRelativeHumidity : HumiditySensor
{
}
/// <summary>
/// Ambient temperature sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_AMBIENT_TEMPERATURE"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "AmbientTemperature", hideInUI = true)]
public class AndroidAmbientTemperature : AmbientTemperatureSensor
{
}
/// <summary>
/// Game rotation vector sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_GAME_ROTATION_VECTOR"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "GameRotationVector", hideInUI = true)]
public class AndroidGameRotationVector : AttitudeSensor
{
}
/// <summary>
/// Step counter sensor device on Android.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_STEP_COUNTER"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = "StepCounter", hideInUI = true)]
public class AndroidStepCounter : StepCounter
{
}
/// <summary>
/// Hinge angle sensor device on Android.
/// This sensor is usually available on foldable devices.
/// </summary>
/// <seealso href="https://developer.android.com/reference/android/hardware/Sensor#TYPE_HINGE_ANGLE"/>
[InputControlLayout(stateType = typeof(AndroidSensorState), variants = nameof(AndroidSensorType.HingeAngle), hideInUI = true)]
public class AndroidHingeAngle : HingeAngle
{
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 830105192d35a17469c56711468d1e8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,174 @@
#if UNITY_EDITOR || UNITY_ANDROID
using System.Linq;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Android.LowLevel;
namespace UnityEngine.InputSystem.Android
{
/// <summary>
/// Initializes custom android devices.
/// You can use 'adb shell dumpsys input' from terminal to output information about all input devices.
/// </summary>
#if UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
public
#else
internal
#endif
class AndroidSupport
{
internal const string kAndroidInterface = "Android";
public static void Initialize()
{
InputSystem.RegisterLayout<AndroidGamepad>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidGameController"));
InputSystem.RegisterLayout<AndroidJoystick>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidGameController"));
InputSystem.RegisterLayout<DualShock4GamepadAndroid>();
InputSystem.RegisterLayout<XboxOneGamepadAndroid>();
////TODO: capability matching does not yet support bitmasking so these remain handled by OnFindLayoutForDevice for now
InputSystem.RegisterLayout<AndroidGamepadWithDpadAxes>();
InputSystem.RegisterLayout<AndroidGamepadWithDpadButtons>();
InputSystem.RegisterProcessor<AndroidCompensateDirectionProcessor>();
InputSystem.RegisterProcessor<AndroidCompensateRotationProcessor>();
// Add sensors
InputSystem.RegisterLayout<AndroidAccelerometer>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.Accelerometer));
InputSystem.RegisterLayout<AndroidMagneticFieldSensor>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.MagneticField));
InputSystem.RegisterLayout<AndroidGyroscope>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.Gyroscope));
InputSystem.RegisterLayout<AndroidLightSensor>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.Light));
InputSystem.RegisterLayout<AndroidPressureSensor>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.Pressure));
InputSystem.RegisterLayout<AndroidProximity>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.Proximity));
InputSystem.RegisterLayout<AndroidGravitySensor>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.Gravity));
InputSystem.RegisterLayout<AndroidLinearAccelerationSensor>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.LinearAcceleration));
InputSystem.RegisterLayout<AndroidRotationVector>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.RotationVector));
InputSystem.RegisterLayout<AndroidRelativeHumidity>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.RelativeHumidity));
InputSystem.RegisterLayout<AndroidAmbientTemperature>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.AmbientTemperature));
InputSystem.RegisterLayout<AndroidGameRotationVector>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.GameRotationVector));
InputSystem.RegisterLayout<AndroidStepCounter>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.StepCounter));
InputSystem.RegisterLayout<AndroidHingeAngle>(
matches: new InputDeviceMatcher()
.WithInterface(kAndroidInterface)
.WithDeviceClass("AndroidSensor")
.WithCapability("sensorType", AndroidSensorType.HingeAngle));
InputSystem.onFindLayoutForDevice += OnFindLayoutForDevice;
}
internal static string OnFindLayoutForDevice(ref InputDeviceDescription description,
string matchedLayout, InputDeviceExecuteCommandDelegate executeCommandDelegate)
{
// If we already have a matching layout, someone registered a better match.
// We only want to act as a fallback.
if (!string.IsNullOrEmpty(matchedLayout) && matchedLayout != "AndroidGamepad" && matchedLayout != "AndroidJoystick")
return null;
if (description.interfaceName != "Android" || string.IsNullOrEmpty(description.capabilities))
return null;
////TODO: these should just be Controller and Sensor; the interface is already Android
switch (description.deviceClass)
{
case "AndroidGameController":
{
var caps = AndroidDeviceCapabilities.FromJson(description.capabilities);
// Note: Gamepads have both AndroidInputSource.Gamepad and AndroidInputSource.Joystick in input source, while
// Joysticks don't have AndroidInputSource.Gamepad in their input source
if ((caps.inputSources & AndroidInputSource.Gamepad) != AndroidInputSource.Gamepad)
return "AndroidJoystick";
if (caps.motionAxes == null)
return "AndroidGamepadWithDpadButtons";
// Vendor Ids, Product Ids can be found here http://www.linux-usb.org/usb.ids
const int kVendorMicrosoft = 0x045e;
const int kVendorSony = 0x054c;
// Tested with controllers: PS4 DualShock; XboxOne; Nvidia Shield
// Tested on devices: Shield console Android 9; Galaxy s9+ Android 10
if (caps.motionAxes.Contains(AndroidAxis.Z) &&
caps.motionAxes.Contains(AndroidAxis.Rz) &&
caps.motionAxes.Contains(AndroidAxis.HatX) &&
caps.motionAxes.Contains(AndroidAxis.HatY))
{
if (caps.vendorId == kVendorMicrosoft)
return "XboxOneGamepadAndroid";
if (caps.vendorId == kVendorSony)
return "DualShock4GamepadAndroid";
}
// Fallback to generic gamepads
if (caps.motionAxes.Contains(AndroidAxis.HatX) &&
caps.motionAxes.Contains(AndroidAxis.HatY))
return "AndroidGamepadWithDpadAxes";
return "AndroidGamepadWithDpadButtons";
}
default:
return null;
}
}
}
}
#endif // UNITY_EDITOR || UNITY_ANDROID

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c8442e8094ce974598eacc0302ddabb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: