Added vehicle sample.

This commit is contained in:
Lasse Öörni 2013-08-28 23:35:10 +00:00
parent 04b00263b0
commit 26901cdc4c
10 changed files with 679 additions and 24 deletions

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "AnimationController.h"
#include "Character.h"
#include "MemoryBuffer.h"
@ -17,9 +39,12 @@ Character::Character(Context* context) :
void Character::OnNodeSet(Node* node)
{
// Component has been inserted into its scene node. Subscribe to events now
SubscribeToEvent(node, E_NODECOLLISION, HANDLER(Character, HandleNodeCollision));
SubscribeToEvent(GetScene()->GetComponent<PhysicsWorld>(), E_PHYSICSPRESTEP, HANDLER(Character, HandleFixedUpdate));
if (node)
{
// Component has been inserted into its scene node. Subscribe to events now
SubscribeToEvent(node, E_NODECOLLISION, HANDLER(Character, HandleNodeCollision));
SubscribeToEvent(GetScene()->GetComponent<PhysicsWorld>(), E_PHYSICSPRESTEP, HANDLER(Character, HandleFixedUpdate));
}
}
void Character::HandleNodeCollision(StringHash eventType, VariantMap& eventData)
@ -71,9 +96,9 @@ void Character::HandleFixedUpdate(StringHash eventType, VariantMap& eventData)
// Velocity on the XZ plane
Vector3 planeVelocity(velocity.x_, 0.0f, velocity.z_);
if (controls_.IsDown(CTRL_UP))
if (controls_.IsDown(CTRL_FORWARD))
moveDir += Vector3::FORWARD;
if (controls_.IsDown(CTRL_DOWN))
if (controls_.IsDown(CTRL_BACK))
moveDir += Vector3::BACK;
if (controls_.IsDown(CTRL_LEFT))
moveDir += Vector3::LEFT;

View File

@ -27,13 +27,11 @@
using namespace Urho3D;
const int CTRL_UP = 1;
const int CTRL_DOWN = 2;
const int CTRL_FORWARD = 1;
const int CTRL_BACK = 2;
const int CTRL_LEFT = 4;
const int CTRL_RIGHT = 8;
const int CTRL_FIRE = 16;
const int CTRL_JUMP = 32;
const int CTRL_ALL = 63;
const int CTRL_JUMP = 16;
const float MOVE_FORCE = 0.8f;
const float INAIR_MOVE_FORCE = 0.02f;
@ -54,13 +52,15 @@ public:
/// Handle node being assigned.
virtual void OnNodeSet(Node* node);
/// Movement controls.
Controls controls_;
private:
/// Handle physics collision event.
void HandleNodeCollision(StringHash eventType, VariantMap& eventData);
/// Handle physics world update event.
void HandleFixedUpdate(StringHash eventType, VariantMap& eventData);
/// Movement controls.
Controls controls_;
/// Grounded flag for movement.
bool onGround_;
/// Jump flag.

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "AnimatedModel.h"
#include "AnimationController.h"
#include "Camera.h"
@ -71,6 +93,7 @@ void CharacterDemo::CreateScene()
// Create camera and define viewport. Camera does not necessarily have to belong to the scene
cameraNode_ = new Node(context_);
Camera* camera = cameraNode_->CreateComponent<Camera>();
camera->SetFarClip(300.0f);
GetSubsystem<Renderer>()->SetViewport(0, new Viewport(context_, scene_, camera));
// Create static scene content. First create a zone for ambient lighting and fog control
@ -230,8 +253,8 @@ void CharacterDemo::HandleUpdate(StringHash eventType, VariantMap& eventData)
// Get movement controls and assign them to the character logic component. If UI has a focused element, clear controls
if (!ui->GetFocusElement())
{
character_->controls_.Set(CTRL_UP, input->GetKeyDown('W'));
character_->controls_.Set(CTRL_DOWN, input->GetKeyDown('S'));
character_->controls_.Set(CTRL_FORWARD, input->GetKeyDown('W'));
character_->controls_.Set(CTRL_BACK, input->GetKeyDown('S'));
character_->controls_.Set(CTRL_LEFT, input->GetKeyDown('A'));
character_->controls_.Set(CTRL_RIGHT, input->GetKeyDown('D'));
character_->controls_.Set(CTRL_JUMP, input->GetKeyDown(KEY_SPACE));
@ -247,7 +270,7 @@ void CharacterDemo::HandleUpdate(StringHash eventType, VariantMap& eventData)
firstPerson_ = !firstPerson_;
}
else
character_->controls_.Set(CTRL_UP | CTRL_DOWN | CTRL_LEFT | CTRL_RIGHT | CTRL_JUMP, false);
character_->controls_.Set(CTRL_FORWARD | CTRL_BACK | CTRL_LEFT | CTRL_RIGHT | CTRL_JUMP, false);
// Set rotation already here so that it's updated every rendering frame instead of every physics frame
character_->GetNode()->SetRotation(Quaternion(character_->controls_.yaw_, Vector3::UP));

