Browse Source

Convert the Navigation sample to move the Jack character from current position to new target. Shift + LMB to teleport.

Lasse Öörni 12 years ago
parent
commit
2af8244543

+ 41 - 53
Bin/Data/LuaScripts/15_Navigation.lua

@@ -5,20 +5,17 @@
 --     - Rebuilding the navigation mesh partially when adding or removing objects
 --     - Rebuilding the navigation mesh partially when adding or removing objects
 --     - Visualizing custom debug geometry
 --     - Visualizing custom debug geometry
 --     - Raycasting drawable components
 --     - Raycasting drawable components
---     - Make a node follow the Detour path
+--     - Making a node follow the Detour path
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
 local scene_ = nil
 local scene_ = nil
 local cameraNode = nil
 local cameraNode = nil
-local startPos = nil
 local endPos = nil
 local endPos = nil
-local currentPath = nil
+local currentPath = {}
 local yaw = 0.0
 local yaw = 0.0
 local pitch = 0.0
 local pitch = 0.0
 local drawDebug = false
 local drawDebug = false
-local startPosDefined = false
-local endPosDefined = false
 
 
 local cache = GetCache()
 local cache = GetCache()
 local input = GetInput()
 local input = GetInput()
@@ -100,9 +97,9 @@ function CreateScene()
     end
     end
 
 
     -- Create Jack node that will follow the path
     -- Create Jack node that will follow the path
-    Jack = scene_:CreateChild("Jack")
-    Jack.position = Vector3(-5, 0, 20)
-    local modelObject = Jack:CreateComponent("AnimatedModel")
+    jackNode = scene_:CreateChild("Jack")
+    jackNode.position = Vector3(-5, 0, 20)
+    local modelObject = jackNode:CreateComponent("AnimatedModel")
     modelObject.model = cache:GetResource("Model", "Models/Jack.mdl")
     modelObject.model = cache:GetResource("Model", "Models/Jack.mdl")
     modelObject.material = cache:GetResource("Material", "Materials/Jack.xml")
     modelObject.material = cache:GetResource("Material", "Materials/Jack.xml")
     modelObject.castShadows = true
     modelObject.castShadows = true
@@ -142,7 +139,7 @@ function CreateUI()
     -- Construct new Text object, set string to display and font to use
     -- Construct new Text object, set string to display and font to use
     local instructionText = ui.root:CreateChild("Text")
     local instructionText = ui.root:CreateChild("Text")
     instructionText.text = "Use WASD keys to move, RMB to rotate view\n"..
     instructionText.text = "Use WASD keys to move, RMB to rotate view\n"..
