Browse Source

Merge remote-tracking branch 'remotes/mike3d/master'

Lasse Öörni 11 years ago
parent
commit
0cbf491a48
60 changed files with 1655 additions and 939 deletions
  1. 16 6
      Bin/Data/LuaScripts/01_HelloWorld.lua
  2. 11 7
      Bin/Data/LuaScripts/02_HelloGUI.lua
  3. 24 14
      Bin/Data/LuaScripts/03_Sprites.lua
  4. 2 6
      Bin/Data/LuaScripts/04_StaticScene.lua
  5. 2 7
      Bin/Data/LuaScripts/05_AnimatingScene.lua
  6. 28 19
      Bin/Data/LuaScripts/06_SkeletalAnimation.lua
  7. 18 8
      Bin/Data/LuaScripts/07_Billboards.lua
  8. 26 9
      Bin/Data/LuaScripts/08_Decals.lua
  9. 49 7
      Bin/Data/LuaScripts/09_MultipleViewports.lua
  10. 5 9
      Bin/Data/LuaScripts/10_RenderToTexture.lua
  11. 28 11
      Bin/Data/LuaScripts/11_Physics.lua
  12. 26 9
      Bin/Data/LuaScripts/12_PhysicsStressTest.lua
  13. 25 8
      Bin/Data/LuaScripts/13_Ragdolls.lua
  14. 25 14
      Bin/Data/LuaScripts/14_SoundEffects.lua
  15. 92 24
      Bin/Data/LuaScripts/15_Navigation.lua
  16. 29 16
      Bin/Data/LuaScripts/16_Chat.lua
  17. 10 4
      Bin/Data/LuaScripts/17_SceneReplication.lua
  18. 98 44
      Bin/Data/LuaScripts/18_CharacterDemo.lua
  19. 38 23
      Bin/Data/LuaScripts/19_VehicleDemo.lua
  20. 30 12
      Bin/Data/LuaScripts/20_HugeObjectCount.lua
  21. 0 4
      Bin/Data/LuaScripts/23_Water.lua
  22. 28 5
      Bin/Data/LuaScripts/24_Urho2DSprite.lua
  23. 15 2
      Bin/Data/LuaScripts/25_Urho2DParticle.lua
  24. 27 14
      Bin/Data/LuaScripts/26_ConsoleInput.lua
  25. 29 6
      Bin/Data/LuaScripts/27_Urho2DPhysics.lua
  26. 29 5
      Bin/Data/LuaScripts/28_Urho2DPhysicsRope.lua
  27. 0 5
      Bin/Data/LuaScripts/30_LightAnimation.lua
  28. 5 10
      Bin/Data/LuaScripts/31_MaterialAnimation.lua
  29. 95 13
      Bin/Data/LuaScripts/Utilities/Sample.lua
  30. 17 139
      Bin/Data/LuaScripts/Utilities/Touch.lua
  31. 8 1
      Bin/Data/Scripts/01_HelloWorld.as
  32. 9 1
      Bin/Data/Scripts/02_HelloGUI.as
  33. 8 0
      Bin/Data/Scripts/03_Sprites.as
  34. 5 6
      Bin/Data/Scripts/04_StaticScene.as
  35. 3 5
      Bin/Data/Scripts/05_AnimatingScene.as
  36. 22 15
      Bin/Data/Scripts/06_SkeletalAnimation.as
  37. 17 10
      Bin/Data/Scripts/07_Billboards.as
  38. 29 14
      Bin/Data/Scripts/08_Decals.as
  39. 50 10
      Bin/Data/Scripts/09_MultipleViewports.as
  40. 5 6
      Bin/Data/Scripts/10_RenderToTexture.as
  41. 27 12
      Bin/Data/Scripts/11_Physics.as
  42. 25 10
      Bin/Data/Scripts/12_PhysicsStressTest.as
  43. 43 28
      Bin/Data/Scripts/13_Ragdolls.as
  44. 22 14
      Bin/Data/Scripts/14_SoundEffects.as
  45. 79 13
      Bin/Data/Scripts/15_Navigation.as
  46. 28 17
      Bin/Data/Scripts/16_Chat.as
  47. 44 40
      Bin/Data/Scripts/17_SceneReplication.as
  48. 108 49
      Bin/Data/Scripts/18_CharacterDemo.as
  49. 31 9
      Bin/Data/Scripts/19_VehicleDemo.as
  50. 23 6
      Bin/Data/Scripts/20_HugeObjectCount.as
  51. 3 4
      Bin/Data/Scripts/23_Water.as
  52. 28 7
      Bin/Data/Scripts/24_Urho2DSprite.as
  53. 13 2
      Bin/Data/Scripts/25_Urho2DParticle.as
  54. 11 0
      Bin/Data/Scripts/26_ConsoleInput.as
  55. 28 8
      Bin/Data/Scripts/27_Urho2DPhysics.as
  56. 26 6
      Bin/Data/Scripts/28_Urho2DPhysicsRope.as
  57. 6 8
      Bin/Data/Scripts/30_LightAnimation.as
  58. 6 8
      Bin/Data/Scripts/31_MaterialAnimation.as
  59. 100 7
      Bin/Data/Scripts/Utilities/Sample.as
  60. 21 163
      Bin/Data/Scripts/Utilities/Touch.as

+ 16 - 6
Bin/Data/LuaScripts/01_HelloWorld.lua

@@ -9,10 +9,10 @@ require "LuaScripts/Utilities/Sample"
 function Start()
     -- Execute the common startup for samples
     SampleStart()
-    
+
     -- Create "Hello World" Text
     CreateText()
-    
+
     -- Finally, hook-up this HelloWorld instance to handle update events
     SubscribeToEvents()
 end
@@ -27,11 +27,11 @@ function CreateText()
     -- Set font and text color
     helloText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 30)
     helloText.color = Color(0.0, 1.0, 0.0)
-    
+
     -- Align Text center-screen
-    helloText.horizontalAlignment = HA_CENTER;
-    helloText.verticalAlignment = VA_CENTER;
-    
+    helloText.horizontalAlignment = HA_CENTER
+    helloText.verticalAlignment = VA_CENTER
+
     -- Add Text instance to the UI root element
     ui.root:AddChild(helloText)
 end
@@ -44,3 +44,13 @@ end
 function HandleUpdate(eventType, eventData)
     -- Do nothing for now, could be extended to eg. animate the display
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 11 - 7
Bin/Data/LuaScripts/02_HelloGUI.lua

@@ -11,12 +11,6 @@
 require "LuaScripts/Utilities/Sample"
 
 local window = nil
-
-local cache = GetCache()
-local engine = GetEngine()
-local input = GetInput()
-local ui = GetUI()
-
 local dragBeginPosition = IntVector2(0, 0)
 
 function Start()
@@ -110,7 +104,7 @@ function InitWindow()
 
     -- Subscribe to buttonClose release (following a 'press') events
     SubscribeToEvent(buttonClose, "Released", "HandleClosePressed")
-    
+
     -- Subscribe also to all UI mouse clicks just to see where we have clicked
     SubscribeToEvent("UIMouseClick", "HandleControlClicked")
 end
@@ -176,3 +170,13 @@ function HandleControlClicked(eventType, eventData)
     -- Update the Window's title text
     windowTitle.text = "Hello " .. name .. "!"
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 24 - 14
Bin/Data/LuaScripts/03_Sprites.lua

@@ -15,47 +15,47 @@ local VAR_VELOCITY = ShortStringHash("Velocity")
 function Start()
     -- Execute the common startup for samples
     SampleStart()
-    
+
     -- Create the sprites to the user interface
-    CreateSprites();
+    CreateSprites()
 
     -- Hook up to the frame update events
-    SubscribeToEvents();
+    SubscribeToEvents()
 end
 
 function CreateSprites()
     local decalTex = cache:GetResource("Texture2D", "Textures/UrhoDecal.dds")
-    
+
     local width = graphics.width
     local height = graphics.height
-    
+
     for i = 1, numSprites do
         -- Create a new sprite, set it to use the texture
         local sprite = Sprite:new()
         sprite.texture = decalTex
-        sprite:SetFullImageRect()       
-        
+        sprite:SetFullImageRect()
+
         -- The UI root element is as big as the rendering window, set random position within it
         sprite.position = Vector2(Random(width), Random(height))
-        
+
         -- Set sprite size & hotspot in its center
         sprite:SetSize(128, 128)
         sprite.hotSpot = IntVector2(64, 64)
-        
+
         -- Set random rotation in degrees and random scale
         sprite.rotation = Random(360.0)
         sprite.scale = Vector2(1.0, 1.0) * (Random(1.0) + 0.5)
-        
+
         -- Set random color and additive blending mode
         sprite:SetColor(Color(Random(0.5) + 0.5, Random(0.5) + 0.5, Random(0.5) + 0.5, 1.0))
         sprite.blendMode = BLEND_ADD
 
         -- Add as a child of the root UI element
         ui.root:AddChild(sprite)
-        
+
         -- Store sprite's velocity as a custom variable
         sprite:SetVar(VAR_VELOCITY, Variant(Vector2(Random(200.0) - 100.0, Random(200.0) - 100.0)))
-        
+
         table.insert(sprites, sprite)
     end
 end
@@ -72,10 +72,10 @@ function MoveSprites(timeStep)
     for i = 1, numSprites do
         local sprite = sprites[i]
         sprite.rotation = sprite.rotation + timeStep * 30
-        
+
         local newPos = sprite.position
         newPos = newPos + sprite:GetVar(VAR_VELOCITY):GetVector2() * timeStep
-        
+
         if newPos.x >= width then
             newPos.x = newPos.x - width
         elseif newPos.x < 0 then
@@ -95,3 +95,13 @@ function HandleUpdate(eventType, eventData)
 
     MoveSprites(timeStep)
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 2 - 6
Bin/Data/LuaScripts/04_StaticScene.lua

@@ -3,14 +3,10 @@
 --     - Creating a 3D scene with static content
 --     - Displaying the scene using the Renderer subsystem
 --     - Handling keyboard and mouse input to move a freelook camera
+--     - Applying a material shader to animate vegetation (simulate wind)
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -68,7 +64,7 @@ function CreateScene()
         mushroomNode:SetScale(0.5 + Random(2.0))
         local mushroomObject = mushroomNode:CreateComponent("StaticModel")
         mushroomObject.model = cache:GetResource("Model", "Models/Mushroom.mdl")
-        mushroomObject.material = cache:GetResource("Material", "Materials/Mushroom.xml")
+        mushroomObject.material = cache:GetResource("Material", "Materials/MushroomWind.xml") -- Apply Vegetation Windy shader
     end
 
     -- Create a scene node for the camera, which we will move around

+ 2 - 7
Bin/Data/LuaScripts/05_AnimatingScene.lua

@@ -6,11 +6,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -59,9 +54,9 @@ function CreateScene()
         boxObject.material = cache:GetResource("Material", "Materials/Stone.xml")
 
         -- Add the Rotator script object which will rotate the scene node each frame, when the scene sends its update event.
-        -- This requires the C++ component LuaScriptInstance in the scene node, which acts as a container. We need to tell the 
+        -- This requires the C++ component LuaScriptInstance in the scene node, which acts as a container. We need to tell the
         -- class name to instantiate the object
-        
+
 
         local object = boxNode:CreateScriptObject("Rotator")
         object.rotationSpeed = {10.0, 20.0, 30.0}

+ 28 - 19
Bin/Data/LuaScripts/06_SkeletalAnimation.lua

@@ -8,22 +8,16 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
 
     -- Create the scene content
     CreateScene()
-    
+
     -- Create the UI content
     CreateInstructions()
-    
+
     -- Setup the viewport for displaying the scene
     SetupViewport()
 
@@ -33,7 +27,7 @@ end
 
 function CreateScene()
     scene_ = Scene()
-    
+
     -- Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     -- Also create a DebugRenderer component so that we can draw debug geometry
     scene_:CreateComponent("Octree")
@@ -45,7 +39,7 @@ function CreateScene()
     local planeObject = planeNode:CreateComponent("StaticModel")
     planeObject.model = cache:GetResource("Model", "Models/Plane.mdl")
     planeObject.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
-    
+
     -- Create a Zone component for ambient lighting & fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -54,7 +48,7 @@ function CreateScene()
     zone.fogColor = Color(0.5, 0.5, 0.7)
     zone.fogStart = 100.0
     zone.fogEnd = 300.0
-    
+
     -- Create a directional light to the world. Enable cascaded shadows on it
     local lightNode = scene_:CreateChild("DirectionalLight")
     lightNode.direction = Vector3(0.6, -1.0, 0.8)
@@ -64,13 +58,13 @@ function CreateScene()
     light.shadowBias = BiasParameters(0.00025, 0.5)
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
-    
+
     -- Create animated models
     local uint NUM_MODELS = 100
     local MODEL_MOVE_SPEED = 2.0
     local MODEL_ROTATE_SPEED = 100.0
     local bounds = BoundingBox(Vector3(-47.0, 0.0, -47.0), Vector3(47.0, 0.0, 47.0))
-    
+
     for i = 1, NUM_MODELS do
         local modelNode = scene_:CreateChild("Jack")
         modelNode.position = Vector3(Random(90.0) - 45.0, 0.0, Random(90.0) - 45.0)
@@ -79,7 +73,7 @@ function CreateScene()
         modelObject.model = cache:GetResource("Model", "Models/Jack.mdl")
         modelObject.material = cache:GetResource("Material", "Materials/Jack.xml")
         modelObject.castShadows = true
-        
+
         -- Create an AnimationState for a walk animation. Its time position will need to be manually updated to advance the
         -- animation, The alternative would be to use an AnimationController component which updates the animation automatically,
         -- but we need to update the model's position manually in any case
@@ -89,7 +83,7 @@ function CreateScene()
         state.weight = 1.0
         state.looped = true
 
-        -- Create our Mover script object that will move & animate the model during each frame's update. 
+        -- Create our Mover script object that will move & animate the model during each frame's update.
 
         local object = modelNode:CreateScriptObject("Mover")
         object:SetParameters(MODEL_MOVE_SPEED, MODEL_ROTATE_SPEED, bounds)
@@ -99,7 +93,7 @@ function CreateScene()
     cameraNode = scene_:CreateChild("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     camera.farClip = 300.0
-    
+
     -- Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0, 5.0, 0.0)
 end
@@ -128,7 +122,7 @@ end
 function SubscribeToEvents()
     -- Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate")
-    
+
     -- Subscribe HandlePostRenderUpdate() function for processing the post-render update event, sent after Renderer subsystem is
     -- done with defining the draw calls for the viewports (but before actually executing them.) We will request debug geometry
     -- rendering during that event
@@ -209,14 +203,14 @@ end
 function Mover:Update(timeStep)
     local node = self:GetNode()
     node:Translate(Vector3(0.0, 0.0, 1.0) * self.moveSpeed * timeStep)
-    
+
     -- If in risk of going outside the plane, rotate the model right
     local pos = node.position
     local bounds = self.bounds
     if pos.x < bounds.min.x or pos.x > bounds.max.x or pos.z < bounds.min.z or pos.z > bounds.max.z then
         node:Yaw(self.rotationSpeed * timeStep)
     end
-    
+
     -- Get the model's first (only) animation state and advance its time
     local model = node:GetComponent("AnimatedModel")
     local state = model:GetAnimationState(0)
@@ -224,3 +218,18 @@ function Mover:Update(timeStep)
         state:AddTime(timeStep)
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>"..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />"..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>"..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">"..
+        "        <element type=\"Text\">"..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />"..
+        "            <attribute name=\"Text\" value=\"SPACE\" />"..
+        "        </element>"..
+        "    </add>"..
+        "</patch>"
+end

+ 18 - 8
Bin/Data/LuaScripts/07_Billboards.lua

@@ -6,13 +6,8 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
 local lightNodes = {}
 local billboardNodes = {}
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
 
 function Start()
     -- Execute the common startup for samples
@@ -20,7 +15,7 @@ function Start()
 
     -- Create the scene content
     CreateScene()
-    
+
     -- Create the UI content
     CreateInstructions()
 
@@ -111,7 +106,7 @@ function CreateScene()
 
         -- After modifying the billboards, they need to be "commited" so that the BillboardSet updates its internals
         billboardObject:Commit()
-        
+
         table.insert(billboardNodes, smokeNode)
     end
 
@@ -148,7 +143,7 @@ function CreateScene()
         -- The spot lights will not have anything near them, so move the near plane of the shadow camera farther
         -- for better shadow depth resolution
         light.shadowNearFarRatio = 0.01
-        
+
         table.insert(lightNodes, lightNode)
     end
 
@@ -270,3 +265,18 @@ function HandlePostRenderUpdate(eventType, eventData)
         renderer:DrawDebugGeometry(true)
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 26 - 9
Bin/Data/LuaScripts/08_Decals.lua

@@ -7,12 +7,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -93,7 +87,7 @@ function CreateScene()
             boxObject.occluder = true
         end
     end
-    
+
     -- Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_:CreateChild("Camera")
     local camera = cameraNode:CreateComponent("Camera")
@@ -112,7 +106,7 @@ function CreateUI()
     ui.cursor = cursor
     -- Set starting position of the cursor at the rendering window center
     cursor:SetPosition(graphics.width / 2, graphics.height / 2)
-    
+
     -- Construct new Text object, set string to display and font to use
     local instructionText = ui.root:CreateChild("Text")
     instructionText.text =
@@ -153,7 +147,7 @@ function MoveCamera(timeStep)
     if ui.focusElement ~= nil then
         return
     end
-    
+
     -- Movement speed as world units per second
     local MOVE_SPEED = 20.0
     -- Mouse sensitivity as degrees per pixel
@@ -253,3 +247,26 @@ function HandlePostRenderUpdate(eventType, eventData)
         renderer:DrawDebugGeometry(false)
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Paint</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 49 - 7
Bin/Data/LuaScripts/09_MultipleViewports.lua

@@ -5,12 +5,7 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
 local rearCameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
 
 function Start()
     -- Execute the common startup for samples
@@ -36,7 +31,7 @@ function CreateScene()
     -- Also create a DebugRenderer component so that we can draw debug geometry
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("DebugRenderer")
-    
+
     -- Create scene node & StaticModel component for showing a static plane
     local planeNode = scene_:CreateChild("Plane")
     planeNode.scale = Vector3(100.0, 1.0, 100.0)
@@ -136,7 +131,7 @@ function SetupViewports()
     -- Set up the front camera viewport
     local viewport = Viewport:new(scene_, cameraNode:GetComponent("Camera"))
     renderer:SetViewport(0, viewport)
-    
+
     -- Clone the default render path so that we do not interfere with the other viewport, then add
     -- bloom and FXAA post process effects to the front viewport. Render path commands can be tagged
     -- for example with the effect name to allow easy toggling on and off. We start with the effects
@@ -229,3 +224,50 @@ function HandlePostRenderUpdate(eventType, eventData)
         renderer:DrawDebugGeometry(false)
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element\">" ..
+        "        <element type=\"Button\">" ..
+        "            <attribute name=\"Name\" value=\"Button3\" />" ..
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" ..
+        "            <attribute name=\"Size\" value=\"96 96\" />" ..
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" ..
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" ..
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" ..
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" ..
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" ..
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"Label\" />" ..
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" ..
+        "                <attribute name=\"Text\" value=\"FXAA\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"F\" />" ..
+        "            </element>" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Bloom</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"B\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 5 - 9
Bin/Data/LuaScripts/10_RenderToTexture.lua

