| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- // Copyright (c) 2008-2022 the Urho3D project
- // License: MIT
- #include <Urho3D/Core/Context.h>
- #include <Urho3D/Graphics/Material.h>
- #include <Urho3D/Graphics/Model.h>
- #include <Urho3D/Graphics/StaticModel.h>
- #include <Urho3D/Physics/CollisionShape.h>
- #include <Urho3D/Physics/Constraint.h>
- #include <Urho3D/Physics/PhysicsEvents.h>
- #include <Urho3D/Physics/PhysicsWorld.h>
- #include <Urho3D/Physics/RigidBody.h>
- #include <Urho3D/Resource/ResourceCache.h>
- #include <Urho3D/Scene/Scene.h>
- #include "Vehicle.h"
- Vehicle::Vehicle(Context* context) :
- LogicComponent(context)
- {
- // Only the physics update event is needed: unsubscribe from the rest for optimization
- SetUpdateEventMask(USE_FIXEDUPDATE);
- }
- void Vehicle::RegisterObject(Context* context)
- {
- context->RegisterFactory<Vehicle>();
- URHO3D_ATTRIBUTE("Controls Yaw", float, controls_.yaw_, 0.0f, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Controls Pitch", float, controls_.pitch_, 0.0f, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Steering", float, steering_, 0.0f, AM_DEFAULT);
- // Register wheel node IDs as attributes so that the wheel nodes can be reaquired on deserialization. They need to be tagged
- // as node ID's so that the deserialization code knows to rewrite the IDs in case they are different on load than on save
- URHO3D_ATTRIBUTE("Front Left Node", unsigned, frontLeftID_, 0, AM_DEFAULT | AM_NODEID);
- URHO3D_ATTRIBUTE("Front Right Node", unsigned, frontRightID_, 0, AM_DEFAULT | AM_NODEID);
- URHO3D_ATTRIBUTE("Rear Left Node", unsigned, rearLeftID_, 0, AM_DEFAULT | AM_NODEID);
- URHO3D_ATTRIBUTE("Rear Right Node", unsigned, rearRightID_, 0, AM_DEFAULT | AM_NODEID);
- }
- void Vehicle::ApplyAttributes()
- {
- // This function is called on each Serializable after the whole scene has been loaded. Reacquire wheel nodes from ID's
- // as well as all required physics components
- Scene* scene = GetScene();
- frontLeft_ = scene->GetNode(frontLeftID_);
- frontRight_ = scene->GetNode(frontRightID_);
- rearLeft_ = scene->GetNode(rearLeftID_);
- rearRight_ = scene->GetNode(rearRightID_);
- hullBody_ = node_->GetComponent<RigidBody>();
- GetWheelComponents();
- }
- void Vehicle::FixedUpdate(float timeStep)
- {
- float newSteering = 0.0f;
- float accelerator = 0.0f;
- // Read controls
- if (controls_.buttons_ & CTRL_LEFT)
- newSteering = -1.0f;
- if (controls_.buttons_ & CTRL_RIGHT)
- newSteering = 1.0f;
- if (controls_.buttons_ & CTRL_FORWARD)
- accelerator = 1.0f;
- if (controls_.buttons_ & CTRL_BACK)
- accelerator = -0.5f;
- // When steering, wake up the wheel rigidbodies so that their orientation is updated
- if (newSteering != 0.0f)
- {
- frontLeftBody_->Activate();
- frontRightBody_->Activate();
- steering_ = steering_ * 0.95f + newSteering * 0.05f;
- }
- else
- steering_ = steering_ * 0.8f + newSteering * 0.2f;
- // Set front wheel angles
- Quaternion steeringRot(0, steering_ * MAX_WHEEL_ANGLE, 0);
- frontLeftAxis_->SetOtherAxis(steeringRot * Vector3::LEFT);
- frontRightAxis_->SetOtherAxis(steeringRot * Vector3::RIGHT);
- Quaternion hullRot = hullBody_->GetRotation();
- if (accelerator != 0.0f)
- {
- // Torques are applied in world space, so need to take the vehicle & wheel rotation into account
- Vector3 torqueVec = Vector3(ENGINE_POWER * accelerator, 0.0f, 0.0f);
- frontLeftBody_->ApplyTorque(hullRot * steeringRot * torqueVec);
- frontRightBody_->ApplyTorque(hullRot * steeringRot * torqueVec);
- rearLeftBody_->ApplyTorque(hullRot * torqueVec);
- rearRightBody_->ApplyTorque(hullRot * torqueVec);
- }
- // Apply downforce proportional to velocity
- Vector3 localVelocity = hullRot.Inverse() * hullBody_->GetLinearVelocity();
- hullBody_->ApplyForce(hullRot * Vector3::DOWN * Abs(localVelocity.z_) * DOWN_FORCE);
- }
- void Vehicle::Init()
- {
- // This function is called only from the main program when initially creating the vehicle, not on scene load
- auto* cache = GetSubsystem<ResourceCache>();
- auto* hullObject = node_->CreateComponent<StaticModel>();
- hullBody_ = node_->CreateComponent<RigidBody>();
- auto* hullShape = node_->CreateComponent<CollisionShape>();
- node_->SetScale(Vector3(1.5f, 1.0f, 3.0f));
- hullObject->SetModel(cache->GetResource<Model>("Models/Box.mdl"));
- hullObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));
- hullObject->SetCastShadows(true);
- hullShape->SetBox(Vector3::ONE);
- hullBody_->SetMass(4.0f);
- hullBody_->SetLinearDamping(0.2f); // Some air resistance
- hullBody_->SetAngularDamping(0.5f);
- hullBody_->SetCollisionLayer(1);
- InitWheel("FrontLeft", Vector3(-0.6f, -0.4f, 0.3f), frontLeft_, frontLeftID_);
- InitWheel("FrontRight", Vector3(0.6f, -0.4f, 0.3f), frontRight_, frontRightID_);
- InitWheel("RearLeft", Vector3(-0.6f, -0.4f, -0.3f), rearLeft_, rearLeftID_);
- InitWheel("RearRight", Vector3(0.6f, -0.4f, -0.3f), rearRight_, rearRightID_);
- GetWheelComponents();
- }
- void Vehicle::InitWheel(const String& name, const Vector3& offset, WeakPtr<Node>& wheelNode, unsigned& wheelNodeID)
- {
- auto* cache = GetSubsystem<ResourceCache>();
- // Note: do not parent the wheel to the hull scene node. Instead create it on the root level and let the physics
- // constraint keep it together
- wheelNode = GetScene()->CreateChild(name);
- wheelNode->SetPosition(node_->LocalToWorld(offset));
- wheelNode->SetRotation(node_->GetRotation() * (offset.x_ >= 0.0 ? Quaternion(0.0f, 0.0f, -90.0f) :
- Quaternion(0.0f, 0.0f, 90.0f)));
- wheelNode->SetScale(Vector3(0.8f, 0.5f, 0.8f));
- // Remember the ID for serialization
- wheelNodeID = wheelNode->GetID();
- auto* wheelObject = wheelNode->CreateComponent<StaticModel>();
- auto* wheelBody = wheelNode->CreateComponent<RigidBody>();
- auto* wheelShape = wheelNode->CreateComponent<CollisionShape>();
- auto* wheelConstraint = wheelNode->CreateComponent<Constraint>();
- wheelObject->SetModel(cache->GetResource<Model>("Models/Cylinder.mdl"));
- wheelObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));
- wheelObject->SetCastShadows(true);
- wheelShape->SetSphere(1.0f);
- wheelBody->SetFriction(1.0f);
- wheelBody->SetMass(1.0f);
- wheelBody->SetLinearDamping(0.2f); // Some air resistance
- wheelBody->SetAngularDamping(0.75f); // Could also use rolling friction
- wheelBody->SetCollisionLayer(1);
- wheelConstraint->SetConstraintType(CONSTRAINT_HINGE);
- wheelConstraint->SetOtherBody(GetComponent<RigidBody>()); // Connect to the hull body
- wheelConstraint->SetWorldPosition(wheelNode->GetPosition()); // Set constraint's both ends at wheel's location
- wheelConstraint->SetAxis(Vector3::UP); // Wheel rotates around its local Y-axis
- wheelConstraint->SetOtherAxis(offset.x_ >= 0.0 ? Vector3::RIGHT : Vector3::LEFT); // Wheel's hull axis points either left or right
- wheelConstraint->SetLowLimit(Vector2(-180.0f, 0.0f)); // Let the wheel rotate freely around the axis
- wheelConstraint->SetHighLimit(Vector2(180.0f, 0.0f));
- wheelConstraint->SetDisableCollision(true); // Let the wheel intersect the vehicle hull
- }
- void Vehicle::GetWheelComponents()
- {
- frontLeftAxis_ = frontLeft_->GetComponent<Constraint>();
- frontRightAxis_ = frontRight_->GetComponent<Constraint>();
- frontLeftBody_ = frontLeft_->GetComponent<RigidBody>();
- frontRightBody_ = frontRight_->GetComponent<RigidBody>();
- rearLeftBody_ = rearLeft_->GetComponent<RigidBody>();
- rearRightBody_ = rearRight_->GetComponent<RigidBody>();
- }
|