-        "Shift+LMB to set path start, LMB to set path end\n"..
+        "LMB to set destination, SHIFT+LMB to teleport\n"..
         "MMB to add or remove obstacles\n"..
         "MMB to add or remove obstacles\n"..
         "Space to toggle debug geometry"
         "Space to toggle debug geometry"
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
@@ -209,7 +206,7 @@ function MoveCamera(timeStep)
     if input:GetKeyDown(KEY_D) then
     if input:GetKeyDown(KEY_D) then
         cameraNode:TranslateRelative(Vector3(1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
         cameraNode:TranslateRelative(Vector3(1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
     end
     end
-    -- Set route start/endpoint with left mouse button, recalculate route if applicable
+    -- Set destination or teleport with left mouse button
     if input:GetMouseButtonPress(MOUSEB_LEFT) then
     if input:GetMouseButtonPress(MOUSEB_LEFT) then
         SetPathPoint()
         SetPathPoint()
     end
     end
@@ -227,16 +224,18 @@ function SetPathPoint()
     local result, hitPos, hitDrawable = Raycast(250.0)
     local result, hitPos, hitDrawable = Raycast(250.0)
     local navMesh = scene_:GetComponent("NavigationMesh")
     local navMesh = scene_:GetComponent("NavigationMesh")
     if result then
     if result then
-        local setStart = input:GetQualifierDown(QUAL_SHIFT)
-        if setStart then
-            startPos = navMesh:FindNearestPoint(hitPos, Vector3.ONE)
-            startPosDefined = true
+        local pathPos = navMesh:FindNearestPoint(hitPos, Vector3.ONE)
+
+        if input:GetQualifierDown(QUAL_SHIFT) then
+            -- Teleport
+            currentPath = {}
+            jackNode:LookAt(pathPos, Vector3(0.0, 1.0, 0.0))
+            jackNode.position = pathPos;
         else
         else
-            endPos = navMesh:FindNearestPoint(hitPos, Vector3.ONE)
-            endPosDefined = true
+            -- Calculate path from Jack's current position to the end point
+            endPos = pathPos;
+            currentPath = navMesh:FindPath(jackNode.position, endPos);
         end
         end
-        if startPosDefined then Jack.position = startPos end -- Reset Jack position to start
-        --RecalculatePath()
     end
     end
 end
 end
 
 
@@ -261,7 +260,9 @@ function AddOrRemoveObject()
         -- Rebuild part of the navigation mesh, then recalculate path if applicable
         -- Rebuild part of the navigation mesh, then recalculate path if applicable
         local navMesh = scene_:GetComponent("NavigationMesh")
         local navMesh = scene_:GetComponent("NavigationMesh")
         navMesh:Build(updateBox)
         navMesh:Build(updateBox)
-        --RecalculatePath()
+        if table.maxn(currentPath) > 0 then
+            currentPath = navMesh:FindPath(jackNode.position, endPos);
+        end
     end
     end
 end
 end
 
 
@@ -277,15 +278,6 @@ function CreateMushroom(pos)
     return mushroomNode
     return mushroomNode
 end
 end
 
 
-function RecalculatePath()
-    if not startPosDefined or not endPosDefined then
-        return
-    end
-
-    local navMesh = scene_:GetComponent("NavigationMesh")
-    currentPath = navMesh:FindPath(startPos, endPos)
-end
-
 function Raycast(maxDistance)
 function Raycast(maxDistance)
     local hitPos = nil
     local hitPos = nil
     local hitDrawable = nil
     local hitDrawable = nil
@@ -319,22 +311,21 @@ function HandleUpdate(eventType, eventData)
     MoveCamera(timeStep)
     MoveCamera(timeStep)
 
 
     -- Make Jack follow the Detour path
     -- Make Jack follow the Detour path
-    followPath(timeStep)
+    FollowPath(timeStep)
 end
 end
 
 
-function followPath(timeStep)
-    if startPosDefined and endPosDefined then
-
-        -- Get next waypoint to reach
-        local navMesh = scene_:GetComponent("NavigationMesh")
-		currentPath = navMesh:FindPath(Jack.position, navMesh:FindNearestPoint(endPos, Vector3.ONE))
-		if table.maxn(currentPath) < 2 then return end
-		local nextWaypoint = currentPath[2] -- NB: currentPath[1] is the starting position
-		if math.floor(Jack.position.x) ==  math.floor(endPos.x) and math.floor(Jack.position.z) ==  math.floor(endPos.z) then return end
+function FollowPath(timeStep)
+    if table.maxn(currentPath) > 0 then
+        local nextWaypoint = currentPath[1] -- NB: currentPath[1] is the next waypoint in order
 
 
         -- Rotate Jack toward next waypoint to reach and move
         -- Rotate Jack toward next waypoint to reach and move
-        Jack:LookAt(nextWaypoint, Vector3.UP)
-        Jack:TranslateRelative(Vector3.FORWARD * 5 * timeStep)
+        jackNode:LookAt(nextWaypoint, Vector3(0.0, 1.0, 0.0))
+        jackNode:TranslateRelative(Vector3(0.0, 0.0, 1.0) * 5 * timeStep)
+
+        -- Remove waypoint if reached it
+        if (jackNode.position - nextWaypoint):Length() < 0.1 then
+            table.remove(currentPath, 1)
+        end
     end
     end
 end
 end
 
 
@@ -346,22 +337,19 @@ function HandlePostRenderUpdate(eventType, eventData)
     end
     end
     
     
     -- Visualize the start and end points and the last calculated path
     -- Visualize the start and end points and the last calculated path
-    -- Note the convenience accessor to the DebugRenderer component
-    local debug = scene_:GetComponent("DebugRenderer")
-    if startPosDefined then
-        debug:AddBoundingBox(BoundingBox(startPos - Vector3(0.1, 0.1, 0.1), startPos + Vector3(0.1, 0.1, 0.1)), Color(1.0, 1.0, 1.0))
-    end
-    
-    if endPosDefined then
+    local size = table.maxn(currentPath)
+    if size > 0 then
+        local debug = scene_:GetComponent("DebugRenderer")
         debug:AddBoundingBox(BoundingBox(endPos - Vector3(0.1, 0.1, 0.1), endPos + Vector3(0.1, 0.1, 0.1)), Color(1.0, 1.0, 1.0))
         debug:AddBoundingBox(BoundingBox(endPos - Vector3(0.1, 0.1, 0.1), endPos + Vector3(0.1, 0.1, 0.1)), Color(1.0, 1.0, 1.0))
-    end
-    
-    if currentPath ~= nil then
+        
         -- Draw the path with a small upward bias so that it does not clip into the surfaces
         -- Draw the path with a small upward bias so that it does not clip into the surfaces
         local bias = Vector3(0.0, 0.05, 0.0)
         local bias = Vector3(0.0, 0.05, 0.0)
-        local size = table.maxn(currentPath)
-        for i = 1, size - 1 do
-            debug:AddLine(currentPath[i] + bias, currentPath[i + 1] + bias, Color(1.0, 1.0, 1.0))
+        debug:AddLine(jackNode.position + bias, currentPath[1] + bias, Color(1.0, 1.0, 1.0))
+
+        if size > 1 then
+            for i = 1, size - 1 do
+                debug:AddLine(currentPath[i] + bias, currentPath[i + 1] + bias, Color(1.0, 1.0, 1.0))
+            end
         end
         end
     end
     end
 end
 end

+ 51 - 60
Bin/Data/Scripts/15_Navigation.as

@@ -5,21 +5,18 @@
 //     - Rebuilding the navigation mesh partially when adding or removing objects
 //     - Rebuilding the navigation mesh partially when adding or removing objects
 //     - Visualizing custom debug geometry
 //     - Visualizing custom debug geometry
 //     - Raycasting drawable components
 //     - Raycasting drawable components
-//     - Make a node follow the Detour path
+//     - Making a node follow the Detour path
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
 Scene@ scene_;
 Scene@ scene_;
 Node@ cameraNode;
 Node@ cameraNode;
-Vector3 startPos;
 Vector3 endPos;
 Vector3 endPos;
 Array<Vector3> currentPath;
 Array<Vector3> currentPath;
 float yaw = 0.0f;
 float yaw = 0.0f;
 float pitch = 0.0f;
 float pitch = 0.0f;
 bool drawDebug = false;
 bool drawDebug = false;
-bool startPosDefined = false;
-bool endPosDefined = false;
-Node@ Jack;
+Node@ jackNode;
 
 
 void Start()
 void Start()
 {
 {
@@ -97,8 +94,9 @@ void CreateScene()
     }
     }
 
 
     // Create Jack node that will follow the path
     // Create Jack node that will follow the path
-    Jack = scene_.CreateChild("Jack");
-    Jack.position = Vector3(-5.0f, 0.0f, 20.0f);
+    jackNode = scene_.CreateChild("Jack");
+    jackNode.position = Vector3(-5.0f, 0.0f, 20.0f);
+    AnimatedModel@ modelObject = jackNode.CreateComponent("AnimatedModel");
     modelObject.model = cache.GetResource("Model", "Models/Jack.mdl");
     modelObject.model = cache.GetResource("Model", "Models/Jack.mdl");
     modelObject.material = cache.GetResource("Material", "Materials/Jack.xml");
     modelObject.material = cache.GetResource("Material", "Materials/Jack.xml");
     modelObject.castShadows = true;
     modelObject.castShadows = true;
@@ -140,7 +138,7 @@ void CreateUI()
     Text@ instructionText = ui.root.CreateChild("Text");
     Text@ instructionText = ui.root.CreateChild("Text");
     instructionText.text =
     instructionText.text =
         "Use WASD keys to move, RMB to rotate view\n"
         "Use WASD keys to move, RMB to rotate view\n"
-        "Shift+LMB to set path start, LMB to set path end\n"
+        "LMB to set destination, SHIFT+LMB to teleport\n"
         "MMB to add or remove obstacles\n"
         "MMB to add or remove obstacles\n"
         "Space to toggle debug geometry";
         "Space to toggle debug geometry";
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
@@ -178,12 +176,12 @@ void MoveCamera(float timeStep)
     // Do not move if the UI has a focused element (the console)
     // Do not move if the UI has a focused element (the console)
     if (ui.focusElement !is null)
     if (ui.focusElement !is null)
         return;
         return;
-    
+
     // Movement speed as world units per second
     // Movement speed as world units per second
     const float MOVE_SPEED = 20.0f;
     const float MOVE_SPEED = 20.0f;
     // Mouse sensitivity as degrees per pixel
     // Mouse sensitivity as degrees per pixel
     const float MOUSE_SENSITIVITY = 0.1f;
     const float MOUSE_SENSITIVITY = 0.1f;
-    
+
     // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
     // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
     // Only move the camera when the cursor is hidden
     // Only move the camera when the cursor is hidden
     if (!ui.cursor.visible)
     if (!ui.cursor.visible)
@@ -207,7 +205,7 @@ void MoveCamera(float timeStep)
     if (input.keyDown['D'])
     if (input.keyDown['D'])
         cameraNode.TranslateRelative(Vector3(1.0f, 0.0f, 0.0f) * MOVE_SPEED * timeStep);
         cameraNode.TranslateRelative(Vector3(1.0f, 0.0f, 0.0f) * MOVE_SPEED * timeStep);
 
 
-    // Set route start/endpoint with left mouse button, recalculate route if applicable
+    // Set destination or teleport with left mouse button
     if (input.mouseButtonPress[MOUSEB_LEFT])
     if (input.mouseButtonPress[MOUSEB_LEFT])
         SetPathPoint();
         SetPathPoint();
     // Add or remove objects with middle mouse button, then rebuild navigation mesh partially
     // Add or remove objects with middle mouse button, then rebuild navigation mesh partially
@@ -224,24 +222,24 @@ void SetPathPoint()
     Vector3 hitPos;
     Vector3 hitPos;
     Drawable@ hitDrawable;
     Drawable@ hitDrawable;
     NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
     NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
-    
+
     if (Raycast(250.0f, hitPos, hitDrawable))
     if (Raycast(250.0f, hitPos, hitDrawable))
     {
     {
-        bool setStart = input.qualifierDown[QUAL_SHIFT];
-        if (setStart)
+        Vector3 pathPos = navMesh.FindNearestPoint(hitPos, Vector3(1.0f, 1.0f, 1.0f));
+
+        if (input.qualifierDown[QUAL_SHIFT])
         {
         {
-            startPos = navMesh.FindNearestPoint(hitPos, Vector3(1.0f, 1.0f, 1.0f));
-            startPosDefined = true;
+            // Teleport
+            currentPath.Clear();
+            jackNode.LookAt(pathPos, Vector3(0.0f, 1.0f, 0.0f));
+            jackNode.position = pathPos;
         }
         }
         else
         else
         {
         {
-            endPos = navMesh.FindNearestPoint(hitPos, Vector3(1.0f, 1.0f, 1.0f));
-            endPosDefined = true;
+            // Calculate path from Jack's current position to the end point
+            endPos = pathPos;
+            currentPath = navMesh.FindPath(jackNode.position, endPos);
         }
         }
-        
-        //RecalculatePath();
-        if (startPosDefined)
-            Jack.position = startPos; // Reset Jack position to start
     }
     }
 }
 }
 
 
@@ -250,13 +248,13 @@ void AddOrRemoveObject()
     // Raycast and check if we hit a mushroom node. If yes, remove it, if no, create a new one
     // Raycast and check if we hit a mushroom node. If yes, remove it, if no, create a new one
     Vector3 hitPos;
     Vector3 hitPos;
     Drawable@ hitDrawable;
     Drawable@ hitDrawable;
-    
+
     if (Raycast(250.0f, hitPos, hitDrawable))
     if (Raycast(250.0f, hitPos, hitDrawable))
     {
     {
         // The part of the navigation mesh we must update, which is the world bounding box of the associated
         // The part of the navigation mesh we must update, which is the world bounding box of the associated
         // drawable component
         // drawable component
         BoundingBox updateBox;
         BoundingBox updateBox;
-        
+
         Node@ hitNode = hitDrawable.node;
         Node@ hitNode = hitDrawable.node;
         if (hitNode.name == "Mushroom")
         if (hitNode.name == "Mushroom")
         {
         {
@@ -269,11 +267,12 @@ void AddOrRemoveObject()
             StaticModel@ newObject = newNode.GetComponent("StaticModel");
             StaticModel@ newObject = newNode.GetComponent("StaticModel");
             updateBox = newObject.worldBoundingBox;
             updateBox = newObject.worldBoundingBox;
         }
         }
-        
-        // Rebuild part of the navigation mesh, then recalculate path if applicable
+
+        // Rebuild part of the navigation mesh, then rebuild the path if applicable
         NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
         NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
         navMesh.Build(updateBox);
         navMesh.Build(updateBox);
-        //RecalculatePath();
+        if (currentPath.length > 0)
+            currentPath = navMesh.FindPath(jackNode.position, endPos);
     }
     }
 }
 }
 
 