@@ -5,12 +5,8 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
 local rttScene_ = nil
-local cameraNode = nil
 local rttCameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
 
 function Start()
     -- Execute the common startup for samples
@@ -67,7 +63,7 @@ function CreateScene()
     rttCameraNode = rttScene_:CreateChild("Camera")
     local camera = rttCameraNode:CreateComponent("Camera")
     camera.farClip = 100.0
-    
+
     -- Create a point light to the camera scene node
     local light = rttCameraNode:CreateComponent("Light")
     light.lightType = LIGHT_POINT
@@ -94,7 +90,7 @@ function CreateScene()
     light.lightType = LIGHT_DIRECTIONAL
     light.color = Color(0.2, 0.2, 0.2)
     light.specularIntensity = 1.0
-    
+
     -- Create a "floor" consisting of several tiles
     for y = -5, 5 do
         for x = -5, 5 do
@@ -127,7 +123,7 @@ function CreateScene()
     local renderTexture = Texture2D:new()
     renderTexture:SetSize(1024, 768, Graphics:GetRGBFormat(), TEXTURE_RENDERTARGET)
     renderTexture.filterMode = FILTER_BILINEAR
-    
+
     -- Create a new material from scratch, use the diffuse unlit technique, assign the render texture
     -- as its diffuse texture, then assign the material to the screen plane object
     local renderMaterial = Material:new()
@@ -186,7 +182,7 @@ function MoveCamera(timeStep)
     yaw = yaw + MOUSE_SENSITIVITY * mouseMove.x
     pitch = pitch + MOUSE_SENSITIVITY * mouseMove.y
     pitch = Clamp(pitch, -90.0, 90.0)
-    
+
     -- Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
     cameraNode.rotation = Quaternion(pitch, yaw, 0.0)
     -- Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
@@ -213,7 +209,7 @@ end
 function HandleUpdate(eventType, eventData)
     -- Take the frame time step, which is stored as a float
     local timeStep = eventData:GetFloat("TimeStep")
-    
+
     -- Move the camera, scale movement with time step
     MoveCamera(timeStep)
 end

+ 28 - 11
Bin/Data/LuaScripts/11_Physics.lua

@@ -7,12 +7,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -40,7 +34,7 @@ function CreateScene()
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("PhysicsWorld")
     scene_:CreateComponent("DebugRenderer")
-    
+
     -- Create a Zone component for ambient lighting & fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -59,7 +53,7 @@ function CreateScene()
     light.shadowBias = BiasParameters(0.00025, 0.5)
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
-    
+
     -- Create skybox. The Skybox component is used like StaticModel, but it will be always located at the camera, giving the
     -- illusion of the box planes being far away. Use just the ordinary Box model and a suitable material, whose shader will
     -- generate the necessary 3D texture coordinates for cube mapping
@@ -85,7 +79,7 @@ function CreateScene()
     -- Set a box shape of size 1 x 1 x 1 for collision. The shape will be scaled with the scene node scale, so the
     -- rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
     shape:SetBox(Vector3(1.0, 1.0, 1.0))
-    
+
     -- Create a pyramid of movable physics objects
     for y = 0, 7 do
         for x = -y, y do
@@ -182,12 +176,12 @@ function MoveCamera(timeStep)
     if input:GetKeyDown(KEY_D) then
         cameraNode:Translate(Vector3(1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
     end
-    
+
     -- "Shoot" a physics object with left mousebutton
     if input:GetMouseButtonPress(MOUSEB_LEFT) then
         SpawnObject()
     end
-    
+
     -- Check for loading/saving the scene. Save the scene to the file Data/Scenes/Physics.xml relative to the executable
     -- directory
     if input:GetKeyPress(KEY_F5) then
@@ -244,3 +238,26 @@ function HandlePostRenderUpdate(eventType, eventData)
         scene_:GetComponent("PhysicsWorld"):DrawDebugGeometry(true)
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 26 - 9
Bin/Data/LuaScripts/12_PhysicsStressTest.lua

@@ -7,12 +7,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -40,7 +34,7 @@ function CreateScene()
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("PhysicsWorld")
     scene_:CreateComponent("DebugRenderer")
-    
+
     -- Create a Zone component for ambient lighting & fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -49,7 +43,7 @@ function CreateScene()
     zone.fogColor = Color(0.5, 0.5, 0.7)
     zone.fogStart = 100.0
     zone.fogEnd = 300.0
-    
+
     -- Create a directional light to the world. Enable cascaded shadows on it
     local lightNode = scene_:CreateChild("DirectionalLight")
     lightNode.direction = Vector3(0.6, -1.0, 0.8)
@@ -59,7 +53,7 @@ function CreateScene()
     light.shadowBias = BiasParameters(0.00025, 0.5)
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
-    
+
     if true then
         -- Create a floor object, 500 x 500 world units. Adjust position so that the ground is at zero Y
         local floorNode = scene_:CreateChild("Floor")
@@ -258,3 +252,26 @@ function HandlePostRenderUpdate(eventType, eventData)
         scene_:GetComponent("PhysicsWorld"):DrawDebugGeometry(true)
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 25 - 8
Bin/Data/LuaScripts/13_Ragdolls.lua

@@ -6,12 +6,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -89,7 +83,7 @@ function CreateScene()
             modelObject.castShadows = true
             -- Set the model to also update when invisible to avoid staying invisible when the model should come into
             -- view, but does not as the bounding box is not updated
-            modelObject.updateInvisible = true;
+            modelObject.updateInvisible = true
 
             -- Create a rigid body and a collision shape. These will act as a trigger for transforming the
             -- model into a ragdoll when hit by a moving object
@@ -124,7 +118,7 @@ function CreateInstructions()
         "Use WASD keys and mouse to move\n"..
         "LMB to spawn physics objects\n"..
         "F5 to save scene, F7 to load\n"..
-        "Space to toggle physics debug geometry");
+        "Space to toggle physics debug geometry")
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     -- The text has multiple rows. Center them in relation to each other
     instructionText.textAlignment = HA_CENTER
@@ -371,3 +365,26 @@ function CreateRagdoll:CreateRagdollConstraint(boneName, parentName, type, axis,
     constraint.highLimit = highLimit
     constraint.lowLimit = lowLimit
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 25 - 14
Bin/Data/LuaScripts/14_SoundEffects.lua

@@ -5,8 +5,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-
 local soundNames = {
     "Fist",
     "Explosion",
@@ -18,11 +16,11 @@ local soundResourceNames = {
     "Sounds/BigExplosion.wav",
     "Sounds/Powerup.wav"
     }
-    
+
 function Start()
     -- Execute the common startup for samples
     SampleStart()
-    
+
     -- Enable OS cursor
     input.mouseVisible = true
 
@@ -37,7 +35,7 @@ function CreateUI()
     local uiStyle = cache:GetResource("XMLFile", "UI/DefaultStyle.xml")
     -- Set style to the UI root so that elements will inherit it
     ui.root.defaultStyle = uiStyle
-    
+
     -- Create buttons for playing back sounds
     for i, v in ipairs(soundNames) do
         local button = CreateButton((i - 1) * 140 + 20, 20, 120, 40, v)
@@ -45,11 +43,11 @@ function CreateUI()
         button:SetVar(ShortStringHash("SoundResource"), Variant(soundResourceNames[i]))
         SubscribeToEvent(button, "Pressed", "HandlePlaySound")
     end
-    
+
     -- Create buttons for playing/stopping music
     local button = CreateButton(20, 80, 120, 40, "Play Music")
     SubscribeToEvent(button, "Released", "HandlePlayMusic")
-    
+
     button = CreateButton(160, 80, 120, 40, "Stop Music")
     SubscribeToEvent(button, "Released", "HandleStopMusic")
 
@@ -57,7 +55,7 @@ function CreateUI()
     local slider = CreateSlider(20, 140, 200, 20, "Sound Volume")
     slider.value = audio:GetMasterGain(SOUND_EFFECT)
     SubscribeToEvent(slider, "SliderChanged", "HandleSoundVolume")
-    
+
     slider = CreateSlider(20, 200, 200, 20, "Music Volume")
     slider.value = audio:GetMasterGain(SOUND_MUSIC)
     SubscribeToEvent(slider, "SliderChanged", "HandleMusicVolume")
@@ -65,18 +63,18 @@ end
 
 function CreateButton(x, y, xSize, ySize, text)
     local font = cache:GetResource("Font", "Fonts/Anonymous Pro.ttf")
-    
+
     -- Create the button and center the text onto it
     local button = ui.root:CreateChild("Button")
     button:SetStyleAuto()
     button:SetPosition(x, y)
     button:SetSize(xSize, ySize)
-    
+
     local buttonText = button:CreateChild("Text")
     buttonText:SetAlignment(HA_CENTER, VA_CENTER)
     buttonText:SetFont(font, 12)
     buttonText:SetText(text)
-    
+
     return button
 end
 
@@ -88,14 +86,14 @@ function CreateSlider(x, y, xSize, ySize, text)
     sliderText:SetPosition(x, y)
     sliderText:SetFont(font, 12)
     sliderText:SetText(text)
-    
+
     local slider = ui.root:CreateChild("Slider")
     slider:SetStyleAuto()
     slider:SetPosition(x, y + 20)
     slider:SetSize(xSize, ySize)
     -- Use 0-1 range for controlling sound/music master volume
     slider.range = 1.0
-    
+
     return slider
 end
 
@@ -130,7 +128,7 @@ function HandlePlayMusic(eventType, eventData)
     local music = cache:GetResource("Sound", "Music/Ninja Gods.ogg")
     -- Set the song to loop
     music.looped = true
-    
+
     -- Create a scene node and a sound source for the music
     local musicNode = scene_:CreateChild("Music")
     local musicSource = musicNode:CreateComponent("SoundSource")
@@ -153,3 +151,16 @@ function HandleMusicVolume(eventType, eventData)
     local newVolume = eventData:GetFloat("Value")
     audio:SetMasterGain(SOUND_MUSIC, newVolume)
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 92 - 24
Bin/Data/LuaScripts/15_Navigation.lua

@@ -9,13 +9,8 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
 local endPos = nil
 local currentPath = {}
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
 
 function Start()
     -- Execute the common startup for samples
@@ -23,10 +18,10 @@ function Start()
 
     -- Create the scene content
     CreateScene()
-    
+
     -- Create the UI content
     CreateUI()
-    
+
     -- Setup the viewport for displaying the scene
     SetupViewport()
 
@@ -40,14 +35,14 @@ function CreateScene()
     -- Also create a DebugRenderer component so that we can draw debug geometry
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("DebugRenderer")
-    
+
     -- Create scene node & StaticModel component for showing a static plane
     local planeNode = scene_:CreateChild("Plane")
     planeNode.scale = Vector3(100.0, 1.0, 100.0)
     local planeObject = planeNode:CreateComponent("StaticModel")
     planeObject.model = cache:GetResource("Model", "Models/Plane.mdl")
     planeObject.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
-    
+
     -- Create a Zone component for ambient lighting & fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -72,7 +67,7 @@ function CreateScene()
     for i = 1, NUM_MUSHROOMS do
         CreateMushroom(Vector3(Random(90.0) - 45.0, 0.0, Random(90.0) - 45.0))
     end
-    
+
     -- Create randomly sized boxes. If boxes are big enough, make them occluders. Occluders will be software rasterized before
     -- rendering to a low-resolution depth-only buffer to test the objects in the view frustum for visibility
     local NUM_BOXES = 20
@@ -110,12 +105,12 @@ function CreateScene()
     -- physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example)
     -- it will use renderable geometry instead
     navMesh:Build()
-    
+
     -- Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_:CreateChild("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     camera.farClip = 300.0
-    
+
     -- Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0, 5.0, 0.0)
 end
@@ -129,7 +124,7 @@ function CreateUI()
     ui.cursor = cursor
     -- Set starting position of the cursor at the rendering window center
     cursor:SetPosition(graphics.width / 2, graphics.height / 2)
-    
+
     -- Construct new Text object, set string to display and font to use
     local instructionText = ui.root:CreateChild("Text")
     instructionText.text = "Use WASD keys to move, RMB to rotate view\n"..
@@ -169,12 +164,12 @@ function MoveCamera(timeStep)
     if ui.focusElement ~= nil then
         return
     end
-    
+
     -- Movement speed as world units per second
     local MOVE_SPEED = 20.0
     -- Mouse sensitivity as degrees per pixel
     local MOUSE_SENSITIVITY = 0.1
-    
+
     -- 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
     if not ui.cursor.visible then
@@ -224,11 +219,11 @@ function SetPathPoint()
             -- Teleport
             currentPath = {}
             jackNode:LookAt(Vector3(pathPos.x, jackNode.position.y, pathPos.z), Vector3(0.0, 1.0, 0.0))
-            jackNode.position = pathPos;
+            jackNode.position = pathPos
         else
             -- Calculate path from Jack's current position to the end point
-            endPos = pathPos;
-            currentPath = navMesh:FindPath(jackNode.position, endPos);
+            endPos = pathPos
+            currentPath = navMesh:FindPath(jackNode.position, endPos)
         end
     end
 end
@@ -240,7 +235,7 @@ function AddOrRemoveObject()
         -- The part of the navigation mesh we must update, which is the world bounding box of the associated
         -- drawable component
         local updateBox = nil
-        
+
         local hitNode = hitDrawable:GetNode()
         if hitNode.name == "Mushroom" then
             updateBox = hitDrawable.worldBoundingBox
@@ -250,12 +245,12 @@ function AddOrRemoveObject()
             local newObject = newNode:GetComponent("StaticModel")
             updateBox = newObject.worldBoundingBox
         end
-        
+
         -- Rebuild part of the navigation mesh, then recalculate path if applicable
         local navMesh = scene_:GetComponent("NavigationMesh")
         navMesh:Build(updateBox)
         if table.maxn(currentPath) > 0 then
-            currentPath = navMesh:FindPath(jackNode.position, endPos);
+            currentPath = navMesh:FindPath(jackNode.position, endPos)
         end
     end
 end
@@ -293,7 +288,7 @@ function Raycast(maxDistance)
         hitDrawable = result.drawable
         return true, hitPos, hitDrawable
     end
-    
+
     return false, nil, nil
 end
 
@@ -335,13 +330,13 @@ function HandlePostRenderUpdate(eventType, eventData)
         local navMesh = scene_:GetComponent("NavigationMesh")
         navMesh:DrawDebugGeometry(true)
     end
-    
+
     -- Visualize the start and end points and the last calculated path
     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))
-        
+
         -- 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)
         debug:AddLine(jackNode.position + bias, currentPath[1] + bias, Color(1.0, 1.0, 1.0))
@@ -353,3 +348,76 @@ function HandlePostRenderUpdate(eventType, eventData)
         end
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element\">" ..
+        "        <element type=\"Button\">" ..
+        "            <attribute name=\"Name\" value=\"Button3\" />" ..
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" ..
+        "            <attribute name=\"Size\" value=\"96 96\" />" ..
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" ..
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" ..
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" ..
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" ..
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" ..
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"Label\" />" ..
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" ..
+        "                <attribute name=\"Text\" value=\"Teleport\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"LSHIFT\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "            </element>" ..
+        "        </element>" ..
+        "        <element type=\"Button\">" ..
+        "            <attribute name=\"Name\" value=\"Button4\" />" ..
+        "            <attribute name=\"Position\" value=\"-120 -12\" />" ..
+        "            <attribute name=\"Size\" value=\"96 96\" />" ..
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" ..
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" ..
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" ..
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" ..
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" ..
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"Label\" />" ..
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" ..
+        "                <attribute name=\"Text\" value=\"Obstacles\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"MIDDLE\" />" ..
+        "            </element>" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Set</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 29 - 16
Bin/Data/LuaScripts/16_Chat.lua

@@ -28,7 +28,7 @@ function Start()
 
     -- Create the user interface
     CreateUI()
-    
+
     -- Subscribe to UI and network events
     SubscribeToEvents()
 end
@@ -48,22 +48,22 @@ function CreateUI()
     buttonContainer:SetFixedSize(graphics.width, 20)
     buttonContainer:SetPosition(0, graphics.height - 20)
     buttonContainer.layoutMode = LM_HORIZONTAL
-    
+
     textEdit = buttonContainer:CreateChild("LineEdit")
     textEdit:SetStyleAuto()
-    
+
     sendButton = CreateButton("Send", 70)
     connectButton = CreateButton("Connect", 90)
     disconnectButton = CreateButton("Disconnect", 100)
     startServerButton = CreateButton("Start Server", 110)
 
     UpdateButtons()
-    
+
     local size = (graphics.height - 20) / chatHistoryText.rowHeight
     for i = 1, size do
         table.insert(chatHistory, "")
     end
-    
+
     -- No viewports or scene is defined. However, the default zone's fog color controls the fill color
     renderer.defaultZone.fogColor = Color(0.0, 0.0, 0.1)
 end
@@ -78,7 +78,7 @@ function SubscribeToEvents()
 
     -- Subscribe to log messages so that we can pipe them to the chat window
     SubscribeToEvent("LogMessage", "HandleLogMessage")
-    
+
     -- Subscribe to network events
     SubscribeToEvent("NetworkMessage", "HandleNetworkMessage")
     SubscribeToEvent("ServerConnected", "HandleConnectionStatus")
@@ -88,7 +88,7 @@ end
 
 function CreateButton(text, width)
     local font = cache:GetResource("Font", "Fonts/Anonymous Pro.ttf")
-    
+
     local button = buttonContainer:CreateChild("Button")
     button:SetStyleAuto()
     button:SetFixedWidth(width)
@@ -97,14 +97,14 @@ function CreateButton(text, width)
     buttonText:SetFont(font, 12)
     buttonText:SetAlignment(HA_CENTER, VA_CENTER)
     buttonText.text = text
-    
+
     return button
 end
 
 function ShowChatText(row)
     table.remove(chatHistory, 1)
     table.insert(chatHistory, row)
-    
+
     -- Concatenate all the rows in history
     local allRows = ""
     for i, r in ipairs(chatHistory) do
@@ -116,7 +116,7 @@ end
 function UpdateButtons()
     local serverConnection = network.serverConnection
     local serverRunning = network.serverRunning
-    
+
     -- Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     sendButton.visible = serverConnection ~= nil
     connectButton.visible = (serverConnection == nil) and (not serverRunning)
@@ -151,15 +151,15 @@ function HandleConnect(eventType, eventData)
     if address == "" then
         address = "localhost" -- Use localhost to connect if nothing else specified
     end
-        
+
     -- Empty the text edit after reading the address to connect to
     textEdit.text = ""
-    
+
     -- Connect to server, do not specify a client scene as we are not using scene replication, just messages.
     -- At connect time we could also send identity parameters (such as username) in a VariantMap, but in this
     -- case we skip it for simplicity
     network:Connect(address, CHAT_SERVER_PORT, nil)
-    
+
     UpdateButtons()
 end
 
@@ -174,13 +174,13 @@ function HandleDisconnect(eventType, eventData)
             network:StopServer()
         end
     end
-    
+
     UpdateButtons()
 end
 
 function HandleStartServer(eventType, eventData)
     network:StartServer(CHAT_SERVER_PORT)
-    
+
     UpdateButtons()
 end
 
@@ -205,4 +205,17 @@ end
 
 function HandleConnectionStatus(eventType, eventData)
     UpdateButtons()
-end
+end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 10 - 4
Bin/Data/LuaScripts/17_SceneReplication.lua

