Browse Source

Added scene load/save to C++ VehicleDemo.

Lasse Öörni 12 years ago
parent
commit
1034f8856f

+ 2 - 2
Source/Samples/18_CharacterDemo/CharacterDemo.cpp

@@ -91,8 +91,8 @@ void CharacterDemo::CreateScene()
     scene_->CreateComponent<Octree>();
     scene_->CreateComponent<PhysicsWorld>();
     
-    // Create camera and define viewport. We will be doing load / save, in which case it's convenient to create the camera
-    // outside the scene, so that it isn't being destroyed and recreated, and we don't have to redefine the viewport on load
+    // Create camera and define viewport. We will be doing load / save, so it's convenient to create the camera outside the scene,
+    // so that it won't be destroyed and recreated, and we don't have to redefine the viewport on load
     cameraNode_ = new Node(context_);
     Camera* camera = cameraNode_->CreateComponent<Camera>();
     camera->SetFarClip(300.0f);

+ 53 - 16
Source/Samples/19_VehicleDemo/Vehicle.cpp

@@ -22,6 +22,7 @@
 
 #include "CollisionShape.h"
 #include "Constraint.h"
+#include "Context.h"
 #include "Material.h"
 #include "Model.h"
 #include "PhysicsEvents.h"
@@ -38,14 +39,45 @@ Vehicle::Vehicle(Context* context) :
 {
 }
 
+void Vehicle::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Vehicle>();
+    
+    ATTRIBUTE(Vehicle, VAR_FLOAT, "Controls Yaw", controls_.yaw_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Vehicle, VAR_FLOAT, "Controls Pitch", controls_.pitch_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Vehicle, VAR_FLOAT, "Steering", 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
+    ATTRIBUTE(Vehicle, VAR_INT, "Front Left Node", frontLeftID_, 0, AM_DEFAULT | AM_NODEID);
+    ATTRIBUTE(Vehicle, VAR_INT, "Front Right Node", frontRightID_, 0, AM_DEFAULT | AM_NODEID);
+    ATTRIBUTE(Vehicle, VAR_INT, "Rear Left Node", rearLeftID_, 0, AM_DEFAULT | AM_NODEID);
+    ATTRIBUTE(Vehicle, VAR_INT, "Rear Right Node", rearRightID_, 0, AM_DEFAULT | AM_NODEID);
+}
+
 void Vehicle::OnNodeSet(Node* node)
 {
     if (node)
         SubscribeToEvent(GetScene()->GetComponent<PhysicsWorld>(), E_PHYSICSPRESTEP, HANDLER(Vehicle, HandleFixedUpdate));
 }
 
+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::Init()
 {
+    // This function is called only from the main program when initially creating the vehicle, not on scene load
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     
     StaticModel* hullObject = node_->CreateComponent<StaticModel>();
@@ -61,32 +93,29 @@ void Vehicle::Init()
     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>();
+    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();
 }
 
-Node* Vehicle::InitWheel(const String& name, const Vector3& offset)
+void Vehicle::InitWheel(const String& name, const Vector3& offset, WeakPtr<Node>& wheelNode, unsigned& wheelNodeID)
 {
     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 = 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();
+    
     StaticModel* wheelObject = wheelNode->CreateComponent<StaticModel>();
     RigidBody* wheelBody = wheelNode->CreateComponent<RigidBody>();
     CollisionShape* wheelShape = wheelNode->CreateComponent<CollisionShape>();
@@ -109,8 +138,16 @@ Node* Vehicle::InitWheel(const String& name, const Vector3& offset)
     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::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>();
 }
 
 void Vehicle::HandleFixedUpdate(StringHash eventType, VariantMap& eventData)

+ 15 - 2
Source/Samples/19_VehicleDemo/Vehicle.h

@@ -55,8 +55,13 @@ public:
     /// Construct.
     Vehicle(Context* context);
     
+    /// Register object factory and attributes.
+    static void RegisterObject(Context* context);
+    
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
+    /// Perform post-load after deserialization. Acquire the components from the scene nodes.
+    virtual void ApplyAttributes();
     
     /// Initialize the vehicle. Create rendering and physics components.
     void Init();
@@ -65,8 +70,10 @@ public:
     Controls controls_;
     
 private:
