Character.cpp 4.2 KB

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