@@ -16,8 +16,6 @@ local CTRL_BACK = 2
 local CTRL_LEFT = 4
 local CTRL_RIGHT = 8
 
-local scene_ = nil
-local cameraNode = nil
 local instructionsText = nil
 local buttonContainer = nil
 local textEdit = nil
@@ -25,8 +23,6 @@ local connectButton = nil
 local disconnectButton = nil
 local startServerButton = nil
 local clients = {}
-local yaw = 0.0
-local pitch = 1.0
 local clientObjectID = 0
 
 function Start()
@@ -392,3 +388,13 @@ end
 function HandleClientObjectID(eventType, eventData)
     clientObjectID = eventData:GetUInt("ID")
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 98 - 44
Bin/Data/LuaScripts/18_CharacterDemo.lua

@@ -16,7 +16,7 @@ CTRL_FORWARD = 1
 CTRL_BACK = 2
 CTRL_LEFT = 4
 CTRL_RIGHT = 8
-CTRL_JUMP = 16
+local CTRL_JUMP = 16
 
 local MOVE_FORCE = 0.8
 local INAIR_MOVE_FORCE = 0.02
@@ -24,9 +24,9 @@ local BRAKE_FORCE = 0.2
 local JUMP_FORCE = 7.0
 local YAW_SENSITIVITY = 0.1
 local INAIR_THRESHOLD_TIME = 0.1
+firstPerson = false -- First person camera flag
 
-scene_ = nil
-characterNode = nil
+local characterNode = nil
 
 function Start()
     -- Execute the common startup for samples
@@ -41,12 +41,6 @@ function Start()
     -- Create the UI content
     CreateInstructions()
 
-    -- Activate mobile stuff only when appropriate
-    if GetPlatform() == "Android" or GetPlatform() == "iOS" then 
-        SetLogoVisible(false)
-        InitTouchInput()
-    end
-
     -- Subscribe to necessary events
     SubscribeToEvents()
 end
@@ -152,7 +146,7 @@ function CreateCharacter()
     characterNode:CreateComponent("AnimationController")
 
     -- Set the head bone for manual control
-    object.skeleton:GetBone("Bip01_Head").animated = false;
+    object.skeleton:GetBone("Bip01_Head").animated = false
 
     -- Create rigidbody, and set non-zero mass so that the body becomes dynamic
     local body = characterNode:CreateComponent("RigidBody")
@@ -198,10 +192,8 @@ function SubscribeToEvents()
     -- Subscribe to PostUpdate event for updating the camera position after physics simulation
     SubscribeToEvent("PostUpdate", "HandlePostUpdate")
 
-    -- Subscribe to mobile touch events
-    if touchEnabled == true then
-        SubscribeToTouchEvents()
-    end
+    -- Unsubscribe the SceneUpdate event from base class as the camera node is being controlled in HandlePostUpdate() in this sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 
 function HandleUpdate(eventType, eventData)
@@ -217,45 +209,60 @@ function HandleUpdate(eventType, eventData)
     -- Clear previous controls
     character.controls:Set(CTRL_FORWARD + CTRL_BACK + CTRL_LEFT + CTRL_RIGHT + CTRL_JUMP, false)
 
-    if touchEnabled == true then
-        -- Update controls using touch (mobile)
-        updateTouches()
-    else 
-        -- Update controls using keys (desktop)
-        if ui.focusElement == nil then
+    -- Update controls using touch utility
+    if touchEnabled then UpdateTouches(character.controls) end
+
+    -- Update controls using keys
+    if ui.focusElement == nil then
+        if not touchEnabled or not useGyroscope then
             if input:GetKeyDown(KEY_W) then character.controls:Set(CTRL_FORWARD, true) end
             if input:GetKeyDown(KEY_S) then character.controls:Set(CTRL_BACK, true) end
             if input:GetKeyDown(KEY_A) then character.controls:Set(CTRL_LEFT, true) end
             if input:GetKeyDown(KEY_D) then character.controls:Set(CTRL_RIGHT, true) end
-            if input:GetKeyDown(KEY_SPACE) then character.controls:Set(CTRL_JUMP, true) end
-
-            -- Add character yaw & pitch from the mouse motion
+        end
+        if input:GetKeyDown(KEY_SPACE) then character.controls:Set(CTRL_JUMP, true) end
+
+        -- Add character yaw & pitch from the mouse motion or touch input
+        if touchEnabled then
+            for i=0, input.numTouches - 1 do
+                local state = input:GetTouch(i)
+                if not state.touchedElement then -- Touch on empty space
+                    local camera = cameraNode:GetComponent("Camera")
+                    if not camera then return end
+
+                    character.controls.yaw = character.controls.yaw + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x
+                    character.controls.pitch = character.controls.pitch + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y
+                end
+            end
+        else
             character.controls.yaw = character.controls.yaw + input.mouseMoveX * YAW_SENSITIVITY
             character.controls.pitch = character.controls.pitch + input.mouseMoveY * YAW_SENSITIVITY
-            -- Limit pitch
-            character.controls.pitch = Clamp(character.controls.pitch, -80.0, 80.0)
+        end
+        -- Limit pitch
+        character.controls.pitch = Clamp(character.controls.pitch, -80.0, 80.0)
 
-            -- Switch between 1st and 3rd person
-            if input:GetKeyPress(KEY_F) then
-                firstPerson = not firstPerson
-                newFirstPerson = firstPerson
-            end
+        -- Switch between 1st and 3rd person
+        if input:GetKeyPress(KEY_F) then
+            firstPerson = not firstPerson
+        end
 
-            -- Check for loading / saving the scene
-            if input:GetKeyPress(KEY_F5) then
-                scene_:SaveXML(fileSystem:GetProgramDir().."Data/Scenes/CharacterDemo.xml")
-            end
-            if input:GetKeyPress(KEY_F7) then
-                scene_:LoadXML(fileSystem:GetProgramDir().."Data/Scenes/CharacterDemo.xml")
-                -- After loading we have to reacquire the character scene node, as it has been recreated
-                -- Simply find by name as there's only one of them
-                characterNode = scene_:GetChild("Jack", true)
-                if characterNode == nil then
-                    return
-                end
+        -- Turn on/off gyroscope on mobile platform
+        if input:GetKeyPress(KEY_G) then
+            useGyroscope = not useGyroscope
+        end
+
+        -- Check for loading / saving the scene
+        if input:GetKeyPress(KEY_F5) then
+            scene_:SaveXML(fileSystem:GetProgramDir().."Data/Scenes/CharacterDemo.xml")
+        end
+        if input:GetKeyPress(KEY_F7) then
+            scene_:LoadXML(fileSystem:GetProgramDir().."Data/Scenes/CharacterDemo.xml")
+            -- After loading we have to reacquire the character scene node, as it has been recreated
+            -- Simply find by name as there's only one of them
+            characterNode = scene_:GetChild("Jack", true)
+            if characterNode == nil then
+                return
             end
-        else
-            character.controls:Set(CTRL_FORWARD + CTRL_BACK + CTRL_LEFT + CTRL_RIGHT + CTRL_JUMP, false)
         end
     end
 
@@ -434,3 +441,50 @@ function Character:FixedUpdate(timeStep)
     -- Reset grounded flag for next frame
     self.onGround = false
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element\">" ..
+        "        <element type=\"Button\">" ..
+        "            <attribute name=\"Name\" value=\"Button3\" />" ..
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" ..
+        "            <attribute name=\"Size\" value=\"96 96\" />" ..
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" ..
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" ..
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" ..
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" ..
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" ..
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"Label\" />" ..
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" ..
+        "                <attribute name=\"Text\" value=\"Gyroscope\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"G\" />" ..
+        "            </element>" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">1st/3rd</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"F\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Jump</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 38 - 23
Bin/Data/LuaScripts/19_VehicleDemo.lua

@@ -2,6 +2,7 @@
 -- 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
+--     - Saving and loading the variables of a script object, including node & component references
 
 require "LuaScripts/Utilities/Sample"
 
@@ -16,15 +17,13 @@ local ENGINE_POWER = 10.0
 local DOWN_FORCE = 10.0
 local MAX_WHEEL_ANGLE = 22.5
 
-local scene_ = nil
-local cameraNode = nil
 local vehicleNode = nil
 
 function Start()
 
     -- Execute the common startup for samples
     SampleStart()
-    
+
     -- Create static scene content
     CreateScene()
 
@@ -49,9 +48,9 @@ function CreateScene()
     cameraNode = Node()
     local camera = cameraNode:CreateComponent("Camera")
     camera.farClip = 500.0
-    
+
     renderer:SetViewport(0, Viewport:new(scene_, camera))
-    
+
     -- Create static scene content. First create a zone for ambient lighting and fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -83,7 +82,7 @@ function CreateScene()
     -- 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.occluder = true
-    
+
     local body = terrainNode:CreateComponent("RigidBody")
     body.collisionLayer = 2 -- Use layer bitmask 2 for static geometry
     local shape = terrainNode:CreateComponent("CollisionShape")
@@ -103,7 +102,7 @@ function CreateScene()
         object.model = cache:GetResource("Model", "Models/Mushroom.mdl")
         object.material = cache:GetResource("Material", "Materials/Mushroom.xml")
         object.castShadows = true
-        
+
         local body = objectNode:CreateComponent("RigidBody")
         body.collisionLayer = 2
         local shape = objectNode:CreateComponent("CollisionShape")
@@ -114,7 +113,7 @@ end
 function CreateVehicle()
     vehicleNode = scene_:CreateChild("Vehicle")
     vehicleNode.position = Vector3(0.0, 5.0, 0.0)
-    
+
     -- Create the vehicle logic script object
     local vehicle = vehicleNode:CreateScriptObject("Vehicle")
     -- Create the rendering and physics components
@@ -124,7 +123,7 @@ end
 function CreateInstructions()
     -- Construct new Text object, set string to display and font to use
     local instructionText = ui.root:CreateChild("Text")
-    instructionText.text = "Use WASD keys to drive, mouse to rotate camera\n"..
+    instructionText.text = "Use WASD keys to drive, mouse/touch 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
@@ -142,6 +141,9 @@ function SubscribeToEvents()
 
     -- Subscribe to PostUpdate event for updating the camera position after physics simulation
     SubscribeToEvent("PostUpdate", "HandlePostUpdate")
+
+    -- Unsubscribe the SceneUpdate event from base class as the camera node is being controlled in HandlePostUpdate() in this sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 
 function HandleUpdate(eventType, eventData)
@@ -161,9 +163,22 @@ function HandleUpdate(eventType, eventData)
         vehicle.controls:Set(CTRL_LEFT, input:GetKeyDown(KEY_A))
         vehicle.controls:Set(CTRL_RIGHT, input:GetKeyDown(KEY_D))
 
-        -- Add yaw & pitch from the mouse motion. Used only for the camera, does not affect motion
-        vehicle.controls.yaw = vehicle.controls.yaw + input.mouseMoveX * YAW_SENSITIVITY
-        vehicle.controls.pitch = vehicle.controls.pitch + input.mouseMoveY * YAW_SENSITIVITY
+        -- Add yaw & pitch from the mouse motion or touch input. Used only for the camera, does not affect motion
+        if touchEnabled then
+            for i=0, input.numTouches - 1 do
+                local state = input:GetTouch(i)
+                if not state.touchedElement then -- Touch on empty space
+                    local camera = cameraNode:GetComponent("Camera")
+                    if not camera then return end
+
+                    vehicle.controls.yaw = vehicle.controls.yaw + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x
+                    vehicle.controls.pitch = vehicle.controls.pitch + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y
+                end
+            end
+        else
+            vehicle.controls.yaw = vehicle.controls.yaw + input.mouseMoveX * YAW_SENSITIVITY
+            vehicle.controls.pitch = vehicle.controls.pitch + input.mouseMoveY * YAW_SENSITIVITY
+        end
         -- Limit pitch
         vehicle.controls.pitch = Clamp(vehicle.controls.pitch, 0.0, 80.0)
 
@@ -187,7 +202,7 @@ function HandlePostUpdate(eventType, eventData)
     if vehicleNode == nil then
         return
     end
-    
+
     local vehicle = vehicleNode:GetScriptObject()
     if vehicle == nil then
         return
@@ -197,7 +212,7 @@ function HandlePostUpdate(eventType, eventData)
     local dir = Quaternion(vehicleNode.rotation:YawAngle(), Vector3(0.0, 1.0, 0.0))
     dir = dir * Quaternion(vehicle.controls.yaw, Vector3(0.0, 1.0, 0.0))
     dir = dir * Quaternion(vehicle.controls.pitch, Vector3(1.0, 0.0, 0.0))
-    
+
     local cameraTargetPos = vehicleNode.position - dir * Vector3(0.0, 0.0, CAMERA_DISTANCE)
     local cameraStartPos = vehicleNode.position
     -- Raycast camera against static objects (physics collision mask 2)
@@ -244,13 +259,13 @@ function Vehicle:Init()
     local hullObject = node:CreateComponent("StaticModel")
     self.hullBody = node:CreateComponent("RigidBody")
     local hullShape = node:CreateComponent("CollisionShape")
-    
+
     node.scale = Vector3(1.5, 1.0, 3.0)
     hullObject.model = cache:GetResource("Model", "Models/Box.mdl")
     hullObject.material = cache:GetResource("Material", "Materials/Stone.xml")
     hullObject.castShadows = true
     hullShape:SetBox(Vector3(1.0, 1.0, 1.0))
-    
+
     self.hullBody.mass = 4.0
     self.hullBody.linearDamping = 0.2 -- Some air resistance
     self.hullBody.angularDamping = 0.5
@@ -268,12 +283,12 @@ function Vehicle:PostInit()
     self.frontRight = scene_:GetChild("FrontRight")
     self.rearLeft = scene_:GetChild("RearLeft")
     self.rearRight = scene_:GetChild("RearRight")
-    
+
     self.frontLeftAxis = self.frontLeft:GetComponent("Constraint")
     self.frontRightAxis = self.frontRight:GetComponent("Constraint")
-    
+
     self.hullBody = self.node:GetComponent("RigidBody")
-    
+
     self.frontLeftBody = self.frontLeft:GetComponent("RigidBody")
     self.frontRightBody = self.frontRight:GetComponent("RigidBody")
     self.rearLeftBody = self.rearLeft:GetComponent("RigidBody")
@@ -311,13 +326,13 @@ function Vehicle:InitWheel(name, offset)
     wheelConstraint.otherBody = node:GetComponent("RigidBody")
     wheelConstraint.worldPosition = wheelNode.worldPosition -- Set constraint's both ends at wheel's location
     wheelConstraint.axis = Vector3(0.0, 1.0, 0.0) -- Wheel rotates around its local Y-axis
-    
+
     if offset.x >= 0.0 then -- Wheel's hull axis points either left or right
         wheelConstraint.otherAxis = Vector3(1.0, 0.0, 0.0)
     else
         wheelConstraint.otherAxis = Vector3(-1.0, 0.0, 0.0)
     end
-    
+
     wheelConstraint.lowLimit = Vector2(-180.0, 0.0) -- Let the wheel rotate freely around the axis
     wheelConstraint.highLimit = Vector2(180.0, 0.0)
     wheelConstraint.disableCollision = true -- Let the wheel intersect the vehicle hull
@@ -341,7 +356,7 @@ function Vehicle:FixedUpdate(timeStep)
     if self.controls:IsDown(CTRL_BACK) then
         accelerator = -0.5
     end
-    
+
     -- When steering, wake up the wheel rigidbodies so that their orientation is updated
     if newSteering ~= 0.0 then
         self.frontLeftBody:Activate()
@@ -368,4 +383,4 @@ function Vehicle:FixedUpdate(timeStep)
     -- Apply downforce proportional to velocity
     local localVelocity = self.hullBody.rotation:Inverse() * self.hullBody.linearVelocity
     self.hullBody:ApplyForce(self.hullBody.rotation * Vector3(0.0, -1.0, 0.0) * Abs(localVelocity.z) * DOWN_FORCE)
-end
+end

+ 30 - 12
Bin/Data/LuaScripts/20_HugeObjectCount.lua

@@ -7,11 +7,7 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
 local boxNodes = {}
-local yaw = 0.0
-local pitch = 0.0
 local animate = false
 local useGroups = false
 
@@ -51,14 +47,13 @@ function CreateScene()
     zone.fogColor = Color(0.2, 0.2, 0.2)
     zone.fogStart = 200.0
     zone.fogEnd = 300.0
-    
+
     -- Create a directional light
     local lightNode = scene_:CreateChild("DirectionalLight")
     lightNode.direction = Vector3(-0.6, -1.0, -0.8) -- The direction vector does not need to be normalized
     local light = lightNode:CreateComponent("Light")
     light.lightType = LIGHT_DIRECTIONAL
 
-
     if not useGroups then
         light.color = Color(0.7, 0.35, 0.0)
 
@@ -74,8 +69,8 @@ function CreateScene()
             end
         end
     else
-        light.color = Color(0.6, 0.6, 0.6);
-        light.specularIntensity = 1.5;
+        light.color = Color(0.6, 0.6, 0.6)
+        light.specularIntensity = 1.5
 
         -- Create StaticModelGroups in the scene
         local lastGroup = nil
@@ -91,11 +86,11 @@ function CreateScene()
                     lastGroup.model = cache:GetResource("Model", "Models/Box.mdl")
                 end
 
-                local boxNode = scene_:CreateChild("Box");
+                local boxNode = scene_:CreateChild("Box")
                 boxNode.position = Vector3(x * 0.3, 0.0, y * 0.3)
                 boxNode:SetScale(0.25)
                 table.insert(boxNodes, boxNode)
-                lastGroup:AddInstanceNode(boxNode);
+                lastGroup:AddInstanceNode(boxNode)
             end
         end
     end
@@ -118,7 +113,7 @@ function CreateInstructions()
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     -- The text has multiple rows. Center them in relation to each other
     instructionText.textAlignment = HA_CENTER
-    
+
     -- Position the text relative to the screen center
     instructionText.horizontalAlignment = HA_CENTER
     instructionText.verticalAlignment = VA_CENTER
@@ -197,9 +192,32 @@ function HandleUpdate(eventType, eventData)
 
     -- Move the camera, scale movement with time step
     MoveCamera(timeStep)
-    
+
     -- Animate scene if enabled
     if animate then
         AnimateObjects(timeStep)
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Group</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"G\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Animation</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 0 - 4
Bin/Data/LuaScripts/23_Water.lua

@@ -5,14 +5,10 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
 local reflectionCameraNode = nil
 local waterNode = nil
 local waterPlane = Plane()
 local waterClipPlane = Plane()
-local yaw = 0.0
-local pitch = 0.0
 
 function Start()
     -- Execute the common startup for samples

+ 28 - 5
Bin/Data/LuaScripts/24_Urho2DSprite.lua

@@ -7,9 +7,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -57,7 +54,7 @@ function CreateScene()
     for i = 1, NUM_SPRITES do
         local spriteNode = scene_:CreateChild("StaticSprite2D")
         spriteNode.position = Vector3(Random(-halfWidth, halfWidth), Random(-halfHeight, halfHeight), 0.0)
-        
+
         local staticSprite = spriteNode:CreateComponent("StaticSprite2D")
         -- Set color
         staticSprite.color = Color(Random(1.0), Random(1.0), Random(1.0), 1.0)
@@ -102,7 +99,7 @@ function CreateScene()
 
     local spriteNode = scene_:CreateChild("AnimatedSprite2D")
     spriteNode.position = Vector3(0.0, 0.0, -1.0)