@@ -287,17 +286,8 @@ Node@ CreateMushroom(const Vector3& pos)
     mushroomObject.model = cache.GetResource("Model", "Models/Mushroom.mdl");
     mushroomObject.model = cache.GetResource("Model", "Models/Mushroom.mdl");
     mushroomObject.material = cache.GetResource("Material", "Materials/Mushroom.xml");
     mushroomObject.material = cache.GetResource("Material", "Materials/Mushroom.xml");
     mushroomObject.castShadows = true;
     mushroomObject.castShadows = true;
-    
-    return mushroomNode;
-}
-
-void RecalculatePath()
-{
-    if (!startPosDefined || !endPosDefined)
-        return;
 
 
-    NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
-    currentPath = navMesh.FindPath(startPos, endPos);
+    return mushroomNode;
 }
 }
 
 
 bool Raycast(float maxDistance, Vector3& hitPos, Drawable@& hitDrawable)
 bool Raycast(float maxDistance, Vector3& hitPos, Drawable@& hitDrawable)
@@ -324,22 +314,19 @@ bool Raycast(float maxDistance, Vector3& hitPos, Drawable@& hitDrawable)
     return false;
     return false;
 }
 }
 
 
-void followPath(float timeStep)
+void FollowPath(float timeStep)
 {
 {
-    if (startPosDefined && endPosDefined)
+    if (currentPath.length > 0)
     {
     {
-        // Get next waypoint to reach
-        NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
-		currentPath = navMesh.FindPath(Jack.position, endPos);
-		if (currentPath.length < 2)
-		    return;
-		Vector3 nextWaypoint = currentPath[1]; // NB: currentPath[0] is the starting position
-        if (Floor(Jack.position.x) ==  Floor(endPos.x) && Floor(Jack.position.z) ==  Floor(endPos.z))
-            return;
+        Vector3 nextWaypoint = currentPath[0]; // NB: currentPath[0] is the next waypoint in order
 
 
         // Rotate Jack toward next waypoint to reach and move
         // Rotate Jack toward next waypoint to reach and move
-        Jack.LookAt(nextWaypoint, Vector3(0.0f, 1.0f, 0.0f));
-        Jack.TranslateRelative(Vector3(0.0f, 0.0f, 1.0f) * 5 * timeStep);
+        jackNode.LookAt(nextWaypoint, Vector3(0.0f, 1.0f, 0.0f));
+        jackNode.TranslateRelative(Vector3(0.0f, 0.0f, 1.0f) * 5 * timeStep);
+
+        // Remove waypoint if reached it
+        if ((jackNode.position - nextWaypoint).length < 0.1)
+            currentPath.Erase(0);
     }
     }
 }
 }
 
 
