Character.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #include "AnimationController.h"
  2. #include "Character.h"
  3. #include "MemoryBuffer.h"
  4. #include "PhysicsEvents.h"
  5. #include "PhysicsWorld.h"
  6. #include "RigidBody.h"
  7. #include "Scene.h"
  8. #include "SceneEvents.h"
  9. Character::Character(Context* context) :
  10. Component(context),
  11. onGround_(false),
  12. okToJump_(true),
  13. inAirTimer_(0.0f)
  14. {
  15. }
  16. void Character::OnNodeSet(Node* node)
  17. {
  18. // Component has been inserted into its scene node. Subscribe to events now
  19. SubscribeToEvent(node, E_NODECOLLISION, HANDLER(Character, HandleNodeCollision));
  20. SubscribeToEvent(GetScene()->GetComponent<PhysicsWorld>(), E_PHYSICSPRESTEP, HANDLER(Character, HandleFixedUpdate));
  21. }
  22. void Character::HandleNodeCollision(StringHash eventType, VariantMap& eventData)
  23. {
  24. // Check collision contacts and see if character is standing on ground (look for a contact that has near vertical normal)
  25. using namespace NodeCollision;
  26. MemoryBuffer contacts(eventData[P_CONTACTS].GetBuffer());
  27. while (!contacts.IsEof())
  28. {
  29. Vector3 contactPosition = contacts.ReadVector3();
  30. Vector3 contactNormal = contacts.ReadVector3();
  31. float contactDistance = contacts.ReadFloat();
  32. float contactImpulse = contacts.ReadFloat();
  33. // If contact is below node center and mostly vertical, assume it's a ground contact
  34. if (contactPosition.y_ < (node_->GetPosition().y_ + 1.0f))
  35. {
  36. float level = Abs(contactNormal.y_);
  37. if (level > 0.75)
  38. onGround_ = true;
  39. }
  40. }
  41. }
  42. void Character::HandleFixedUpdate(StringHash eventType, VariantMap& eventData)
  43. {
  44. using namespace PhysicsPreStep;
  45. float timeStep = eventData[P_TIMESTEP].GetFloat();
  46. /// \todo Could cache the components for faster access instead of finding them each frame
  47. RigidBody* body = GetComponent<RigidBody>();
  48. AnimationController* animCtrl = GetComponent<AnimationController>();
  49. // Update the in air timer. Reset if grounded
  50. if (!onGround_)
  51. inAirTimer_ += timeStep;
  52. else
  53. inAirTimer_ = 0.0f;
  54. // When character has been in air less than 1/10 second, it's still interpreted as being on ground
  55. bool softGrounded = inAirTimer_ < INAIR_THRESHOLD_TIME;
  56. // Update movement & animation
  57. const Quaternion& rot = node_->GetRotation();
  58. Vector3 moveDir = Vector3::ZERO;
  59. const Vector3& velocity = body->GetLinearVelocity();
  60. // Velocity on the XZ plane
  61. Vector3 planeVelocity(velocity.x_, 0.0f, velocity.z_);
  62. if (controls_.IsDown(CTRL_UP))
  63. moveDir += Vector3::FORWARD;
  64. if (controls_.IsDown(CTRL_DOWN))
  65. moveDir += Vector3::BACK;
  66. if (controls_.IsDown(CTRL_LEFT))
  67. moveDir += Vector3::LEFT;
  68. if (controls_.IsDown(CTRL_RIGHT))
  69. moveDir += Vector3::RIGHT;
  70. // Normalize move vector so that diagonal strafing is not faster
  71. if (moveDir.LengthSquared() > 0.0f)
  72. moveDir.Normalize();
  73. // If in air, allow control, but slower than when on ground
  74. body->ApplyImpulse(rot * moveDir * (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
  75. if (softGrounded)
  76. {
  77. // When on ground, apply a braking force to limit maximum ground velocity
  78. Vector3 brakeForce = -planeVelocity * BRAKE_FORCE;
  79. body->ApplyImpulse(brakeForce);
  80. // Jump. Must release jump control inbetween jumps
  81. if (controls_.IsDown(CTRL_JUMP))
  82. {
  83. if (okToJump_)
  84. {
  85. body->ApplyImpulse(Vector3::UP * JUMP_FORCE);
  86. okToJump_ = false;
  87. }
  88. }
  89. else
  90. okToJump_ = true;
  91. }
  92. // Play walk animation if moving on ground, otherwise fade it out
  93. if (softGrounded && !moveDir.Equals(Vector3::ZERO))
  94. animCtrl->PlayExclusive("Models/Jack_Walk.ani", 0, true, 0.2f);
  95. else
  96. animCtrl->Stop("Models/Jack_Walk.ani", 0.2f);
  97. // Set walk animation speed proportional to velocity
  98. animCtrl->SetSpeed("Models/Jack_Walk.ani", planeVelocity.Length() * 0.3f);
  99. // Reset grounded flag for next frame
  100. onGround_ = false;
  101. }