View File

@ -33,12 +33,12 @@ namespace Urho3D
using namespace Urho3D;
class Character;
/// Moving character example.
/// This sample demonstrates:
/// - Controlling a humanoid character through physics;
/// - Driving animations using the AnimationController component;
/// - Implementing 1st and 3rd person cameras, using raycasts to avoid the 3rd person camera
/// Moving character example.
/// This sample demonstrates:
/// - Controlling a humanoid character through physics;
/// - Driving animations using the AnimationController component;
/// - Implementing 1st and 3rd person cameras, using raycasts to avoid the 3rd person camera
/// clipping into scenery
class CharacterDemo : public Sample
{
@ -56,7 +56,7 @@ private:
void CreateScene();
/// Create controllable character.
void CreateCharacter();
/// Construct an instruction text to the UI.
/// Construct an instruction text to the UI.
void CreateInstructions();
/// Subscribe to necessary events.
void SubscribeToEvents();
@ -69,7 +69,7 @@ private:
SharedPtr<Scene> scene_;
/// Camera scene node.
SharedPtr<Node> cameraNode_;
/// The controllable character.
/// The controllable character component.
WeakPtr<Character> character_;
/// First person camera flag.
bool firstPerson_;

View File

@ -0,0 +1,32 @@
#
# Copyright (c) 2008-2013 the Urho3D project.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Define target name
set (TARGET_NAME 19_VehicleDemo)
# Define source files
file (GLOB CPP_FILES *.cpp)
file (GLOB H_FILES *.h)
set (SOURCE_FILES ${CPP_FILES} ${H_FILES} ${COMMON_SAMPLE_H_FILES})
# Setup target with resource copying
setup_main_executable ()

View File

@ -0,0 +1,161 @@
//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "CollisionShape.h"
#include "Constraint.h"
#include "Material.h"
#include "Model.h"
#include "PhysicsEvents.h"
#include "PhysicsWorld.h"
#include "ResourceCache.h"
#include "RigidBody.h"
#include "Scene.h"
#include "StaticModel.h"
#include "Vehicle.h"
Vehicle::Vehicle(Context* context) :
Component(context),
steering_(0.0f)
{
}
void Vehicle::OnNodeSet(Node* node)
{
if (node)
SubscribeToEvent(GetScene()->GetComponent<PhysicsWorld>(), E_PHYSICSPRESTEP, HANDLER(Vehicle, HandleFixedUpdate));
}
void Vehicle::Init()
{
ResourceCache* cache = GetSubsystem<ResourceCache>();
StaticModel* hullObject = node_->CreateComponent<StaticModel>();
hullBody_ = node_->CreateComponent<RigidBody>();
CollisionShape* 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);
frontLeft_ = InitWheel("FrontLeft", Vector3(-0.6f, -0.4f, 0.3f));
frontRight_ = InitWheel("FrontRight", Vector3(0.6f, -0.4f, 0.3f));
rearLeft_ = InitWheel("RearLeft", Vector3(-0.6f, -0.4f, -0.3f));
rearRight_ = InitWheel("RearRight", Vector3(0.6f, -0.4f, -0.3f));
frontLeftAxis_ = frontLeft_->GetComponent<Constraint>();
frontRightAxis_ = frontRight_->GetComponent<Constraint>();
frontLeftBody_ = frontLeft_->GetComponent<RigidBody>();
frontRightBody_ = frontRight_->GetComponent<RigidBody>();
rearLeftBody_ = rearLeft_->GetComponent<RigidBody>();
rearRightBody_ = rearRight_->GetComponent<RigidBody>();
}
Node* Vehicle::InitWheel(const String& name, const Vector3& offset)
{
ResourceCache* 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
Node* wheelNode = GetScene()->CreateChild(name);
wheelNode->SetPosition(node_->LocalToWorld(offset));
wheelNode->SetRotation(node_->GetWorldRotation() * (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));
StaticModel* wheelObject = wheelNode->CreateComponent<StaticModel>();
RigidBody* wheelBody = wheelNode->CreateComponent<RigidBody>();
CollisionShape* wheelShape = wheelNode->CreateComponent<CollisionShape>();
Constraint* 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->GetWorldPosition()); // 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
return wheelNode;
}
void Vehicle::HandleFixedUpdate(StringHash eventType, VariantMap& eventData)
{
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);
}

