Vehicle.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. //
  2. // Copyright (c) 2008-2020 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 "Vehicle.h"
  23. #include <Urho3D/Core/Context.h>
  24. #include <Urho3D/Graphics/DebugRenderer.h>
  25. #include <Urho3D/Graphics/DecalSet.h>
  26. #include <Urho3D/Graphics/Material.h>
  27. #include <Urho3D/Graphics/Model.h>
  28. #include <Urho3D/Graphics/ParticleEffect.h>
  29. #include <Urho3D/Graphics/ParticleEmitter.h>
  30. #include <Urho3D/Graphics/StaticModel.h>
  31. #include <Urho3D/IO/Log.h>
  32. #include <Urho3D/Physics/CollisionShape.h>
  33. #include <Urho3D/Physics/Constraint.h>
  34. #include <Urho3D/Physics/PhysicsEvents.h>
  35. #include <Urho3D/Physics/PhysicsWorld.h>
  36. #include <Urho3D/Physics/RaycastVehicle.h>
  37. #include <Urho3D/Resource/ResourceCache.h>
  38. #include <Urho3D/Scene/Scene.h>
  39. using namespace Urho3D;
  40. const float CHASSIS_WIDTH = 2.6f;
  41. void Vehicle::RegisterObject(Context* context)
  42. {
  43. context->RegisterFactory<Vehicle>();
  44. URHO3D_ATTRIBUTE("Steering", float, steering_, 0.0f, AM_DEFAULT);
  45. URHO3D_ATTRIBUTE("Controls Yaw", float, controls_.yaw_, 0.0f, AM_DEFAULT);
  46. URHO3D_ATTRIBUTE("Controls Pitch", float, controls_.pitch_, 0.0f, AM_DEFAULT);
  47. }
  48. Vehicle::Vehicle(Urho3D::Context* context)
  49. : LogicComponent(context),
  50. steering_(0.0f)
  51. {
  52. SetUpdateEventMask(USE_FIXEDUPDATE | USE_POSTUPDATE);
  53. engineForce_ = 0.0f;
  54. brakingForce_ = 50.0f;
  55. vehicleSteering_ = 0.0f;
  56. maxEngineForce_ = 2500.0f;
  57. wheelRadius_ = 0.5f;
  58. suspensionRestLength_ = 0.6f;
  59. wheelWidth_ = 0.4f;
  60. suspensionStiffness_ = 14.0f;
  61. suspensionDamping_ = 2.0f;
  62. suspensionCompression_ = 4.0f;
  63. wheelFriction_ = 1000.0f;
  64. rollInfluence_ = 0.12f;
  65. emittersCreated = false;
  66. }
  67. Vehicle::~Vehicle() = default;
  68. void Vehicle::Init()
  69. {
  70. auto* vehicle = node_->CreateComponent<RaycastVehicle>();
  71. vehicle->Init();
  72. auto* hullBody = node_->GetComponent<RigidBody>();
  73. hullBody->SetMass(800.0f);
  74. hullBody->SetLinearDamping(0.2f); // Some air resistance
  75. hullBody->SetAngularDamping(0.5f);
  76. hullBody->SetCollisionLayer(1);
  77. // This function is called only from the main program when initially creating the vehicle, not on scene load
  78. auto* cache = GetSubsystem<ResourceCache>();
  79. auto* hullObject = node_->CreateComponent<StaticModel>();
  80. // Setting-up collision shape
  81. auto* hullColShape = node_->CreateComponent<CollisionShape>();
  82. Vector3 v3BoxExtents = Vector3::ONE;
  83. hullColShape->SetBox(v3BoxExtents);
  84. node_->SetScale(Vector3(2.3f, 1.0f, 4.0f));
  85. hullObject->SetModel(cache->GetResource<Model>("Models/Box.mdl"));
  86. hullObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));
  87. hullObject->SetCastShadows(true);
  88. float connectionHeight = -0.4f;
  89. bool isFrontWheel = true;
  90. Vector3 wheelDirection(0, -1, 0);
  91. Vector3 wheelAxle(-1, 0, 0);
  92. // We use not scaled coordinates here as everything will be scaled.
  93. // Wheels are on bottom at edges of the chassis
  94. // Note we don't set wheel nodes as children of hull (while we could) to avoid scaling to affect them.
  95. float wheelX = CHASSIS_WIDTH / 2.0f - wheelWidth_;
  96. // Front left
  97. connectionPoints_[0] = Vector3(-wheelX, connectionHeight, 2.5f - GetWheelRadius() * 2.0f);
  98. // Front right
  99. connectionPoints_[1] = Vector3(wheelX, connectionHeight, 2.5f - GetWheelRadius() * 2.0f);
  100. // Back left
  101. connectionPoints_[2] = Vector3(-wheelX, connectionHeight, -2.5f + GetWheelRadius() * 2.0f);
  102. // Back right
  103. connectionPoints_[3] = Vector3(wheelX, connectionHeight, -2.5f + GetWheelRadius() * 2.0f);
  104. const Color LtBrown(0.972f, 0.780f, 0.412f);
  105. for (int id = 0; id < sizeof(connectionPoints_) / sizeof(connectionPoints_[0]); id++)
  106. {
  107. Node* wheelNode = GetScene()->CreateChild();
  108. Vector3 connectionPoint = connectionPoints_[id];
  109. // Front wheels are at front (z > 0)
  110. // back wheels are at z < 0
  111. // Setting rotation according to wheel position
  112. bool isFrontWheel = connectionPoints_[id].z_ > 0.0f;
  113. wheelNode->SetRotation(connectionPoint.x_ >= 0.0 ? Quaternion(0.0f, 0.0f, -90.0f) : Quaternion(0.0f, 0.0f, 90.0f));
  114. wheelNode->SetWorldPosition(node_->GetWorldPosition() + node_->GetWorldRotation() * connectionPoints_[id]);
  115. vehicle->AddWheel(wheelNode, wheelDirection, wheelAxle, suspensionRestLength_, wheelRadius_, isFrontWheel);
  116. vehicle->SetWheelSuspensionStiffness(id, suspensionStiffness_);
  117. vehicle->SetWheelDampingRelaxation(id, suspensionDamping_);
  118. vehicle->SetWheelDampingCompression(id, suspensionCompression_);
  119. vehicle->SetWheelFrictionSlip(id, wheelFriction_);
  120. vehicle->SetWheelRollInfluence(id, rollInfluence_);
  121. wheelNode->SetScale(Vector3(1.0f, 0.65f, 1.0f));
  122. auto* pWheel = wheelNode->CreateComponent<StaticModel>();
  123. pWheel->SetModel(cache->GetResource<Model>("Models/Cylinder.mdl"));
  124. pWheel->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));
  125. pWheel->SetCastShadows(true);
  126. CreateEmitter(connectionPoints_[id]);
  127. }
  128. emittersCreated = true;
  129. vehicle->ResetWheels();
  130. }
  131. void Vehicle::CreateEmitter(Vector3 place)
  132. {
  133. auto* cache = GetSubsystem<ResourceCache>();
  134. Node* emitter = GetScene()->CreateChild();
  135. emitter->SetWorldPosition(node_->GetWorldPosition() + node_->GetWorldRotation() * place + Vector3(0, -wheelRadius_, 0));
  136. auto* particleEmitter = emitter->CreateComponent<ParticleEmitter>();
  137. particleEmitter->SetEffect(cache->GetResource<ParticleEffect>("Particle/Dust.xml"));
  138. particleEmitter->SetEmitting(false);
  139. particleEmitterNodeList_.Push(emitter);
  140. emitter->SetTemporary(true);
  141. }
  142. /// Applying attributes
  143. void Vehicle::ApplyAttributes()
  144. {
  145. auto* vehicle = node_->GetOrCreateComponent<RaycastVehicle>();
  146. if (emittersCreated)
  147. return;
  148. for (const auto& connectionPoint : connectionPoints_)
  149. {
  150. CreateEmitter(connectionPoint);
  151. }
  152. emittersCreated = true;
  153. }
  154. void Vehicle::FixedUpdate(float timeStep)
  155. {
  156. float newSteering = 0.0f;
  157. float accelerator = 0.0f;
  158. bool brake = false;
  159. auto* vehicle = node_->GetComponent<RaycastVehicle>();
  160. // Read controls
  161. if (controls_.buttons_ & CTRL_LEFT)
  162. {
  163. newSteering = -1.0f;
  164. }
  165. if (controls_.buttons_ & CTRL_RIGHT)
  166. {
  167. newSteering = 1.0f;
  168. }
  169. if (controls_.buttons_ & CTRL_FORWARD)
  170. {
  171. accelerator = 1.0f;
  172. }
  173. if (controls_.buttons_ & CTRL_BACK)
  174. {
  175. accelerator = -0.5f;
  176. }
  177. if (controls_.buttons_ & CTRL_BRAKE)
  178. {
  179. brake = true;
  180. }
  181. // When steering, wake up the wheel rigidbodies so that their orientation is updated
  182. if (newSteering != 0.0f)
  183. {
  184. SetSteering(GetSteering() * 0.95f + newSteering * 0.05f);
  185. }
  186. else
  187. {
  188. SetSteering(GetSteering() * 0.8f + newSteering * 0.2f);
  189. }
  190. // Set front wheel angles
  191. vehicleSteering_ = steering_;
  192. int wheelIndex = 0;
  193. vehicle->SetSteeringValue(wheelIndex, vehicleSteering_);
  194. wheelIndex = 1;
  195. vehicle->SetSteeringValue(wheelIndex, vehicleSteering_);
  196. // apply forces
  197. engineForce_ = maxEngineForce_ * accelerator;
  198. // 2x wheel drive
  199. vehicle->SetEngineForce(2, engineForce_);
  200. vehicle->SetEngineForce(3, engineForce_);
  201. for (int i = 0; i < vehicle->GetNumWheels(); i++)
  202. {
  203. if (brake)
  204. {
  205. vehicle->SetBrake(i, brakingForce_);
  206. }
  207. else
  208. {
  209. vehicle->SetBrake(i, 0.0f);
  210. }
  211. }
  212. }
  213. void Vehicle::PostUpdate(float timeStep)
  214. {
  215. auto* vehicle = node_->GetComponent<RaycastVehicle>();
  216. auto* vehicleBody = node_->GetComponent<RigidBody>();
  217. Vector3 velocity = vehicleBody->GetLinearVelocity();
  218. Vector3 accel = (velocity - prevVelocity_) / timeStep;
  219. float planeAccel = Vector3(accel.x_, 0.0f, accel.z_).Length();
  220. for (int i = 0; i < vehicle->GetNumWheels(); i++)
  221. {
  222. Node* emitter = particleEmitterNodeList_[i];
  223. auto* particleEmitter = emitter->GetComponent<ParticleEmitter>();
  224. if (vehicle->WheelIsGrounded(i) && (vehicle->GetWheelSkidInfoCumulative(i) < 0.9f || vehicle->GetBrake(i) > 2.0f ||
  225. planeAccel > 15.0f))
  226. {
  227. particleEmitterNodeList_[i]->SetWorldPosition(vehicle->GetContactPosition(i));
  228. if (!particleEmitter->IsEmitting())
  229. {
  230. particleEmitter->SetEmitting(true);
  231. }
  232. URHO3D_LOGDEBUG("GetWheelSkidInfoCumulative() = " +
  233. String(vehicle->GetWheelSkidInfoCumulative(i)) + " " +
  234. String(vehicle->GetMaxSideSlipSpeed()));
  235. /* TODO: Add skid marks here */
  236. }
  237. else if (particleEmitter->IsEmitting())
  238. {
  239. particleEmitter->SetEmitting(false);
  240. }
  241. }
  242. prevVelocity_ = velocity;
  243. }