-    
+
     local animatedSprite = spriteNode:CreateComponent("AnimatedSprite2D")
     -- Set animation
     animatedSprite.animation = animation
@@ -167,6 +164,9 @@ end
 function SubscribeToEvents()
     -- Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate")
+
+    -- Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 
 function HandleUpdate(eventType, eventData)
@@ -179,3 +179,26 @@ function HandleUpdate(eventType, eventData)
     -- Update scene
     scene_:Update(timeStep)
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 15 - 2
Bin/Data/LuaScripts/25_Urho2DParticle.lua

@@ -6,8 +6,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
 local particleNode = nil
 
 function Start()
@@ -87,7 +85,12 @@ function SetupViewport()
 end
 
 function SubscribeToEvents()
+    -- Subscribe HandleMouseMove() function for tracking mouse/touch move events
     SubscribeToEvent("MouseMove", "HandleMouseMove")
+    if touchEnabled then SubscribeToEvent("TouchMove", "HandleMouseMove") end
+
+    -- Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 
 function HandleMouseMove(eventType, eventData)
@@ -98,3 +101,13 @@ function HandleMouseMove(eventType, eventData)
         particleNode.position = camera:ScreenToWorldPoint(Vector3(x / graphics.width, y / graphics.height, 10.0))
     end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 27 - 14
Bin/Data/LuaScripts/26_ConsoleInput.lua

@@ -33,7 +33,7 @@ local urhoThreatLevels = {
 function Start()
     -- Execute the common startup for samples
     SampleStart()
-    
+
     -- Disable default execution of Lua from the console
     SetExecuteConsoleCommands(false)
 
@@ -43,17 +43,17 @@ function Start()
 
     -- Subscribe key down event
     SubscribeToEvent("KeyDown", "HandleEscKeyDown")
-    
+
     -- Hide logo to make room for the console
     SetLogoVisible(false)
 
     -- Show the console by default, make it large
     console.numRows = graphics.height / 16
-    console.numBufferedRows = 2 * console.numRows;
-    console.commandInterpreter = "LuaScript";
+    console.numBufferedRows = 2 * console.numRows
+    console.commandInterpreter = "LuaScript"
     console.visible = true
-    console.closeButton.visible = false;
-    
+    console.closeButton.visible = false
+
     -- Show OS mouse cursor
     input.mouseVisible = true
 
@@ -67,7 +67,7 @@ function Start()
     StartGame()
 
     -- Randomize from system clock
-    SetRandomSeed(time.systemTime)
+    SetRandomSeed(time:GetSystemTime())
 end
 
 function HandleConsoleCommand(eventType, eventData)
@@ -96,7 +96,7 @@ function StartGame()
           "objective is to survive as long as possible. Beware of hunger and the merciless\n" ..
           "predator cichlid Urho, who appears from time to time. Evading Urho is easier\n" ..
           "with an empty stomach. Type 'help' for available commands.")
-    
+
     gameOn = true
     foodAvailable = false
     eatenLastTurn = false
@@ -109,7 +109,7 @@ function EndGame(message)
     Print(message)
     Print("Game over! You survived " .. numTurns .. " turns.\n" ..
           "Do you want to play again (Y/N)?")
-    
+
     gameOn = false
 end
 
@@ -125,7 +125,7 @@ function Advance()
     elseif urhoThreat == 0 and Random() < 0.2 then
         urhoThreat = urhoThreat + 1
     end
-    
+
     if urhoThreat > 0 then
         Print(urhoThreatLevels[urhoThreat] .. ".")
     end
@@ -139,9 +139,9 @@ function Advance()
             Print("You are " .. hungerLevels[hunger + 1] .. ".")
         end
     end
-    
+
     eatenLastTurn = false
-    
+
     if foodAvailable then
         Print("The floating pieces of fish food are quickly eaten by other fish in the tank.")
         foodAvailable = false
@@ -149,7 +149,7 @@ function Advance()
         Print("The overhead dispenser drops pieces of delicious fish food to the water!")
         foodAvailable = true
     end
-    
+
     numTurns = numTurns + 1
 end
 
@@ -163,7 +163,7 @@ function HandleInput(input)
         Print("Empty input given!")
         return
     end
-    
+
     if inputLower == "quit" or inputLower == "exit" then
         engine:Exit()
     elseif gameOn then
@@ -230,3 +230,16 @@ function Print(input)
     -- Logging appears both in the engine console and stdout
     Log:WriteRaw(input .. "\n")
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 29 - 6
Bin/Data/LuaScripts/27_Urho2DPhysics.lua

@@ -5,9 +5,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -46,7 +43,7 @@ function CreateScene()
 
     -- Create 2D physics world component
     scene_:CreateComponent("PhysicsWorld2D")
-    
+
     local boxSprite = cache:GetResource("Sprite2D", "Urho2D/Box.png")
     local ballSprite = cache:GetResource("Sprite2D", "Urho2D/Ball.png")
 
@@ -54,7 +51,7 @@ function CreateScene()
     local groundNode = scene_:CreateChild("Ground")
     groundNode.position = Vector3(0.0, -3.0, 0.0)
     groundNode.scale = Vector3(200.0, 1.0, 0.0)
-    
+
     -- Create 2D rigid body for gound
     local groundBody = groundNode:CreateComponent("RigidBody2D")
 
@@ -67,7 +64,7 @@ function CreateScene()
     groundShape.size = Vector2(0.32, 0.32)
     -- Set friction
     groundShape.friction = 0.5
-    
+
     local NUM_OBJECTS = 100
     for i = 1, NUM_OBJECTS do
         local node  = scene_:CreateChild("RigidBody")
@@ -158,6 +155,9 @@ end
 function SubscribeToEvents()
     -- Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate")
+
+    -- Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 
 function HandleUpdate(eventType, eventData)
@@ -167,3 +167,26 @@ function HandleUpdate(eventType, eventData)
     -- Move the camera, scale movement with time step
     MoveCamera(timeStep)
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 29 - 5
Bin/Data/LuaScripts/28_Urho2DPhysicsRope.lua

@@ -3,10 +3,8 @@
 --     - Create revolute constraint
 --     - Create roop constraint
 --     - Displaying physics debug geometry
-require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
+require "LuaScripts/Utilities/Sample"
 
 function Start()
     -- Execute the common startup for samples
@@ -43,7 +41,7 @@ function CreateScene()
     local camera = cameraNode:CreateComponent("Camera")
     camera.orthographic = true
     camera.orthoSize = graphics.height * 0.05
-    
+
     -- Create 2D physics world component
     local physicsWorld = scene_:CreateComponent("PhysicsWorld2D")
     physicsWorld.drawJoint = true
@@ -157,6 +155,9 @@ end
 function SubscribeToEvents()
     -- Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate")
+
+    -- Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 
 function HandleUpdate(eventType, eventData)
@@ -167,6 +168,29 @@ function HandleUpdate(eventType, eventData)
     MoveCamera(timeStep)
 
     local physicsWorld = scene_:GetComponent("PhysicsWorld2D")
-    physicsWorld:DrawDebugGeometry();
+    physicsWorld:DrawDebugGeometry()
+
+end
 
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
 end

+ 0 - 5
Bin/Data/LuaScripts/30_LightAnimation.lua

@@ -4,11 +4,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()

+ 5 - 10
Bin/Data/LuaScripts/31_MaterialAnimation.lua

@@ -4,11 +4,6 @@
 
 require "LuaScripts/Utilities/Sample"
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-
 function Start()
     -- Execute the common startup for samples
     SampleStart()
@@ -61,11 +56,11 @@ function CreateScene()
     local mushroomMat = cache:GetResource("Material", "Materials/Mushroom.xml")
     -- Apply shader parameter animation to material
     local specColorAnimation = ValueAnimation:new()
-    specColorAnimation:SetKeyFrame(0.0, Variant(Color(0.1, 0.1, 0.1, 16.0)));
-    specColorAnimation:SetKeyFrame(1.0, Variant(Color(1.0, 0.0, 0.0, 2.0)));
-    specColorAnimation:SetKeyFrame(2.0, Variant(Color(1.0, 1.0, 0.0, 2.0)));
-    specColorAnimation:SetKeyFrame(3.0, Variant(Color(0.1, 0.1, 0.1, 16.0)));
-    mushroomMat:SetShaderParameterAnimation("MatSpecColor", specColorAnimation);
+    specColorAnimation:SetKeyFrame(0.0, Variant(Color(0.1, 0.1, 0.1, 16.0)))
+    specColorAnimation:SetKeyFrame(1.0, Variant(Color(1.0, 0.0, 0.0, 2.0)))
+    specColorAnimation:SetKeyFrame(2.0, Variant(Color(1.0, 1.0, 0.0, 2.0)))
+    specColorAnimation:SetKeyFrame(3.0, Variant(Color(0.1, 0.1, 0.1, 16.0)))
+    mushroomMat:SetShaderParameterAnimation("MatSpecColor", specColorAnimation)
 
     local NUM_OBJECTS = 200
     for i = 1, NUM_OBJECTS do

+ 95 - 13
Bin/Data/LuaScripts/Utilities/Sample.lua

@@ -5,10 +5,29 @@
 --    - Toggle rendering options from the keys 1-8
 --    - Take screenshots with key 9
 --    - Handle Esc key down to hide Console or exit application
+--    - Init touch input on mobile platform using screen joysticks (patched for each individual sample)
 
 local logoSprite = nil
+scene_ = nil -- Scene
+screenJoystickIndex = M_MAX_UNSIGNED -- Screen joystick index for navigational controls (mobile platforms only)
+screenJoystickSettingsIndex = M_MAX_UNSIGNED -- Screen joystick index for settings (mobile platforms only)
+touchEnabled = false -- Flag to indicate whether touch input has been enabled
+paused = false -- Pause flag
+drawDebug = false -- Draw debug geometry flag
+cameraNode = nil -- Camera scene node
+yaw = 0 -- Camera yaw angle
+pitch = 0 -- Camera pitch angle
+TOUCH_SENSITIVITY = 2
 
 function SampleStart()
+    if GetPlatform() == "Android" or GetPlatform() == "iOS" or input.touchEmulation then
+        -- On mobile platform, enable touch by adding a screen joystick
+        InitTouchInput()
+    elseif input:GetNumJoysticks() == 0 then
+        -- On desktop platform, do not detect touch when we already got a joystick
+        SubscribeToEvent("TouchBegin", "HandleTouchBegin")
+    end
+
     -- Create logo
     CreateLogo()
 
@@ -20,6 +39,22 @@ function SampleStart()
 
     -- Subscribe key down event
     SubscribeToEvent("KeyDown", "HandleKeyDown")
+
+    -- Subscribe scene update event
+    SubscribeToEvent("SceneUpdate", "HandleSceneUpdate")
+end
+
+function InitTouchInput()
+    touchEnabled = true
+    local layout = cache:GetResource("XMLFile", "UI/ScreenJoystick_Samples.xml")
+    local patchString = GetScreenJoystickPatchString()
+    if patchString ~= "" then
+        -- Patch the screen joystick layout further on demand
+        local patchFile = XMLFile()
+        if patchFile:FromString(patchString) then layout:Patch(patchFile) end
+    end
+    screenJoystickIndex = input:AddScreenJoystick(layout, cache:GetResource("XMLFile", "UI/DefaultStyle.xml"))
+    input:SetScreenJoystickVisible(screenJoystickSettingsIndex, true)
 end
 
 function SetLogoVisible(enable)
@@ -34,31 +69,31 @@ function CreateLogo()
     if logoTexture == nil then
         return
     end
-    
+
     -- Create logo sprite and add to the UI layout
     logoSprite = ui.root:CreateChild("Sprite")
-    
+
     -- Set logo sprite texture
     logoSprite:SetTexture(logoTexture)
-    
+
     local textureWidth = logoTexture.width
     local textureHeight = logoTexture.height
-    
+
     -- Set logo sprite scale
     logoSprite:SetScale(256 / textureWidth)
-    
+
     -- Set logo sprite size
     logoSprite:SetSize(textureWidth, textureHeight)
-    
+
     -- Set logo sprite hot spot
     logoSprite.hotSpot = IntVector2(0, textureHeight)
-    
+
     -- Set logo sprite alignment
     logoSprite:SetAlignment(HA_LEFT, VA_BOTTOM);
-    
+
     -- Make logo not fully opaque to show the scene underneath
     logoSprite.opacity = 0.75
-    
+
     -- Set a low priority for the logo so that other UI elements can be drawn on top
     logoSprite.priority = -100
 end
@@ -75,11 +110,12 @@ function CreateConsoleAndDebugHud()
     if uiStyle == nil then
         return
     end
-    
+
     -- Create console
     engine:CreateConsole()
     console.defaultStyle = uiStyle
-    
+    console.background.opacity = 0.8
+
     -- Create debug HUD
     engine:CreateDebugHud()
     debugHud.defaultStyle = uiStyle
@@ -103,8 +139,18 @@ function HandleKeyDown(eventType, eventData)
     end
 
     if ui.focusElement == nil then
+        -- Preferences / Pause
+        if key == KEY_SELECT and touchEnabled then
+            paused = not paused
+            if screenJoystickSettingsIndex == M_MAX_UNSIGNED then
+                -- Lazy initialization
+                screenJoystickSettingsIndex = input:AddScreenJoystick(cache:GetResource("XMLFile", "UI/ScreenJoystickSettings_Samples.xml"), cache:GetResource("XMLFile", "UI/DefaultStyle.xml"))
+            else
+                input:SetScreenJoystickVisible(screenJoystickSettingsIndex, paused)
+            end
+
         -- Texture quality
-        if key == KEY_1 then
+        elseif key == KEY_1 then
             local quality = renderer.textureQuality
             quality = quality + 1
             if quality > QUALITY_HIGH then
@@ -160,7 +206,7 @@ function HandleKeyDown(eventType, eventData)
         -- Instancing
         elseif key == KEY_8 then
             renderer.dynamicInstancing = not renderer.dynamicInstancing
-        
+
         -- Take screenshot
         elseif key == KEY_9 then
             local screenshot = Image()
@@ -172,3 +218,39 @@ function HandleKeyDown(eventType, eventData)
         end
     end
 end
+
+function HandleSceneUpdate(eventType, eventData)
+    -- Move the camera by touch, if the camera node is initialized by descendant sample class
+    if touchEnabled and cameraNode then
+        for i=0, input:GetNumTouches()-1 do
+            local state = input:GetTouch(i)
+            if not state.touchedElement then -- Touch on empty space
+                if state.delta.x or state.delta.y then
+                    local camera = cameraNode:GetComponent("Camera")
+                    if not camera then return end
+
+                    yaw = yaw + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x
+                    pitch = pitch + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y
+
+                    -- Construct new orientation for the camera scene node from yaw and pitch; roll is fixed to zero
+                    cameraNode:SetRotation(Quaternion(pitch, yaw, 0))
+                else
+                    -- Move the cursor to the touch position
+                    local cursor = ui:GetCursor()
+                    if cursor and cursor:IsVisible() then cursor:SetPosition(state.position) end
+                end
+            end
+        end
+    end
+end
+
+function HandleTouchBegin(eventType, eventData)
+    -- On some platforms like Windows the presence of touch input can only be detected dynamically
+    InitTouchInput()
+    UnsubscribeFromEvent("TouchBegin")
+end
+
+-- Create empty XML patch instructions for screen joystick layout if none defined
+function GetScreenJoystickPatchString()
+    return ""
+end

+ 17 - 139
Bin/Data/LuaScripts/Utilities/Touch.lua

@@ -1,128 +1,42 @@
 -- Mobile framework for Android/iOS
 -- Gamepad from Ninja Snow War
--- Gyroscope (activated by default)
 -- Touches patterns:
 --     - 1 finger touch  = pick object through raycast
 --     - 1 or 2 fingers drag  = rotate camera
---     - 3 fingers touch = switch between first/third person view
---     - 4 fingers touch = switch shadows on/off
 --     - 2 fingers sliding in opposite direction (up/down) = zoom in/out
 
--- 3 fingers touch & 4 fingers touch could be used to switch gyroscope on/off, activate/deactivate secondary viewport, activate a panel GUI, switch debug HUD/geometry, toggle console, switch the gyroscope...
+-- Setup: Call the update function 'UpdateTouches()' from HandleUpdate or equivalent update handler function
 
--- Setup:
--- - On init, call this script using 'require "LuaScripts/Utilities/Touch" end' then 'InitTouchInput()' on mobile platforms
---   -> to detect platform, use 'if GetPlatform() == "Android" or GetPlatform() == "iOS"')
--- - Subscribe to touch events (Begin, Move, End) using 'SubscribeToTouchEvents()'
--- - Call the update function 'updateTouches()' from HandleUpdate or equivalent update handler function
-
-TOUCH_SENSITIVITY = 5.0
-GYROSCOPE_THRESHOLD = 0.1
+local GYROSCOPE_THRESHOLD = 0.1
 CAMERA_MIN_DIST = 1.0
-CAMERA_INITIAL_DIST = 5.0
 CAMERA_MAX_DIST = 20.0
 
-local moveTouchID = -1
-local rotateTouchID = -1
-local fireTouchID = -1
-local moveButton
-local fireButton
-local touchButtonSize = 96
-local touchButtonBorder = 12
 local zoom = false
-local newFirstPerson = nil
-local shadowMode = true
-
-firstPerson = false
-touchEnabled = false
-cameraDistance = CAMERA_INITIAL_DIST
-cameraNode = nil
-
--- Create Gamepad Buttons
-
-function InitTouchInput()
-    moveButton = ui.root:CreateChild("BorderImage")
-    moveButton.texture = cache:GetResource("Texture2D", "Textures/TouchInput.png")
-    moveButton.imageRect = IntRect(0, 0, 96, 96) -- Crop right side of the texture
-    moveButton:SetAlignment(HA_LEFT, VA_BOTTOM)
-    moveButton:SetPosition(touchButtonBorder, -touchButtonBorder)
-    moveButton:SetSize(touchButtonSize, touchButtonSize)
-    moveButton.opacity = 0.25
+useGyroscope = false
+cameraDistance = 5.0
 
-    fireButton = ui.root:CreateChild("BorderImage")
-    fireButton.texture = cache:GetResource("Texture2D", "Textures/TouchInput.png")
-    fireButton.imageRect = IntRect(96, 0, 192, 96) -- Crop left side of the texture
-    fireButton:SetAlignment(HA_RIGHT, VA_BOTTOM)
-    fireButton:SetPosition(-touchButtonBorder, -touchButtonBorder)
-    fireButton:SetSize(touchButtonSize, touchButtonSize)
-    fireButton.opacity = 0.25
-
-    touchEnabled = true
-end
-
-function SubscribeToTouchEvents()
-    SubscribeToEvent("TouchBegin", "HandleTouchBegin")
-    SubscribeToEvent("TouchEnd", "HandleTouchEnd")
-end
+function UpdateTouches(controls) -- Called from HandleUpdate
 
-function updateTouches(controls) -- Called from HandleUpdate
-    local controls = characterNode:GetScriptObject().controls
-    local camera = cameraNode:GetComponent("Camera")
     zoom = false -- reset bool
 
-    -- Touch Inputs
-    if touchEnabled then
+    -- Zoom in/out
+    if input.numTouches == 2 then
+        local touch1 = input:GetTouch(0)
+        local touch2 = input:GetTouch(1)
 
