Vehicle.cpp 8.8 KB

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