@@ -352,7 +339,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     MoveCamera(timeStep);
     MoveCamera(timeStep);
 
 
     // Make Jack follow the Detour path
     // Make Jack follow the Detour path
-    followPath(timeStep);
+    FollowPath(timeStep);
 }
 }
 
 
 void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
 void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
@@ -364,18 +351,22 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
         navMesh.DrawDebugGeometry(true);
         navMesh.DrawDebugGeometry(true);
     }
     }
 
 
-    // Visualize the start and end points and the last calculated path
-    // Note the convenience accessor to the DebugRenderer component
-    DebugRenderer@ debug = scene_.debugRenderer;
-    if (startPosDefined)
-        debug.AddBoundingBox(BoundingBox(startPos - Vector3(0.1f, 0.1f, 0.1f), startPos + Vector3(0.1f, 0.1f, 0.1f)), Color(1.0f, 1.0f, 1.0f));
-    if (endPosDefined)
-        debug.AddBoundingBox(BoundingBox(endPos - Vector3(0.1f, 0.1f, 0.1f), endPos + Vector3(0.1f, 0.1f, 0.1f)), Color(1.0f, 1.0f, 1.0f));
-    if (currentPath.length > 1)
+    if (currentPath.length > 0)
     {
     {
+        // Visualize the current calculated path
+        // Note the convenience accessor to the DebugRenderer component
+        DebugRenderer@ debug = scene_.debugRenderer;
+        debug.AddBoundingBox(BoundingBox(endPos - Vector3(0.1f, 0.1f, 0.1f), endPos + Vector3(0.1f, 0.1f, 0.1f)),
+            Color(1.0f, 1.0f, 1.0f));
+
         // Draw the path with a small upward bias so that it does not clip into the surfaces
         // Draw the path with a small upward bias so that it does not clip into the surfaces
         Vector3 bias(0.0f, 0.05f, 0.0f);
         Vector3 bias(0.0f, 0.05f, 0.0f);
-        for (uint i = 0; i < currentPath.length - 1; ++i)
-            debug.AddLine(currentPath[i] + bias, currentPath[i + 1] + bias, Color(1.0f, 1.0f, 1.0f));
+        debug.AddLine(jackNode.position + bias, currentPath[0] + bias, Color(1.0f, 1.0f, 1.0f));
+
+        if (currentPath.length > 1)
+        {
+            for (uint i = 0; i < currentPath.length - 1; ++i)
+                debug.AddLine(currentPath[i] + bias, currentPath[i + 1] + bias, Color(1.0f, 1.0f, 1.0f));
+        }
     }
     }
 }
 }