-    /// Initialize a wheel and return its scene node.
-    Node* InitWheel(const String& name, const Vector3& offset);
+    /// Initialize a wheel and remember its scene node and ID.
+    void InitWheel(const String& name, const Vector3& offset, WeakPtr<Node>& wheelNode, unsigned& wheelNodeID);
+    /// Acquire wheel components from wheel scene nodes.
+    void GetWheelComponents();
     /// Handle physics world update event.
     void HandleFixedUpdate(StringHash eventType, VariantMap& eventData);
     
@@ -87,6 +94,12 @@ private:
     WeakPtr<RigidBody> rearLeftBody_;
     WeakPtr<RigidBody> rearRightBody_;
     
+    // IDs of the wheel scene nodes for serialization.
+    unsigned frontLeftID_;
+    unsigned frontRightID_;
+    unsigned rearLeftID_;
+    unsigned rearRightID_;
+    
     /// Current left/right steering amount (-1 to 1.)
     float steering_;
 };

+ 27 - 4
Source/Samples/19_VehicleDemo/VehicleDemo.cpp

@@ -25,6 +25,7 @@
 #include "Constraint.h"
 #include "CoreEvents.h"
 #include "Engine.h"
+#include "FileSystem.h"
 #include "Font.h"
 #include "Input.h"
 #include "Light.h"
@@ -55,8 +56,8 @@ 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>();
+    // Register factory and attributes for the Vehicle component so it can be created via CreateComponent, and loaded / saved
+    Vehicle::RegisterObject(context);
 }
 
 void VehicleDemo::Start()
@@ -87,7 +88,8 @@ void VehicleDemo::CreateScene()
     scene_->CreateComponent<Octree>();
     scene_->CreateComponent<PhysicsWorld>();
     
-    // Create camera and define viewport. Camera does not necessarily have to belong to the scene
+    // Create camera and define viewport. We will be doing load / save, so it's convenient to create the camera outside the scene,
+    // so that it won't be destroyed and recreated, and we don't have to redefine the viewport on load
     cameraNode_ = new Node(context_);
     Camera* camera = cameraNode_->CreateComponent<Camera>();
     camera->SetFarClip(500.0f);
@@ -174,9 +176,12 @@ void VehicleDemo::CreateInstructions()
     // 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"
+        "Use WASD keys to drive, mouse to rotate camera\n"
+        "F5 to save scene, F7 to load"
     );
     instructionText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);
+    // The text has multiple rows. Center them in relation to each other
+    instructionText->SetTextAlignment(HA_CENTER);
     
     // Position the text relative to the screen center
     instructionText->SetHorizontalAlignment(HA_CENTER);
@@ -217,6 +222,24 @@ void VehicleDemo::HandleUpdate(StringHash eventType, VariantMap& eventData)
             vehicle_->controls_.pitch_ += (float)input->GetMouseMoveY() * YAW_SENSITIVITY;
             // Limit pitch
             vehicle_->controls_.pitch_ = Clamp(vehicle_->controls_.pitch_, 0.0f, 80.0f);
+
+            // Check for loading / saving the scene
+            if (input->GetKeyPress(KEY_F5))
+            {
+                File saveFile(context_, GetSubsystem<FileSystem>()->GetProgramDir() + "Data/Scenes/VehicleDemo.xml",
+                    FILE_WRITE);
+                scene_->SaveXML(saveFile);
+            }
+            if (input->GetKeyPress(KEY_F7))
+            {
+                File loadFile(context_, GetSubsystem<FileSystem>()->GetProgramDir() + "Data/Scenes/VehicleDemo.xml", FILE_READ);
+                scene_->LoadXML(loadFile);
+                // After loading we have to reacquire the weak pointer to the Vehicle component, as it has been recreated
+                // Simply find the vehicle's scene node by name as there's only one of them
+                Node* vehicleNode = scene_->GetChild("Vehicle", true);
+                if (vehicleNode)
+                    vehicle_ = vehicleNode->GetComponent<Vehicle>();
+            }
         }
         else
             vehicle_->controls_.Set(CTRL_FORWARD | CTRL_BACK | CTRL_LEFT | CTRL_RIGHT, false);

+ 1 - 0
Source/Samples/19_VehicleDemo/VehicleDemo.h

@@ -38,6 +38,7 @@ class Vehicle;
 /// 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;
+///     - Defining attributes (including node and component references) of a custom component so that it can be saved and loaded;
 class VehicleDemo : public Sample
 {
     OBJECT(VehicleDemo);