CharacterController.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. using System.Runtime.InteropServices;
  5. namespace BansheeEngine
  6. {
  7. /** @addtogroup Physics
  8. * @{
  9. */
  10. /// <summary>
  11. /// Special physics controller meant to be used for game characters. Uses the "slide-and-collide" physics instead of
  12. /// of the standard physics model to handle various issues with manually moving kinematic objects.Uses a capsule to
  13. /// represent the character's bounds.
  14. /// </summary>
  15. public sealed class CharacterController : Component
  16. {
  17. internal NativeCharacterController native;
  18. [SerializeField]
  19. internal SerializableData serializableData = new SerializableData();
  20. /// <summary>
  21. /// Triggered when the controller hits a collider.
  22. /// </summary>
  23. public event Action<ControllerColliderCollision> OnColliderHit;
  24. /// <summary>
  25. /// Triggered when the controller hits another character controller.
  26. /// </summary>
  27. public event Action<ControllerControllerCollision> OnControllerHit;
  28. /// <summary>
  29. /// Position of the bottom of the controller. Position takes contact offset into account. Changing this value will
  30. /// teleport the character to the location. Use <see cref="Move"/> for movement that includes physics.
  31. /// </summary>
  32. public Vector3 FootPosition
  33. {
  34. get
  35. {
  36. if (native != null)
  37. return native.FootPosition;
  38. return Vector3.Zero;
  39. }
  40. set
  41. {
  42. if (native != null)
  43. {
  44. native.FootPosition = value;
  45. UpdatePositionFromController();
  46. }
  47. }
  48. }
  49. /// <summary>
  50. /// Radius of the controller capsule.
  51. /// </summary>
  52. public float Radius
  53. {
  54. get { return serializableData.radius; }
  55. set
  56. {
  57. serializableData.radius = value;
  58. if(native != null)
  59. UpdateDimensions();
  60. }
  61. }
  62. /// <summary>
  63. /// Height between the centers of the two spheres of the controller capsule.
  64. /// </summary>
  65. public float Height
  66. {
  67. get { return serializableData.height; }
  68. set
  69. {
  70. serializableData.height = value;
  71. if(native != null)
  72. UpdateDimensions();
  73. }
  74. }
  75. /// <summary>
  76. /// Up direction of capsule. Determines capsule orientation.
  77. /// </summary>
  78. public Vector3 Up
  79. {
  80. get { return serializableData.up; }
  81. set
  82. {
  83. serializableData.up = value;
  84. if (native != null)
  85. native.Up = value;
  86. }
  87. }
  88. /// <summary>
  89. /// Controls what happens when character encounters a height higher than its step offset.
  90. /// </summary>
  91. public CharacterClimbingMode ClimbingMode
  92. {
  93. get { return serializableData.climbingMode; }
  94. set
  95. {
  96. serializableData.climbingMode = value;
  97. if (native != null)
  98. native.ClimbingMode = value;
  99. }
  100. }
  101. /// <summary>
  102. /// Controls what happens when character encounters a slope higher than its slope offset.
  103. /// </summary>
  104. public CharacterNonWalkableMode NonWalkableMode
  105. {
  106. get { return serializableData.nonWalkableMode; }
  107. set
  108. {
  109. serializableData.nonWalkableMode = value;
  110. if (native != null)
  111. native.NonWalkableMode = value;
  112. }
  113. }
  114. /// <summary>
  115. /// Represents minimum distance that the character will move during a call to <see cref="Move"/>. This is used to
  116. /// stop the recursive motion algorithm when the remaining distance is too small.
  117. /// </summary>
  118. public float MinMoveDistance
  119. {
  120. get { return serializableData.minMoveDistance; }
  121. set
  122. {
  123. serializableData.minMoveDistance = value;
  124. if (native != null)
  125. native.MinMoveDistance = value;
  126. }
  127. }
  128. /// <summary>
  129. /// Contact offset specifies a skin around the object within which contacts will be generated. It should be a small
  130. /// positive non-zero value.
  131. /// </summary>
  132. public float ContactOffset
  133. {
  134. get { return serializableData.contactOffset; }
  135. set
  136. {
  137. serializableData.contactOffset = value;
  138. if (native != null)
  139. native.ContactOffset = value;
  140. }
  141. }
  142. /// <summary>
  143. /// Controls which obstacles will the character be able to automatically step over without being stopped. This is
  144. /// the height of the maximum obstacle that will be stepped over (with exceptions, <see cref="ClimbingMode"/>).
  145. /// </summary>
  146. public float StepOffset
  147. {
  148. get { return serializableData.stepOffset; }
  149. set
  150. {
  151. serializableData.stepOffset = value;
  152. if (native != null)
  153. native.StepOffset = value;
  154. }
  155. }
  156. /// <summary>
  157. /// Controls which slopes should the character consider too steep and won't be able to move over.
  158. /// <see cref="NonWalkableMode"/> for more information.
  159. /// </summary>
  160. public Radian SlopeLimit
  161. {
  162. get { return serializableData.slopeLimit; }
  163. set
  164. {
  165. serializableData.slopeLimit = value;
  166. if (native != null)
  167. native.SlopeLimit = value;
  168. }
  169. }
  170. /// <summary>
  171. /// Determines what can the controller collide with. Objects that are allowed to collide with this object must have
  172. /// the same bits set in their own layers.
  173. /// </summary>
  174. public ulong Layer
  175. {
  176. get { return serializableData.layer; }
  177. set
  178. {
  179. serializableData.layer = value;
  180. if (native != null)
  181. native.Layer = value;
  182. }
  183. }
  184. /// <summary>
  185. /// Moves the controller in the specified direction by the specified amount, while interacting with surrounding
  186. /// geometry.Returns flags signaling where collision occurred after the movement.
  187. ///
  188. /// Does not account for gravity, you must apply it manually.
  189. /// </summary>
  190. /// <param name="position">Position to move the controller to, in world space.</param>
  191. public CharacterCollisionFlag Move(Vector3 position)
  192. {
  193. if (native == null)
  194. return 0;
  195. CharacterCollisionFlag output = native.Move(position);
  196. UpdatePositionFromController();
  197. return output;
  198. }
  199. /// <summary>
  200. /// Triggered when the controller hits a collider.
  201. /// </summary>
  202. /// <param name="data">Data about the collision.</param>
  203. internal void DoOnColliderHit(ControllerColliderCollision data)
  204. {
  205. if (OnColliderHit != null)
  206. OnColliderHit(data);
  207. }
  208. /// <summary>
  209. /// Triggered when the controller hits another character controller.
  210. /// </summary>
  211. /// <param name="data">Data about the collision.</param>
  212. internal void DoOnControllerHit(ControllerControllerCollision data)
  213. {
  214. if (OnControllerHit != null)
  215. OnControllerHit(data);
  216. }
  217. private void OnInitialize()
  218. {
  219. NotifyFlags = TransformChangedFlags.Transform;
  220. }
  221. private void OnEnable()
  222. {
  223. RestoreNative();
  224. }
  225. private void OnDisable()
  226. {
  227. DestroyNative();
  228. }
  229. private void OnDestroy()
  230. {
  231. DestroyNative();
  232. }
  233. private void OnTransformChanged(TransformChangedFlags flags)
  234. {
  235. if (!SceneObject.Active || native == null)
  236. return;
  237. native.Position = SceneObject.Position;
  238. }
  239. /// <summary>
  240. /// Updates the position by copying it from the controller to the component's scene object.
  241. /// </summary>
  242. private void UpdatePositionFromController()
  243. {
  244. NotifyFlags = 0;
  245. SceneObject.Position = native.Position;
  246. NotifyFlags = TransformChangedFlags.Transform;
  247. }
  248. /// <summary>
  249. /// Updates the dimensions of the controller by taking account scale of the parent scene object.
  250. /// </summary>
  251. private void UpdateDimensions()
  252. {
  253. Vector3 scale = SceneObject.Scale;
  254. float height = serializableData.height * MathEx.Abs(scale.y);
  255. float radius = serializableData.radius * MathEx.Abs(MathEx.Max(scale.x, scale.z));
  256. native.Height = height;
  257. native.Radius = radius;
  258. }
  259. /// <summary>
  260. /// Restores the internal character controller representation and initializes it with data stored by the component.
  261. /// </summary>
  262. private void RestoreNative()
  263. {
  264. ScriptCharacterControllerData initData = new ScriptCharacterControllerData();
  265. initData.position = SceneObject.Position;
  266. initData.contactOffset = serializableData.contactOffset;
  267. initData.stepOffset = serializableData.stepOffset;
  268. initData.slopeLimit = serializableData.slopeLimit;
  269. initData.minMoveDistance = serializableData.minMoveDistance;
  270. initData.height = serializableData.height;
  271. initData.radius = serializableData.radius;
  272. initData.up = serializableData.up;
  273. initData.climbingMode = serializableData.climbingMode;
  274. initData.nonWalkableMode = serializableData.nonWalkableMode;
  275. native = new NativeCharacterController(initData);
  276. native.Component = this;
  277. native.Layer = serializableData.layer;
  278. UpdateDimensions();
  279. }
  280. /// <summary>
  281. /// Destroys the internal character controller representation.
  282. /// </summary>
  283. private void DestroyNative()
  284. {
  285. if (native != null)
  286. {
  287. native.Destroy();
  288. native = null;
  289. }
  290. }
  291. /// <summary>
  292. /// Holds all data the character controller component needs to persist through serialization.
  293. /// </summary>
  294. [SerializeObject]
  295. internal class SerializableData
  296. {
  297. public float contactOffset = 0.1f;
  298. public float stepOffset = 0.5f;
  299. public Radian slopeLimit = new Degree(45.0f);
  300. public float minMoveDistance = 0.0f;
  301. public float height = 1.0f;
  302. public float radius = 0.25f;
  303. public Vector3 up = Vector3.YAxis;
  304. public CharacterClimbingMode climbingMode = CharacterClimbingMode.Normal;
  305. public CharacterNonWalkableMode nonWalkableMode = CharacterNonWalkableMode.Prevent;
  306. public ulong layer = 1;
  307. }
  308. }
  309. /// <summary>
  310. /// Controls climbing behaviour for a capsule character controller. Normally the character controller will not
  311. /// automatically climb when heights are greater than the assigned step offset.However due to the shape of the capsule
  312. /// it might automatically climb over slightly larger heights than assigned step offsets.
  313. /// </summary>
  314. public enum CharacterClimbingMode
  315. {
  316. /// <summary>
  317. /// Normal behaviour. Capsule character controller will be able to auto-step even above the step offset.
  318. /// </summary>
  319. Normal,
  320. /// <summary>
  321. /// The system will attempt to limit auto-step to the provided step offset and no higher.
  322. /// </summary>
  323. Constrained
  324. }
  325. /// <summary>
  326. /// Controls behaviour when a character controller reaches a slope thats larger than its slope offset.
  327. /// </summary>
  328. public enum CharacterNonWalkableMode
  329. {
  330. /// <summary>
  331. /// Character will be prevented from going further, but will be allowed to move laterally.
  332. /// </summary>
  333. Prevent,
  334. /// <summary>
  335. /// Character will be prevented from going further, but also slide down the slope.
  336. /// </summary>
  337. PreventAndSlide
  338. }
  339. /// <summary>
  340. /// Reports in which directions is the character colliding with other objects.
  341. /// </summary>
  342. public enum CharacterCollisionFlag
  343. {
  344. /// <summary>
  345. /// Character is colliding with its sides.
  346. /// </summary>
  347. Sides = 0x1,
  348. /// <summary>
  349. /// Character is colliding with the ceiling.
  350. /// </summary>
  351. Up = 0x2,
  352. /// <summary>
  353. /// Character is colliding with the ground.
  354. /// </summary>
  355. Down = 0x4
  356. }
  357. /// <summary>
  358. /// Used for passing CharacterController initialization data between native and managed code.
  359. /// </summary>
  360. [StructLayout(LayoutKind.Sequential)]
  361. internal struct ScriptCharacterControllerData // Note: Must match C++ struct CHAR_CONTROLLER_DESC
  362. {
  363. public Vector3 position;
  364. public float contactOffset;
  365. public float stepOffset;
  366. public Radian slopeLimit;
  367. public float minMoveDistance;
  368. public float height;
  369. public float radius;
  370. public Vector3 up;
  371. public CharacterClimbingMode climbingMode;
  372. public CharacterNonWalkableMode nonWalkableMode;
  373. }
  374. /// <summary>
  375. /// Used for passing ControllerCollision data between native and managed code.
  376. /// </summary>
  377. internal struct ScriptControllerCollision // Note: Must match C++ struct ScriptControllerCollision
  378. {
  379. public Vector3 position;
  380. public Vector3 normal;
  381. public Vector3 motionDir;
  382. public float motionAmount;
  383. public NativeCollider collider;
  384. public int triangleIndex;
  385. public NativeCharacterController controller;
  386. }
  387. /// <summary>
  388. /// Contains data about a collision of a character controller and another object.
  389. /// </summary>
  390. public class ControllerCollision
  391. {
  392. /// <summary>
  393. /// Contact position.
  394. /// </summary>
  395. public Vector3 position;
  396. /// <summary>
  397. /// Contact normal.
  398. /// </summary>
  399. public Vector3 normal;
  400. /// <summary>
  401. /// Direction of motion after the hit.
  402. /// </summary>
  403. public Vector3 motionDir;
  404. /// <summary>
  405. /// Magnitude of motion after the hit.
  406. /// </summary>
  407. public float motionAmount;
  408. };
  409. /// <summary>
  410. /// Contains data about a collision of a character controller and a collider.
  411. /// </summary>
  412. public class ControllerColliderCollision : ControllerCollision
  413. {
  414. /// <summary>
  415. /// Collider that was touched.
  416. /// </summary>
  417. public Collider collider;
  418. /// <summary>
  419. /// Touched triangle index for mesh colliders.
  420. /// </summary>
  421. public int triangleIndex;
  422. };
  423. /// <summary>
  424. /// Contains data about a collision between two character controllers.
  425. /// </summary>
  426. public class ControllerControllerCollision : ControllerCollision
  427. {
  428. /// <summary>
  429. /// Controller that was touched.
  430. /// </summary>
  431. public CharacterController controller;
  432. };
  433. /** @} */
  434. }