View File

@ -0,0 +1,90 @@
//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#pragma once
#include "Component.h"
#include "Controls.h"
namespace Urho3D
{
class Constraint;
class Node;
class RigidBody;
}
using namespace Urho3D;
const int CTRL_FORWARD = 1;
const int CTRL_BACK = 2;
const int CTRL_LEFT = 4;
const int CTRL_RIGHT = 8;
const float YAW_SENSITIVITY = 0.1f;
const float ENGINE_POWER = 10.0f;
const float DOWN_FORCE = 10.0f;
const float MAX_WHEEL_ANGLE = 22.5f;
/// Vehicle component, responsible for physical movement according to controls.
class Vehicle : public Component
{
OBJECT(Vehicle)
public:
/// Construct.
Vehicle(Context* context);
/// Handle node being assigned.
virtual void OnNodeSet(Node* node);
/// Initialize the vehicle. Create rendering and physics components.
void Init();
/// Movement controls.
Controls controls_;
private:
/// Initialize a wheel and return its scene node.
Node* InitWheel(const String& name, const Vector3& offset);
/// Handle physics world update event.
void HandleFixedUpdate(StringHash eventType, VariantMap& eventData);
// Wheel scene nodes.
WeakPtr<Node> frontLeft_;
WeakPtr<Node> frontRight_;
WeakPtr<Node> rearLeft_;
WeakPtr<Node> rearRight_;
// Steering axle constraints.
WeakPtr<Constraint> frontLeftAxis_;
WeakPtr<Constraint> frontRightAxis_;
// Hull and wheel rigid bodies.
WeakPtr<RigidBody> hullBody_;
WeakPtr<RigidBody> frontLeftBody_;
WeakPtr<RigidBody> frontRightBody_;
WeakPtr<RigidBody> rearLeftBody_;
WeakPtr<RigidBody> rearRightBody_;
/// Current left/right steering amount (-1 to 1.)
float steering_;
};

View File

