Character.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using System;
  2. using AtomicEngine;
  3. public class Character : CSComponent
  4. {
  5. void Start()
  6. {
  7. SubscribeToEvent("NodeCollision", HandleNodeCollision);
  8. }
  9. void FixedUpdate(float timeStep)
  10. {
  11. /// TODO: Could cache the components for faster access instead of finding them each frame
  12. /// Also, switch to generic version of GetComponent
  13. ///
  14. RigidBody body = (RigidBody)Node.GetComponent("RigidBody");
  15. AnimationController animCtrl = (AnimationController)Node.GetComponent("AnimationController", true);
  16. // Update the in air timer. Reset if grounded
  17. if (!onGround)
  18. inAirTimer += timeStep;
  19. else
  20. inAirTimer = 0.0f;
  21. // When character has been in air less than 1/10 second, it's still interpreted as being on ground
  22. bool softGrounded = inAirTimer < INAIR_THRESHOLD_TIME;
  23. // Update movement & animation
  24. Quaternion rot = Node.Rotation;
  25. Vector3 moveDir = Vector3.Zero;
  26. Vector3 velocity = body.LinearVelocity;
  27. // Velocity on the XZ plane
  28. Vector3 planeVelocity = new Vector3(velocity.X, 0.0f, velocity.Z);
  29. var input = GetSubsystem<Input>();
  30. if (input.GetKeyDown(Constants.KEY_W))
  31. {
  32. moveDir += Vector3.Forward;
  33. }
  34. if (input.GetKeyDown(Constants.KEY_S))
  35. {
  36. moveDir += Vector3.Back;
  37. }
  38. if (input.GetKeyDown(Constants.KEY_A))
  39. {
  40. moveDir += Vector3.Left;
  41. }
  42. if (input.GetKeyDown(Constants.KEY_D))
  43. {
  44. moveDir += Vector3.Right;
  45. }
  46. float breakAdjust = ((float)input.MouseMoveWheel) * timeStep * 4.0f;
  47. breakForce -= breakAdjust;
  48. breakForce = Clamp<float>(breakForce, MIN_BRAKE_FORCE, MAX_BRAKE_FORCE);
  49. // Normalize move vector so that diagonal strafing is not faster
  50. if (moveDir.LengthSquared > 0.0f)
  51. moveDir.Normalize();
  52. // If in air, allow control, but slower than when on ground
  53. body.ApplyImpulse(rot * moveDir * (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
  54. if (softGrounded)
  55. {
  56. // When on ground, apply a braking force to limit maximum ground velocity
  57. Vector3 brakeForce = -planeVelocity * breakForce;
  58. body.ApplyImpulse(brakeForce);
  59. // Jump. Must release jump control inbetween jumps
  60. if (input.GetKeyDown(Constants.KEY_SPACE))
  61. {
  62. if (okToJump)
  63. {
  64. body.ApplyImpulse(Vector3.Up * JUMP_FORCE);
  65. okToJump = false;
  66. animCtrl.PlayExclusive("Models/Mutant/Mutant_Jump1.ani", 0, false, 0.2f);
  67. }
  68. }
  69. else
  70. okToJump = true;
  71. }
  72. if (!onGround)
  73. {
  74. animCtrl.PlayExclusive("Models/Mutant/Mutant_Jump1.ani", 0, false, 0.2f);
  75. }
  76. else
  77. {
  78. // Play walk animation if moving on ground, otherwise fade it out
  79. if (softGrounded && !moveDir.Equals(Vector3.Zero))
  80. animCtrl.PlayExclusive("Models/Mutant/Mutant_Run.ani", 0, true, 0.2f);
  81. else
  82. animCtrl.PlayExclusive("Models/Mutant/Mutant_Idle0.ani", 0, true, 0.2f);
  83. // Set walk animation speed proportional to velocity
  84. animCtrl.SetSpeed("Models/Mutant/Mutant_Run.ani", planeVelocity.Length * 0.3f);
  85. }
  86. // Reset grounded flag for next frame
  87. onGround = false;
  88. }
  89. void HandleNodeCollision(uint eventType, ScriptVariantMap eventData)
  90. {
  91. PhysicsNodeCollision nodeCollision = eventData.GetPtr<PhysicsNodeCollision>("PhysicsNodeCollision");
  92. if (nodeCollision == null)
  93. return;
  94. // TODO: We need to be able to subscribe to specific object's events
  95. if (nodeCollision.OtherNode == Node)
  96. {
  97. var nodePosition = Node.Position;
  98. for (uint i = 0; i < nodeCollision.Contacts.Size; i++)
  99. {
  100. var contact = nodeCollision.Contacts.At(i);
  101. var contactPosition = contact.Position;
  102. // TODO: This needs to be flipped when can listen to specific node
  103. // If contact is below node center and pointing up, assume it's a ground contact
  104. if (contactPosition.Y > (nodePosition.Y - 1.0f))
  105. {
  106. float level = contact.Normal.Y;
  107. if (level < 0.75f)
  108. onGround = true;
  109. }
  110. }
  111. }
  112. }
  113. const float MOVE_FORCE = 0.8f;
  114. const float INAIR_MOVE_FORCE = 0.02f;
  115. const float JUMP_FORCE = 7.0f;
  116. const float YAW_SENSITIVITY = 0.1f;
  117. const float INAIR_THRESHOLD_TIME = 0.1f;
  118. const float MIN_BRAKE_FORCE = 0.1f;
  119. const float MAX_BRAKE_FORCE = 0.4f;
  120. float breakForce = 0.2f;
  121. /// Grounded flag for movement.
  122. bool onGround = false;
  123. /// Jump flag.
  124. bool okToJump = true;
  125. /// In air timer. Due to possible physics inaccuracy, character can be off ground for max. 1/10 second and still be allowed to move.
  126. float inAirTimer = 0.0f;
  127. // TODO: add this to a utility class
  128. static T Clamp<T>(T val, T min, T max) where T : IComparable<T>
  129. {
  130. if (val.CompareTo(min) < 0) return min;
  131. else if (val.CompareTo(max) > 0) return max;
  132. else return val;
  133. }
  134. }