Character.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. //
  2. // Copyright (c) 2008-2015 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include <Urho3D/Urho3D.h>
  23. #include <Urho3D/Core/Context.h>
  24. #include <Urho3D/Graphics/AnimationController.h>
  25. #include <Urho3D/IO/MemoryBuffer.h>
  26. #include <Urho3D/Physics/PhysicsEvents.h>
  27. #include <Urho3D/Physics/PhysicsWorld.h>
  28. #include <Urho3D/Physics/RigidBody.h>
  29. #include <Urho3D/Scene/Scene.h>
  30. #include <Urho3D/Scene/SceneEvents.h>
  31. #include "Character.h"
  32. Character::Character(Context* context) :
  33. LogicComponent(context),
  34. onGround_(false),
  35. okToJump_(true),
  36. inAirTimer_(0.0f)
  37. {
  38. // Only the physics update event is needed: unsubscribe from the rest for optimization
  39. SetUpdateEventMask(USE_FIXEDUPDATE);
  40. }
  41. void Character::RegisterObject(Context* context)
  42. {
  43. context->RegisterFactory<Character>();
  44. // These macros register the class attributes to the Context for automatic load / save handling.
  45. // We specify the Default attribute mode which means it will be used both for saving into file, and network replication
  46. ATTRIBUTE("Controls Yaw", float, controls_.yaw_, 0.0f, AM_DEFAULT);
  47. ATTRIBUTE("Controls Pitch", float, controls_.pitch_, 0.0f, AM_DEFAULT);
  48. ATTRIBUTE("On Ground", bool, onGround_, false, AM_DEFAULT);
  49. ATTRIBUTE("OK To Jump", bool, okToJump_, true, AM_DEFAULT);
  50. ATTRIBUTE("In Air Timer", float, inAirTimer_, 0.0f, AM_DEFAULT);
  51. }
  52. void Character::Start()
  53. {
  54. // Component has been inserted into its scene node. Subscribe to events now
  55. SubscribeToEvent(GetNode(), E_NODECOLLISION, HANDLER(Character, HandleNodeCollision));
  56. }
  57. void Character::FixedUpdate(float timeStep)
  58. {
  59. /// \todo Could cache the components for faster access instead of finding them each frame
  60. RigidBody* body = GetComponent<RigidBody>();
  61. AnimationController* animCtrl = GetComponent<AnimationController>();
  62. // Update the in air timer. Reset if grounded
  63. if (!onGround_)
  64. inAirTimer_ += timeStep;
  65. else
  66. inAirTimer_ = 0.0f;
  67. // When character has been in air less than 1/10 second, it's still interpreted as being on ground
  68. bool softGrounded = inAirTimer_ < INAIR_THRESHOLD_TIME;
  69. // Update movement & animation
  70. const Quaternion& rot = node_->GetRotation();
  71. Vector3 moveDir = Vector3::ZERO;
  72. const Vector3& velocity = body->GetLinearVelocity();
  73. // Velocity on the XZ plane
  74. Vector3 planeVelocity(velocity.x_, 0.0f, velocity.z_);
  75. if (controls_.IsDown(CTRL_FORWARD))
  76. moveDir += Vector3::FORWARD;
  77. if (controls_.IsDown(CTRL_BACK))
  78. moveDir += Vector3::BACK;
  79. if (controls_.IsDown(CTRL_LEFT))
  80. moveDir += Vector3::LEFT;
  81. if (controls_.IsDown(CTRL_RIGHT))
  82. moveDir += Vector3::RIGHT;
  83. // Normalize move vector so that diagonal strafing is not faster
  84. if (moveDir.LengthSquared() > 0.0f)
  85. moveDir.Normalize();
  86. // If in air, allow control, but slower than when on ground
  87. body->ApplyImpulse(rot * moveDir * (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
  88. if (softGrounded)
  89. {
  90. // When on ground, apply a braking force to limit maximum ground velocity
  91. Vector3 brakeForce = -planeVelocity * BRAKE_FORCE;
  92. body->ApplyImpulse(brakeForce);
  93. // Jump. Must release jump control inbetween jumps
  94. if (controls_.IsDown(CTRL_JUMP))
  95. {
  96. if (okToJump_)
  97. {
  98. body->ApplyImpulse(Vector3::UP * JUMP_FORCE);
  99. okToJump_ = false;
  100. }
  101. }
  102. else
  103. okToJump_ = true;
  104. }
  105. // Play walk animation if moving on ground, otherwise fade it out
  106. if (softGrounded && !moveDir.Equals(Vector3::ZERO))
  107. animCtrl->PlayExclusive("Models/Jack_Walk.ani", 0, true, 0.2f);
  108. else
  109. animCtrl->Stop("Models/Jack_Walk.ani", 0.2f);
  110. // Set walk animation speed proportional to velocity
  111. animCtrl->SetSpeed("Models/Jack_Walk.ani", planeVelocity.Length() * 0.3f);
  112. // Reset grounded flag for next frame
  113. onGround_ = false;
  114. }
  115. void Character::HandleNodeCollision(StringHash eventType, VariantMap& eventData)
  116. {
  117. // Check collision contacts and see if character is standing on ground (look for a contact that has near vertical normal)
  118. using namespace NodeCollision;
  119. MemoryBuffer contacts(eventData[P_CONTACTS].GetBuffer());
  120. while (!contacts.IsEof())
  121. {
  122. Vector3 contactPosition = contacts.ReadVector3();
  123. Vector3 contactNormal = contacts.ReadVector3();
  124. /*float contactDistance = */contacts.ReadFloat();
  125. /*float contactImpulse = */contacts.ReadFloat();
  126. // If contact is below node center and mostly vertical, assume it's a ground contact
  127. if (contactPosition.y_ < (node_->GetPosition().y_ + 1.0f))
  128. {
  129. float level = Abs(contactNormal.y_);
  130. if (level > 0.75)
  131. onGround_ = true;
  132. }
  133. }
  134. }