+ 43 - 53
Source/Samples/15_Navigation/Navigation.cpp

@@ -135,9 +135,9 @@ void Navigation::CreateScene()
     }
     }
 
 
     // Create Jack node that will follow the path
     // Create Jack node that will follow the path
-    Node* modelNode = scene_->CreateChild("Jack");
-    modelNode->SetPosition(Vector3(-5.0f, 0.0f, 20.0f));
-    AnimatedModel* modelObject = modelNode->CreateComponent<AnimatedModel>();
+    jackNode_ = scene_->CreateChild("Jack");
+    jackNode_->SetPosition(Vector3(-5.0f, 0.0f, 20.0f));
+    AnimatedModel* modelObject = jackNode_->CreateComponent<AnimatedModel>();
     modelObject->SetModel(cache->GetResource<Model>("Models/Jack.mdl"));
     modelObject->SetModel(cache->GetResource<Model>("Models/Jack.mdl"));
     modelObject->SetMaterial(cache->GetResource<Material>("Materials/Jack.xml"));
     modelObject->SetMaterial(cache->GetResource<Material>("Materials/Jack.xml"));
     modelObject->SetCastShadows(true);
     modelObject->SetCastShadows(true);
@@ -184,7 +184,7 @@ void Navigation::CreateUI()
     Text* instructionText = ui->GetRoot()->CreateChild<Text>();
     Text* instructionText = ui->GetRoot()->CreateChild<Text>();
     instructionText->SetText(
     instructionText->SetText(
         "Use WASD keys to move, RMB to rotate view\n"
         "Use WASD keys to move, RMB to rotate view\n"
-        "Shift+LMB to set path start, LMB to set path end\n"
+        "LMB to set destination, SHIFT+LMB to teleport\n"
         "MMB to add or remove obstacles\n"
         "MMB to add or remove obstacles\n"
         "Space to toggle debug geometry"
         "Space to toggle debug geometry"
     );
     );