-        -- Zoom in/out
-        if input.numTouches == 2 then
-            local touch1 = input:GetTouch(0)
-            local touch2 = input:GetTouch(1)
+        -- Check for zoom pattern (touches moving in opposite directions and on empty space)
+        if not touch1.touchedElement and not touch2.touchedElement and ((touch1.delta.y > 0 and touch2.delta.y < 0) or (touch1.delta.y < 0 and touch2.delta.y > 0)) then zoom = true else zoom = false end
 
-            -- Check for zoom pattern (touches moving in opposite directions)
-            if (touch1.delta.y > 0 and touch2.delta.y < 0) or (touch1.delta.y < 0 and touch2.delta.y > 0) then zoom = true else zoom = false end
-
-            -- Check for zoom direction (in/out)
-            if zoom then
-                if Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y) then sens = -1 else sens = 1 end
-                cameraDistance = cameraDistance + Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50
-                cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST) -- Restrict zoom range to [1;20]
-            end
+        -- Check for zoom direction (in/out)
+        if zoom then
+            if Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y) then sens = -1 else sens = 1 end
+            cameraDistance = cameraDistance + Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50
+            cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST) -- Restrict zoom range to [1;20]
         end
-
-        -- Switch 1st/3rd person mode
-        if input.numTouches == 3 then newFirstPerson = not firstPerson end
-
-        -- Switch draw debug
-        if input.numTouches == 4 then shadowMode = not renderer.drawShadows end
-
-        -- Rotate and Move
-        if not zoom then
-            for i = 0, input.numTouches-1 do -- numtouches=[0;4]
-
-                local touch = input:GetTouch(i) -- TouchState
-
-                if touch.touchID == rotateTouchID then
-                    controls.yaw = controls.yaw + TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.x
-                    controls.pitch = controls.pitch + TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.y
-                    controls.pitch = Clamp(controls.pitch, -80, 80) -- Limit pitch
-                end
-
-                if touch.touchID == moveTouchID then
-                    local relX = touch.position.x - moveButton.screenPosition.x - touchButtonSize / 2
-                    local relY = touch.position.y - moveButton.screenPosition.y - touchButtonSize / 2
-                        if relY < 0 and Abs(relX * 3 / 2) < Abs(relY) then controls:Set(CTRL_FORWARD, true) end
-                        if relY > 0 and Abs(relX * 3 / 2) < Abs(relY) then controls:Set(CTRL_BACK, true) end
-                        if relX < 0 and Abs(relY * 3 / 2) < Abs(relX) then controls:Set(CTRL_LEFT, true) end
-                        if relX > 0 and Abs(relY * 3 / 2) < Abs(relX) then controls:Set(CTRL_RIGHT, true) end
-                    end
-                end
-        end
-
-        if fireTouchID >= 0 then controls:Set(CTRL_JUMP, true) end
     end
 
     -- Gyroscope (emulated by SDL through a virtual joystick)
-    if input.numJoysticks > 0 then -- numJoysticks = 1 on iOS & Android
+    if useGyroscope and input.numJoysticks > 0 then -- numJoysticks = 1 on iOS & Android
         local joystick = input:GetJoystickByIndex(0) -- JoystickState
         if joystick.numAxes >= 2 then
             if joystick:GetAxisPosition(0) < -GYROSCOPE_THRESHOLD then controls:Set(CTRL_LEFT, true) end
@@ -132,39 +46,3 @@ function updateTouches(controls) -- Called from HandleUpdate
         end
     end
 end
-
-function HandleTouchBegin(eventType, eventData)
-
-    local touchID = eventData:GetInt("TouchID") -- Get #touches or dragging value
-
-    local pos = IntVector2(eventData:GetInt("X"), eventData:GetInt("Y")) -- Get touch coordinates
-    local element = ui:GetElementAt(pos, false) -- Get gamepad UIElement touched (if any)
-
-    -- Check for gamepad button touched. If none, rotate
-    if element == moveButton then moveTouchID = touchID
-    elseif element == fireButton then fireTouchID = touchID
-    else rotateTouchID = touchID end
-
-    -- Raycast of RigidBodies (for example to acquire a target)
-    local camera = cameraNode:GetComponent("Camera")
-    local cameraRay = camera:GetScreenRay(eventData:GetInt("X") / graphics.width, eventData:GetInt("Y") / graphics.height)
-    local result = scene_:GetComponent("PhysicsWorld"):RaycastSingle(cameraRay, camera.farClip, 2) -- NB: here we restrict targets to layer 2
-    if result.body ~= nil then print("Physics raycast hit " .. result.body:GetNode().name) end
-
-    -- Raycast of drawable components (for targets with or without physics)
-    local result2 = scene_:GetComponent("Octree"):RaycastSingle(cameraRay, RAY_TRIANGLE, camera.farClip, DRAWABLE_GEOMETRY)
-    if result2.drawable ~= nil then print("Drawable raycast hit " .. result2.drawable:GetNode().name) end
-end
-
-
-function HandleTouchEnd(eventType, eventData)
-    local touchID = eventData:GetInt("TouchID")
-
-    if touchID == moveTouchID then moveTouchID = -1 end
-    if touchID == rotateTouchID then rotateTouchID = -1 end
-    if touchID == fireTouchID then fireTouchID = -1 end
-
-    -- On-release Update
-    firstPerson = newFirstPerson
-    renderer.drawShadows = shadowMode
-end

+ 8 - 1
Bin/Data/Scripts/01_HelloWorld.as

@@ -10,7 +10,7 @@ void Start()
 {
     // Execute the common startup for samples
     SampleStart();
-    
+
     // Create "Hello World" Text
     CreateText();
 
@@ -49,3 +49,10 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Do nothing for now, could be extended to eg. animate the display
 }
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 9 - 1
Bin/Data/Scripts/02_HelloGUI.as

@@ -32,7 +32,7 @@ void Start()
 
     // Create and add some controls to the Window
     InitControls();
-    
+
     // Create a draggable Fish
     CreateDraggableFish();
 }
@@ -176,3 +176,11 @@ void HandleControlClicked(StringHash eventType, VariantMap& eventData)
     // Update the Window's title text
     windowTitle.text = "Hello " + name + "!";
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 8 - 0
Bin/Data/Scripts/03_Sprites.as

@@ -106,3 +106,11 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move sprites, scale movement with time step
     MoveSprites(timeStep);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 5 - 6
Bin/Data/Scripts/04_StaticScene.as

@@ -3,14 +3,10 @@
 //     - Creating a 3D scene with static content
 //     - Displaying the scene using the Renderer subsystem
 //     - Handling keyboard and mouse input to move a freelook camera
+//     - Applying a material shader to animate vegetation (simulate wind)
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -71,7 +67,7 @@ void CreateScene()
         mushroomNode.SetScale(0.5f + Random(2.0f));
         StaticModel@ mushroomObject = mushroomNode.CreateComponent("StaticModel");
         mushroomObject.model = cache.GetResource("Model", "Models/Mushroom.mdl");
-        mushroomObject.material = cache.GetResource("Material", "Materials/Mushroom.xml");
+        mushroomObject.material = cache.GetResource("Material", "Materials/MushroomWind.xml"); // Apply Vegetation Windy shader
     }
 
     // Create a scene node for the camera, which we will move around
@@ -151,3 +147,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 3 - 5
Bin/Data/Scripts/05_AnimatingScene.as

@@ -6,11 +6,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -164,3 +159,6 @@ class Rotator : ScriptObject
         node.Rotate(Quaternion(rotationSpeed.x * timeStep, rotationSpeed.y * timeStep, rotationSpeed.z * timeStep));
     }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 22 - 15
Bin/Data/Scripts/06_SkeletalAnimation.as

@@ -8,12 +8,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -21,10 +15,10 @@ void Start()
 
     // Create the scene content
     CreateScene();
-    
+
     // Create the UI content
     CreateInstructions();
-    
+
     // Setup the viewport for displaying the scene
     SetupViewport();
 
@@ -35,7 +29,7 @@ void Start()
 void CreateScene()
 {
     scene_ = Scene();
-    
+
     // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
@@ -47,7 +41,7 @@ void CreateScene()
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
-    
+
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -56,7 +50,7 @@ void CreateScene()
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogStart = 100.0f;
     zone.fogEnd = 300.0f;
-    
+
     // Create a directional light to the world. Enable cascaded shadows on it
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
@@ -66,7 +60,7 @@ void CreateScene()
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
-    
+
     // Create animated models
     const uint NUM_MODELS = 100;
     const float MODEL_MOVE_SPEED = 2.0f;
@@ -103,7 +97,7 @@ void CreateScene()
     cameraNode = scene_.CreateChild("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     camera.farClip = 300.0f;
-    
+
     // Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
 }
@@ -112,7 +106,7 @@ void CreateInstructions()
 {
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
-    instructionText.text = 
+    instructionText.text =
         "Use WASD keys and mouse to move\n"
         "Space to toggle debug geometry";
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
@@ -136,7 +130,7 @@ void SubscribeToEvents()
 {
     // Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate");
-    
+
     // Subscribe HandlePostRenderUpdate() function for processing the post-render update event, sent after Renderer subsystem is
     // done with defining the draw calls for the viewports (but before actually executing them.) We will request debug geometry
     // rendering during that event
@@ -226,3 +220,16 @@ class Mover : ScriptObject
             state.AddTime(timeStep);
     }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+    "<patch>"+
+    "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />"+
+    "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>"+
+    "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">"+
+    "        <element type=\"Text\">"+
+    "            <attribute name=\"Name\" value=\"KeyBinding\" />"+
+    "            <attribute name=\"Text\" value=\"SPACE\" />"+
+    "        </element>"+
+    "    </add>"+
+    "</patch>";

+ 17 - 10
Bin/Data/Scripts/07_Billboards.as

@@ -6,12 +6,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -19,10 +13,10 @@ void Start()
 
     // Create the scene content
     CreateScene();
-    
+
     // Create the UI content
     CreateInstructions();
-    
+
     // Setup the viewport for displaying the scene
     SetupViewport();
 
@@ -33,7 +27,7 @@ void Start()
 void CreateScene()
 {
     scene_ = Scene();
-    
+
     // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
@@ -168,7 +162,7 @@ void CreateInstructions()
 {
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
-    instructionText.text = 
+    instructionText.text =
         "Use WASD keys and mouse to move\n"
         "Space to toggle debug geometry";
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
@@ -278,3 +272,16 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
         renderer.DrawDebugGeometry(true);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 29 - 14
Bin/Data/Scripts/08_Decals.as

@@ -7,12 +7,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -20,10 +14,10 @@ void Start()
 
     // Create the scene content
     CreateScene();
-    
+
     // Create the UI content
     CreateUI();
-    
+
     // Setup the viewport for displaying the scene
     SetupViewport();
 
@@ -39,14 +33,14 @@ void CreateScene()
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create scene node & StaticModel component for showing a static plane
     Node@ planeNode = scene_.CreateChild("Plane");
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
-    
+
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -96,7 +90,7 @@ void CreateScene()
         if (size >= 3.0f)
             boxObject.occluder = true;
     }
-    
+
     // Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_.CreateChild("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
@@ -116,7 +110,7 @@ void CreateUI()
     ui.cursor = cursor;
     // Set starting position of the cursor at the rendering window center
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
-    
+
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
     instructionText.text =
@@ -159,12 +153,12 @@ void MoveCamera(float timeStep)
     // Do not move if the UI has a focused element (the console)
     if (ui.focusElement !is null)
         return;
-    
+
     // Movement speed as world units per second
     const float MOVE_SPEED = 20.0f;
     // Mouse sensitivity as degrees per pixel
     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
     // Only move the camera when the cursor is hidden
     if (!ui.cursor.visible)
@@ -259,3 +253,24 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
         renderer.DrawDebugGeometry(false);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Paint</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 50 - 10
Bin/Data/Scripts/09_MultipleViewports.as

@@ -5,12 +5,7 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
 Node@ rearCameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
 
 void Start()
 {
@@ -19,10 +14,10 @@ void Start()
 
     // Create the scene content
     CreateScene();
-    
+
     // Create the UI content
     CreateInstructions();
-    
+
     // Setup the viewports for displaying the scene
     SetupViewports();
 
@@ -38,14 +33,14 @@ void CreateScene()
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create scene node & StaticModel component for showing a static plane
     Node@ planeNode = scene_.CreateChild("Plane");
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
-    
+
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -141,7 +136,7 @@ void SetupViewports()
     // Set up the front camera viewport
     Viewport@ viewport = Viewport(scene_, cameraNode.GetComponent("Camera"));
     renderer.viewports[0] = viewport;
-    
+
     // Clone the default render path so that we do not interfere with the other viewport, then add
     // bloom and FXAA post process effects to the front viewport. Render path commands can be tagged
     // for example with the effect name to allow easy toggling on and off. We start with the effects
@@ -229,3 +224,48 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
         renderer.DrawDebugGeometry(false);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element\">" +
+        "        <element type=\"Button\">" +
+        "            <attribute name=\"Name\" value=\"Button3\" />" +
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" +
+        "            <attribute name=\"Size\" value=\"96 96\" />" +
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" +
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" +
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" +
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" +
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" +
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"Label\" />" +
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" +
+        "                <attribute name=\"Text\" value=\"FXAA\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "                <attribute name=\"Text\" value=\"F\" />" +
+        "            </element>" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Bloom</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"B\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 5 - 6
Bin/Data/Scripts/10_RenderToTexture.as

@@ -5,12 +5,8 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
 Scene@ rttScene_;
-Node@ cameraNode;
 Node@ rttCameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
 
 void Start()
 {
@@ -77,7 +73,7 @@ void CreateScene()
         light.lightType = LIGHT_POINT;
         light.range = 30.0f;
     }
-    
+
     {
         // Create the scene in which we move around
         scene_ = Scene();
@@ -224,7 +220,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
 {
     // Take the frame time step, which is stored as a float
     float timeStep = eventData["TimeStep"].GetFloat();
-    
+
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
 }
@@ -240,3 +236,6 @@ class Rotator : ScriptObject
         node.Rotate(Quaternion(rotationSpeed.x * timeStep, rotationSpeed.y * timeStep, rotationSpeed.z * timeStep));
     }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 27 - 12
Bin/Data/Scripts/11_Physics.as

@@ -7,12 +7,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -42,7 +36,7 @@ void CreateScene()
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -61,7 +55,7 @@ void CreateScene()
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
-    
+
     // Create skybox. The Skybox component is used like StaticModel, but it will be always located at the camera, giving the
     // illusion of the box planes being far away. Use just the ordinary Box model and a suitable material, whose shader will
     // generate the necessary 3D texture coordinates for cube mapping
@@ -89,7 +83,7 @@ void CreateScene()
         // rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
         shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
     }
-    
+
     {
         // Create a pyramid of movable physics objects
         for (int y = 0; y < 8; ++y)
@@ -102,9 +96,9 @@ void CreateScene()
                 boxObject.model = cache.GetResource("Model", "Models/Box.mdl");
                 boxObject.material = cache.GetResource("Material", "Materials/StoneEnvMapSmall.xml");
                 boxObject.castShadows = true;
-                
+
                 // Create RigidBody and CollisionShape components like above. Give the RigidBody mass to make it movable
-                // and also adjust friction. The actual mass is not important; only the mass ratios between colliding 
+                // and also adjust friction. The actual mass is not important; only the mass ratios between colliding
                 // objects are significant
                 RigidBody@ body = boxNode.CreateComponent("RigidBody");
                 body.mass = 1.0f;
@@ -114,7 +108,7 @@ void CreateScene()
             }
         }
     }
-    
+
     // Create the camera. Set far clip to match the fog. Note: now we actually create the camera node outside
     // the scene, because we want it to be unaffected by scene load / save
     cameraNode = Node();
@@ -255,3 +249,24 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
         scene_.physicsWorld.DrawDebugGeometry(true);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 25 - 10
Bin/Data/Scripts/12_PhysicsStressTest.as

@@ -6,12 +6,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -41,7 +35,7 @@ void CreateScene()
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -50,7 +44,7 @@ void CreateScene()
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogStart = 100.0f;
     zone.fogEnd = 300.0f;
-    
+
     // Create a directional light to the world. Enable cascaded shadows on it
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
@@ -98,7 +92,7 @@ void CreateScene()
             shape.SetTriangleMesh(mushroomObject.model);
         }
     }
-    
+
     {
         // Create a large amount of falling physics objects
         const uint NUM_OBJECTS = 1000;
@@ -121,7 +115,7 @@ void CreateScene()
             shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
         }
     }
-    
+
     // Create the camera. Limit far clip distance to match the fog. Note: now we actually create the camera node outside
     // the scene, because we want it to be unaffected by scene load / save
     cameraNode = Node();
@@ -260,3 +254,24 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
         scene_.physicsWorld.DrawDebugGeometry(true);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 43 - 28
Bin/Data/Scripts/13_Ragdolls.as

@@ -6,12 +6,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -41,7 +35,7 @@ void CreateScene()
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -50,7 +44,7 @@ void CreateScene()
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogStart = 100.0f;
     zone.fogEnd = 300.0f;
-    
+
     // Create a directional light to the world. Enable cascaded shadows on it
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
@@ -60,7 +54,7 @@ void CreateScene()
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
-    
+
     {
         // Create a floor object, 500 x 500 world units. Adjust position so that the ground is at zero Y
         Node@ floorNode = scene_.CreateChild("Floor");
@@ -80,7 +74,7 @@ void CreateScene()
         // rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
         shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
     }
-    
+
     // Create animated models
     for (int z = -1; z <= 1; ++z)
     {
@@ -107,7 +101,7 @@ void CreateScene()
             // Create the capsule shape with an offset so that it is correctly aligned with the model, which
             // has its origin at the feet
             shape.SetCapsule(0.7f, 2.0f, Vector3(0.0f, 1.0f, 0.0f));
-            
+
             // Create a custom script object that reacts to collisions and creates the ragdoll
             modelNode.CreateScriptObject(scriptFile, "CreateRagdoll");
         }
@@ -226,9 +220,9 @@ void SpawnObject()
     body.rollingFriction = 0.15f;
     CollisionShape@ shape = boxNode.CreateComponent("CollisionShape");
     shape.SetSphere(1.0f);
-    
+
     const float OBJECT_VELOCITY = 10.0f;
-    
+
     // Set initial velocity for the RigidBody based on camera forward vector. Add also a slight up component
     // to overcome gravity better
     body.linearVelocity = cameraNode.rotation * Vector3(0.0f, 0.25f, 1.0f) * OBJECT_VELOCITY;
@@ -269,7 +263,7 @@ class CreateRagdoll : ScriptObject
             // We do not need the physics components in the AnimatedModel's root scene node anymore
             node.RemoveComponent("RigidBody");
             node.RemoveComponent("CollisionShape");
-            
+
             // Create RigidBody & CollisionShape components to bones
             CreateRagdollBone("Bip01_Pelvis", SHAPE_BOX, Vector3(0.3f, 0.2f, 0.25f), Vector3(0.0f, 0.0f, 0.0f),
                 Quaternion(0.0f, 0.0f, 0.0f));
@@ -297,23 +291,23 @@ class CreateRagdoll : ScriptObject
             // Create Constraints between bones
             CreateRagdollConstraint("Bip01_L_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_R_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_R_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_L_Calf", "Bip01_L_Thigh", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_L_Calf", "Bip01_L_Thigh", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_R_Calf", "Bip01_R_Thigh", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_R_Calf", "Bip01_R_Thigh", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_Spine1", "Bip01_Pelvis", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, 1.0f), 
