CharacterController.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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 void Move(Vector3 position)
  189. {
  190. if (native == null)
  191. return;
  192. native.Move(position);
  193. UpdatePositionFromController();
  194. }
  195. /// <summary>
  196. /// Triggered when the controller hits a collider.
  197. /// </summary>
  198. /// <param name="data">Data about the collision.</param>
  199. internal void DoOnColliderHit(ControllerColliderCollision data)
  200. {
  201. if (OnColliderHit != null)
  202. OnColliderHit(data);
  203. }
  204. /// <summary>
  205. /// Triggered when the controller hits another character controller.
  206. /// </summary>
  207. /// <param name="data">Data about the collision.</param>
  208. internal void DoOnControllerHit(ControllerControllerCollision data)
  209. {
  210. if (OnControllerHit != null)
  211. OnControllerHit(data);
  212. }
  213. private void OnInitialize()
  214. {
  215. NotifyFlags = TransformChangedFlags.Transform;
  216. }
  217. private void OnEnable()
  218. {
  219. RestoreNative();
  220. }
  221. private void OnDisable()
  222. {
  223. DestroyNative();
  224. }
  225. private void OnDestroy()
  226. {
  227. DestroyNative();
  228. }
  229. private void OnTransformChanged(TransformChangedFlags flags)
  230. {
  231. if (!SceneObject.Active || native == null)
  232. return;
  233. native.Position = SceneObject.Position;
  234. }
  235. /// <summary>
  236. /// Updates the position by copying it from the controller to the component's scene object.
  237. /// </summary>
  238. private void UpdatePositionFromController()
  239. {
  240. NotifyFlags = 0;
  241. SceneObject.Position = native.Position;
  242. NotifyFlags = TransformChangedFlags.Transform;
  243. }
  244. /// <summary>
  245. /// Updates the dimensions of the controller by taking account scale of the parent scene object.
  246. /// </summary>
  247. private void UpdateDimensions()
  248. {
  249. Vector3 scale = SceneObject.Scale;
  250. float height = serializableData.height * MathEx.Abs(scale.y);
  251. float radius = serializableData.radius * MathEx.Abs(MathEx.Max(scale.x, scale.z));
  252. native.Height = height;
  253. native.Radius = radius;
  254. }
  255. /// <summary>
  256. /// Restores the internal character controller representation and initializes it with data stored by the component.
  257. /// </summary>
  258. private void RestoreNative()
  259. {
  260. ScriptCharacterControllerData initData = new ScriptCharacterControllerData();
  261. initData.position = SceneObject.Position;
  262. initData.contactOffset = serializableData.contactOffset;
  263. initData.stepOffset = serializableData.stepOffset;
  264. initData.slopeLimit = serializableData.slopeLimit;
  265. initData.minMoveDistance = serializableData.minMoveDistance;
  266. initData.height = serializableData.height;
  267. initData.radius = serializableData.radius;
  268. initData.up = serializableData.up;
  269. initData.climbingMode = serializableData.climbingMode;
  270. initData.nonWalkableMode = serializableData.nonWalkableMode;
  271. native = new NativeCharacterController(initData);
  272. native.Component = this;
  273. native.Layer = serializableData.layer;
  274. UpdateDimensions();
  275. }
  276. /// <summary>
  277. /// Destroys the internal character controller representation.
  278. /// </summary>
  279. private void DestroyNative()
  280. {
  281. if (native != null)
  282. {
  283. native.Destroy();
  284. native = null;
  285. }
  286. }
  287. /// <summary>
  288. /// Holds all data the character controller component needs to persist through serialization.
  289. /// </summary>
  290. [SerializeObject]
  291. internal class SerializableData
  292. {
  293. public float contactOffset = 0.1f;
  294. public float stepOffset = 0.5f;
  295. public Radian slopeLimit = new Degree(45.0f);
  296. public float minMoveDistance = 0.0f;
  297. public float height = 0.0f;
  298. public float radius = 1.0f;
  299. public Vector3 up = Vector3.YAxis;
  300. public CharacterClimbingMode climbingMode = CharacterClimbingMode.Normal;
  301. public CharacterNonWalkableMode nonWalkableMode = CharacterNonWalkableMode.Prevent;
  302. public ulong layer = 1;
  303. }
  304. }
  305. /// <summary>
  306. /// Controls climbing behaviour for a capsule character controller. Normally the character controller will not
  307. /// automatically climb when heights are greater than the assigned step offset.However due to the shape of the capsule
  308. /// it might automatically climb over slightly larger heights than assigned step offsets.
  309. /// </summary>
  310. public enum CharacterClimbingMode
  311. {
  312. /// <summary>
  313. /// Normal behaviour. Capsule character controller will be able to auto-step even above the step offset.
  314. /// </summary>
  315. Normal,
  316. /// <summary>
  317. /// The system will attempt to limit auto-step to the provided step offset and no higher.
  318. /// </summary>
  319. Constrained
  320. }
  321. /// <summary>
  322. /// Controls behaviour when a character controller reaches a slope thats larger than its slope offset.
  323. /// </summary>
  324. public enum CharacterNonWalkableMode
  325. {
  326. /// <summary>
  327. /// Character will be prevented from going further, but will be allowed to move laterally.
  328. /// </summary>
  329. Prevent,
  330. /// <summary>
  331. /// Character will be prevented from going further, but also slide down the slope.
  332. /// </summary>
  333. PreventAndSlide
  334. }
  335. /// <summary>
  336. /// Reports in which directions is the character colliding with other objects.
  337. /// </summary>
  338. public enum CharacterCollisionFlag
  339. {
  340. /// <summary>
  341. /// Character is colliding with its sides.
  342. /// </summary>
  343. Sides = 0x1,
  344. /// <summary>
  345. /// Character is colliding with the ceiling.
  346. /// </summary>
  347. Up = 0x2,
  348. /// <summary>
  349. /// Character is colliding with the ground.
  350. /// </summary>
  351. Down = 0x4
  352. }
  353. /// <summary>
  354. /// Used for passing CharacterController initialization data between native and managed code.
  355. /// </summary>
  356. [StructLayout(LayoutKind.Sequential)]
  357. internal struct ScriptCharacterControllerData // Note: Must match C++ struct CHAR_CONTROLLER_DESC
  358. {
  359. public Vector3 position;
  360. public float contactOffset;
  361. public float stepOffset;
  362. public Radian slopeLimit;
  363. public float minMoveDistance;
  364. public float height;
  365. public float radius;
  366. public Vector3 up;
  367. public CharacterClimbingMode climbingMode;
  368. public CharacterNonWalkableMode nonWalkableMode;
  369. }
  370. /// <summary>
  371. /// Used for passing ControllerCollision data between native and managed code.
  372. /// </summary>
  373. internal struct ScriptControllerCollision // Note: Must match C++ struct ScriptControllerCollision
  374. {
  375. public Vector3 position;
  376. public Vector3 normal;
  377. public Vector3 motionDir;
  378. public float motionAmount;
  379. public NativeCollider collider;
  380. public int triangleIndex;
  381. public NativeCharacterController controller;
  382. }
  383. /// <summary>
  384. /// Contains data about a collision of a character controller and another object.
  385. /// </summary>
  386. public class ControllerCollision
  387. {
  388. /// <summary>
  389. /// Contact position.
  390. /// </summary>
  391. public Vector3 position;
  392. /// <summary>
  393. /// Contact normal.
  394. /// </summary>
  395. public Vector3 normal;
  396. /// <summary>
  397. /// Direction of motion after the hit.
  398. /// </summary>
  399. public Vector3 motionDir;
  400. /// <summary>
  401. /// Magnitude of motion after the hit.
  402. /// </summary>
  403. public float motionAmount;
  404. };
  405. /// <summary>
  406. /// Contains data about a collision of a character controller and a collider.
  407. /// </summary>
  408. public class ControllerColliderCollision : ControllerCollision
  409. {
  410. /// <summary>
  411. /// Collider that was touched.
  412. /// </summary>
  413. public Collider collider;
  414. /// <summary>
  415. /// Touched triangle index for mesh colliders.
  416. /// </summary>
  417. public int triangleIndex;
  418. };
  419. /// <summary>
  420. /// Contains data about a collision between two character controllers.
  421. /// </summary>
  422. public class ControllerControllerCollision : ControllerCollision
  423. {
  424. /// <summary>
  425. /// Controller that was touched.
  426. /// </summary>
  427. public CharacterController controller;
  428. };
  429. }