@ -0,0 +1,251 @@
//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "Camera.h"
#include "CollisionShape.h"
#include "Constraint.h"
#include "CoreEvents.h"
#include "Engine.h"
#include "Font.h"
#include "Input.h"
#include "Light.h"
#include "Material.h"
#include "Model.h"
#include "Octree.h"
#include "PhysicsWorld.h"
#include "ProcessUtils.h"
#include "Renderer.h"
#include "RigidBody.h"
#include "ResourceCache.h"
#include "Scene.h"
#include "StaticModel.h"
#include "Terrain.h"
#include "Text.h"
#include "UI.h"
#include "Vehicle.h"
#include "Zone.h"
#include "VehicleDemo.h"
#include "DebugNew.h"
const float CAMERA_MIN_DIST = 3.0f;
const float CAMERA_MAX_DIST = 8.0f;
DEFINE_APPLICATION_MAIN(VehicleDemo)
VehicleDemo::VehicleDemo(Context* context) :
Sample(context)
{
// Register factory for the Vehicle component so it can be created via CreateComponent
context_->RegisterFactory<Vehicle>();
}
void VehicleDemo::Start()
{
// Execute base class startup
Sample::Start();
// Create static scene content
CreateScene();
// Create the controllable vehicle
CreateVehicle();
// Create the UI content
CreateInstructions();
// Subscribe to necessary events
SubscribeToEvents();
}
void VehicleDemo::CreateScene()
{
ResourceCache* cache = GetSubsystem<ResourceCache>();
scene_ = new Scene(context_);
// Create scene subsystem components
scene_->CreateComponent<Octree>();
scene_->CreateComponent<PhysicsWorld>();
// Create camera and define viewport. Camera does not necessarily have to belong to the scene
cameraNode_ = new Node(context_);
Camera* camera = cameraNode_->CreateComponent<Camera>();
camera->SetFarClip(500.0f);
GetSubsystem<Renderer>()->SetViewport(0, new Viewport(context_, scene_, camera));
// Create static scene content. First create a zone for ambient lighting and fog control
Node* zoneNode = scene_->CreateChild("Zone");
Zone* zone = zoneNode->CreateComponent<Zone>();
zone->SetAmbientColor(Color(0.15f, 0.15f, 0.15f));
zone->SetFogColor(Color(0.5f, 0.5f, 0.7f));
zone->SetFogStart(300.0f);
zone->SetFogEnd(500.0f);
zone->SetBoundingBox(BoundingBox(-2000.0f, 2000.0f));
// Create a directional light with cascaded shadow mapping
Node* lightNode = scene_->CreateChild("DirectionalLight");
lightNode->SetDirection(Vector3(0.3f, -0.5f, 0.425f));
Light* light = lightNode->CreateComponent<Light>();
light->SetLightType(LIGHT_DIRECTIONAL);
light->SetCastShadows(true);
light->SetShadowBias(BiasParameters(0.0001f, 0.5f));
light->SetShadowCascade(CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f));
light->SetSpecularIntensity(0.5f);
// Create heightmap terrain with collision
Node* terrainNode = scene_->CreateChild("Terrain");
terrainNode->SetPosition(Vector3::ZERO);
Terrain* terrain = terrainNode->CreateComponent<Terrain>();
terrain->SetPatchSize(64);
terrain->SetSpacing(Vector3(2.0f, 0.1f, 2.0f)); // Spacing between vertices and vertical resolution of the height map
terrain->SetSmoothing(true);
terrain->SetHeightMap(cache->GetResource<Image>("Textures/HeightMap.png"));
terrain->SetMaterial(cache->GetResource<Material>("Materials/Terrain.xml"));
// The terrain consists of large triangles, which fits well for occlusion rendering, as a hill can occlude all
// terrain patches and other objects behind it
terrain->SetOccluder(true);
RigidBody* body = terrainNode->CreateComponent<RigidBody>();
body->SetCollisionLayer(2); // Use layer bitmask 2 for static geometry
CollisionShape* shape = terrainNode->CreateComponent<CollisionShape>();
shape->SetTerrain();
// Create 1000 mushrooms in the terrain. Always face outward along the terrain normal
const unsigned NUM_MUSHROOMS = 1000;
for (unsigned i = 0; i < NUM_MUSHROOMS; ++i)
{
Node* objectNode = scene_->CreateChild("Mushroom");
Vector3 position(Random(2000.0f) - 1000.0f, 0.0f, Random(2000.0f) - 1000.0f);
position.y_ = terrain->GetHeight(position) - 0.1f;
objectNode->SetPosition(position);
// Create a rotation quaternion from up vector to terrain normal
objectNode->SetRotation(Quaternion(Vector3::UP, terrain->GetNormal(position)));
objectNode->SetScale(3.0f);
StaticModel* object = objectNode->CreateComponent<StaticModel>();
object->SetModel(cache->GetResource<Model>("Models/Mushroom.mdl"));
object->SetMaterial(cache->GetResource<Material>("Materials/Mushroom.xml"));
object->SetCastShadows(true);
RigidBody* body = objectNode->CreateComponent<RigidBody>();
body->SetCollisionLayer(2);
CollisionShape* shape = objectNode->CreateComponent<CollisionShape>();
shape->SetTriangleMesh(object->GetModel(), 0);
}
}
void VehicleDemo::CreateVehicle()
{
ResourceCache* cache = GetSubsystem<ResourceCache>();
Node* vehicleNode = scene_->CreateChild("Vehicle");
vehicleNode->SetPosition(Vector3(0.0f, 5.0f, 0.0f));
// Create the vehicle logic component
vehicle_ = vehicleNode->CreateComponent<Vehicle>();
// Create the rendering and physics components
vehicle_->Init();
}
void VehicleDemo::CreateInstructions()
{
ResourceCache* cache = GetSubsystem<ResourceCache>();
UI* ui = GetSubsystem<UI>();
// Construct new Text object, set string to display and font to use
Text* instructionText = ui->GetRoot()->CreateChild<Text>();
instructionText->SetText(
"Use WASD keys to drive, mouse to rotate camera"
);
instructionText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);
// Position the text relative to the screen center
instructionText->SetHorizontalAlignment(HA_CENTER);
instructionText->SetVerticalAlignment(VA_CENTER);
instructionText->SetPosition(0, ui->GetRoot()->GetHeight() / 4);
}
void VehicleDemo::SubscribeToEvents()
{
// Subscribe to Update event for setting the character controls before physics simulation
SubscribeToEvent(E_UPDATE, HANDLER(VehicleDemo, HandleUpdate));
// Subscribe to PostUpdate event for updating the camera position after physics simulation
SubscribeToEvent(E_POSTUPDATE, HANDLER(VehicleDemo, HandlePostUpdate));
}
void VehicleDemo::HandleUpdate(StringHash eventType, VariantMap& eventData)
{
using namespace Update;
float timeStep = eventData[P_TIMESTEP].GetFloat();
Input* input = GetSubsystem<Input>();
if (vehicle_)
{
UI* ui = GetSubsystem<UI>();
// Get movement controls and assign them to the vehicle component. If UI has a focused element, clear controls
if (!ui->GetFocusElement())
{
vehicle_->controls_.Set(CTRL_FORWARD, input->GetKeyDown('W'));
vehicle_->controls_.Set(CTRL_BACK, input->GetKeyDown('S'));
vehicle_->controls_.Set(CTRL_LEFT, input->GetKeyDown('A'));
vehicle_->controls_.Set(CTRL_RIGHT, input->GetKeyDown('D'));
// Add yaw & pitch from the mouse motion. Used only for the camera, does not affect motion
vehicle_->controls_.yaw_ += (float)input->GetMouseMoveX() * YAW_SENSITIVITY;
vehicle_->controls_.pitch_ += (float)input->GetMouseMoveY() * YAW_SENSITIVITY;
// Limit pitch
vehicle_->controls_.pitch_ = Clamp(vehicle_->controls_.pitch_, 1.0f, 80.0f);
}
else
vehicle_->controls_.Set(CTRL_FORWARD | CTRL_BACK | CTRL_LEFT | CTRL_RIGHT, false);
}
}
void VehicleDemo::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
{
if (!vehicle_)
return;
Node* vehicleNode = vehicle_->GetNode();
// Get camera lookat dir from vehicle yaw + camera yaw and camera pitch. Do not apply vehicle's pitch
// angle, because it changes abruptly when going over bumps in the terrain
Quaternion rot = Quaternion(0.0f, vehicleNode->GetRotation().YawAngle() + vehicle_->controls_.yaw_, 0.0f);
Quaternion dir = rot * Quaternion(vehicle_->controls_.pitch_, Vector3::RIGHT);
Vector3 aimPoint = vehicleNode->GetPosition() + rot * Vector3(0.0f, 1.7f, 0.0f);
// Collide camera ray with static physics objects (layer bitmask 2) to ensure we see the vehicle properly
Vector3 rayDir = dir * Vector3::BACK;
float rayDistance = CAMERA_MAX_DIST;
PhysicsRaycastResult result;
scene_->GetComponent<PhysicsWorld>()->RaycastSingle(result, Ray(aimPoint, rayDir), rayDistance, 2);
if (result.body_)
rayDistance = Min(rayDistance, result.distance_);
rayDistance = Clamp(rayDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST);
cameraNode_->SetPosition(aimPoint + rayDir * rayDistance);
cameraNode_->SetRotation(dir);
}