+            CreateRagdollConstraint("Bip01_Spine1", "Bip01_Pelvis", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, 1.0f),
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 0.0f), Vector2(-10.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_Head", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(-1.0f, 0.0f, 0.0f), 
+            CreateRagdollConstraint("Bip01_Head", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(-1.0f, 0.0f, 0.0f),
                 Vector3(-1.0f, 0.0f, 0.0f), Vector2(0.0f, 30.0f), Vector2(0.0f, 0.0f));
             CreateRagdollConstraint("Bip01_L_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0.0f, -1.0f, 0.0f),
                 Vector3(0.0f, 1.0f, 0.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f), false);
-            CreateRagdollConstraint("Bip01_R_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0.0f, -1.0f, 0.0f), 
+            CreateRagdollConstraint("Bip01_R_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0.0f, -1.0f, 0.0f),
                 Vector3(0.0f, 1.0f, 0.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f), false);
-            CreateRagdollConstraint("Bip01_L_Forearm", "Bip01_L_UpperArm", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_L_Forearm", "Bip01_L_UpperArm", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_R_Forearm", "Bip01_R_UpperArm", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_R_Forearm", "Bip01_R_UpperArm", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
 
             // Disable keyframe animation from all bones so that they will not interfere with the ragdoll
@@ -321,13 +315,13 @@ class CreateRagdoll : ScriptObject
             Skeleton@ skeleton = model.skeleton;
             for (uint i = 0; i < skeleton.numBones; ++i)
                 skeleton.bones[i].animated = false;
-            
+
             // Finally remove self (the ScriptInstance which holds this script object) from the scene node. Note that this must
             // be the last operation performed in the function
             self.Remove();
         }
     }
-    
+
     void CreateRagdollBone(const String&in boneName, ShapeType type, const Vector3&in size, const Vector3&in position,
         const Quaternion&in rotation)
     {
@@ -338,7 +332,7 @@ class CreateRagdoll : ScriptObject
             log.Warning("Could not find bone " + boneName + " for creating ragdoll physics components");
             return;
         }
-        
+
         RigidBody@ body = boneNode.CreateComponent("RigidBody");
         // Set mass to make movable
         body.mass = 1.0f;
@@ -348,7 +342,7 @@ class CreateRagdoll : ScriptObject
         // Set rest thresholds to ensure the ragdoll rigid bodies come to rest to not consume CPU endlessly
         body.linearRestThreshold = 1.5f;
         body.angularRestThreshold = 2.5f;
-    
+
         CollisionShape@ shape = boneNode.CreateComponent("CollisionShape");
         // We use either a box or a capsule shape for all of the bones
         if (type == SHAPE_BOX)
@@ -356,7 +350,7 @@ class CreateRagdoll : ScriptObject
         else
             shape.SetCapsule(size.x, size.y, position, rotation);
     }
-    
+
     void CreateRagdollConstraint(const String&in boneName, const String&in parentName, ConstraintType type,
         const Vector3&in axis, const Vector3&in parentAxis, const Vector2&in highLimit, const Vector2&in lowLimit,
         bool disableCollision = true)
@@ -373,7 +367,7 @@ class CreateRagdoll : ScriptObject
             log.Warning("Could not find bone " + parentName + " for creating ragdoll constraint");
             return;
         }
-        
+
         Constraint@ constraint = boneNode.CreateComponent("Constraint");
         constraint.constraintType = type;
         // Most of the constraints in the ragdoll will work better when the connected bodies don't collide against each other
@@ -389,3 +383,24 @@ class CreateRagdoll : ScriptObject
         constraint.lowLimit = lowLimit;
     }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 22 - 14
Bin/Data/Scripts/14_SoundEffects.as

@@ -5,8 +5,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-
 Array<String> soundNames = {
     "Fist",
     "Explosion",
@@ -23,7 +21,7 @@ void Start()
 {
     // Execute the common startup for samples
     SampleStart();
-    
+
     // Enable OS cursor
     input.mouseVisible = true;
 
@@ -39,7 +37,7 @@ void CreateUI()
     XMLFile@ uiStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
     // Set style to the UI root so that elements will inherit it
     ui.root.defaultStyle = uiStyle;
-    
+
     // Create buttons for playing back sounds
     for (uint i = 0; i < soundNames.length; ++i)
     {
@@ -48,11 +46,11 @@ void CreateUI()
         button.vars["SoundResource"] = soundResourceNames[i];
         SubscribeToEvent(button, "Pressed", "HandlePlaySound");
     }
-    
+
     // Create buttons for playing/stopping music
     Button@ button = CreateButton(20, 80, 120, 40, "Play Music");
     SubscribeToEvent(button, "Released", "HandlePlayMusic");
-    
+
     button = CreateButton(160, 80, 120, 40, "Stop Music");
     SubscribeToEvent(button, "Released", "HandleStopMusic");
 
@@ -60,7 +58,7 @@ void CreateUI()
     Slider@ slider = CreateSlider(20, 140, 200, 20, "Sound Volume");
     slider.value = audio.masterGain[SOUND_EFFECT];
     SubscribeToEvent(slider, "SliderChanged", "HandleSoundVolume");
-    
+
     slider = CreateSlider(20, 200, 200, 20, "Music Volume");
     slider.value = audio.masterGain[SOUND_MUSIC];
     SubscribeToEvent(slider, "SliderChanged", "HandleMusicVolume");
@@ -69,18 +67,18 @@ void CreateUI()
 Button@ CreateButton(int x, int y, int xSize, int ySize, const String&in text)
 {
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
-    
+
     // Create the button and center the text onto it
     Button@ button = ui.root.CreateChild("Button");
     button.SetStyleAuto();
     button.SetPosition(x, y);
     button.SetSize(xSize, ySize);
-    
+
     Text@ buttonText = button.CreateChild("Text");
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.SetFont(font, 12);
     buttonText.text = text;
-    
+
     return button;
 }
 
@@ -93,14 +91,14 @@ Slider@ CreateSlider(int x, int y, int xSize, int ySize, const String& text)
     sliderText.SetPosition(x, y);
     sliderText.SetFont(font, 12);
     sliderText.text = text;
-    
+
     Slider@ slider = ui.root.CreateChild("Slider");
     slider.SetStyleAuto();
     slider.SetPosition(x, y + 20);
     slider.SetSize(xSize, ySize);
     // Use 0-1 range for controlling sound/music master volume
     slider.range = 1.0f;
-    
+
     return slider;
 }
 
@@ -108,7 +106,7 @@ void HandlePlaySound(StringHash eventType, VariantMap& eventData)
 {
     Button@ button = GetEventSender();
     String soundResourceName = button.vars["SoundResource"].GetString();
-    
+
     // Get the sound resource
     Sound@ sound = cache.GetResource("Sound", soundResourceName);
 
@@ -136,7 +134,7 @@ void HandlePlayMusic(StringHash eventType, VariantMap& eventData)
     Sound@ music = cache.GetResource("Sound", "Music/Ninja Gods.ogg");
     // Set the song to loop
     music.looped = true;
-    
+
     // Create a scene node and a sound source for the music
     Node@ musicNode = scene_.CreateChild("Music");
     SoundSource@ musicSource = musicNode.CreateComponent("SoundSource");
@@ -163,3 +161,13 @@ void HandleMusicVolume(StringHash eventType, VariantMap& eventData)
     audio.masterGain[SOUND_MUSIC] = newVolume;
 }
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 79 - 13
Bin/Data/Scripts/15_Navigation.as

@@ -9,13 +9,8 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
 Vector3 endPos;
 Array<Vector3> currentPath;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
 Node@ jackNode;
 
 void Start()
@@ -25,10 +20,10 @@ void Start()
 
     // Create the scene content
     CreateScene();
-    
+
     // Create the UI content
     CreateUI();
-    
+
     // Setup the viewport for displaying the scene
     SetupViewport();
 
@@ -44,14 +39,14 @@ void CreateScene()
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create scene node & StaticModel component for showing a static plane
     Node@ planeNode = scene_.CreateChild("Plane");
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
-    
+
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -113,12 +108,12 @@ void CreateScene()
     // physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example)
     // it will use renderable geometry instead
     navMesh.Build();
-    
+
     // Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_.CreateChild("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     camera.farClip = 300.0f;
-    
+
     // Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
 }
@@ -133,7 +128,7 @@ void CreateUI()
     ui.cursor = cursor;
     // Set starting position of the cursor at the rendering window center
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
-    
+
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
     instructionText.text =
@@ -325,7 +320,7 @@ void FollowPath(float timeStep)
         float distance = (jackNode.position - nextWaypoint).length;
         if (move > distance)
             move = distance;
-        
+
         jackNode.LookAt(nextWaypoint, Vector3(0.0f, 1.0f, 0.0f));
         jackNode.Translate(Vector3(0.0f, 0.0f, 1.0f) * move);
 
@@ -375,3 +370,74 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
         }
     }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element\">" +
+        "        <element type=\"Button\">" +
+        "            <attribute name=\"Name\" value=\"Button3\" />" +
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" +
+        "            <attribute name=\"Size\" value=\"96 96\" />" +
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" +
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" +
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" +
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" +
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" +
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"Label\" />" +
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" +
+        "                <attribute name=\"Text\" value=\"Teleport\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "                <attribute name=\"Text\" value=\"LSHIFT\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "                <attribute name=\"Text\" value=\"LEFT\" />" +
+        "            </element>" +
+        "        </element>" +
+        "        <element type=\"Button\">" +
+        "            <attribute name=\"Name\" value=\"Button4\" />" +
+        "            <attribute name=\"Position\" value=\"-120 -12\" />" +
+        "            <attribute name=\"Size\" value=\"96 96\" />" +
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" +
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" +
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" +
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" +
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" +
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"Label\" />" +
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" +
+        "                <attribute name=\"Text\" value=\"Obstacles\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "                <attribute name=\"Text\" value=\"MIDDLE\" />" +
+        "            </element>" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Set</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 28 - 17
Bin/Data/Scripts/16_Chat.as

@@ -29,7 +29,7 @@ void Start()
 
     // Create the user interface
     CreateUI();
-    
+
     // Subscribe to UI and network events
     SubscribeToEvents();
 }
@@ -50,10 +50,10 @@ void CreateUI()
     buttonContainer.SetFixedSize(graphics.width, 20);
     buttonContainer.SetPosition(0, graphics.height - 20);
     buttonContainer.layoutMode = LM_HORIZONTAL;
-    
+
     textEdit = buttonContainer.CreateChild("LineEdit");
     textEdit.SetStyleAuto();
-    
+
     sendButton = CreateButton("Send", 70);
     connectButton = CreateButton("Connect", 90);
     disconnectButton = CreateButton("Disconnect", 100);
@@ -78,7 +78,7 @@ void SubscribeToEvents()
 
     // Subscribe to log messages so that we can pipe them to the chat window
     SubscribeToEvent("LogMessage", "HandleLogMessage");
-    
+
     // Subscribe to network events
     SubscribeToEvent("NetworkMessage", "HandleNetworkMessage");
     SubscribeToEvent("ServerConnected", "HandleConnectionStatus");
@@ -89,7 +89,7 @@ void SubscribeToEvents()
 Button@ CreateButton(const String&in text, int width)
 {
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
-    
+
     Button@ button = buttonContainer.CreateChild("Button");
     button.SetStyleAuto();
     button.SetFixedWidth(width);
@@ -98,7 +98,7 @@ Button@ CreateButton(const String&in text, int width)
     buttonText.SetFont(font, 12);
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.text = text;
-    
+
     return button;
 }
 
@@ -111,7 +111,7 @@ void ShowChatText(const String& row)
     String allRows;
     for (uint i = 0; i < chatHistory.length; ++i)
         allRows += chatHistory[i] + "\n";
-    
+
     chatHistoryText.text = allRows;
 }
 
@@ -119,7 +119,7 @@ void UpdateButtons()
 {
     Connection@ serverConnection = network.serverConnection;
     bool serverRunning = network.serverRunning;
-    
+
     // Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     sendButton.visible = serverConnection !is null;
     connectButton.visible = serverConnection is null && !serverRunning;
@@ -139,7 +139,7 @@ void HandleSend(StringHash eventType, VariantMap& eventData)
         return; // Do not send an empty message
 
     Connection@ serverConnection = network.serverConnection;
-    
+
     if (serverConnection !is null)
     {
         // A VectorBuffer object is convenient for constructing a message to send
@@ -159,12 +159,12 @@ void HandleConnect(StringHash eventType, VariantMap& eventData)
         address = "localhost"; // Use localhost to connect if nothing else specified
     // Empty the text edit after reading the address to connect to
     textEdit.text = "";
-    
+
     // Connect to server, do not specify a client scene as we are not using scene replication, just messages.
     // At connect time we could also send identity parameters (such as username) in a VariantMap, but in this
     // case we skip it for simplicity
     network.Connect(address, CHAT_SERVER_PORT, null);
-    
+
     UpdateButtons();
 }
 
@@ -177,7 +177,7 @@ void HandleDisconnect(StringHash eventType, VariantMap& eventData)
     // Or if we were running a server, stop it
     else if (network.serverRunning)
         network.StopServer();
-    
+
     UpdateButtons();
 }
 
@@ -195,21 +195,21 @@ void HandleNetworkMessage(StringHash eventType, VariantMap& eventData)
     {
         VectorBuffer msg = eventData["Data"].GetBuffer();
         String text = msg.ReadString();
-        
+
         // If we are the server, prepend the sender's IP address and port and echo to everyone
         // If we are a client, just display the message
         if (network.serverRunning)
         {
             Connection@ sender = eventData["Connection"].GetPtr();
-            
+
             text = sender.ToString() + " " + text;
-            
+
             VectorBuffer sendMsg;
             sendMsg.WriteString(text);
             // Broadcast as in-order and reliable
             network.BroadcastMessage(MSG_CHAT, true, true, sendMsg);
         }
-        
+
         ShowChatText(text);
     }
 }
@@ -217,4 +217,15 @@ void HandleNetworkMessage(StringHash eventType, VariantMap& eventData)
 void HandleConnectionStatus(StringHash eventType, VariantMap& eventData)
 {
     UpdateButtons();
-}
+}
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 44 - 40
Bin/Data/Scripts/17_SceneReplication.as

@@ -23,8 +23,6 @@ class Client
     Node@ object;
 }
 
-Scene@ scene_;
-Node@ cameraNode;
 Text@ instructionsText;
 UIElement@ buttonContainer;
 LineEdit@ textEdit;
@@ -32,8 +30,6 @@ Button@ connectButton;
 Button@ disconnectButton;
 Button@ startServerButton;
 Array<Client> clients;
-float yaw = 0.0f;
-float pitch = 1.0f;
 uint clientObjectID = 0;
 
 void Start()
@@ -43,10 +39,10 @@ void Start()
 
     // Create the scene content
     CreateScene();
-    
+
     // Create the UI content
     CreateUI();
-    
+
     // Setup the viewport for displaying the scene
     SetupViewport();
 
@@ -62,7 +58,7 @@ void CreateScene()
     // when a client connects
     scene_.CreateComponent("Octree", LOCAL);
     scene_.CreateComponent("PhysicsWorld", LOCAL);
-    
+
     // All static scene content and the camera are also created as local, so that they are unaffected by scene replication and are
     // not removed from the client upon connection. Create a Zone component first for ambient lighting & fog control.
     Node@ zoneNode = scene_.CreateChild("Zone", LOCAL);
@@ -71,7 +67,7 @@ void CreateScene()
     zone.ambientColor = Color(0.1f, 0.1f, 0.1f);
     zone.fogStart = 100.0f;
     zone.fogEnd = 300.0f;
-    
+
     // Create a directional light without shadows
     Node@ lightNode = scene_.CreateChild("DirectionalLight", LOCAL);
     lightNode.direction = Vector3(0.5f, -1.0f, 0.5f);
@@ -79,7 +75,7 @@ void CreateScene()
     light.lightType = LIGHT_DIRECTIONAL;
     light.color = Color(0.2f, 0.2f, 0.2f);
     light.specularIntensity = 1.0f;
-    
+
     // Create a "floor" consisting of several tiles. Make the tiles physical but leave small cracks between them
     for (int y = -20; y <= 20; ++y)
     {
@@ -98,12 +94,12 @@ void CreateScene()
             shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
         }
     }
-    
+
     // Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_.CreateChild("Camera", LOCAL);
     Camera@ camera = cameraNode.CreateComponent("Camera");
     camera.farClip = 300.0f;
-    
+
     // Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
 }
@@ -121,7 +117,7 @@ void CreateUI()
     ui.cursor = cursor;
     // Set starting position of the cursor at the rendering window center
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
-    
+
     // Construct the instructions text element
     instructionsText = ui.root.CreateChild("Text");
     instructionsText.text = "Use WASD keys to move and RMB to rotate view";
@@ -137,14 +133,14 @@ void CreateUI()
     buttonContainer.SetFixedSize(500, 20);
     buttonContainer.SetPosition(20, 20);
     buttonContainer.layoutMode = LM_HORIZONTAL;
-    
+
     textEdit = buttonContainer.CreateChild("LineEdit");
     textEdit.SetStyleAuto();
-    
+
     connectButton = CreateButton("Connect", 90);
     disconnectButton = CreateButton("Disconnect", 100);
     startServerButton = CreateButton("Start Server", 110);
-    
+
     UpdateButtons();
 }
 
@@ -159,12 +155,12 @@ void SubscribeToEvents()
 {
     // Subscribe to fixed timestep physics updates for setting or applying controls
     SubscribeToEvent("PhysicsPreStep", "HandlePhysicsPreStep");
-    
+
     // Subscribe HandlePostUpdate() method for processing update events. Subscribe to PostUpdate instead
     // of the usual Update so that physics simulation has already proceeded for the frame, and can
     // accurately follow the object with the camera
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
-    
+
     // Subscribe to button actions
     SubscribeToEvent(connectButton, "Released", "HandleConnect");
     SubscribeToEvent(disconnectButton, "Released", "HandleDisconnect");
@@ -183,16 +179,16 @@ void SubscribeToEvents()
 Button@ CreateButton(const String& text, int width)
 {
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
-    
+
     Button@ button = buttonContainer.CreateChild("Button");
     button.SetStyleAuto();
     button.SetFixedWidth(width);
-    
+
     Text@ buttonText = button.CreateChild("Text");
     buttonText.SetFont(font, 12);
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.text = text;
-    
+
     return button;
 }
 
@@ -200,7 +196,7 @@ void UpdateButtons()
 {
     Connection@ serverConnection = network.serverConnection;
     bool serverRunning = network.serverRunning;
-    
+
     // Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     connectButton.visible = serverConnection is null && !serverRunning;
     disconnectButton.visible = serverConnection !is null || serverRunning;
@@ -217,7 +213,7 @@ Node@ CreateControllableObject()
     StaticModel@ ballObject = ballNode.CreateComponent("StaticModel");
     ballObject.model = cache.GetResource("Model", "Models/Sphere.mdl");
     ballObject.material = cache.GetResource("Material", "Materials/StoneSmall.xml");
-    
+
     // Create the physics components
     RigidBody@ body = ballNode.CreateComponent("RigidBody");
     body.mass = 1.0f;
@@ -227,12 +223,12 @@ Node@ CreateControllableObject()
     body.angularDamping = 0.5f;
     CollisionShape@ shape = ballNode.CreateComponent("CollisionShape");
     shape.SetSphere(1.0f);
-    
+
     // Create a random colored point light at the ball so that can see better where is going
     Light@ light = ballNode.CreateComponent("Light");
     light.range = 3.0f;
     light.color = Color(0.5f + (RandomInt() & 1) * 0.5f, 0.5f + (RandomInt() & 1) * 0.5f, 0.5f + (RandomInt() & 1) * 0.5f);
-    
+
     return ballNode;
 }
 
@@ -240,10 +236,10 @@ void MoveCamera()
 {
     // Right mouse button controls mouse cursor visibility: hide when pressed
     ui.cursor.visible = !input.mouseButtonDown[MOUSEB_RIGHT];
-    
+
     // Mouse sensitivity as degrees per pixel
     const float MOUSE_SENSITIVITY = 0.1f;
-    
+
     // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch and only move the camera
     // when the cursor is hidden
     if (!ui.cursor.visible)
@@ -253,7 +249,7 @@ void MoveCamera()
         pitch += MOUSE_SENSITIVITY * mouseMove.y;
         pitch = Clamp(pitch, 1.0f, 90.0f);
     }
-    
+
     // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
     cameraNode.rotation = Quaternion(pitch, yaw, 0.0f);
 
@@ -265,13 +261,13 @@ void MoveCamera()
         if (ballNode !is null)
         {
             const float CAMERA_DISTANCE = 5.0f;
-            
+
             // Move camera some distance away from the ball
             cameraNode.position = ballNode.position + cameraNode.rotation * Vector3(0.0f, 0.0f, -1.0f) * CAMERA_DISTANCE;
             showInstructions = true;
         }
     }
