暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PlayerInput.cs 89KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine.Events;
  4. using UnityEngine.InputSystem.LowLevel;
  5. using UnityEngine.InputSystem.Users;
  6. using UnityEngine.InputSystem.Utilities;
  7. #if UNITY_EDITOR
  8. using UnityEngine.InputSystem.Editor;
  9. #endif
  10. #if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI
  11. using UnityEngine.InputSystem.UI;
  12. #endif
  13. ////TODO: add support for keeping a player's InputUser alive and reconnecting back to it
  14. ////TODO: when joining is *off*, allow auto-switching even in multiplayer
  15. ////TODO: differentiate not only by already paired devices but rather take control schemes into account; allow two players to be on the same
  16. //// device as long as they are using different control schemes
  17. ////TODO: allow PlayerInput to be set up in a way where it's in an unpaired/non-functional state and expects additional configuration
  18. ////REVIEW: callback behaviors have been very confusing for users; simplify&clarify this
  19. ////REVIEW: having everything coupled to component enable/disable is quite restrictive; can we allow PlayerInputs
  20. //// to be disabled without them leaving the game? would help when wanting to keep players around in the background
  21. //// and only temporarily disable them
  22. ////TODO: add support for "continuous" callbacks
  23. ////TODO: add event for control scheme switches
  24. ////TODO: add ability to name players
  25. ////TODO: refresh caches when asset is modified at runtime
  26. ////TODO: handle required actions ahead of time so that we catch it if a device matches by type but doesn't otherwise
  27. ////TODO: handle case of control scheme not having any devices in its requirements
  28. ////TODO: add method to pass an object implementing a generated action interface (IXXXActions) and have it hooked up automatically
  29. //// (or maybe look for implementation on components in same object?)
  30. ////TODO: warn if control schemes have no device requirements
  31. ////FIXME: why can't I join with a mouse left click?
  32. namespace UnityEngine.InputSystem
  33. {
  34. /// <summary>
  35. /// Represents a separate player in the game complete with a set of actions exclusive
  36. /// to the player and a set of paired device.
  37. /// </summary>
  38. /// <remarks>
  39. /// PlayerInput is a high-level wrapper around much of the input system's functionality
  40. /// which is meant to help getting set up with the new input system quickly. It takes
  41. /// care of <see cref="InputAction"/> bookkeeping and has a custom UI(requires the "Unity UI" package) to help
  42. /// setting up input.
  43. ///
  44. /// The component supports local multiplayer implicitly. Each PlayerInput instance
  45. /// represents a distinct user with its own set of devices and actions. To orchestrate
  46. /// player management and facilitate mechanics such as joining by device activity, use
  47. /// <see cref="UnityEngine.InputSystem.PlayerInputManager"/>.
  48. ///
  49. /// The way PlayerInput notifies script code of events is determined by <see cref="notificationBehavior"/>.
  50. /// By default, this is set to <see cref="UnityEngine.InputSystem.PlayerNotifications.SendMessages"/> which will use
  51. /// <see cref="GameObject.SendMessage(string,object)"/> to send messages to the <see cref="GameObject"/>
  52. /// that PlayerInput sits on.
  53. ///
  54. /// <example>
  55. /// <code>
  56. /// // Component to sit next to PlayerInput.
  57. /// [RequireComponent(typeof(PlayerInput))]
  58. /// public class MyPlayerLogic : MonoBehaviour
  59. /// {
  60. /// public GameObject projectilePrefab;
  61. ///
  62. /// private Vector2 m_Look;
  63. /// private Vector2 m_Move;
  64. /// private bool m_Fire;
  65. ///
  66. /// // 'Fire' input action has been triggered. For 'Fire' we want continuous
  67. /// // action (that is, firing) while the fire button is held such that the action
  68. /// // gets triggered repeatedly while the button is down. We can easily set this
  69. /// // up by having a "Press" interaction on the button and setting it to repeat
  70. /// // at fixed intervals.
  71. /// public void OnFire()
  72. /// {
  73. /// Instantiate(projectilePrefab);
  74. /// }
  75. ///
  76. /// // 'Move' input action has been triggered.
  77. /// public void OnMove(InputValue value)
  78. /// {
  79. /// m_Move = value.Get&lt;Vector2&gt;();
  80. /// }
  81. ///
  82. /// // 'Look' input action has been triggered.
  83. /// public void OnLook(InputValue value)
  84. /// {
  85. /// m_Look = value.Get&lt;Vector2&gt;();
  86. /// }
  87. ///
  88. /// public void OnUpdate()
  89. /// {
  90. /// // Update transform from m_Move and m_Look
  91. /// }
  92. /// }
  93. /// </code>
  94. /// </example>
  95. ///
  96. /// It is also possible to use the polling API of <see cref="InputAction"/>s (see
  97. /// <see cref="InputAction.triggered"/> and <see cref="InputAction.ReadValue{TValue}"/>)
  98. /// in combination with PlayerInput.
  99. ///
  100. /// <example>
  101. /// <code>
  102. /// // Component to sit next to PlayerInput.
  103. /// [RequireComponent(typeof(PlayerInput))]
  104. /// public class MyPlayerLogic : MonoBehaviour
  105. /// {
  106. /// public GameObject projectilePrefab;
  107. ///
  108. /// private PlayerInput m_PlayerInput;
  109. /// private InputAction m_LookAction;
  110. /// private InputAction m_MoveAction;
  111. /// private InputAction m_FireAction;
  112. ///
  113. /// public void OnUpdate()
  114. /// {
  115. /// // First update we look up all the data we need.
  116. /// // NOTE: We don't do this in OnEnable as PlayerInput itself performing some
  117. /// // initialization work in OnEnable.
  118. /// if (m_PlayerInput == null)
  119. /// {
  120. /// m_PlayerInput = GetComponent&lt;PlayerInput&gt;();
  121. /// m_FireAction = m_PlayerInput.actions["fire"];
  122. /// m_LookAction = m_PlayerInput.actions["look"];
  123. /// m_MoveAction = m_PlayerInput.actions["move"];
  124. /// }
  125. ///
  126. /// if (m_FireAction.triggered)
  127. /// /* firing logic... */;
  128. ///
  129. /// var move = m_MoveAction.ReadValue&lt;Vector2&gt;();
  130. /// var look = m_LookAction.ReadValue&lt;Vector2&gt;();
  131. /// /* Update transform from move&amp;look... */
  132. /// }
  133. /// }
  134. /// </code>
  135. /// </example>
  136. ///
  137. /// When enabled, PlayerInput will create an <see cref="InputUser"/> and pair devices to the
  138. /// user which are then specific to the player. The set of devices can be controlled explicitly
  139. /// when instantiating a PlayerInput through <see cref="Instantiate(GameObject,int,string,int,InputDevice[])"/>
  140. /// or <see cref="Instantiate(GameObject,int,string,int,InputDevice)"/>. This also makes it possible
  141. /// to assign the same device to two different players, e.g. for split-keyboard play.
  142. ///
  143. /// <example>
  144. /// <code>
  145. /// var p1 = PlayerInput.Instantiate(playerPrefab,
  146. /// controlScheme: "KeyboardLeft", device: Keyboard.current);
  147. /// var p2 = PlayerInput.Instantiate(playerPrefab,
  148. /// controlScheme: "KeyboardRight", device: Keyboard.current);
  149. /// </code>
  150. /// </example>
  151. ///
  152. /// If no specific devices are given to a PlayerInput, the component will look for compatible
  153. /// devices present in the system and pair them to itself automatically. If the PlayerInput's
  154. /// <see cref="actions"/> have control schemes defined for them, PlayerInput will look for a
  155. /// control scheme for which all required devices are available and not paired to any other player.
  156. /// It will try <see cref="defaultControlScheme"/> first (if set), but then fall back to trying
  157. /// all available schemes in order. Once a scheme is found for which all required devices are
  158. /// available, PlayerInput will pair those devices to itself and select the given scheme.
  159. ///
  160. /// If no control schemes are defined, PlayerInput will try to bind as many as-of-yet unpaired
  161. /// devices to itself as it can match to bindings present in the <see cref="actions"/>. This means
  162. /// that if, for example, there's binding for both keyboard and gamepad and there is one keyboard
  163. /// and two gamepads available when PlayerInput is enabled, all three devices will be paired to
  164. /// the player.
  165. ///
  166. /// Note that when using <see cref="PlayerInputManager"/>, device pairing to players is controlled
  167. /// from the joining logic. In that case, PlayerInput will automatically pair the device from which
  168. /// the player joined. If control schemes are present in <see cref="actions"/>, the first one compatible
  169. /// with that device is chosen. If additional devices are required, these will be paired from the pool
  170. /// of currently unpaired devices.
  171. ///
  172. /// Device pairings can be changed at any time by either manually controlling pairing through
  173. /// <see cref="InputUser.PerformPairingWithDevice"/> (and related methods) using a PlayerInput's
  174. /// assigned <see cref="user"/> or by switching control schemes (e.g. using
  175. /// <see cref="SwitchCurrentControlScheme(string,InputDevice[])"/>), if any are present in the PlayerInput's
  176. /// <see cref="actions"/>.
  177. ///
  178. /// When a player loses a device paired to it (e.g. when it is unplugged or loses power), <see cref="InputUser"/>
  179. /// will signal <see cref="InputUserChange.DeviceLost"/> which is also surfaced as a message,
  180. /// <see cref="deviceLostEvent"/>, or <see cref="onDeviceLost"/> (depending on <see cref="notificationBehavior"/>).
  181. /// When a device is reconnected, <see cref="InputUser"/> will signal <see cref="InputUserChange.DeviceRegained"/>
  182. /// which also is surfaced as a message, as <see cref="deviceRegainedEvent"/>, or <see cref="onDeviceRegained"/>
  183. /// (depending on <see cref="notificationBehavior"/>).
  184. ///
  185. /// When there is only a single active PlayerInput in the game, joining is not enabled (see
  186. /// <see cref="PlayerInputManager.joiningEnabled"/>), and <see cref="neverAutoSwitchControlSchemes"/> is not
  187. /// set to <c>true</c>, device pairings for the player will also update automatically based on device usage.
  188. ///
  189. /// If control schemes are present in <see cref="actions"/>, then if a device is used (not merely plugged in
  190. /// but rather receives input on a non-noisy, non-synthetic control) which is compatible with a control scheme
  191. /// other than the currently used one, PlayerInput will attempt to switch to that control scheme. Success depends
  192. /// on whether all device requirements for that scheme are met from the set of available devices. If a control
  193. /// scheme happens, <see cref="InputUser"/> signals <see cref="InputUserChange.ControlSchemeChanged"/> on
  194. /// <see cref="InputUser.onChange"/>.
  195. ///
  196. /// If no control schemes are present in <see cref="actions"/>, PlayerInput will automatically pair any newly
  197. /// available device to itself if the given device has any bindings available for it.
  198. ///
  199. /// Both behaviors described in the previous two paragraphs are automatically disabled if more than one
  200. /// PlayerInput is active.
  201. /// </remarks>
  202. /// <seealso cref="UnityEngine.InputSystem.PlayerInputManager"/>
  203. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
  204. [AddComponentMenu("Input/Player Input")]
  205. [DisallowMultipleComponent]
  206. [HelpURL(InputSystem.kDocUrl + "/manual/PlayerInput.html")]
  207. public class PlayerInput : MonoBehaviour
  208. {
  209. /// <summary>
  210. /// Name of the message that is sent with <c>UnityEngine.Object.SendMessage</c> when a
  211. /// player loses a device.
  212. /// </summary>
  213. /// <seealso cref="onDeviceLost"/>
  214. public const string DeviceLostMessage = "OnDeviceLost";
  215. /// <summary>
  216. /// Name of the message that is sent with <c>UnityEngine.Object.SendMessage</c> when a
  217. /// player regains a device.
  218. /// </summary>
  219. /// <seealso cref="onDeviceRegained"/>
  220. public const string DeviceRegainedMessage = "OnDeviceRegained";
  221. /// <summary>
  222. /// Name of the message that is sent with <c>UnityEngine.Object.SendMessage</c> when the
  223. /// controls used by a player are changed.
  224. /// </summary>
  225. /// <seealso cref="onControlsChanged"/>
  226. public const string ControlsChangedMessage = "OnControlsChanged";
  227. /// <summary>
  228. /// Whether input is on the player is active.
  229. /// </summary>
  230. /// <value>If true, the player is receiving input.</value>
  231. /// <seealso cref="ActivateInput"/>
  232. /// <seealso cref="DeactivateInput"/>
  233. public bool inputIsActive => m_InputActive;
  234. [Obsolete("Use inputIsActive instead.")]
  235. public bool active => inputIsActive;
  236. /// <summary>
  237. /// Unique, zero-based index of the player. For example, <c>2</c> for the third player.
  238. /// </summary>
  239. /// <value>Unique index of the player.</value>
  240. /// <remarks>
  241. /// Once assigned, a player index will not change.
  242. ///
  243. /// Note that the player index does not necessarily correspond to the player's index in <see cref="all"/>.
  244. /// The array will always contain all currently enabled players so when a player is disabled or destroyed,
  245. /// it will be removed from the array. However, the player index of the remaining players will not change.
  246. /// </remarks>
  247. public int playerIndex => m_PlayerIndex;
  248. /// <summary>
  249. /// If split-screen is enabled (<see cref="UnityEngine.InputSystem.PlayerInputManager.splitScreen"/>),
  250. /// this is the index of the screen area used by the player.
  251. /// </summary>
  252. /// <value>Index of split-screen area assigned to player or -1 if the player is not
  253. /// using split-screen.</value>
  254. /// <remarks>
  255. /// Split screen areas are enumerated row by row and within rows, column by column. So, if, for example,
  256. /// there are four separate split-screen areas, the upper left one is #0, the upper right one is #1,
  257. /// the lower left one is #2, and the lower right one is #3.
  258. ///
  259. /// Split screen areas are usually assigned automatically but players can also be assigned to
  260. /// areas explicitly through <see cref="Instantiate(GameObject,int,string,int,InputDevice)"/> or
  261. /// <see cref="PlayerInputManager.JoinPlayer(int,int,string,InputDevice)"/>.
  262. /// </remarks>
  263. /// <seealso cref="camera"/>
  264. /// <seealso cref="PlayerInputManager.splitScreen"/>
  265. public int splitScreenIndex => m_SplitScreenIndex;
  266. /// <summary>
  267. /// Input actions associated with the player.
  268. /// </summary>
  269. /// <value>Asset holding the player's input actions.</value>
  270. /// <remarks>
  271. /// Note that every player will maintain a unique copy of the given actions such that
  272. /// each player receives an identical copy. When assigning the same actions to multiple players,
  273. /// the first player will use the given actions as is but any subsequent player will make a copy
  274. /// of the actions using <see cref="Object.Instantiate(Object)"/>.
  275. ///
  276. /// The asset may contain an arbitrary number of action maps. By setting <see cref="defaultActionMap"/>,
  277. /// one of them can be selected to enabled automatically when PlayerInput is enabled. If no default
  278. /// action map is selected, none of the action maps will be enabled by PlayerInput itself. Use
  279. /// <see cref="SwitchCurrentActionMap"/> or just call <see cref="InputActionMap.Enable"/> directly
  280. /// to enable a specific map.
  281. ///
  282. /// Notifications will be sent for all actions in the asset, not just for those in the first action
  283. /// map. This means that if additional maps are manually enabled and disabled, notifications will
  284. /// be sent for their actions as they receive input.
  285. /// </remarks>
  286. /// <seealso cref="InputUser.actions"/>
  287. /// <seealso cref="SwitchCurrentActionMap"/>
  288. public InputActionAsset actions
  289. {
  290. get
  291. {
  292. if (!m_ActionsInitialized && gameObject.activeInHierarchy)
  293. InitializeActions();
  294. return m_Actions;
  295. }
  296. set
  297. {
  298. if (m_Actions == value)
  299. return;
  300. // Make sure that if we already have actions, they get disabled.
  301. if (m_Actions != null)
  302. {
  303. m_Actions.Disable();
  304. if (m_ActionsInitialized)
  305. UninitializeActions();
  306. }
  307. m_Actions = value;
  308. if (m_Enabled)
  309. {
  310. ClearCaches();
  311. AssignUserAndDevices();
  312. InitializeActions();
  313. if (m_InputActive)
  314. ActivateInput();
  315. }
  316. }
  317. }
  318. /// <summary>
  319. /// Name of the currently active control scheme.
  320. /// </summary>
  321. /// <value>Name of the currently active control scheme or <c>null</c>.</value>
  322. /// <remarks>
  323. /// Note that this property will be <c>null</c> if there are no control schemes
  324. /// defined in <see cref="actions"/>.
  325. /// </remarks>
  326. /// <seealso cref="SwitchCurrentControlScheme(UnityEngine.InputSystem.InputDevice[])"/>
  327. /// <seealso cref="defaultControlScheme"/>
  328. /// <seealso cref="InputActionAsset.controlSchemes"/>
  329. public string currentControlScheme
  330. {
  331. get
  332. {
  333. if (!m_InputUser.valid)
  334. return null;
  335. var scheme = m_InputUser.controlScheme;
  336. return scheme?.name;
  337. }
  338. }
  339. /// <summary>
  340. /// The default control scheme to try.
  341. /// </summary>
  342. /// <value>Name of the default control scheme.</value>
  343. /// <remarks>
  344. /// When PlayerInput is enabled and this is not <c>null</c> and not empty, the PlayerInput
  345. /// will look up the control scheme in <see cref="InputActionAsset.controlSchemes"/> of
  346. /// <see cref="actions"/>. If found, PlayerInput will try to activate the scheme. This will
  347. /// succeed only if all devices required by the control scheme are either already paired to
  348. /// the player or are available as devices not used by other PlayerInputs.
  349. ///
  350. /// Note that this property only determines the first control scheme to try. If using the
  351. /// control scheme fails, PlayerInput will fall back to trying the other control schemes
  352. /// (if any) available from <see cref="actions"/>.
  353. /// </remarks>
  354. /// <seealso cref="SwitchCurrentControlScheme(InputDevice[])"/>
  355. /// <seealso cref="currentControlScheme"/>
  356. public string defaultControlScheme
  357. {
  358. get => m_DefaultControlScheme;
  359. set => m_DefaultControlScheme = value;
  360. }
  361. /// <summary>
  362. /// If true, do not automatically switch control schemes even when there is only a single player.
  363. /// By default, this property is false.
  364. /// </summary>
  365. /// <value>If true, do not switch control schemes when other devices are used.</value>
  366. /// <remarks>
  367. /// By default, when there is only a single PlayerInput enabled, we assume that the game is in
  368. /// single-player mode and that the player should be able to freely switch between the control schemes
  369. /// supported by the game. For example, if the player is currently using mouse and keyboard, but is
  370. /// then switching to a gamepad, PlayerInput should automatically switch to the control scheme for
  371. /// gamepads, if present.
  372. ///
  373. /// When there is more than one PlayerInput or when joining is enabled <see cref="PlayerInputManager"/>,
  374. /// this behavior is automatically turned off as we wouldn't know which player is switching if a
  375. /// currently unpaired device is used.
  376. ///
  377. /// By setting this property to true, auto-switching of control schemes is forcibly turned off and
  378. /// will thus not be performed even if there is only a single PlayerInput in the game.
  379. ///
  380. /// Note that you can still switch control schemes manually using <see
  381. /// cref="SwitchCurrentControlScheme(string,InputDevice[])"/>.
  382. /// </remarks>
  383. /// <seealso cref="currentControlScheme"/>
  384. /// <seealso cref="isSinglePlayer"/>
  385. public bool neverAutoSwitchControlSchemes
  386. {
  387. get => m_NeverAutoSwitchControlSchemes;
  388. set
  389. {
  390. if (m_NeverAutoSwitchControlSchemes == value)
  391. return;
  392. m_NeverAutoSwitchControlSchemes = value;
  393. if (m_Enabled)
  394. {
  395. if (!value && !m_OnUnpairedDeviceUsedHooked)
  396. StartListeningForUnpairedDeviceActivity();
  397. else if (value && m_OnUnpairedDeviceUsedHooked)
  398. StopListeningForUnpairedDeviceActivity();
  399. }
  400. }
  401. }
  402. ////REVIEW: this is inconsistent; currentControlScheme is a string, this is an InputActionMap
  403. /// <summary>
  404. /// The currently enabled action map.
  405. /// </summary>
  406. /// <value>Reference to the currently enabled action or <c>null</c> if no action
  407. /// map has been enabled by PlayerInput.</value>
  408. /// <remarks>
  409. /// Note that the concept of "current action map" is local to PlayerInput. You can still freely
  410. /// enable and disable action maps directly on the <see cref="actions"/> asset. This property
  411. /// only tracks which action map has been enabled under the control of PlayerInput, i.e. either
  412. /// by means of <see cref="defaultActionMap"/> or by using <see cref="SwitchCurrentActionMap"/>.
  413. /// </remarks>
  414. /// <seealso cref="SwitchCurrentActionMap"/>
  415. public InputActionMap currentActionMap
  416. {
  417. get => m_CurrentActionMap;
  418. set
  419. {
  420. // If someone switches maps from an action callback, we may get here recursively
  421. // from Disable(). To avoid that, we null out the current action map while
  422. // we disable it.
  423. var oldMap = m_CurrentActionMap;
  424. m_CurrentActionMap = null;
  425. oldMap?.Disable();
  426. // Switch to new map.
  427. m_CurrentActionMap = value;
  428. m_CurrentActionMap?.Enable();
  429. }
  430. }
  431. /// <summary>
  432. /// Name (see <see cref="InputActionMap.name"/>) or ID (see <see cref="InputActionMap.id"/>) of the action
  433. /// map to enable by default.
  434. /// </summary>
  435. /// <value>Action map to enable by default or <c>null</c>.</value>
  436. /// <remarks>
  437. /// By default, when enabled, PlayerInput will not enable any of the actions in the <see cref="actions"/>
  438. /// asset. By setting this property, however, PlayerInput can be made to automatically enable the respective
  439. /// action map.
  440. /// </remarks>
  441. /// <seealso cref="currentActionMap"/>
  442. /// <seealso cref="SwitchCurrentActionMap"/>
  443. public string defaultActionMap
  444. {
  445. get => m_DefaultActionMap;
  446. set => m_DefaultActionMap = value;
  447. }
  448. /// <summary>
  449. /// Determines how the component notifies listeners about input actions and other input-related
  450. /// events pertaining to the player.
  451. /// </summary>
  452. /// <value>How to trigger notifications on events.</value>
  453. /// <remarks>
  454. /// By default, the component will use <see cref="GameObject.SendMessage(string,object)"/> to send messages
  455. /// to the <see cref="GameObject"/>. This can be changed by selecting a different <see cref="UnityEngine.InputSystem.PlayerNotifications"/>
  456. /// behavior.
  457. /// </remarks>
  458. /// <seealso cref="actionEvents"/>
  459. /// <seealso cref="deviceLostEvent"/>
  460. /// <seealso cref="deviceRegainedEvent"/>
  461. public PlayerNotifications notificationBehavior
  462. {
  463. get => m_NotificationBehavior;
  464. set
  465. {
  466. if (m_NotificationBehavior == value)
  467. return;
  468. if (m_Enabled)
  469. UninitializeActions();
  470. m_NotificationBehavior = value;
  471. if (m_Enabled)
  472. InitializeActions();
  473. }
  474. }
  475. /// <summary>
  476. /// List of events invoked in response to actions being triggered.
  477. /// </summary>
  478. /// <remarks>
  479. /// This array is only used if <see cref="notificationBehavior"/> is set to
  480. /// <see cref="UnityEngine.InputSystem.PlayerNotifications.InvokeUnityEvents"/>.
  481. /// </remarks>
  482. public ReadOnlyArray<ActionEvent> actionEvents
  483. {
  484. get => m_ActionEvents;
  485. set
  486. {
  487. if (m_Enabled)
  488. UninitializeActions();
  489. m_ActionEvents = value.ToArray();
  490. if (m_Enabled)
  491. InitializeActions();
  492. }
  493. }
  494. /// <summary>
  495. /// Event that is triggered when the player loses a device (e.g. the batteries run out).
  496. /// </summary>
  497. /// <remarks>
  498. /// This event is only used if <see cref="notificationBehavior"/> is set to
  499. /// <see cref="UnityEngine.InputSystem.PlayerNotifications.InvokeUnityEvents"/>.
  500. /// </remarks>
  501. public DeviceLostEvent deviceLostEvent
  502. {
  503. get
  504. {
  505. if (m_DeviceLostEvent == null)
  506. m_DeviceLostEvent = new DeviceLostEvent();
  507. return m_DeviceLostEvent;
  508. }
  509. }
  510. /// <summary>
  511. /// Event that is triggered when the player recovers from device loss and is good to go again.
  512. /// </summary>
  513. /// <remarks>
  514. /// This event is only used if <see cref="notificationBehavior"/> is set to
  515. /// <see cref="UnityEngine.InputSystem.PlayerNotifications.InvokeUnityEvents"/>.
  516. /// </remarks>
  517. public DeviceRegainedEvent deviceRegainedEvent
  518. {
  519. get
  520. {
  521. if (m_DeviceRegainedEvent == null)
  522. m_DeviceRegainedEvent = new DeviceRegainedEvent();
  523. return m_DeviceRegainedEvent;
  524. }
  525. }
  526. /// <summary>
  527. /// Event that is triggered when the controls used by the player change.
  528. /// </summary>
  529. /// <remarks>
  530. /// This event is only used if <see cref="notificationBehavior"/> is set to
  531. /// <see cref="UnityEngine.InputSystem.PlayerNotifications.InvokeUnityEvents"/>.
  532. ///
  533. /// The event is trigger when the set of <see cref="devices"/> used by the player change,
  534. /// when the player switches to a different control scheme (see <see cref="currentControlScheme"/>),
  535. /// or when the bindings used by the player are changed (e.g. when rebinding them). Also,
  536. /// for <see cref="Keyboard"/> devices, the event is triggered when the currently used
  537. /// keyboard layout (see <see cref="Keyboard.keyboardLayout"/>) changes.
  538. /// </remarks>
  539. public ControlsChangedEvent controlsChangedEvent
  540. {
  541. get
  542. {
  543. if (m_ControlsChangedEvent == null)
  544. m_ControlsChangedEvent = new ControlsChangedEvent();
  545. return m_ControlsChangedEvent;
  546. }
  547. }
  548. /// <summary>
  549. /// If <see cref="notificationBehavior"/> is set to <see cref="PlayerNotifications.InvokeCSharpEvents"/>, this
  550. /// event is triggered when an action fires.
  551. /// </summary>
  552. /// <value>Callbacks that get called when an action triggers.</value>
  553. /// <remarks>
  554. /// If <see cref="notificationBehavior"/> is not set to <see cref="PlayerNotifications.InvokeCSharpEvents"/>, the
  555. /// value of this property is ignored.
  556. ///
  557. /// The callbacks are called in sync (and with the same argument) with <see cref="InputAction.started"/>,
  558. /// <see cref="InputAction.performed"/>, and <see cref="InputAction.canceled"/>.
  559. /// </remarks>
  560. /// <seealso cref="InputActionMap.actionTriggered"/>
  561. /// <seealso cref="InputAction.started"/>
  562. /// <seealso cref="InputAction.performed"/>
  563. /// <seealso cref="InputAction.canceled"/>
  564. /// <seealso cref="actions"/>
  565. public event Action<InputAction.CallbackContext> onActionTriggered
  566. {
  567. add
  568. {
  569. if (value == null)
  570. throw new ArgumentNullException(nameof(value));
  571. m_ActionTriggeredCallbacks.AddCallback(value);
  572. }
  573. remove
  574. {
  575. if (value == null)
  576. throw new ArgumentNullException(nameof(value));
  577. m_ActionTriggeredCallbacks.RemoveCallback(value);
  578. }
  579. }
  580. /// <summary>
  581. /// If <see cref="notificationBehavior"/> is <see cref="PlayerNotifications.InvokeCSharpEvents"/>, this event
  582. /// is triggered when a device paired to the player is disconnected.
  583. /// </summary>
  584. /// <value>Callbacks that get called when the player loses a device.</value>
  585. /// <remarks>
  586. /// If <see cref="notificationBehavior"/> is not <see cref="PlayerNotifications.InvokeCSharpEvents"/>, the value
  587. /// of this property is ignored.
  588. ///
  589. /// The argument is the player that lost its device (i.e. the player on which the callback is installed).
  590. /// </remarks>
  591. /// <seealso cref="onDeviceRegained"/>
  592. /// <seealso cref="InputUserChange.DeviceLost"/>
  593. public event Action<PlayerInput> onDeviceLost
  594. {
  595. add
  596. {
  597. if (value == null)
  598. throw new ArgumentNullException(nameof(value));
  599. m_DeviceLostCallbacks.AddCallback(value);
  600. }
  601. remove
  602. {
  603. if (value == null)
  604. throw new ArgumentNullException(nameof(value));
  605. m_DeviceLostCallbacks.RemoveCallback(value);
  606. }
  607. }
  608. /// <summary>
  609. /// If <see cref="notificationBehavior"/> is <see cref="PlayerNotifications.InvokeCSharpEvents"/>, this event
  610. /// is triggered when the player previously lost a device and has now regained it or an equivalent device.
  611. /// </summary>
  612. /// <value>Callbacks that get called when the player regains a device.</value>
  613. /// <remarks>
  614. /// If <see cref="notificationBehavior"/> is not <see cref="PlayerNotifications.InvokeCSharpEvents"/>, the value
  615. /// of this property is ignored.
  616. ///
  617. /// The argument is the player that regained a device (i.e. the player on which the callback is installed).
  618. /// </remarks>
  619. /// <seealso cref="onDeviceLost"/>
  620. /// <seealso cref="InputUserChange.DeviceRegained"/>
  621. public event Action<PlayerInput> onDeviceRegained
  622. {
  623. add
  624. {
  625. if (value == null)
  626. throw new ArgumentNullException(nameof(value));
  627. m_DeviceRegainedCallbacks.AddCallback(value);
  628. }
  629. remove
  630. {
  631. if (value == null)
  632. throw new ArgumentNullException(nameof(value));
  633. m_DeviceRegainedCallbacks.RemoveCallback(value);
  634. }
  635. }
  636. /// <summary>
  637. /// If <see cref="notificationBehavior"/> is <see cref="PlayerNotifications.InvokeCSharpEvents"/>, this event
  638. /// is triggered when the controls used by the players are changed.
  639. /// </summary>
  640. /// <remarks>
  641. /// The callback is invoked when the set of <see cref="devices"/> used by the player change,
  642. /// when the player switches to a different control scheme (see <see cref="currentControlScheme"/>),
  643. /// or when the bindings used by the player are changed (e.g. when rebinding them). Also,
  644. /// for <see cref="Keyboard"/> devices, the callback is invoked when the currently used
  645. /// keyboard layout (see <see cref="Keyboard.keyboardLayout"/>) changes.
  646. /// </remarks>
  647. public event Action<PlayerInput> onControlsChanged
  648. {
  649. add
  650. {
  651. if (value == null)
  652. throw new ArgumentNullException(nameof(value));
  653. m_ControlsChangedCallbacks.AddCallback(value);
  654. }
  655. remove
  656. {
  657. if (value == null)
  658. throw new ArgumentNullException(nameof(value));
  659. m_ControlsChangedCallbacks.RemoveCallback(value);
  660. }
  661. }
  662. ////TODO: clarify the relationship to raycasting in the UI input module
  663. /// <summary>
  664. /// Optional camera associated with the player.
  665. /// </summary>
  666. /// <value>Camera specific to the player or <c>null</c>.</value>
  667. /// <remarks>
  668. /// This is <c>null</c> by default.
  669. ///
  670. /// Associating a camera with a player is necessary only when using split-screen (see <see cref="PlayerInputManager.splitScreen"/>).
  671. /// </remarks>
  672. public
  673. #if UNITY_EDITOR
  674. // camera property is deprecated and only available in Editor.
  675. new
  676. #endif
  677. Camera camera
  678. {
  679. get => m_Camera;
  680. set => m_Camera = value;
  681. }
  682. #if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI
  683. /// <summary>
  684. /// UI InputModule that should have it's input actions synchronized to this PlayerInput's actions.
  685. /// </summary>
  686. public InputSystemUIInputModule uiInputModule
  687. {
  688. get => m_UIInputModule;
  689. set
  690. {
  691. if (m_UIInputModule == value)
  692. return;
  693. if (m_UIInputModule != null && m_UIInputModule.actionsAsset == m_Actions)
  694. m_UIInputModule.actionsAsset = null;
  695. m_UIInputModule = value;
  696. if (m_UIInputModule != null && m_Actions != null)
  697. m_UIInputModule.actionsAsset = m_Actions;
  698. }
  699. }
  700. #endif
  701. /// <summary>
  702. /// The internal user tied to the player.
  703. /// </summary>
  704. public InputUser user => m_InputUser;
  705. /// <summary>
  706. /// The devices paired to the player.
  707. /// </summary>
  708. /// <value>List of devices paired to player.</value>
  709. /// <remarks>
  710. /// </remarks>
  711. /// <seealso cref="InputUser.pairedDevices"/>
  712. public ReadOnlyArray<InputDevice> devices
  713. {
  714. get
  715. {
  716. if (!m_InputUser.valid)
  717. return new ReadOnlyArray<InputDevice>();
  718. return m_InputUser.pairedDevices;
  719. }
  720. }
  721. /// <summary>
  722. /// Whether the player is missed required devices. This means that the player's
  723. /// input setup is probably at least partially non-functional.
  724. /// </summary>
  725. /// <value>True if the player is missing devices required by the control scheme.</value>
  726. /// <remarks>
  727. /// This can happen, for example, if the a device is unplugged during the game.
  728. /// </remarks>
  729. /// <seealso cref="InputControlScheme.deviceRequirements"/>
  730. /// <seealso cref="InputUser.hasMissingRequiredDevices"/>
  731. public bool hasMissingRequiredDevices => user.valid && user.hasMissingRequiredDevices;
  732. /// <summary>
  733. /// List of all players that are currently joined. Sorted by <see cref="playerIndex"/> in
  734. /// increasing order.
  735. /// </summary>
  736. /// <value>List of active PlayerInputs.</value>
  737. /// <remarks>
  738. /// While the list is sorted by <see cref="playerIndex"/>, note that this does not mean that the <see cref="playerIndex"/>
  739. /// of a player corresponds to the index in this list. If, for example, three players join and then the second player leaves,
  740. /// the list will contain one player with <see cref="playerIndex"/> 0 followed by one player with <see cref="playerIndex"/> 2.
  741. /// </remarks>
  742. /// <seealso cref="PlayerInputManager.JoinPlayer(int,int,string,InputDevice)"/>
  743. /// <seealso cref="Instantiate(GameObject,int,string,int,InputDevice)"/>
  744. public static ReadOnlyArray<PlayerInput> all => new ReadOnlyArray<PlayerInput>(s_AllActivePlayers, 0, s_AllActivePlayersCount);
  745. /// <summary>
  746. /// Whether PlayerInput operates in single-player mode.
  747. /// </summary>
  748. /// <value>If true, there is at most a single PlayerInput.</value>
  749. /// <remarks>
  750. /// Single-player mode is active while there is at most one PlayerInput (there can also be none) and
  751. /// while joining is not enabled in <see cref="PlayerInputManager"/> (if one exists). See <see cref="PlayerInputManager.joiningEnabled"/>.
  752. ///
  753. /// Automatic control scheme switching (if enabled) is predicated on single-player mode being active.
  754. /// </remarks>
  755. /// <seealso cref="neverAutoSwitchControlSchemes"/>
  756. public static bool isSinglePlayer =>
  757. s_AllActivePlayersCount <= 1 &&
  758. (PlayerInputManager.instance == null || !PlayerInputManager.instance.joiningEnabled);
  759. /// <summary>
  760. /// Return the first device of the given type from <see cref="devices"/> paired to the player.
  761. /// If no device of this type is paired to the player, return <c>null</c>.
  762. /// </summary>
  763. /// <typeparam name="TDevice">Type of device to look for (such as <see cref="Mouse"/>). Can be a supertype
  764. /// of the actual device type. For example, querying for <see cref="Pointer"/>, may return a <see cref="Mouse"/>.</typeparam>
  765. /// <returns>The first device paired to the player that is of the given type or <c>null</c> if the player
  766. /// does not have a matching device.</returns>
  767. /// <seealso cref="devices"/>
  768. public TDevice GetDevice<TDevice>()
  769. where TDevice : InputDevice
  770. {
  771. foreach (var device in devices)
  772. if (device is TDevice deviceOfType)
  773. return deviceOfType;
  774. return null;
  775. }
  776. /// <summary>
  777. /// Enable input on the player.
  778. /// </summary>
  779. /// <remarks>
  780. /// Input will automatically be activated when the PlayerInput component is enabled. However, this method
  781. /// can be called to reactivate input after deactivating it with <see cref="DeactivateInput"/>.
  782. ///
  783. /// Note that activating input will activate the current action map only (see <see cref="currentActionMap"/>).
  784. /// </remarks>
  785. /// <see cref="inputIsActive"/>
  786. /// <seealso cref="DeactivateInput"/>
  787. public void ActivateInput()
  788. {
  789. m_InputActive = true;
  790. // If we have no current action map but there's a default
  791. // action map, make it current.
  792. if (m_CurrentActionMap == null && m_Actions != null && !string.IsNullOrEmpty(m_DefaultActionMap))
  793. SwitchCurrentActionMap(m_DefaultActionMap);
  794. else
  795. m_CurrentActionMap?.Enable();
  796. }
  797. /// <summary>
  798. /// Disable input on the player.
  799. /// </summary>
  800. /// <remarks>
  801. /// Input is automatically activated when the PlayerInput component is enabled. This method can be
  802. /// used to deactivate input manually.
  803. ///
  804. /// Note that activating input will deactivate the current action map only (see <see cref="currentActionMap"/>).
  805. /// </remarks>
  806. /// <see cref="ActivateInput"/>
  807. /// <see cref="inputIsActive"/>
  808. public void DeactivateInput()
  809. {
  810. m_CurrentActionMap?.Disable();
  811. m_InputActive = false;
  812. }
  813. [Obsolete("Use DeactivateInput instead.")]
  814. public void PassivateInput()
  815. {
  816. DeactivateInput();
  817. }
  818. /// <summary>
  819. /// Switch the current control scheme to one that fits the given set of devices.
  820. /// </summary>
  821. /// <param name="devices">A list of input devices. Note that if any of the devices is already paired to another
  822. /// player, the device will end up paired to both players.</param>
  823. /// <returns>True if the switch was successful, false otherwise. The latter can happen, for example, if
  824. /// <see cref="actions"/> does not have a control scheme that fits the given set of devices.</returns>
  825. /// <exception cref="ArgumentNullException"><paramref name="devices"/> is <c>null</c>.</exception>
  826. /// <exception cref="InvalidOperationException"><see cref="actions"/> has not been assigned.</exception>
  827. /// <remarks>
  828. /// The player's currently paired devices (see <see cref="devices"/>) will get unpaired.
  829. ///
  830. /// <example>
  831. /// <code>
  832. /// // Switch the first player to keyboard and mouse.
  833. /// PlayerInput.all[0]
  834. /// .SwitchCurrentControlScheme(Keyboard.current, Mouse.current);
  835. /// </code>
  836. /// </example>
  837. /// </remarks>
  838. /// <seealso cref="currentControlScheme"/>
  839. /// <seealso cref="InputActionAsset.controlSchemes"/>
  840. public bool SwitchCurrentControlScheme(params InputDevice[] devices)
  841. {
  842. if (devices == null)
  843. throw new ArgumentNullException(nameof(devices));
  844. if (actions == null)
  845. throw new InvalidOperationException(
  846. "Must set actions on PlayerInput in order to be able to switch control schemes");
  847. // Find control scheme matching given devices in associated action asset
  848. var scheme = InputControlScheme.FindControlSchemeForDevices(devices, actions.controlSchemes);
  849. if (!scheme.HasValue)
  850. return false;
  851. var controlScheme = scheme.Value;
  852. SwitchControlSchemeInternal(ref controlScheme, devices);
  853. return true;
  854. }
  855. ////REVIEW: these should just be SwitchControlScheme
  856. /// <summary>
  857. /// Switch the player to use the given control scheme together with the given devices.
  858. /// </summary>
  859. /// <param name="controlScheme">Name of the control scheme. See <see cref="InputControlScheme.name"/>.</param>
  860. /// <param name="devices">A list of devices.</param>
  861. /// <exception cref="ArgumentNullException"><paramref name="devices"/> is <c>null</c> -or- <paramref name="controlScheme"/> is
  862. /// <c>null</c> or empty.</exception>
  863. /// <remarks>
  864. /// This method can be used to explicitly force a combination of control scheme and a specific set of
  865. /// devices.
  866. ///
  867. /// <example>
  868. /// <code>
  869. /// // Put player 1 on the "Gamepad" control scheme together
  870. /// // with the second gamepad.
  871. /// PlayerInput.all[0].SwitchControlScheme(
  872. /// "Gamepad",
  873. /// Gamepad.all[1]);
  874. /// </code>
  875. /// </example>
  876. ///
  877. /// The player's currently paired devices (see <see cref="devices"/>) will get unpaired.
  878. /// </remarks>
  879. /// <seealso cref="InputActionAsset.controlSchemes"/>
  880. /// <seealso cref="currentControlScheme"/>
  881. public void SwitchCurrentControlScheme(string controlScheme, params InputDevice[] devices)
  882. {
  883. if (string.IsNullOrEmpty(controlScheme))
  884. throw new ArgumentNullException(nameof(controlScheme));
  885. if (devices == null)
  886. throw new ArgumentNullException(nameof(devices));
  887. user.FindControlScheme(controlScheme, out InputControlScheme scheme); // throws if not found
  888. SwitchControlSchemeInternal(ref scheme, devices);
  889. }
  890. public void SwitchCurrentActionMap(string mapNameOrId)
  891. {
  892. // Must be enabled.
  893. if (!m_Enabled)
  894. {
  895. Debug.LogError($"Cannot switch to actions '{mapNameOrId}'; input is not enabled", this);
  896. return;
  897. }
  898. // Must have actions.
  899. if (m_Actions == null)
  900. {
  901. Debug.LogError($"Cannot switch to actions '{mapNameOrId}'; no actions set on PlayerInput", this);
  902. return;
  903. }
  904. // Must have map.
  905. var actionMap = m_Actions.FindActionMap(mapNameOrId);
  906. if (actionMap == null)
  907. {
  908. Debug.LogError($"Cannot find action map '{mapNameOrId}' in actions '{m_Actions}'", this);
  909. return;
  910. }
  911. currentActionMap = actionMap;
  912. }
  913. /// <summary>
  914. /// Return the Nth player.
  915. /// </summary>
  916. /// <param name="playerIndex">Index of the player to return.</param>
  917. /// <returns>The player with the given player index or <c>null</c> if no such
  918. /// player exists.</returns>
  919. /// <seealso cref="PlayerInput.playerIndex"/>
  920. public static PlayerInput GetPlayerByIndex(int playerIndex)
  921. {
  922. for (var i = 0; i < s_AllActivePlayersCount; ++i)
  923. if (s_AllActivePlayers[i].playerIndex == playerIndex)
  924. return s_AllActivePlayers[i];
  925. return null;
  926. }
  927. /// <summary>
  928. /// Find the first PlayerInput who the given device is paired to.
  929. /// </summary>
  930. /// <param name="device">An input device.</param>
  931. /// <returns>The player who is paired to the given device or <c>null</c> if no
  932. /// PlayerInput currently is paired to <paramref name="device"/>.</returns>
  933. /// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
  934. /// <remarks>
  935. /// <example>
  936. /// <code>
  937. /// // Find the player paired to first gamepad.
  938. /// var player = PlayerInput.FindFirstPairedToDevice(Gamepad.all[0]);
  939. /// </code>
  940. /// </example>
  941. /// </remarks>
  942. public static PlayerInput FindFirstPairedToDevice(InputDevice device)
  943. {
  944. if (device == null)
  945. throw new ArgumentNullException(nameof(device));
  946. for (var i = 0; i < s_AllActivePlayersCount; ++i)
  947. {
  948. if (ReadOnlyArrayExtensions.ContainsReference(s_AllActivePlayers[i].devices, device))
  949. return s_AllActivePlayers[i];
  950. }
  951. return null;
  952. }
  953. /// <summary>
  954. /// Instantiate a player object and set up and enable its inputs.
  955. /// </summary>
  956. /// <param name="prefab">Prefab to clone. Must contain a PlayerInput component somewhere in its hierarchy.</param>
  957. /// <param name="playerIndex">Player index to assign to the player. See <see cref="PlayerInput.playerIndex"/>.
  958. /// By default will be assigned automatically based on how many players are in <see cref="all"/>.</param>
  959. /// <param name="controlScheme">Control scheme to activate</param>
  960. /// <param name="splitScreenIndex"></param>
  961. /// <param name="pairWithDevice">Device to pair to the user. By default, this is <c>null</c> which means
  962. /// that PlayerInput will automatically pair with available, unpaired devices based on the control schemes (if any)
  963. /// present in <see cref="actions"/> or on the bindings therein (if no control schemes are present).</param>
  964. /// <returns></returns>
  965. /// <exception cref="ArgumentNullException"><paramref name="prefab"/> is <c>null</c>.</exception>
  966. public static PlayerInput Instantiate(GameObject prefab, int playerIndex = -1, string controlScheme = null,
  967. int splitScreenIndex = -1, InputDevice pairWithDevice = null)
  968. {
  969. if (prefab == null)
  970. throw new ArgumentNullException(nameof(prefab));
  971. // Set initialization data.
  972. s_InitPlayerIndex = playerIndex;
  973. s_InitSplitScreenIndex = splitScreenIndex;
  974. s_InitControlScheme = controlScheme;
  975. if (pairWithDevice != null)
  976. ArrayHelpers.AppendWithCapacity(ref s_InitPairWithDevices, ref s_InitPairWithDevicesCount, pairWithDevice);
  977. return DoInstantiate(prefab);
  978. }
  979. ////TODO: allow instantiating with an existing InputUser
  980. /// <summary>
  981. /// A wrapper around <see cref="Object.Instantiate(Object)"/> that allows instantiating a player prefab and
  982. /// automatically pair one or more specific devices to the newly created player.
  983. /// </summary>
  984. /// <param name="prefab">A player prefab containing a <see cref="PlayerInput"/> component in its hierarchy.</param>
  985. /// <param name="playerIndex"></param>
  986. /// <param name="controlScheme"></param>
  987. /// <param name="splitScreenIndex"></param>
  988. /// <param name="pairWithDevices"></param>
  989. /// <returns></returns>
  990. /// <remarks>
  991. /// Note that unlike <see cref="Object.Instantiate(Object)"/>, this method will always activate the resulting
  992. /// <see cref="GameObject"/> and its components.
  993. /// </remarks>
  994. public static PlayerInput Instantiate(GameObject prefab, int playerIndex = -1, string controlScheme = null,
  995. int splitScreenIndex = -1, params InputDevice[] pairWithDevices)
  996. {
  997. if (prefab == null)
  998. throw new ArgumentNullException(nameof(prefab));
  999. // Set initialization data.
  1000. s_InitPlayerIndex = playerIndex;
  1001. s_InitSplitScreenIndex = splitScreenIndex;
  1002. s_InitControlScheme = controlScheme;
  1003. if (pairWithDevices != null)
  1004. {
  1005. for (var i = 0; i < pairWithDevices.Length; ++i)
  1006. ArrayHelpers.AppendWithCapacity(ref s_InitPairWithDevices, ref s_InitPairWithDevicesCount, pairWithDevices[i]);
  1007. }
  1008. return DoInstantiate(prefab);
  1009. }
  1010. private static PlayerInput DoInstantiate(GameObject prefab)
  1011. {
  1012. var destroyIfDeviceSetupUnsuccessful = s_DestroyIfDeviceSetupUnsuccessful;
  1013. GameObject instance;
  1014. try
  1015. {
  1016. instance = Object.Instantiate(prefab);
  1017. instance.SetActive(true);
  1018. }
  1019. finally
  1020. {
  1021. // Reset init data.
  1022. s_InitPairWithDevicesCount = 0;
  1023. if (s_InitPairWithDevices != null)
  1024. Array.Clear(s_InitPairWithDevices, 0, s_InitPairWithDevicesCount);
  1025. s_InitControlScheme = null;
  1026. s_InitPlayerIndex = -1;
  1027. s_InitSplitScreenIndex = -1;
  1028. s_DestroyIfDeviceSetupUnsuccessful = false;
  1029. }
  1030. var playerInput = instance.GetComponentInChildren<PlayerInput>();
  1031. if (playerInput == null)
  1032. {
  1033. DestroyImmediate(instance);
  1034. Debug.LogError("The GameObject does not have a PlayerInput component", prefab);
  1035. return null;
  1036. }
  1037. if (destroyIfDeviceSetupUnsuccessful && (!playerInput.user.valid || playerInput.hasMissingRequiredDevices))
  1038. {
  1039. DestroyImmediate(instance);
  1040. return null;
  1041. }
  1042. return playerInput;
  1043. }
  1044. [Tooltip("Input actions associated with the player.")]
  1045. [SerializeField] internal InputActionAsset m_Actions;
  1046. [Tooltip("Determine how notifications should be sent when an input-related event associated with the player happens.")]
  1047. [SerializeField] internal PlayerNotifications m_NotificationBehavior;
  1048. [Tooltip("UI InputModule that should have it's input actions synchronized to this PlayerInput's actions.")]
  1049. #if UNITY_INPUT_SYSTEM_ENABLE_UI
  1050. [SerializeField] internal InputSystemUIInputModule m_UIInputModule;
  1051. [Tooltip("Event that is triggered when the PlayerInput loses a paired device (e.g. its battery runs out).")]
  1052. #endif
  1053. [SerializeField] internal DeviceLostEvent m_DeviceLostEvent;
  1054. [SerializeField] internal DeviceRegainedEvent m_DeviceRegainedEvent;
  1055. [SerializeField] internal ControlsChangedEvent m_ControlsChangedEvent;
  1056. [SerializeField] internal ActionEvent[] m_ActionEvents;
  1057. [SerializeField] internal bool m_NeverAutoSwitchControlSchemes;
  1058. [SerializeField] internal string m_DefaultControlScheme;////REVIEW: should we have IDs for these so we can rename safely?
  1059. [SerializeField] internal string m_DefaultActionMap;
  1060. [SerializeField] internal int m_SplitScreenIndex = -1;
  1061. [Tooltip("Reference to the player's view camera. Note that this is only required when using split-screen and/or "
  1062. + "per-player UIs. Otherwise it is safe to leave this property uninitialized.")]
  1063. [SerializeField] internal Camera m_Camera;
  1064. // Value object we use when sending messages via SendMessage() or BroadcastMessage(). Can be ignored
  1065. // by the receiver. We reuse the same object over and over to avoid allocating garbage.
  1066. [NonSerialized] private InputValue m_InputValueObject;
  1067. [NonSerialized] internal InputActionMap m_CurrentActionMap;
  1068. [NonSerialized] private int m_PlayerIndex = -1;
  1069. [NonSerialized] private bool m_InputActive;
  1070. [NonSerialized] private bool m_Enabled;
  1071. [NonSerialized] internal bool m_ActionsInitialized;
  1072. [NonSerialized] private Dictionary<string, string> m_ActionMessageNames;
  1073. [NonSerialized] private InputUser m_InputUser;
  1074. [NonSerialized] private Action<InputAction.CallbackContext> m_ActionTriggeredDelegate;
  1075. [NonSerialized] private CallbackArray<Action<PlayerInput>> m_DeviceLostCallbacks;
  1076. [NonSerialized] private CallbackArray<Action<PlayerInput>> m_DeviceRegainedCallbacks;
  1077. [NonSerialized] private CallbackArray<Action<PlayerInput>> m_ControlsChangedCallbacks;
  1078. [NonSerialized] private CallbackArray<Action<InputAction.CallbackContext>> m_ActionTriggeredCallbacks;
  1079. [NonSerialized] private Action<InputControl, InputEventPtr> m_UnpairedDeviceUsedDelegate;
  1080. [NonSerialized] private Func<InputDevice, InputEventPtr, bool> m_PreFilterUnpairedDeviceUsedDelegate;
  1081. [NonSerialized] private bool m_OnUnpairedDeviceUsedHooked;
  1082. [NonSerialized] private Action<InputDevice, InputDeviceChange> m_DeviceChangeDelegate;
  1083. [NonSerialized] private bool m_OnDeviceChangeHooked;
  1084. internal static int s_AllActivePlayersCount;
  1085. internal static PlayerInput[] s_AllActivePlayers;
  1086. private static Action<InputUser, InputUserChange, InputDevice> s_UserChangeDelegate;
  1087. // The following information is used when the next PlayerInput component is enabled.
  1088. private static int s_InitPairWithDevicesCount;
  1089. private static InputDevice[] s_InitPairWithDevices;
  1090. private static int s_InitPlayerIndex = -1;
  1091. private static int s_InitSplitScreenIndex = -1;
  1092. private static string s_InitControlScheme;
  1093. internal static bool s_DestroyIfDeviceSetupUnsuccessful;
  1094. private void InitializeActions()
  1095. {
  1096. if (m_ActionsInitialized)
  1097. return;
  1098. if (m_Actions == null)
  1099. return;
  1100. // Check if we need to duplicate our actions by looking at all other players. If any
  1101. // has the same actions, duplicate.
  1102. for (var i = 0; i < s_AllActivePlayersCount; ++i)
  1103. if (s_AllActivePlayers[i].m_Actions == m_Actions && s_AllActivePlayers[i] != this)
  1104. {
  1105. var oldActions = m_Actions;
  1106. m_Actions = Instantiate(m_Actions);
  1107. for (var actionMap = 0; actionMap < oldActions.actionMaps.Count; actionMap++)
  1108. {
  1109. for (var binding = 0; binding < oldActions.actionMaps[actionMap].bindings.Count; binding++)
  1110. m_Actions.actionMaps[actionMap].ApplyBindingOverride(binding, oldActions.actionMaps[actionMap].bindings[binding]);
  1111. }
  1112. break;
  1113. }
  1114. #if UNITY_INPUT_SYSTEM_ENABLE_UI
  1115. if (uiInputModule != null)
  1116. uiInputModule.actionsAsset = m_Actions;
  1117. #endif
  1118. switch (m_NotificationBehavior)
  1119. {
  1120. case PlayerNotifications.SendMessages:
  1121. case PlayerNotifications.BroadcastMessages:
  1122. InstallOnActionTriggeredHook();
  1123. if (m_ActionMessageNames == null)
  1124. CacheMessageNames();
  1125. break;
  1126. case PlayerNotifications.InvokeCSharpEvents:
  1127. InstallOnActionTriggeredHook();
  1128. break;
  1129. case PlayerNotifications.InvokeUnityEvents:
  1130. {
  1131. // Hook up all action events.
  1132. if (m_ActionEvents != null)
  1133. {
  1134. foreach (var actionEvent in m_ActionEvents)
  1135. {
  1136. var id = actionEvent.actionId;
  1137. if (string.IsNullOrEmpty(id))
  1138. continue;
  1139. // Find action for event.
  1140. var action = m_Actions.FindAction(id);
  1141. if (action == null)
  1142. continue;
  1143. action.performed += actionEvent.Invoke;
  1144. action.canceled += actionEvent.Invoke;
  1145. action.started += actionEvent.Invoke;
  1146. }
  1147. }
  1148. break;
  1149. }
  1150. }
  1151. m_ActionsInitialized = true;
  1152. }
  1153. private void UninitializeActions()
  1154. {
  1155. if (!m_ActionsInitialized)
  1156. return;
  1157. if (m_Actions == null)
  1158. return;
  1159. UninstallOnActionTriggeredHook();
  1160. if (m_NotificationBehavior == PlayerNotifications.InvokeUnityEvents && m_ActionEvents != null)
  1161. {
  1162. foreach (var actionEvent in m_ActionEvents)
  1163. {
  1164. var id = actionEvent.actionId;
  1165. if (string.IsNullOrEmpty(id))
  1166. continue;
  1167. // Find action for event.
  1168. var action = m_Actions.FindAction(id);
  1169. if (action != null)
  1170. {
  1171. ////REVIEW: really wish we had a single callback
  1172. action.performed -= actionEvent.Invoke;
  1173. action.canceled -= actionEvent.Invoke;
  1174. action.started -= actionEvent.Invoke;
  1175. }
  1176. }
  1177. }
  1178. m_CurrentActionMap = null;
  1179. m_ActionsInitialized = false;
  1180. }
  1181. private void InstallOnActionTriggeredHook()
  1182. {
  1183. if (m_ActionTriggeredDelegate == null)
  1184. m_ActionTriggeredDelegate = OnActionTriggered;
  1185. foreach (var actionMap in m_Actions.actionMaps)
  1186. actionMap.actionTriggered += m_ActionTriggeredDelegate;
  1187. }
  1188. private void UninstallOnActionTriggeredHook()
  1189. {
  1190. if (m_ActionTriggeredDelegate != null)
  1191. foreach (var actionMap in m_Actions.actionMaps)
  1192. actionMap.actionTriggered -= m_ActionTriggeredDelegate;
  1193. }
  1194. private void OnActionTriggered(InputAction.CallbackContext context)
  1195. {
  1196. if (!m_InputActive)
  1197. return;
  1198. // We shouldn't go through this method when using UnityEvents. With events,
  1199. // the callbacks should be wired up directly rather than going all to this method.
  1200. Debug.Assert(m_NotificationBehavior != PlayerNotifications.InvokeUnityEvents,
  1201. "OnActionTriggered callback should not be installed if notification behavior is set to InvokeUnityEvents");
  1202. switch (m_NotificationBehavior)
  1203. {
  1204. case PlayerNotifications.InvokeCSharpEvents:
  1205. DelegateHelpers.InvokeCallbacksSafe(ref m_ActionTriggeredCallbacks, context, "PlayerInput.onActionTriggered");
  1206. break;
  1207. case PlayerNotifications.BroadcastMessages:
  1208. case PlayerNotifications.SendMessages:
  1209. // ATM we only care about `performed` and, in the case of value actions, `canceled`.
  1210. var action = context.action;
  1211. if (!(context.performed || (context.canceled && action.type == InputActionType.Value)))
  1212. return;
  1213. // Find message name for action.
  1214. if (m_ActionMessageNames == null)
  1215. CacheMessageNames();
  1216. var messageName = m_ActionMessageNames[action.m_Id];
  1217. // Cache value.
  1218. if (m_InputValueObject == null)
  1219. m_InputValueObject = new InputValue();
  1220. m_InputValueObject.m_Context = context;
  1221. // Send message.
  1222. if (m_NotificationBehavior == PlayerNotifications.BroadcastMessages)
  1223. BroadcastMessage(messageName, m_InputValueObject, SendMessageOptions.DontRequireReceiver);
  1224. else
  1225. SendMessage(messageName, m_InputValueObject, SendMessageOptions.DontRequireReceiver);
  1226. // Reset context so calling Get() will result in an exception.
  1227. m_InputValueObject.m_Context = null;
  1228. break;
  1229. }
  1230. }
  1231. private void CacheMessageNames()
  1232. {
  1233. if (m_Actions == null)
  1234. return;
  1235. if (m_ActionMessageNames != null)
  1236. m_ActionMessageNames.Clear();
  1237. else
  1238. m_ActionMessageNames = new Dictionary<string, string>();
  1239. foreach (var action in m_Actions)
  1240. {
  1241. action.MakeSureIdIsInPlace();
  1242. var name = CSharpCodeHelpers.MakeTypeName(action.name);
  1243. m_ActionMessageNames[action.m_Id] = "On" + name;
  1244. }
  1245. }
  1246. private void ClearCaches()
  1247. {
  1248. }
  1249. /// <summary>
  1250. /// Initialize <see cref="user"/> and <see cref="devices"/>.
  1251. /// </summary>
  1252. private void AssignUserAndDevices()
  1253. {
  1254. // If we already have a user at this point, clear out all its paired devices
  1255. // to start the pairing process from scratch.
  1256. if (m_InputUser.valid)
  1257. m_InputUser.UnpairDevices();
  1258. // All our input goes through actions so there's no point setting
  1259. // anything up if we have none.
  1260. if (m_Actions == null)
  1261. {
  1262. // If we have devices we are meant to pair with, do so. Otherwise, don't
  1263. // do anything as we don't know what kind of input to look for.
  1264. if (s_InitPairWithDevicesCount > 0)
  1265. {
  1266. for (var i = 0; i < s_InitPairWithDevicesCount; ++i)
  1267. m_InputUser = InputUser.PerformPairingWithDevice(s_InitPairWithDevices[i], m_InputUser);
  1268. }
  1269. else
  1270. {
  1271. // Make sure user is invalid.
  1272. m_InputUser = new InputUser();
  1273. }
  1274. return;
  1275. }
  1276. // If we have control schemes, try to find the one we should use.
  1277. if (m_Actions.controlSchemes.Count > 0)
  1278. {
  1279. if (!string.IsNullOrEmpty(s_InitControlScheme))
  1280. {
  1281. // We've been given a control scheme to initialize this. Try that one and
  1282. // that one only. Might mean we end up with missing devices.
  1283. var controlScheme = m_Actions.FindControlScheme(s_InitControlScheme);
  1284. if (controlScheme == null)
  1285. {
  1286. Debug.LogError($"No control scheme '{s_InitControlScheme}' in '{m_Actions}'", this);
  1287. }
  1288. else
  1289. {
  1290. TryToActivateControlScheme(controlScheme.Value);
  1291. }
  1292. }
  1293. else if (!string.IsNullOrEmpty(m_DefaultControlScheme))
  1294. {
  1295. // There's a control scheme we should try by default.
  1296. var controlScheme = m_Actions.FindControlScheme(m_DefaultControlScheme);
  1297. if (controlScheme == null)
  1298. {
  1299. Debug.LogError($"Cannot find default control scheme '{m_DefaultControlScheme}' in '{m_Actions}'", this);
  1300. }
  1301. else
  1302. {
  1303. TryToActivateControlScheme(controlScheme.Value);
  1304. }
  1305. }
  1306. // If we did not end up with a usable scheme by now but we've been given devices to pair with,
  1307. // search for a control scheme matching the given devices.
  1308. if (s_InitPairWithDevicesCount > 0 && (!m_InputUser.valid || m_InputUser.controlScheme == null))
  1309. {
  1310. // The devices we've been given may not be all the devices required to satisfy a given control scheme so we
  1311. // want to pick any one control scheme that is the best match for the devices we have regardless of whether
  1312. // we'll need additional devices. TryToActivateControlScheme will take care of that.
  1313. var controlScheme = InputControlScheme.FindControlSchemeForDevices(
  1314. new ReadOnlyArray<InputDevice>(s_InitPairWithDevices, 0, s_InitPairWithDevicesCount), m_Actions.controlSchemes,
  1315. allowUnsuccesfulMatch: true);
  1316. if (controlScheme != null)
  1317. TryToActivateControlScheme(controlScheme.Value);
  1318. }
  1319. // If we don't have a working control scheme by now and we haven't been instructed to use
  1320. // one specific control scheme, try each one in the asset one after the other until we
  1321. // either find one we can use or run out of options.
  1322. else if ((!m_InputUser.valid || m_InputUser.controlScheme == null) && string.IsNullOrEmpty(s_InitControlScheme))
  1323. {
  1324. using (var availableDevices = InputUser.GetUnpairedInputDevices())
  1325. {
  1326. var controlScheme = InputControlScheme.FindControlSchemeForDevices(availableDevices, m_Actions.controlSchemes);
  1327. if (controlScheme != null)
  1328. TryToActivateControlScheme(controlScheme.Value);
  1329. }
  1330. }
  1331. }
  1332. else
  1333. {
  1334. // There's no control schemes in the asset. If we've been given a set of devices,
  1335. // we run with those (regardless of whether there's bindings for them in the actions or not).
  1336. // If we haven't been given any devices, we go through all bindings in the asset and whatever
  1337. // device is present that matches the binding and that isn't used by any other player, we'll
  1338. // pair to the player.
  1339. if (s_InitPairWithDevicesCount > 0)
  1340. {
  1341. for (var i = 0; i < s_InitPairWithDevicesCount; ++i)
  1342. m_InputUser = InputUser.PerformPairingWithDevice(s_InitPairWithDevices[i], m_InputUser);
  1343. }
  1344. else
  1345. {
  1346. // Pair all devices for which we have a binding.
  1347. using (var availableDevices = InputUser.GetUnpairedInputDevices())
  1348. {
  1349. for (var i = 0; i < availableDevices.Count; ++i)
  1350. {
  1351. var device = availableDevices[i];
  1352. if (!HaveBindingForDevice(device))
  1353. continue;
  1354. m_InputUser = InputUser.PerformPairingWithDevice(device, m_InputUser);
  1355. }
  1356. }
  1357. }
  1358. }
  1359. // If we don't have a valid user at this point, we don't have any paired devices.
  1360. if (m_InputUser.valid)
  1361. m_InputUser.AssociateActionsWithUser(m_Actions);
  1362. }
  1363. private bool HaveBindingForDevice(InputDevice device)
  1364. {
  1365. if (m_Actions == null)
  1366. return false;
  1367. var actionMaps = m_Actions.actionMaps;
  1368. for (var i = 0; i < actionMaps.Count; ++i)
  1369. {
  1370. var actionMap = actionMaps[i];
  1371. if (actionMap.IsUsableWithDevice(device))
  1372. return true;
  1373. }
  1374. return false;
  1375. }
  1376. private void UnassignUserAndDevices()
  1377. {
  1378. if (m_InputUser.valid)
  1379. m_InputUser.UnpairDevicesAndRemoveUser();
  1380. if (m_Actions != null)
  1381. m_Actions.devices = null;
  1382. }
  1383. private bool TryToActivateControlScheme(InputControlScheme controlScheme)
  1384. {
  1385. ////FIXME: this will fall apart if account management is involved and a user needs to log in on device first
  1386. // Pair any devices we may have been given.
  1387. if (s_InitPairWithDevicesCount > 0)
  1388. {
  1389. ////REVIEW: should AndPairRemainingDevices() require that there is at least one existing
  1390. //// device paired to the user that is usable with the given control scheme?
  1391. // First make sure that all of the devices actually work with the given control scheme.
  1392. // We're fine having to pair additional devices but we don't want the situation where
  1393. // we have the player grab all the devices in s_InitPairWithDevices along with a control
  1394. // scheme that fits none of them and then AndPairRemainingDevices() supplying the devices
  1395. // actually needed by the control scheme.
  1396. for (var i = 0; i < s_InitPairWithDevicesCount; ++i)
  1397. {
  1398. var device = s_InitPairWithDevices[i];
  1399. if (!controlScheme.SupportsDevice(device))
  1400. return false;
  1401. }
  1402. // We're good. Give the devices to the user.
  1403. for (var i = 0; i < s_InitPairWithDevicesCount; ++i)
  1404. {
  1405. var device = s_InitPairWithDevices[i];
  1406. m_InputUser = InputUser.PerformPairingWithDevice(device, m_InputUser);
  1407. }
  1408. }
  1409. if (!m_InputUser.valid)
  1410. m_InputUser = InputUser.CreateUserWithoutPairedDevices();
  1411. m_InputUser.ActivateControlScheme(controlScheme).AndPairRemainingDevices();
  1412. if (user.hasMissingRequiredDevices)
  1413. {
  1414. m_InputUser.ActivateControlScheme(null);
  1415. m_InputUser.UnpairDevices();
  1416. return false;
  1417. }
  1418. return true;
  1419. }
  1420. private void AssignPlayerIndex()
  1421. {
  1422. if (s_InitPlayerIndex != -1)
  1423. m_PlayerIndex = s_InitPlayerIndex;
  1424. else
  1425. {
  1426. var minPlayerIndex = int.MaxValue;
  1427. var maxPlayerIndex = int.MinValue;
  1428. for (var i = 0; i < s_AllActivePlayersCount; ++i)
  1429. {
  1430. var playerIndex = s_AllActivePlayers[i].playerIndex;
  1431. minPlayerIndex = Math.Min(minPlayerIndex, playerIndex);
  1432. maxPlayerIndex = Math.Max(maxPlayerIndex, playerIndex);
  1433. }
  1434. if (minPlayerIndex != int.MaxValue && minPlayerIndex > 0)
  1435. {
  1436. // There's an index between 0 and the current minimum available.
  1437. m_PlayerIndex = minPlayerIndex - 1;
  1438. }
  1439. else if (maxPlayerIndex != int.MinValue)
  1440. {
  1441. // There may be an index between the minimum and maximum available.
  1442. // Search the range. If there's nothing, create a new maximum.
  1443. for (var i = minPlayerIndex; i < maxPlayerIndex; ++i)
  1444. {
  1445. if (GetPlayerByIndex(i) == null)
  1446. {
  1447. m_PlayerIndex = i;
  1448. return;
  1449. }
  1450. }
  1451. m_PlayerIndex = maxPlayerIndex + 1;
  1452. }
  1453. else
  1454. m_PlayerIndex = 0;
  1455. }
  1456. }
  1457. #if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  1458. void Reset()
  1459. {
  1460. // Set default actions to project wide actions.
  1461. m_Actions = InputSystem.actions;
  1462. // TODO Need to monitor changes?
  1463. }
  1464. #endif
  1465. private void OnEnable()
  1466. {
  1467. m_Enabled = true;
  1468. using (InputActionRebindingExtensions.DeferBindingResolution())
  1469. {
  1470. AssignPlayerIndex();
  1471. InitializeActions();
  1472. AssignUserAndDevices();
  1473. ActivateInput();
  1474. }
  1475. // Split-screen index defaults to player index.
  1476. if (s_InitSplitScreenIndex >= 0)
  1477. m_SplitScreenIndex = splitScreenIndex;
  1478. else
  1479. m_SplitScreenIndex = playerIndex;
  1480. // Add to global list and sort it by player index.
  1481. ArrayHelpers.AppendWithCapacity(ref s_AllActivePlayers, ref s_AllActivePlayersCount, this);
  1482. for (var i = 1; i < s_AllActivePlayersCount; ++i)
  1483. for (var j = i; j > 0 && s_AllActivePlayers[j - 1].playerIndex > s_AllActivePlayers[j].playerIndex; --j)
  1484. s_AllActivePlayers.SwapElements(j, j - 1);
  1485. // If it's the first player, hook into user change notifications.
  1486. if (s_AllActivePlayersCount == 1)
  1487. {
  1488. if (s_UserChangeDelegate == null)
  1489. s_UserChangeDelegate = OnUserChange;
  1490. InputUser.onChange += s_UserChangeDelegate;
  1491. }
  1492. // In single player, set up for automatic device switching.
  1493. if (isSinglePlayer)
  1494. {
  1495. if (m_Actions != null && m_Actions.controlSchemes.Count == 0)
  1496. {
  1497. // No control schemes. We pick up whatever is compatible with the bindings
  1498. // we have.
  1499. StartListeningForDeviceChanges();
  1500. }
  1501. else if (!neverAutoSwitchControlSchemes)
  1502. {
  1503. // We have control schemes so we only listen for unpaired device *input*, i.e.
  1504. // actual use of an unpaired device (as opposed to it merely getting plugged in).
  1505. StartListeningForUnpairedDeviceActivity();
  1506. }
  1507. }
  1508. HandleControlsChanged();
  1509. // Trigger join event.
  1510. PlayerInputManager.instance?.NotifyPlayerJoined(this);
  1511. }
  1512. private void StartListeningForUnpairedDeviceActivity()
  1513. {
  1514. if (m_OnUnpairedDeviceUsedHooked)
  1515. return;
  1516. if (m_UnpairedDeviceUsedDelegate == null)
  1517. m_UnpairedDeviceUsedDelegate = OnUnpairedDeviceUsed;
  1518. if (m_PreFilterUnpairedDeviceUsedDelegate == null)
  1519. m_PreFilterUnpairedDeviceUsedDelegate = OnPreFilterUnpairedDeviceUsed;
  1520. InputUser.onUnpairedDeviceUsed += m_UnpairedDeviceUsedDelegate;
  1521. InputUser.onPrefilterUnpairedDeviceActivity += m_PreFilterUnpairedDeviceUsedDelegate;
  1522. ++InputUser.listenForUnpairedDeviceActivity;
  1523. m_OnUnpairedDeviceUsedHooked = true;
  1524. }
  1525. private void StopListeningForUnpairedDeviceActivity()
  1526. {
  1527. if (!m_OnUnpairedDeviceUsedHooked)
  1528. return;
  1529. InputUser.onUnpairedDeviceUsed -= m_UnpairedDeviceUsedDelegate;
  1530. InputUser.onPrefilterUnpairedDeviceActivity -= m_PreFilterUnpairedDeviceUsedDelegate;
  1531. --InputUser.listenForUnpairedDeviceActivity;
  1532. m_OnUnpairedDeviceUsedHooked = false;
  1533. }
  1534. private void StartListeningForDeviceChanges()
  1535. {
  1536. if (m_OnDeviceChangeHooked)
  1537. return;
  1538. if (m_DeviceChangeDelegate == null)
  1539. m_DeviceChangeDelegate = OnDeviceChange;
  1540. InputSystem.onDeviceChange += m_DeviceChangeDelegate;
  1541. m_OnDeviceChangeHooked = true;
  1542. }
  1543. private void StopListeningForDeviceChanges()
  1544. {
  1545. if (!m_OnDeviceChangeHooked)
  1546. return;
  1547. InputSystem.onDeviceChange -= m_DeviceChangeDelegate;
  1548. m_OnDeviceChangeHooked = false;
  1549. }
  1550. private void OnDisable()
  1551. {
  1552. m_Enabled = false;
  1553. // Remove from global list.
  1554. var index = ArrayHelpers.IndexOfReference(s_AllActivePlayers, this, s_AllActivePlayersCount);
  1555. if (index != -1)
  1556. ArrayHelpers.EraseAtWithCapacity(s_AllActivePlayers, ref s_AllActivePlayersCount, index);
  1557. // Unhook from change notifications if we're the last player.
  1558. if (s_AllActivePlayersCount == 0 && s_UserChangeDelegate != null)
  1559. InputUser.onChange -= s_UserChangeDelegate;
  1560. StopListeningForUnpairedDeviceActivity();
  1561. StopListeningForDeviceChanges();
  1562. // Trigger leave event.
  1563. PlayerInputManager.instance?.NotifyPlayerLeft(this);
  1564. ////TODO: ideally, this shouldn't have to resolve at all and instead wait for someone to need the updated setup
  1565. // Avoid re-resolving bindings over and over while we disassemble
  1566. // the configuration.
  1567. using (InputActionRebindingExtensions.DeferBindingResolution())
  1568. {
  1569. DeactivateInput();
  1570. UnassignUserAndDevices();
  1571. UninitializeActions();
  1572. }
  1573. m_PlayerIndex = -1;
  1574. }
  1575. // ReSharper disable once UnusedMember.Global
  1576. /// <summary>
  1577. /// Debug helper method that can be hooked up to actions when using <see cref="UnityEngine.InputSystem.PlayerNotifications.InvokeUnityEvents"/>.
  1578. /// </summary>
  1579. public void DebugLogAction(InputAction.CallbackContext context)
  1580. {
  1581. Debug.Log(context.ToString());
  1582. }
  1583. private void HandleDeviceLost()
  1584. {
  1585. switch (m_NotificationBehavior)
  1586. {
  1587. case PlayerNotifications.SendMessages:
  1588. SendMessage(DeviceLostMessage, this, SendMessageOptions.DontRequireReceiver);
  1589. break;
  1590. case PlayerNotifications.BroadcastMessages:
  1591. BroadcastMessage(DeviceLostMessage, this, SendMessageOptions.DontRequireReceiver);
  1592. break;
  1593. case PlayerNotifications.InvokeUnityEvents:
  1594. m_DeviceLostEvent?.Invoke(this);
  1595. break;
  1596. case PlayerNotifications.InvokeCSharpEvents:
  1597. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceLostCallbacks, this, "onDeviceLost");
  1598. break;
  1599. }
  1600. }
  1601. private void HandleDeviceRegained()
  1602. {
  1603. switch (m_NotificationBehavior)
  1604. {
  1605. case PlayerNotifications.SendMessages:
  1606. SendMessage(DeviceRegainedMessage, this, SendMessageOptions.DontRequireReceiver);
  1607. break;
  1608. case PlayerNotifications.BroadcastMessages:
  1609. BroadcastMessage(DeviceRegainedMessage, this, SendMessageOptions.DontRequireReceiver);
  1610. break;
  1611. case PlayerNotifications.InvokeUnityEvents:
  1612. m_DeviceRegainedEvent?.Invoke(this);
  1613. break;
  1614. case PlayerNotifications.InvokeCSharpEvents:
  1615. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceRegainedCallbacks, this, "onDeviceRegained");
  1616. break;
  1617. }
  1618. }
  1619. private void HandleControlsChanged()
  1620. {
  1621. switch (m_NotificationBehavior)
  1622. {
  1623. case PlayerNotifications.SendMessages:
  1624. SendMessage(ControlsChangedMessage, this, SendMessageOptions.DontRequireReceiver);
  1625. break;
  1626. case PlayerNotifications.BroadcastMessages:
  1627. BroadcastMessage(ControlsChangedMessage, this, SendMessageOptions.DontRequireReceiver);
  1628. break;
  1629. case PlayerNotifications.InvokeUnityEvents:
  1630. m_ControlsChangedEvent?.Invoke(this);
  1631. break;
  1632. case PlayerNotifications.InvokeCSharpEvents:
  1633. DelegateHelpers.InvokeCallbacksSafe(ref m_ControlsChangedCallbacks, this, "onControlsChanged");
  1634. break;
  1635. }
  1636. }
  1637. private static void OnUserChange(InputUser user, InputUserChange change, InputDevice device)
  1638. {
  1639. switch (change)
  1640. {
  1641. case InputUserChange.DeviceLost:
  1642. case InputUserChange.DeviceRegained:
  1643. for (var i = 0; i < s_AllActivePlayersCount; ++i)
  1644. {
  1645. var player = s_AllActivePlayers[i];
  1646. if (player.m_InputUser == user)
  1647. {
  1648. if (change == InputUserChange.DeviceLost)
  1649. player.HandleDeviceLost();
  1650. else if (change == InputUserChange.DeviceRegained)
  1651. player.HandleDeviceRegained();
  1652. }
  1653. }
  1654. break;
  1655. case InputUserChange.ControlsChanged:
  1656. for (var i = 0; i < s_AllActivePlayersCount; ++i)
  1657. {
  1658. var player = s_AllActivePlayers[i];
  1659. if (player.m_InputUser == user)
  1660. player.HandleControlsChanged();
  1661. }
  1662. break;
  1663. }
  1664. }
  1665. private static bool OnPreFilterUnpairedDeviceUsed(InputDevice device, InputEventPtr eventPtr)
  1666. {
  1667. // Early out if the device isn't usable with any of our control schemes.
  1668. var actions = all[0].actions;
  1669. return actions != null && actions.IsUsableWithDevice(device);
  1670. }
  1671. private void OnUnpairedDeviceUsed(InputControl control, InputEventPtr eventPtr)
  1672. {
  1673. // We only support automatic control scheme switching in single player mode.
  1674. // OnEnable() should automatically unhook us.
  1675. if (!isSinglePlayer || neverAutoSwitchControlSchemes)
  1676. return;
  1677. var player = all[0];
  1678. var actions = player.m_Actions;
  1679. if (actions == null)
  1680. return;
  1681. var device = control.device;
  1682. using (InputActionRebindingExtensions.DeferBindingResolution())
  1683. using (var availableDevices = InputUser.GetUnpairedInputDevices())
  1684. {
  1685. // Put our device first in the list to make sure it's the first one picked for a match.
  1686. if (availableDevices.Count > 1)
  1687. {
  1688. var indexOfDevice = availableDevices.IndexOf(device);
  1689. Debug.Assert(indexOfDevice != -1, "Did not find unpaired device in list of unpaired devices");
  1690. availableDevices.SwapElements(0, indexOfDevice);
  1691. }
  1692. // Add all devices currently already paired to us. This avoids us preventing
  1693. // control schemes switches because of devices we're looking for already being
  1694. // paired to us.
  1695. var currentDevices = player.devices;
  1696. for (var i = 0; i < currentDevices.Count; ++i)
  1697. availableDevices.Add(currentDevices[i]);
  1698. // Find the best control scheme to use.
  1699. if (InputControlScheme.FindControlSchemeForDevices(availableDevices, player.m_Actions.controlSchemes,
  1700. out var controlScheme, out var matchResult, mustIncludeDevice: device))
  1701. {
  1702. try
  1703. {
  1704. // First remove the currently paired devices.
  1705. var userValid = player.user.valid;
  1706. if (userValid)
  1707. player.user.UnpairDevices();
  1708. // Then pair devices that we've picked according to the control scheme.
  1709. var newDevices = matchResult.devices;
  1710. Debug.Assert(newDevices.Count > 0, "Expecting to see at least one device here");
  1711. for (var i = 0; i < newDevices.Count; ++i)
  1712. {
  1713. player.m_InputUser = InputUser.PerformPairingWithDevice(newDevices[i], user: player.m_InputUser);
  1714. if (!userValid && player.actions != null)
  1715. player.m_InputUser.AssociateActionsWithUser(player.actions);
  1716. }
  1717. // And finally switch to the new control scheme.
  1718. player.user.ActivateControlScheme(controlScheme);
  1719. }
  1720. finally
  1721. {
  1722. matchResult.Dispose();
  1723. }
  1724. }
  1725. }
  1726. }
  1727. private void OnDeviceChange(InputDevice device, InputDeviceChange change)
  1728. {
  1729. // If a device was added and we have no control schemes in the actions and we're in
  1730. // single-player mode, pair the device to the player if it works with the bindings we have.
  1731. if (change == InputDeviceChange.Added &&
  1732. isSinglePlayer &&
  1733. m_Actions != null && m_Actions.controlSchemes.Count == 0 &&
  1734. HaveBindingForDevice(device) &&
  1735. m_InputUser.valid)
  1736. {
  1737. InputUser.PerformPairingWithDevice(device, user: m_InputUser);
  1738. }
  1739. }
  1740. private void SwitchControlSchemeInternal(ref InputControlScheme controlScheme, params InputDevice[] devices)
  1741. {
  1742. Debug.Assert(devices != null);
  1743. // Note that we are doing two somwhat uncorrelated actions here:
  1744. // - Switching control scheme
  1745. // - Explicitly pairing with given devices regardless if making sense with respect to control scheme
  1746. using (InputActionRebindingExtensions.DeferBindingResolution())
  1747. {
  1748. // Unpair device previously paired but not part of given devices to pair with
  1749. for (var i = user.pairedDevices.Count - 1; i >= 0; --i)
  1750. {
  1751. if (!devices.ContainsReference(user.pairedDevices[i]))
  1752. user.UnpairDevice(user.pairedDevices[i]);
  1753. }
  1754. // Pair devices not previously paired but that are part of given devices to pair with
  1755. foreach (var device in devices)
  1756. {
  1757. if (!user.pairedDevices.ContainsReference(device))
  1758. InputUser.PerformPairingWithDevice(device, user: user);
  1759. }
  1760. // Only activate control scheme if its a different scheme
  1761. if (!user.controlScheme.HasValue || !user.controlScheme.Value.Equals(controlScheme))
  1762. user.ActivateControlScheme(controlScheme);
  1763. }
  1764. }
  1765. [Serializable]
  1766. public class ActionEvent : UnityEvent<InputAction.CallbackContext>
  1767. {
  1768. public string actionId => m_ActionId;
  1769. public string actionName => m_ActionName;
  1770. [SerializeField] private string m_ActionId;
  1771. [SerializeField] private string m_ActionName;
  1772. public ActionEvent()
  1773. {
  1774. }
  1775. public ActionEvent(InputAction action)
  1776. {
  1777. if (action == null)
  1778. throw new ArgumentNullException(nameof(action));
  1779. if (action.isSingletonAction)
  1780. throw new ArgumentException($"Action must be part of an asset (given action '{action}' is a singleton)");
  1781. if (action.actionMap.asset == null)
  1782. throw new ArgumentException($"Action must be part of an asset (given action '{action}' is not)");
  1783. m_ActionId = action.id.ToString();
  1784. m_ActionName = $"{action.actionMap.name}/{action.name}";
  1785. }
  1786. public ActionEvent(Guid actionGUID, string name = null)
  1787. {
  1788. m_ActionId = actionGUID.ToString();
  1789. m_ActionName = name;
  1790. }
  1791. }
  1792. /// <summary>
  1793. /// Event that is triggered when an <see cref="InputDevice"/> paired to a <see cref="PlayerInput"/> is disconnected.
  1794. /// </summary>
  1795. /// <seealso cref="deviceLostEvent"/>
  1796. [Serializable]
  1797. public class DeviceLostEvent : UnityEvent<PlayerInput>
  1798. {
  1799. }
  1800. /// <summary>
  1801. /// Event that is triggered when a <see cref="PlayerInput"/> regains an <see cref="InputDevice"/> previously lost.
  1802. /// </summary>
  1803. /// <seealso cref="deviceRegainedEvent"/>
  1804. [Serializable]
  1805. public class DeviceRegainedEvent : UnityEvent<PlayerInput>
  1806. {
  1807. }
  1808. /// <summary>
  1809. /// Event that is triggered when the set of controls used by a <see cref="PlayerInput"/> changes.
  1810. /// </summary>
  1811. /// <seealso cref="controlsChangedEvent"/>
  1812. [Serializable]
  1813. public class ControlsChangedEvent : UnityEvent<PlayerInput>
  1814. {
  1815. }
  1816. }
  1817. }