View File

@ -0,0 +1,72 @@
//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#pragma once
#include "Sample.h"
namespace Urho3D
{
class Node;
class Scene;
}
using namespace Urho3D;
class Vehicle;
/// Vehicle example.
/// This sample demonstrates:
/// - Creating a heightmap terrain with collision;
/// - Constructing a physical vehicle with rigid bodies for the hull and the wheels, joined with constraints;
class VehicleDemo : public Sample
{
OBJECT(VehicleDemo);
public:
/// Construct.
VehicleDemo(Context* context);
/// Setup after engine initialization and before running the main loop.
virtual void Start();
private:
/// Create static scene content.
void CreateScene();
/// Create the vehicle.
void CreateVehicle();
/// Construct an instruction text to the UI.
void CreateInstructions();
/// Subscribe to necessary events.
void SubscribeToEvents();
/// Handle application update. Set controls to vehicle.
void HandleUpdate(StringHash eventType, VariantMap& eventData);
/// Handle application post-update. Update camera position after vehicle has moved.
void HandlePostUpdate(StringHash eventType, VariantMap& eventData);
/// Scene.
SharedPtr<Scene> scene_;
/// Camera scene node.
SharedPtr<Node> cameraNode_;
/// The controllable vehicle component.
WeakPtr<Vehicle> vehicle_;
};

View File

@ -59,4 +59,5 @@ add_subdirectory (14_SoundEffects)
add_subdirectory (15_Navigation)
add_subdirectory (16_Chat)
add_subdirectory (17_SceneReplication)
add_subdirectory (18_CharacterDemo)
add_subdirectory (18_CharacterDemo)
add_subdirectory (19_VehicleDemo)