@@ -256,7 +256,7 @@ void Navigation::MoveCamera(float timeStep)
     if (input->GetKeyDown('D'))
     if (input->GetKeyDown('D'))
         cameraNode_->TranslateRelative(Vector3::RIGHT * MOVE_SPEED * timeStep);
         cameraNode_->TranslateRelative(Vector3::RIGHT * MOVE_SPEED * timeStep);
     
     
-    // Set route start/endpoint with left mouse button, recalculate route if applicable
+    // Set destination or teleport with left mouse button
     if (input->GetMouseButtonPress(MOUSEB_LEFT))
     if (input->GetMouseButtonPress(MOUSEB_LEFT))
         SetPathPoint();
         SetPathPoint();
     // Add or remove objects with middle mouse button, then rebuild navigation mesh partially
     // Add or remove objects with middle mouse button, then rebuild navigation mesh partially
@@ -276,23 +276,20 @@ void Navigation::SetPathPoint()
     
     
     if (Raycast(250.0f, hitPos, hitDrawable))
     if (Raycast(250.0f, hitPos, hitDrawable))
     {
     {
-        bool setStart = GetSubsystem<Input>()->GetQualifierDown(QUAL_SHIFT);
-        if (setStart)
+        Vector3 pathPos = navMesh->FindNearestPoint(hitPos, Vector3(1.0f, 1.0f, 1.0f));
+
+        if (GetSubsystem<Input>()->GetQualifierDown(QUAL_SHIFT))
         {
         {
-            startPos_ = navMesh->FindNearestPoint(hitPos, Vector3(1.0f, 1.0f, 1.0f));
-            startPosDefined_ = true;
+            // Teleport
+            currentPath_.Clear();
+            jackNode_->LookAt(pathPos, Vector3(0.0f, 1.0f, 0.0f));
+            jackNode_->SetPosition(pathPos);
         }
         }
         else
         else
         {
         {
-            endPos_ = navMesh->FindNearestPoint(hitPos, Vector3(1.0f, 1.0f, 1.0f));
-            endPosDefined_ = true;
-        }
-        
-        //RecalculatePath();
-        if (startPosDefined_)
-        {
-			Node* Jack = scene_->GetChild("Jack", true);
-            Jack->SetPosition(startPos_); // Reset Jack position to start
+            // Calculate path from Jack's current position to the end point
+            endPos_ = pathPos;
+            navMesh->FindPath(currentPath_, jackNode_->GetPosition(), endPos_);
         }
         }
     }
     }
 }
 }