-    
+
     instructionsText.visible = showInstructions;
 }
 
@@ -287,15 +283,15 @@ void HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
     // and sets them to its server connection object, so that they will be sent to the server automatically at a
     // fixed rate, by default 30 FPS. The server will actually apply the controls (authoritative simulation.)
     Connection@ serverConnection = network.serverConnection;
-    
+
     // Client: collect controls
     if (serverConnection !is null)
     {
         Controls controls;
-        
+
         // Copy mouse yaw
         controls.yaw = yaw;
-        
+
         // Only apply WASD controls if there is no focused UI element
         if (ui.focusElement is null)
         {
@@ -304,7 +300,7 @@ void HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
             controls.Set(CTRL_LEFT, input.keyDown['A']);
             controls.Set(CTRL_RIGHT, input.keyDown['D']);
         }
-        
+
         serverConnection.controls = controls;
         // In case the server wants to do position-based interest management using the NetworkPriority components, we should also
         // tell it our observer (camera) position. In this sample it is not in use, but eg. the NinjaSnowWar game uses it
@@ -323,9 +319,9 @@ void HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
 
             // Torque is relative to the forward vector
             Quaternion rotation(0.0f, connection.controls.yaw, 0.0f);
-            
+
             const float MOVE_TORQUE = 3.0f;
-            
+
             // Movement torque is applied before each simulation step, which happen at 60 FPS. This makes the simulation
             // independent from rendering framerate. We could also apply forces (which would enable in-air control),
             // but want to emphasize that it's a ball which should only control its motion by rolling along the ground
@@ -346,11 +342,11 @@ void HandleConnect(StringHash eventType, VariantMap& eventData)
     String address = textEdit.text.Trimmed();
     if (address.empty)
         address = "localhost"; // Use localhost to connect if nothing else specified
-    
+
     // Connect to server, specify scene to use as a client for replication
     clientObjectID = 0; // Reset own object ID from possible previous connection
     network.Connect(address, SERVER_PORT, scene_);
-    
+
     UpdateButtons();
 }
 
@@ -371,14 +367,14 @@ void HandleDisconnect(StringHash eventType, VariantMap& eventData)
         network.StopServer();
         scene_.Clear(true, false);
     }
-    
+
     UpdateButtons();
 }
 
 void HandleStartServer(StringHash eventType, VariantMap& eventData)
 {
     network.StartServer(SERVER_PORT);
-    
+
     UpdateButtons();
 }
 
@@ -392,7 +388,7 @@ void HandleClientConnected(StringHash eventType, VariantMap& eventData)
     // When a client connects, assign to scene to begin scene replication
     Connection@ newConnection = eventData["Connection"].GetPtr();
     newConnection.scene = scene_;
-    
+
     // Then create a controllable object for that client
     Node@ newObject = CreateControllableObject();
     Client newClient;
@@ -425,3 +421,11 @@ void HandleClientObjectID(StringHash eventType, VariantMap& eventData)
 {
     clientObjectID = eventData["ID"].GetUInt();
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 108 - 49
Bin/Data/Scripts/18_CharacterDemo.as

@@ -22,8 +22,8 @@ const float BRAKE_FORCE = 0.2f;
 const float JUMP_FORCE = 7.0f;
 const float YAW_SENSITIVITY = 0.1f;
 const float INAIR_THRESHOLD_TIME = 0.1f;
+bool firstPerson = false; // First person camera flag
 
-Scene@ scene_;
 Node@ characterNode;
 
 void Start()
@@ -40,13 +40,6 @@ void Start()
     // Create the UI content
     CreateInstructions();
 
-    // Activate mobile stuff when appropriate
-    if (GetPlatform() == "Android" || GetPlatform() == "iOS")
-    {
-        SetLogoVisible(false);
-        InitTouchInput();
-    }
-
     // Subscribe to necessary events
     SubscribeToEvents();
 }
@@ -203,9 +196,8 @@ void SubscribeToEvents()
     // Subscribe to PostUpdate event for updating the camera position after physics simulation
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
 
-    // Subscribe to mobile touch events
-    if (touchEnabled)
-        SubscribeToTouchEvents();
+    // Unsubscribe the SceneUpdate event from base class as the camera node is being controlled in HandlePostUpdate() in this sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -220,48 +212,70 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Clear previous controls
     character.controls.Set(CTRL_FORWARD | CTRL_BACK | CTRL_LEFT | CTRL_RIGHT | CTRL_JUMP, false);
 
+    // Update controls using touch utility
     if (touchEnabled)
-    {
-        // Update controls using touch (mobile)
         UpdateTouches(character.controls);
-    }
-    else
+
+    // Update controls using keys (desktop)
+    if (ui.focusElement is null)
     {
-        // Update controls using keys (desktop)
-        if (ui.focusElement is null)
+        if (touchEnabled || !useGyroscope)
         {
             character.controls.Set(CTRL_FORWARD, input.keyDown['W']);
             character.controls.Set(CTRL_BACK, input.keyDown['S']);
             character.controls.Set(CTRL_LEFT, input.keyDown['A']);
             character.controls.Set(CTRL_RIGHT, input.keyDown['D']);
-            character.controls.Set(CTRL_JUMP, input.keyDown[KEY_SPACE]);
+        }
+        character.controls.Set(CTRL_JUMP, input.keyDown[KEY_SPACE]);
 
-            // Add character yaw & pitch from the mouse motion
+        // Add character yaw & pitch from the mouse motion or touch input
+        if (touchEnabled)
+        {
+            for (uint i = 0; i < input.numTouches; ++i)
+            {
+                TouchState@ state = input.touches[i];
+                if (state.touchedElement is null) // Touch on empty space
+                {
+                    Camera@ camera = cameraNode.GetComponent("Camera");
+                    if (camera is null)
+                        return;
+
+                    character.controls.yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x;
+                    character.controls.pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y;
+                }
+            }
+        }
+        else
+        {
             character.controls.yaw += input.mouseMoveX * YAW_SENSITIVITY;
             character.controls.pitch += input.mouseMoveY * YAW_SENSITIVITY;
-            // Limit pitch
-            character.controls.pitch = Clamp(character.controls.pitch, -80.0f, 80.0f);
+        }
+        // Limit pitch
+        character.controls.pitch = Clamp(character.controls.pitch, -80.0f, 80.0f);
 
-            // Switch between 1st and 3rd person
-            if (input.keyPress['F'])
-                firstPerson = newFirstPerson = !firstPerson;
+        // Switch between 1st and 3rd person
+        if (input.keyPress['F'])
+            firstPerson = !firstPerson;
 
-            // Check for loading / saving the scene
-            if (input.keyPress[KEY_F5])
-            {
-                File saveFile(fileSystem.programDir + "Data/Scenes/CharacterDemo.xml", FILE_WRITE);
-                scene_.SaveXML(saveFile);
-            }
-            if (input.keyPress[KEY_F7])
-            {
-                File loadFile(fileSystem.programDir + "Data/Scenes/CharacterDemo.xml", FILE_READ);
-                scene_.LoadXML(loadFile);
-                // After loading we have to reacquire the character scene node, as it has been recreated
-                // Simply find by name as there's only one of them
-                characterNode = scene_.GetChild("Jack", true);
-                if (characterNode is null)
-                    return;
-            }
+        // Turn on/off gyroscope on mobile platform
+        if (input.keyPress['G'])
+            useGyroscope = !useGyroscope;
+
+        // Check for loading / saving the scene
+        if (input.keyPress[KEY_F5])
+        {
+            File saveFile(fileSystem.programDir + "Data/Scenes/CharacterDemo.xml", FILE_WRITE);
+            scene_.SaveXML(saveFile);
+        }
+        if (input.keyPress[KEY_F7])
+        {
+            File loadFile(fileSystem.programDir + "Data/Scenes/CharacterDemo.xml", FILE_READ);
+            scene_.LoadXML(loadFile);
+            // After loading we have to reacquire the character scene node, as it has been recreated
+            // Simply find by name as there's only one of them
+            characterNode = scene_.GetChild("Jack", true);
+            if (characterNode is null)
+                return;
         }
     }
 
@@ -370,13 +384,13 @@ class Character : ScriptObject
             }
         }
     }
