Character.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include <Urho3D/Core/Context.h>
  4. #include <Urho3D/Graphics/AnimationController.h>
  5. #include <Urho3D/IO/MemoryBuffer.h>
  6. #include <Urho3D/Physics/PhysicsEvents.h>
  7. #include <Urho3D/Physics/PhysicsWorld.h>
  8. #include <Urho3D/Physics/RigidBody.h>
  9. #include <Urho3D/Scene/Scene.h>
  10. #include <Urho3D/Scene/SceneEvents.h>
  11. #include "Character.h"
  12. Character::Character(Context* context) :
  13. LogicComponent(context),
  14. onGround_(false),
  15. okToJump_(true),
  16. inAirTimer_(0.0f)
  17. {
  18. // Only the physics update event is needed: unsubscribe from the rest for optimization
  19. SetUpdateEventMask(LogicComponentEvents::FixedUpdate);
  20. }
  21. void Character::RegisterObject(Context* context)
  22. {
  23. context->RegisterFactory<Character>();
  24. // These macros register the class attributes to the Context for automatic load / save handling.
  25. // We specify the Default attribute mode which means it will be used both for saving into file, and network replication
  26. URHO3D_ATTRIBUTE("Controls Yaw", controls_.yaw_, 0.0f, AM_DEFAULT);
  27. URHO3D_ATTRIBUTE("Controls Pitch", controls_.pitch_, 0.0f, AM_DEFAULT);
  28. URHO3D_ATTRIBUTE("On Ground", onGround_, false, AM_DEFAULT);
  29. URHO3D_ATTRIBUTE("OK To Jump", okToJump_, true, AM_DEFAULT);
  30. URHO3D_ATTRIBUTE("In Air Timer", inAirTimer_, 0.0f, AM_DEFAULT);
  31. }
  32. void Character::Start()
  33. {
  34. // Component has been inserted into its scene node. Subscribe to events now
  35. SubscribeToEvent(GetNode(), E_NODECOLLISION, URHO3D_HANDLER(Character, HandleNodeCollision));
  36. }
  37. void Character::FixedUpdate(float timeStep)
  38. {
  39. /// \todo Could cache the components for faster access instead of finding them each frame
  40. auto* body = GetComponent<RigidBody>();
  41. auto* animCtrl = node_->GetComponent<AnimationController>(true);
  42. // Update the in air timer. Reset if grounded
  43. if (!onGround_)
  44. inAirTimer_ += timeStep;
  45. else
  46. inAirTimer_ = 0.0f;
  47. // When character has been in air less than 1/10 second, it's still interpreted as being on ground
  48. bool softGrounded = inAirTimer_ < INAIR_THRESHOLD_TIME;
  49. // Update movement & animation
  50. const Quaternion& rot = node_->GetRotation();
  51. Vector3 moveDir = Vector3::ZERO;
  52. const Vector3& velocity = body->GetLinearVelocity();
  53. // Velocity on the XZ plane
  54. Vector3 planeVelocity(velocity.x_, 0.0f, velocity.z_);
  55. if (controls_.IsDown(CTRL_FORWARD))
  56. moveDir += Vector3::FORWARD;
  57. if (controls_.IsDown(CTRL_BACK))
  58. moveDir += Vector3::BACK;
  59. if (controls_.IsDown(CTRL_LEFT))
  60. moveDir += Vector3::LEFT;
  61. if (controls_.IsDown(CTRL_RIGHT))
  62. moveDir += Vector3::RIGHT;
  63. // Normalize move vector so that diagonal strafing is not faster
  64. if (moveDir.LengthSquared() > 0.0f)
  65. moveDir.Normalize();
  66. // If in air, allow control, but slower than when on ground
  67. body->ApplyImpulse(rot * moveDir * (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
  68. if (softGrounded)
  69. {
  70. // When on ground, apply a braking force to limit maximum ground velocity
  71. Vector3 brakeForce = -planeVelocity * BRAKE_FORCE;
  72. body->ApplyImpulse(brakeForce);
  73. // Jump. Must release jump control between jumps
  74. if (controls_.IsDown(CTRL_JUMP))
  75. {
  76. if (okToJump_)
  77. {
  78. body->ApplyImpulse(Vector3::UP * JUMP_FORCE);
  79. okToJump_ = false;
  80. animCtrl->PlayExclusive("Models/Mutant/Mutant_Jump1.ani", 0, false, 0.2f);
  81. }
  82. }
  83. else
  84. okToJump_ = true;
  85. }
  86. if ( !onGround_ )
  87. {
  88. animCtrl->PlayExclusive("Models/Mutant/Mutant_Jump1.ani", 0, false, 0.2f);
  89. }
  90. else
  91. {
  92. // Play walk animation if moving on ground, otherwise fade it out
  93. if (softGrounded && !moveDir.Equals(Vector3::ZERO))
  94. animCtrl->PlayExclusive("Models/Mutant/Mutant_Run.ani", 0, true, 0.2f);
  95. else
  96. animCtrl->PlayExclusive("Models/Mutant/Mutant_Idle0.ani", 0, true, 0.2f);
  97. // Set walk animation speed proportional to velocity
  98. animCtrl->SetSpeed("Models/Mutant/Mutant_Run.ani", planeVelocity.Length() * 0.3f);
  99. }
  100. // Reset grounded flag for next frame
  101. onGround_ = false;
  102. }
  103. void Character::HandleNodeCollision(StringHash eventType, VariantMap& eventData)
  104. {
  105. // Check collision contacts and see if character is standing on ground (look for a contact that has near vertical normal)
  106. using namespace NodeCollision;
  107. MemoryBuffer contacts(eventData[P_CONTACTS].GetBuffer());
  108. while (!contacts.IsEof())
  109. {
  110. Vector3 contactPosition = contacts.ReadVector3();
  111. Vector3 contactNormal = contacts.ReadVector3();
  112. /*float contactDistance = */contacts.ReadFloat();
  113. /*float contactImpulse = */contacts.ReadFloat();
  114. // If contact is below node center and pointing up, assume it's a ground contact
  115. if (contactPosition.y_ < (node_->GetPosition().y_ + 1.0f))
  116. {
  117. float level = contactNormal.y_;
  118. if (level > 0.75)
  119. onGround_ = true;
  120. }
  121. }
  122. }