CharacterController.cs 16 KB

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