Vehicle.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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/Material.h>
  25. #include <Urho3D/Graphics/Model.h>
  26. #include <Urho3D/Graphics/StaticModel.h>
  27. #include <Urho3D/Physics/CollisionShape.h>
  28. #include <Urho3D/Physics/Constraint.h>
  29. #include <Urho3D/Physics/PhysicsEvents.h>
  30. #include <Urho3D/Physics/PhysicsWorld.h>
  31. #include <Urho3D/Physics/RigidBody.h>
  32. #include <Urho3D/Resource/ResourceCache.h>
  33. #include <Urho3D/Scene/Scene.h>
  34. #include "Vehicle.h"
  35. Vehicle::Vehicle(Context* context) :
  36. LogicComponent(context),
  37. steering_(0.0f)
  38. {
  39. // Only the physics update event is needed: unsubscribe from the rest for optimization
  40. SetUpdateEventMask(USE_FIXEDUPDATE);
  41. }
  42. void Vehicle::RegisterObject(Context* context)
  43. {
  44. context->RegisterFactory<Vehicle>();
  45. ATTRIBUTE("Controls Yaw", float, controls_.yaw_, 0.0f, AM_DEFAULT);
  46. ATTRIBUTE("Controls Pitch", float, controls_.pitch_, 0.0f, AM_DEFAULT);
  47. ATTRIBUTE("Steering", float, steering_, 0.0f, AM_DEFAULT);
  48. // Register wheel node IDs as attributes so that the wheel nodes can be reaquired on deserialization. They need to be tagged
  49. // as node ID's so that the deserialization code knows to rewrite the IDs in case they are different on load than on save
  50. ATTRIBUTE("Front Left Node", int, frontLeftID_, 0, AM_DEFAULT | AM_NODEID);
  51. ATTRIBUTE("Front Right Node", int, frontRightID_, 0, AM_DEFAULT | AM_NODEID);
  52. ATTRIBUTE("Rear Left Node", int, rearLeftID_, 0, AM_DEFAULT | AM_NODEID);
  53. ATTRIBUTE("Rear Right Node", int, rearRightID_, 0, AM_DEFAULT | AM_NODEID);
  54. }
  55. void Vehicle::ApplyAttributes()
  56. {
  57. // This function is called on each Serializable after the whole scene has been loaded. Reacquire wheel nodes from ID's
  58. // as well as all required physics components
  59. Scene* scene = GetScene();
  60. frontLeft_ = scene->GetNode(frontLeftID_);
  61. frontRight_ = scene->GetNode(frontRightID_);
  62. rearLeft_ = scene->GetNode(rearLeftID_);
  63. rearRight_ = scene->GetNode(rearRightID_);
  64. hullBody_ = node_->GetComponent<RigidBody>();
  65. GetWheelComponents();
  66. }
  67. void Vehicle::FixedUpdate(float timeStep)
  68. {
  69. float newSteering = 0.0f;
  70. float accelerator = 0.0f;
  71. // Read controls
  72. if (controls_.buttons_ & CTRL_LEFT)
  73. newSteering = -1.0f;
  74. if (controls_.buttons_ & CTRL_RIGHT)
  75. newSteering = 1.0f;
  76. if (controls_.buttons_ & CTRL_FORWARD)
  77. accelerator = 1.0f;
  78. if (controls_.buttons_ & CTRL_BACK)
  79. accelerator = -0.5f;
  80. // When steering, wake up the wheel rigidbodies so that their orientation is updated
  81. if (newSteering != 0.0f)
  82. {
  83. frontLeftBody_->Activate();
  84. frontRightBody_->Activate();
  85. steering_ = steering_ * 0.95f + newSteering * 0.05f;
  86. }
  87. else
  88. steering_ = steering_ * 0.8f + newSteering * 0.2f;
  89. // Set front wheel angles
  90. Quaternion steeringRot(0, steering_ * MAX_WHEEL_ANGLE, 0);
  91. frontLeftAxis_->SetOtherAxis(steeringRot * Vector3::LEFT);
  92. frontRightAxis_->SetOtherAxis(steeringRot * Vector3::RIGHT);
  93. Quaternion hullRot = hullBody_->GetRotation();
  94. if (accelerator != 0.0f)
  95. {
  96. // Torques are applied in world space, so need to take the vehicle & wheel rotation into account
  97. Vector3 torqueVec = Vector3(ENGINE_POWER * accelerator, 0.0f, 0.0f);
  98. frontLeftBody_->ApplyTorque(hullRot * steeringRot * torqueVec);
  99. frontRightBody_->ApplyTorque(hullRot * steeringRot * torqueVec);
  100. rearLeftBody_->ApplyTorque(hullRot * torqueVec);
  101. rearRightBody_->ApplyTorque(hullRot * torqueVec);
  102. }
  103. // Apply downforce proportional to velocity
  104. Vector3 localVelocity = hullRot.Inverse() * hullBody_->GetLinearVelocity();
  105. hullBody_->ApplyForce(hullRot * Vector3::DOWN * Abs(localVelocity.z_) * DOWN_FORCE);
  106. }
  107. void Vehicle::Init()
  108. {
  109. // This function is called only from the main program when initially creating the vehicle, not on scene load
  110. ResourceCache* cache = GetSubsystem<ResourceCache>();
  111. StaticModel* hullObject = node_->CreateComponent<StaticModel>();
  112. hullBody_ = node_->CreateComponent<RigidBody>();
  113. CollisionShape* hullShape = node_->CreateComponent<CollisionShape>();
  114. node_->SetScale(Vector3(1.5f, 1.0f, 3.0f));
  115. hullObject->SetModel(cache->GetResource<Model>("Models/Box.mdl"));
  116. hullObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));
  117. hullObject->SetCastShadows(true);
  118. hullShape->SetBox(Vector3::ONE);
  119. hullBody_->SetMass(4.0f);
  120. hullBody_->SetLinearDamping(0.2f); // Some air resistance
  121. hullBody_->SetAngularDamping(0.5f);
  122. hullBody_->SetCollisionLayer(1);
  123. InitWheel("FrontLeft", Vector3(-0.6f, -0.4f, 0.3f), frontLeft_, frontLeftID_);
  124. InitWheel("FrontRight", Vector3(0.6f, -0.4f, 0.3f), frontRight_, frontRightID_);
  125. InitWheel("RearLeft", Vector3(-0.6f, -0.4f, -0.3f), rearLeft_, rearLeftID_);
  126. InitWheel("RearRight", Vector3(0.6f, -0.4f, -0.3f), rearRight_, rearRightID_);
  127. GetWheelComponents();
  128. }
  129. void Vehicle::InitWheel(const String& name, const Vector3& offset, WeakPtr<Node>& wheelNode, unsigned& wheelNodeID)
  130. {
  131. ResourceCache* cache = GetSubsystem<ResourceCache>();
  132. // Note: do not parent the wheel to the hull scene node. Instead create it on the root level and let the physics
  133. // constraint keep it together
  134. wheelNode = GetScene()->CreateChild(name);
  135. wheelNode->SetPosition(node_->LocalToWorld(offset));
  136. wheelNode->SetRotation(node_->GetRotation() * (offset.x_ >= 0.0 ? Quaternion(0.0f, 0.0f, -90.0f) :
  137. Quaternion(0.0f, 0.0f, 90.0f)));
  138. wheelNode->SetScale(Vector3(0.8f, 0.5f, 0.8f));
  139. // Remember the ID for serialization
  140. wheelNodeID = wheelNode->GetID();
  141. StaticModel* wheelObject = wheelNode->CreateComponent<StaticModel>();
  142. RigidBody* wheelBody = wheelNode->CreateComponent<RigidBody>();
  143. CollisionShape* wheelShape = wheelNode->CreateComponent<CollisionShape>();
  144. Constraint* wheelConstraint = wheelNode->CreateComponent<Constraint>();
  145. wheelObject->SetModel(cache->GetResource<Model>("Models/Cylinder.mdl"));
  146. wheelObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));
  147. wheelObject->SetCastShadows(true);
  148. wheelShape->SetSphere(1.0f);
  149. wheelBody->SetFriction(1.0f);
  150. wheelBody->SetMass(1.0f);
  151. wheelBody->SetLinearDamping(0.2f); // Some air resistance
  152. wheelBody->SetAngularDamping(0.75f); // Could also use rolling friction
  153. wheelBody->SetCollisionLayer(1);
  154. wheelConstraint->SetConstraintType(CONSTRAINT_HINGE);
  155. wheelConstraint->SetOtherBody(GetComponent<RigidBody>()); // Connect to the hull body
  156. wheelConstraint->SetWorldPosition(wheelNode->GetPosition()); // Set constraint's both ends at wheel's location
  157. wheelConstraint->SetAxis(Vector3::UP); // Wheel rotates around its local Y-axis
  158. wheelConstraint->SetOtherAxis(offset.x_ >= 0.0 ? Vector3::RIGHT : Vector3::LEFT); // Wheel's hull axis points either left or right
  159. wheelConstraint->SetLowLimit(Vector2(-180.0f, 0.0f)); // Let the wheel rotate freely around the axis
  160. wheelConstraint->SetHighLimit(Vector2(180.0f, 0.0f));
  161. wheelConstraint->SetDisableCollision(true); // Let the wheel intersect the vehicle hull
  162. }
  163. void Vehicle::GetWheelComponents()
  164. {
  165. frontLeftAxis_ = frontLeft_->GetComponent<Constraint>();
  166. frontRightAxis_ = frontRight_->GetComponent<Constraint>();
  167. frontLeftBody_ = frontLeft_->GetComponent<RigidBody>();
  168. frontRightBody_ = frontRight_->GetComponent<RigidBody>();
  169. rearLeftBody_ = rearLeft_->GetComponent<RigidBody>();
  170. rearRightBody_ = rearRight_->GetComponent<RigidBody>();
  171. }