-    
+
     void FixedUpdate(float timeStep)
     {
         /// \todo Could cache the components for faster access instead of finding them each frame
         RigidBody@ body = node.GetComponent("RigidBody");
         AnimationController@ animCtrl = node.GetComponent("AnimationController");
-        
+
         // Update the in air timer. Reset if grounded
         if (!onGround)
             inAirTimer += timeStep;
@@ -384,14 +398,14 @@ class Character : ScriptObject
             inAirTimer = 0.0f;
         // When character has been in air less than 1/10 second, it's still interpreted as being on ground
         bool softGrounded = inAirTimer < INAIR_THRESHOLD_TIME;
-        
+
         // Update movement & animation
         Quaternion rot = node.rotation;
         Vector3 moveDir(0.0f, 0.0f, 0.0f);
         Vector3 velocity = body.linearVelocity;
         // Velocity on the XZ plane
         Vector3 planeVelocity(velocity.x, 0.0f, velocity.z);
-        
+
         if (controls.IsDown(CTRL_FORWARD))
             moveDir += Vector3(0.0f, 0.0f, 1.0f);
         if (controls.IsDown(CTRL_BACK))
@@ -400,20 +414,20 @@ class Character : ScriptObject
             moveDir += Vector3(-1.0f, 0.0f, 0.0f);
         if (controls.IsDown(CTRL_RIGHT))
             moveDir += Vector3(1.0f, 0.0f, 0.0f);
-        
+
         // Normalize move vector so that diagonal strafing is not faster
         if (moveDir.lengthSquared > 0.0f)
             moveDir.Normalize();
-        
+
         // If in air, allow control, but slower than when on ground
         body.ApplyImpulse(rot * moveDir * (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
-        
+
         if (softGrounded)
         {
             // When on ground, apply a braking force to limit maximum ground velocity
             Vector3 brakeForce = -planeVelocity * BRAKE_FORCE;
             body.ApplyImpulse(brakeForce);
-            
+
             // Jump. Must release jump control inbetween jumps
             if (controls.IsDown(CTRL_JUMP))
             {
@@ -426,7 +440,7 @@ class Character : ScriptObject
             else
                 okToJump = true;
         }
-        
+
         // Play walk animation if moving on ground, otherwise fade it out
         if (softGrounded && !moveDir.Equals(Vector3(0.0f, 0.0f, 0.0f)))
             animCtrl.PlayExclusive("Models/Jack_Walk.ani", 0, true, 0.2f);
@@ -439,3 +453,48 @@ class Character : ScriptObject
         onGround = false;
     }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element\">" +
+        "        <element type=\"Button\">" +
+        "            <attribute name=\"Name\" value=\"Button3\" />" +
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" +
+        "            <attribute name=\"Size\" value=\"96 96\" />" +
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" +
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" +
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" +
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" +
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" +
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"Label\" />" +
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" +
+        "                <attribute name=\"Text\" value=\"Gyroscope\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "                <attribute name=\"Text\" value=\"G\" />" +
+        "            </element>" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">1st/3rd</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"F\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Jump</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 31 - 9
Bin/Data/Scripts/19_VehicleDemo.as

@@ -17,15 +17,13 @@ const float ENGINE_POWER = 10.0f;
 const float DOWN_FORCE = 10.0f;
 const float MAX_WHEEL_ANGLE = 22.5f;
 
-Scene@ scene_;
-Node@ cameraNode;
 Node@ vehicleNode;
 
 void Start()
 {
     // Execute the common startup for samples
     SampleStart();
-    
+
     // Create static scene content
     CreateScene();
 
@@ -52,7 +50,7 @@ void CreateScene()
     Camera@ camera = cameraNode.CreateComponent("Camera");
     camera.farClip = 500.0f;
     renderer.viewports[0] = Viewport(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");
@@ -84,7 +82,7 @@ void CreateScene()
     // 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.occluder = true;
-    
+
     RigidBody@ body = terrainNode.CreateComponent("RigidBody");
     body.collisionLayer = 2; // Use layer bitmask 2 for static geometry
     CollisionShape@ shape = terrainNode.CreateComponent("CollisionShape");
@@ -105,7 +103,7 @@ void CreateScene()
         object.model = cache.GetResource("Model", "Models/Mushroom.mdl");
         object.material = cache.GetResource("Material", "Materials/Mushroom.xml");
         object.castShadows = true;
-        
+
         RigidBody@ body = objectNode.CreateComponent("RigidBody");
         body.collisionLayer = 2;
         CollisionShape@ shape = objectNode.CreateComponent("CollisionShape");
@@ -128,7 +126,7 @@ void CreateInstructions()
 {
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
-    instructionText.text = "Use WASD keys to drive, mouse to rotate camera\n"
+    instructionText.text = "Use WASD keys to drive, mouse/touch 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
@@ -147,6 +145,9 @@ void SubscribeToEvents()
 
     // Subscribe to PostUpdate event for updating the camera position after physics simulation
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
+
+    // Unsubscribe the SceneUpdate event from base class as the camera node is being controlled in HandlePostUpdate() in this sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -167,8 +168,27 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
         vehicle.controls.Set(CTRL_RIGHT, input.keyDown['D']);
 
         // Add yaw & pitch from the mouse motion. Used only for the camera, does not affect motion
-        vehicle.controls.yaw += input.mouseMoveX * YAW_SENSITIVITY;
-        vehicle.controls.pitch += input.mouseMoveY * YAW_SENSITIVITY;
+        if (touchEnabled)
+        {
+            for (uint i = 0; i < input.numTouches; ++i)
+            {
+                TouchState@ state = input.touches[i];
+                if (state.touchedElement is null) // Touch on empty space
+                {
+                    Camera@ camera = cameraNode.GetComponent("Camera");
+                    if (camera is null)
+                        return;
+
+                    vehicle.controls.yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x;
+                    vehicle.controls.pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y;
+                }
+            }
+        }
+        else
+        {
+            vehicle.controls.yaw += input.mouseMoveX * YAW_SENSITIVITY;
+            vehicle.controls.pitch += input.mouseMoveY * YAW_SENSITIVITY;
+        }
         // Limit pitch
         vehicle.controls.pitch = Clamp(vehicle.controls.pitch, 0.0f, 80.0f);
 
@@ -369,3 +389,5 @@ class Vehicle : ScriptObject
     }
 }
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 23 - 6
Bin/Data/Scripts/20_HugeObjectCount.as

@@ -7,11 +7,7 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
 Array<Node@> boxNodes;
-float yaw = 0.0f;
-float pitch = 0.0f;
 bool animate = false;
 bool useGroups = false;
 
@@ -207,11 +203,32 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
         useGroups = !useGroups;
         CreateScene();
     }
-    
+
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
-    
+
     // Animate scene if enabled
     if (animate)
         AnimateObjects(timeStep);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Group</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"G\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Animation</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 3 - 4
Bin/Data/Scripts/23_Water.as

@@ -5,14 +5,10 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
 Node@ reflectionCameraNode;
 Node@ waterNode;
 Plane waterPlane;
 Plane waterClipPlane;
-float yaw = 0.0f;
-float pitch = 0.0f;
 
 void Start()
 {
@@ -225,3 +221,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 28 - 7
Bin/Data/Scripts/24_Urho2DSprite.as

@@ -6,8 +6,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
 Array<Node@> spriteNodes;
 
 void Start()
@@ -65,7 +63,7 @@ void CreateScene()
     {
         Node@ spriteNode = scene_.CreateChild("StaticSprite2D");
         spriteNode.position = Vector3(Random(-halfWidth, halfWidth), Random(-halfHeight, halfHeight), 0.0f);
-        
+
         StaticSprite2D@ staticSprite = spriteNode.CreateComponent("StaticSprite2D");
         // Set color
         staticSprite.color = Color(Random(1.0f), Random(1.0f), Random(1.0f), 1.0f);
@@ -73,7 +71,7 @@ void CreateScene()
         staticSprite.blendMode = BLEND_ALPHA;
         // Set sprite
         staticSprite.sprite = sprite;
-        
+
         spriteNode.vars["MoveSpeed"] = Vector3(Random(-2.0f, 2.0f), Random(-2.0f, 2.0f), 0.0f);
         spriteNode.vars["RotateSpeed"] = Random(-90.0f, 90.0f);
 
@@ -123,7 +121,7 @@ void MoveCamera(float timeStep)
 
     // Movement speed as world units per second
     const float MOVE_SPEED = 4.0f;
-    
+
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     if (input.keyDown['W'])
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
@@ -139,7 +137,7 @@ void MoveCamera(float timeStep)
         Camera@ camera = cameraNode.GetComponent("Camera");
         camera.zoom = camera.zoom * 1.01f;
     }
-    
+
     if (input.keyDown[KEY_PAGEDOWN])
     {
         Camera@ camera = cameraNode.GetComponent("Camera");
@@ -151,6 +149,9 @@ void SubscribeToEvents()
 {
     // Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate");
+
+    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -171,7 +172,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
 
         Vector3 moveSpeed = spriteNode.vars["MoveSpeed"].GetVector3();
         Vector3 newPosition = spriteNode.position + moveSpeed * timeStep;
-        
+
         if (newPosition.x < -halfWidth || newPosition.x > halfWidth)
         {
             newPosition.x = spriteNode.position.x;
@@ -191,3 +192,23 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     }
 }
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 13 - 2
Bin/Data/Scripts/25_Urho2DParticle.as

@@ -6,8 +6,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
 Node@ particleNode;
 
 void Start()
@@ -92,7 +90,13 @@ void SetupViewport()
 
 void SubscribeToEvents()
 {
+    // Subscribe HandleMouseMove() function for tracking mouse/touch move events
     SubscribeToEvent("MouseMove", "HandleMouseMove");
+    if (touchEnabled)
+        SubscribeToEvent("TouchMove", "HandleMouseMove");
+
+    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 
 void HandleMouseMove(StringHash eventType, VariantMap& eventData)
@@ -106,3 +110,10 @@ void HandleMouseMove(StringHash eventType, VariantMap& eventData)
     }
 }
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 11 - 0
Bin/Data/Scripts/26_ConsoleInput.as

@@ -238,3 +238,14 @@ void HandleInput(const String&in input)
             Print("Please answer 'y' or 'n'.");
     }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 28 - 8
Bin/Data/Scripts/27_Urho2DPhysics.as

@@ -5,9 +5,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -49,7 +46,7 @@ void CreateScene()
 
     // Create 2D physics world component
     scene_.CreateComponent("PhysicsWorld2D");
-    
+
     Sprite2D@ boxSprite = cache.GetResource("Sprite2D", "Urho2D/Box.png");
     Sprite2D@ ballSprite = cache.GetResource("Sprite2D", "Urho2D/Ball.png");
 
@@ -57,10 +54,10 @@ void CreateScene()
     Node@ groundNode = scene_.CreateChild("Ground");
     groundNode.position = Vector3(0.0f, -3.0f, 0.0f);
     groundNode.scale = Vector3(200.0f, 1.0f, 0.0f);
-    
+
     // Create 2D rigid body for gound
     RigidBody2D@ groundBody = groundNode.CreateComponent("RigidBody2D");
-    
+
     StaticSprite2D@ groundSprite = groundNode.CreateComponent("StaticSprite2D");
     groundSprite.sprite = boxSprite;
 
@@ -146,7 +143,7 @@ void MoveCamera(float timeStep)
 
     // Movement speed as world units per second
     const float MOVE_SPEED = 4.0f;
-    
+
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     if (input.keyDown['W'])
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
@@ -162,7 +159,7 @@ void MoveCamera(float timeStep)
         Camera@ camera = cameraNode.GetComponent("Camera");
         camera.zoom = camera.zoom * 1.01f;
     }
-    
+
     if (input.keyDown[KEY_PAGEDOWN])
     {
         Camera@ camera = cameraNode.GetComponent("Camera");
@@ -174,6 +171,9 @@ void SubscribeToEvents()
 {
     // Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate");
+
+    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -185,3 +185,23 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     MoveCamera(timeStep);
 }
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 26 - 6
Bin/Data/Scripts/28_Urho2DPhysicsRope.as

@@ -6,9 +6,6 @@
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -51,7 +48,7 @@ void CreateScene()
     // Create 2D physics world component
     PhysicsWorld2D@ physicsWorld = scene_.CreateComponent("PhysicsWorld2D");
     physicsWorld.drawJoint = true;
-    
+
     // Create ground.
     Node@ groundNode = scene_.CreateChild("Ground");
     // Create 2D rigid body for gound
@@ -138,7 +135,7 @@ void MoveCamera(float timeStep)
 
     // Movement speed as world units per second
     const float MOVE_SPEED = 4.0f;
-    
+
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     if (input.keyDown['W'])
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
@@ -154,7 +151,7 @@ void MoveCamera(float timeStep)
         Camera@ camera = cameraNode.GetComponent("Camera");
         camera.zoom = camera.zoom * 1.01f;
     }
-    
+
     if (input.keyDown[KEY_PAGEDOWN])
     {
         Camera@ camera = cameraNode.GetComponent("Camera");
@@ -166,6 +163,9 @@ void SubscribeToEvents()
 {
     // Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate");
+
+    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -180,3 +180,23 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     physicsWorld.DrawDebugGeometry();
 }
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 6 - 8
Bin/Data/Scripts/30_LightAnimation.as

@@ -1,14 +1,9 @@
-/// Light animation example.
-/// This sample is base on StaticScene, and it demonstrates:
-///     - Usage of attribute animation for light color animation
+// Light animation example.
+// This sample is base on StaticScene, and it demonstrates:
+//     - Usage of attribute animation for light color animation
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -169,3 +164,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 6 - 8
Bin/Data/Scripts/31_MaterialAnimation.as

@@ -1,14 +1,9 @@
-/// Material animation example.
-/// This sample is base on StaticScene, and it demonstrates:
-///     - Usage of material shader animation for mush room material
+// Material animation example.
+// This sample is base on StaticScene, and it demonstrates:
+//     - Usage of material shader animation for mush room material
 
 #include "Scripts/Utilities/Sample.as"
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-
 void Start()
 {
     // Execute the common startup for samples
@@ -158,3 +153,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 100 - 7
Bin/Data/Scripts/Utilities/Sample.as

@@ -5,11 +5,29 @@
 //    - Toggle rendering options from the keys 1-8;
 //    - Take screenshots with key 9;
 //    - Handle Esc key down to hide Console or exit application;
+//    - Init touch input on mobile platform using screen joysticks (patched for each individual sample)
 
 Sprite@ logoSprite;
+Scene@ scene_;
+uint screenJoystickIndex = M_MAX_UNSIGNED; // Screen joystick index for navigational controls (mobile platforms only)
+uint screenJoystickSettingsIndex = M_MAX_UNSIGNED; // Screen joystick index for settings (mobile platforms only)
+bool touchEnabled = false; // Flag to indicate whether touch input has been enabled
+bool paused = false; // Pause flag
+bool drawDebug = false; // Draw debug geometry flag
+Node@ cameraNode; // Camera scene node
+float yaw = 0.0f; // Camera yaw angle
+float pitch = 0.0f; // Camera pitch angle
+const float TOUCH_SENSITIVITY = 2;
 
 void SampleStart()
 {
+    if (GetPlatform() == "Android" || GetPlatform() == "iOS" || input.touchEmulation)
+        // On mobile platform, enable touch by adding a screen joystick
+        InitTouchInput();
+    else if (input.numJoysticks == 0)
+        // On desktop platform, do not detect touch when we already got a joystick
+        SubscribeToEvent("TouchBegin", "HandleTouchBegin");
+
     // Create logo
     CreateLogo();
 
@@ -21,6 +39,25 @@ void SampleStart()
 
     // Subscribe key down event
     SubscribeToEvent("KeyDown", "HandleKeyDown");
+
+    // Subscribe scene update event
+    SubscribeToEvent("SceneUpdate", "HandleSceneUpdate");
+}
+
+void InitTouchInput()
+{
+    touchEnabled = true;
+
+    XMLFile@ layout = cache.GetResource("XMLFile", "UI/ScreenJoystick_Samples.xml");
+    if (!patchInstructions.empty)
+    {
+        // Patch the screen joystick layout further on demand
+        XMLFile@ patchFile = XMLFile();
+        if (patchFile.FromString(patchInstructions))
+            layout.Patch(patchFile);
+    }
+    screenJoystickIndex = input.AddScreenJoystick(layout, cache.GetResource("XMLFile", "UI/DefaultStyle.xml"));
+    input.screenJoystickVisible[0] = true;
 }
 
 void SetLogoVisible(bool enable)
@@ -56,10 +93,10 @@ void CreateLogo()
 
     // Set logo sprite alignment
     logoSprite.SetAlignment(HA_LEFT, VA_BOTTOM);
-    
+
     // Make logo not fully opaque to show the scene underneath
     logoSprite.opacity = 0.75f;
-    
+
     // Set a low priority for the logo so that other UI elements can be drawn on top
     logoSprite.priority = -100;
 }
@@ -81,6 +118,7 @@ void CreateConsoleAndDebugHud()
     // Create console
     Console@ console = engine.CreateConsole();
     console.defaultStyle = xmlFile;
+    console.background.opacity = 0.8f;
 
     // Create debug HUD
     DebugHud@ debugHud = engine.CreateDebugHud();
@@ -111,8 +149,22 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
     // Common rendering quality controls, only when UI has no focused element
     if (ui.focusElement is null)
     {
+        // Preferences / Pause
+        if (key == KEY_SELECT && touchEnabled)
+        {
+            paused = !paused;
+
+            if (screenJoystickSettingsIndex == M_MAX_UNSIGNED)
+            {
+                // Lazy initialization
+                screenJoystickSettingsIndex = input.AddScreenJoystick(cache.GetResource("XMLFile", "UI/ScreenJoystickSettings_Samples.xml"), cache.GetResource("XMLFile", "UI/DefaultStyle.xml"));
+            }
+            else
+                input.screenJoystickVisible[screenJoystickSettingsIndex] = paused;
+        }
+
         // Texture quality
-        if (key == '1')
+        else if (key == '1')
         {
             int quality = renderer.textureQuality;
             ++quality;
@@ -130,7 +182,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 quality = QUALITY_LOW;
             renderer.materialQuality = quality;
         }
-        
+
         // Specular lighting
         else if (key == '3')
             renderer.specularLighting = !renderer.specularLighting;
@@ -148,7 +200,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 shadowMapSize = 512;
             renderer.shadowMapSize = shadowMapSize;
         }
-        
+
         // Shadow depth and filtering quality
         else if (key == '6')
         {
@@ -158,7 +210,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 quality = SHADOWQUALITY_LOW_16BIT;
             renderer.shadowQuality = quality;
         }
-        
+
         // Occlusion culling
         else if (key == '7')
         {
@@ -166,7 +218,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
-        
+
         // Instancing
         else if (key == '8')
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
@@ -182,3 +234,44 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
         }
     }
 }
+
+void HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
+{
+    // Move the camera by touch, if the camera node is initialized by descendant sample class
+    if (touchEnabled && cameraNode !is null)
+    {
+        for (uint i = 0; i < input.numTouches; ++i)
+        {
+            TouchState@ state = input.touches[i];
+            if (state.touchedElement is null) // Touch on empty space
+            {
+                if (state.delta.x !=0 || state.delta.y !=0)
+                {
+                    Camera@ camera = cameraNode.GetComponent("Camera");
+                    if (camera is null)
+                        return;
+
+                    yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x;
+                    pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y;
+
+                    // Construct new orientation for the camera scene node from yaw and pitch; roll is fixed to zero
+                    cameraNode.rotation = Quaternion(pitch, yaw, 0.0f);
+                }
+                else
+                {
+                    // Move the cursor to the touch position
+                    Cursor@ cursor = ui.cursor;
+                    if (cursor !is null && cursor.visible)
+                        cursor.position = state.position;
+                }
+            }
+        }
+    }
+}
+
+void HandleTouchBegin(StringHash eventType, VariantMap& eventData)
+{
+    // On some platforms like Windows the presence of touch input can only be detected dynamically
+    InitTouchInput();
+    UnsubscribeFromEvent("TouchBegin");
+}

+ 21 - 163
Bin/Data/Scripts/Utilities/Touch.as

@@ -1,144 +1,47 @@
 // Mobile framework for Android/iOS
 // Gamepad from NinjaSnowWar
-// Gyroscope (activated by default)
 // Touches patterns:
 //     - 1 finger touch  = pick object through raycast
 //     - 1 or 2 fingers drag  = rotate camera
-//     - 3 fingers touch = switch between first/third person view
-//     - 4 fingers touch = switch shadows on/off
 //     - 2 fingers sliding in opposite direction (up/down) = zoom in/out
 
-// 3 fingers touch & 4 fingers touch could be used to switch gyroscope on/off, activate/deactivate secondary viewport, activate a panel GUI, switch debug HUD/geometry, toggle console, switch the gyroscope...
+// Setup: Call the update function 'UpdateTouches()' from HandleUpdate or equivalent update handler function
 
-// Setup:
-// - On init, call this script using '#include "Scripts/Utilities/Touch.as"' then 'InitTouchInput()' on mobile platforms
-//   -> to detect platform, use 'if (GetPlatform() == "Android" || GetPlatform() == "iOS")'
-// - Subscribe to touch events (Begin, Move, End) using 'SubscribeToTouchEvents()'
-// - Call the update function 'UpdateTouches()' from HandleUpdate or equivalent update handler function
-
-const float TOUCH_SENSITIVITY = 5.0;
 const float GYROSCOPE_THRESHOLD = 0.1;
 const float CAMERA_MIN_DIST = 1.0f;
-const float CAMERA_INITIAL_DIST = 5.0f;
 const float CAMERA_MAX_DIST = 20.0f;
 
-bool firstPerson = false;
-bool touchEnabled = false;
 bool zoom = false;
-bool newFirstPerson = false;
-bool shadowMode = true;
-
-Node@ cameraNode;
-float cameraDistance = CAMERA_INITIAL_DIST;
-
-BorderImage@ moveButton;
-BorderImage@ fireButton;
-int touchButtonSize = 96;
-int touchButtonBorder = 12;
-int moveTouchID = -1;
-int rotateTouchID = -1;
-int fireTouchID = -1;
-
-void InitTouchInput() // Create Gamepad Buttons
-{
-    moveButton = ui.root.CreateChild("BorderImage");
-    moveButton.texture = cache.GetResource("Texture2D", "Textures/TouchInput.png");
-    moveButton.imageRect = IntRect(0, 0, 96, 96);
-    moveButton.SetAlignment(HA_LEFT, VA_BOTTOM);
-    moveButton.SetPosition(touchButtonBorder, -touchButtonBorder);
-    moveButton.SetSize(touchButtonSize, touchButtonSize);
-    moveButton.opacity = 0.25;
-
-    fireButton = ui.root.CreateChild("BorderImage");
-    fireButton.texture = cache.GetResource("Texture2D", "Textures/TouchInput.png");
-    fireButton.imageRect = IntRect(96, 0, 192, 96);
-    fireButton.SetAlignment(HA_RIGHT, VA_BOTTOM);
-    fireButton.SetPosition(-touchButtonBorder, -touchButtonBorder);
-    fireButton.SetSize(touchButtonSize, touchButtonSize);
-    fireButton.opacity = 0.25;
-    
-    touchEnabled = true;
-}
-
-void SubscribeToTouchEvents()
-{
-    SubscribeToEvent("TouchBegin", "HandleTouchBegin");
-    SubscribeToEvent("TouchEnd", "HandleTouchEnd");
-}
+bool useGyroscope = false;
+float cameraDistance = 5.0;
 
 void UpdateTouches(Controls& controls) // Called from HandleUpdate
 {
-    Camera@ camera = cameraNode.GetComponent("Camera");
     zoom = false; // reset bool
 
-    // Touch Inputs
-    if (touchEnabled)
+    // Zoom in/out
+    if (input.numTouches == 2)
     {
-        // Zoom in/out
-        if (input.numTouches == 2)
-        {
-            TouchState@ touch1 = input.touches[0];
-            TouchState@ touch2 = input.touches[1];
-
-            // Check for zoom pattern (touches moving in opposite directions)
-            if ((touch1.delta.y > 0 && touch2.delta.y < 0) || (touch1.delta.y < 0 && touch2.delta.y > 0))
-                zoom = true;
-            else 
-                zoom = false;
-
-            if (zoom)
-            {
-                int sens = 0;
-                // Check for zoom direction (in/out)
-                if (Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y))
-                    sens = -1;
-                else
-                    sens = 1;
-                cameraDistance += Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50;
-                cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST); // Restrict zoom range to [1;20]
-            }
-        }
-
-        // Switch 1st/3rd person mode
-        if (input.numTouches == 3)
-            newFirstPerson = !firstPerson;
+        TouchState@ touch1 = input.touches[0];
+        TouchState@ touch2 = input.touches[1];
 
-        // Switch draw debug
-        if (input.numTouches == 4)
-            shadowMode = !renderer.drawShadows;
+        // Check for zoom pattern (touches moving in opposite directions)
+        if (touch1.touchedElement is null && touch2.touchedElement is null && ((touch1.delta.y > 0 && touch2.delta.y < 0) || (touch1.delta.y < 0 && touch2.delta.y > 0)))
+            zoom = true;
+        else
+            zoom = false;
 
-        // Rotate and Move
-        if (!zoom)
+        if (zoom)
         {
-            for (uint i = 0; i < input.numTouches; ++i)
-            {
-                TouchState@ touch = input.touches[i];
-
-                if (touch.touchID == rotateTouchID)
-                {
-                    controls.yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.x;
-                    controls.pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.y;
-                    controls.pitch = Clamp(controls.pitch, -80.0f, 80.0f); // Limit pitch
-                }
-
-                if (touch.touchID == moveTouchID)
-                {
-                    int relX = touch.position.x - moveButton.screenPosition.x - touchButtonSize / 2;
-                    int relY = touch.position.y - moveButton.screenPosition.y - touchButtonSize / 2;
-                    if (relY < 0 && Abs(relX * 3 / 2) < Abs(relY))
-                        controls.Set(CTRL_FORWARD, true);
-                    if (relY > 0 && Abs(relX * 3 / 2) < Abs(relY))
-                        controls.Set(CTRL_BACK, true);
-                    if (relX < 0 && Abs(relY * 3 / 2) < Abs(relX))
-                        controls.Set(CTRL_LEFT, true);
-                    if (relX > 0 && Abs(relY * 3 / 2) < Abs(relX))
-                        controls.Set(CTRL_RIGHT, true);
-                }
-            }
+            int sens = 0;
+            // Check for zoom direction (in/out)
+            if (Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y))
+                sens = -1;
+            else
+                sens = 1;
+            cameraDistance += Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50;
+            cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST); // Restrict zoom range to [1;20]
         }
-
-        if (fireTouchID >= 0)
-            controls.Set(CTRL_JUMP, true);
     }
 
     // Gyroscope (emulated by SDL through a virtual joystick)
@@ -158,48 +61,3 @@ void UpdateTouches(Controls& controls) // Called from HandleUpdate
         }
     }
 }
-
-void HandleTouchBegin(StringHash eventType, VariantMap& eventData)
-{
-    int touchID = eventData["TouchID"].GetInt(); // Get #touches or dragging value
-    IntVector2 pos(eventData["X"].GetInt(), eventData["Y"].GetInt()); // Get touch coordinates
-    UIElement@ element = ui.GetElementAt(pos, false); // Get gamepad UIElement touched (if any)
-
-    // Check for gamepad button touched. If none, rotate
-    if (element is moveButton)
-        moveTouchID = touchID;
-    else if (element is fireButton)
-        fireTouchID = touchID;
-    else
-        rotateTouchID = touchID;
-
-    // Init Raycast (for example to acquire a target)
-    Camera@ camera = cameraNode.GetComponent("Camera");
-    Ray cameraRay = camera.GetScreenRay(float(eventData["X"].GetInt()) / graphics.width, float(eventData["Y"].GetInt()) / graphics.height);
-
-    // Raycast of RigidBodies
-    PhysicsRaycastResult result = scene_.physicsWorld.RaycastSingle(cameraRay, camera.farClip, 2); // NB: here we restrict targets to layer 2
-    if (result.body !is null)
-        log.Info("Physics raycast hit " + result.body.node.name);
-
-    // Raycast of drawable components (for targets without physics)
-    RayQueryResult result2 = scene_.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, camera.farClip, DRAWABLE_GEOMETRY);
-    if (result2.drawable !is null)
-        log.Info("Drawable raycast hit " + result2.drawable.node.name);
-}
-
-void HandleTouchEnd(StringHash eventType, VariantMap& eventData)
-{
-    int touchID = eventData["TouchID"].GetInt();
-
-    if (touchID == moveTouchID)
-        moveTouchID = -1;
-    if (touchID == rotateTouchID)
-        rotateTouchID = -1;
-    if (touchID == fireTouchID)
-        fireTouchID = -1;
-
-    // On-release Update
-    firstPerson = newFirstPerson;
-    renderer.drawShadows = shadowMode;
-}