@@ -322,8 +319,10 @@ void Navigation::AddOrRemoveObject()
         }
         }
         
         
         // Rebuild part of the navigation mesh, then recalculate path if applicable
         // Rebuild part of the navigation mesh, then recalculate path if applicable
-        scene_->GetComponent<NavigationMesh>()->Build(updateBox);
-        //RecalculatePath();
+        NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>();
+        navMesh->Build(updateBox);
+        if (currentPath_.Size())
+            navMesh->FindPath(currentPath_, jackNode_->GetPosition(), endPos_);
     }
     }
 }
 }
 
 
@@ -343,15 +342,6 @@ Node* Navigation::CreateMushroom(const Vector3& pos)
     return mushroomNode;
     return mushroomNode;
 }
 }
 
 
-void Navigation::RecalculatePath()
-{
-    if (!startPosDefined_ || !endPosDefined_)
-        return;
-    
-    NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>();
-    navMesh->FindPath(currentPath_, startPos_, endPos_);
-}
-
 bool Navigation::Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawable)
 bool Navigation::Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawable)
 {
 {
     hitDrawable = 0;
     hitDrawable = 0;
@@ -380,23 +370,19 @@ bool Navigation::Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawa
     return false;
     return false;
 }
 }
 
 
-void Navigation::followPath(float timeStep)
+void Navigation::FollowPath(float timeStep)
 {
 {
-    if (startPosDefined_ && endPosDefined_)
+    if (currentPath_.Size())
     {
     {
-        // Get next waypoint to reach
-        NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>();
-        Node* Jack = scene_->GetChild("Jack", true);
-		navMesh->FindPath(currentPath_, Jack->GetPosition(), endPos_);
-		if (currentPath_.Size() < 2)
-		    return;
-		Vector3 nextWaypoint = currentPath_[1]; // NB: currentPath_[0] is the starting position
-        if (floorf(Jack->GetPosition().x_) ==  floorf(endPos_.x_) && floorf(Jack->GetPosition().z_) ==  floorf(endPos_.z_))
-            return;
+        Vector3 nextWaypoint = currentPath_[0]; // NB: currentPath[0] is the next waypoint in order
 
 
         // Rotate Jack toward next waypoint to reach and move
         // Rotate Jack toward next waypoint to reach and move
-        Jack->LookAt(nextWaypoint, Vector3(0.0f, 1.0f, 0.0f));
-        Jack->TranslateRelative(Vector3(0.0f, 0.0f, 1.0f) * 5 * timeStep);
+        jackNode_->LookAt(nextWaypoint, Vector3(0.0f, 1.0f, 0.0f));
+        jackNode_->TranslateRelative(Vector3(0.0f, 0.0f, 1.0f) * 5.0f * timeStep);
+
+        // Remove waypoint if reached it
+        if ((jackNode_->GetPosition() - nextWaypoint).Length() < 0.1f)
+            currentPath_.Erase(0);
     }
     }
 }
 }
 
 
@@ -411,7 +397,7 @@ void Navigation::HandleUpdate(StringHash eventType, VariantMap& eventData)
     MoveCamera(timeStep);
     MoveCamera(timeStep);
 
 
     // Make Jack follow the Detour path
     // Make Jack follow the Detour path
-    followPath(timeStep);
+    FollowPath(timeStep);
 }
 }
 
 
 void Navigation::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
 void Navigation::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
@@ -420,17 +406,21 @@ void Navigation::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventD
     if (drawDebug_)
     if (drawDebug_)
         scene_->GetComponent<NavigationMesh>()->DrawDebugGeometry(true);
         scene_->GetComponent<NavigationMesh>()->DrawDebugGeometry(true);
     
     
-    // Visualize the start and end points and the last calculated path
-    DebugRenderer* debug = scene_->GetComponent<DebugRenderer>();
-    if (startPosDefined_)
-        debug->AddBoundingBox(BoundingBox(startPos_ - 0.1f * Vector3::ONE, startPos_ + 0.1f * Vector3::ONE), Color::WHITE);
-    if (endPosDefined_)
-        debug->AddBoundingBox(BoundingBox(endPos_ - 0.1f * Vector3::ONE, endPos_ + 0.1f * Vector3::ONE), Color::WHITE);
-    if (currentPath_.Size() > 1)
+    if (currentPath_.Size())
     {
     {
+        // Visualize the current calculated path
+        DebugRenderer* debug = scene_->GetComponent<DebugRenderer>();
+        debug->AddBoundingBox(BoundingBox(endPos_ - Vector3(0.1f, 0.1f, 0.1f), endPos_ + Vector3(0.1f, 0.1f, 0.1f)),
+            Color(1.0f, 1.0f, 1.0f));
+
         // Draw the path with a small upward bias so that it does not clip into the surfaces
         // Draw the path with a small upward bias so that it does not clip into the surfaces
-        Vector3 bias = 0.05f * Vector3::UP;
-        for (unsigned i = 0; i < currentPath_.Size() - 1; ++i)
-            debug->AddLine(currentPath_[i] + bias, currentPath_[i + 1] + bias, Color::WHITE);
+        Vector3 bias(0.0f, 0.05f, 0.0f);
+        debug->AddLine(jackNode_->GetPosition() + bias, currentPath_[0] + bias, Color(1.0f, 1.0f, 1.0f));
+
+        if (currentPath_.Size() > 1)
+        {
+            for (unsigned i = 0; i < currentPath_.Size() - 1; ++i)
+                debug->AddLine(currentPath_[i] + bias, currentPath_[i + 1] + bias, Color(1.0f, 1.0f, 1.0f));
+        }
     }
     }
 }
 }

+ 6 - 10
Source/Samples/15_Navigation/Navigation.h

@@ -39,6 +39,8 @@ class Scene;
 ///     - Performing path queries to the navigation mesh
 ///     - Performing path queries to the navigation mesh
 ///     - Rebuilding the navigation mesh partially when adding or removing objects
 ///     - Rebuilding the navigation mesh partially when adding or removing objects
 ///     - Visualizing custom debug geometry
 ///     - Visualizing custom debug geometry
+///     - Raycasting drawable components
+///     - Making a node follow the Detour path
 class Navigation : public Sample
 class Navigation : public Sample
 {
 {
     OBJECT(Navigation);
     OBJECT(Navigation);
@@ -69,10 +71,8 @@ private:
     Node* CreateMushroom(const Vector3& pos);
     Node* CreateMushroom(const Vector3& pos);
     /// Utility function to raycast to the cursor position. Return true if hit
     /// Utility function to raycast to the cursor position. Return true if hit
     bool Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawable);
     bool Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawable);
-    /// Recalculate path. Requires both start and endpoint defined.
-    void RecalculatePath();
-    /// Make Jack follow the Detour path
-    void followPath(float timeStep);
+    /// Make Jack follow the Detour path.
+    void FollowPath(float timeStep);
     /// Handle the logic update event.
     /// Handle the logic update event.
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
     /// Handle the post-render update event.
     /// Handle the post-render update event.
@@ -84,14 +84,10 @@ private:
     SharedPtr<Node> cameraNode_;
     SharedPtr<Node> cameraNode_;
     /// Last calculated path.
     /// Last calculated path.
     PODVector<Vector3> currentPath_;
     PODVector<Vector3> currentPath_;
-    /// Path start position.
-    Vector3 startPos_;
     /// Path end position.
     /// Path end position.
     Vector3 endPos_;
     Vector3 endPos_;
-    /// Start position defined flag.
-    bool startPosDefined_;
-    /// End position defined flag.
-    bool endPosDefined_;
+    /// Jack scene node.
+    SharedPtr<Node> jackNode_;
     /// Camera yaw angle.
     /// Camera yaw angle.
     float yaw_;
     float yaw_;
     /// Camera pitch angle.
     /// Camera pitch angle.