Touch.as 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // Mobile framework for Android/iOS
  2. // Gamepad from NinjaSnowWar
  3. // Gyroscope (activated by default)
  4. // Touches patterns:
  5. // - 1 finger touch = pick object through raycast
  6. // - 1 or 2 fingers drag = rotate camera
  7. // - 3 fingers touch = switch between first/third person view
  8. // - 4 fingers touch = switch shadows on/off
  9. // - 2 fingers sliding in opposite direction (up/down) = zoom in/out
  10. // 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...
  11. // Setup:
  12. // - On init, call this script using '#include "Scripts/Utilities/Touch.as"' then 'InitTouchInput()' on mobile platforms
  13. // -> to detect platform, use 'if (GetPlatform() == "Android" || GetPlatform() == "iOS")'
  14. // - Subscribe to touch events (Begin, Move, End) using 'SubscribeToTouchEvents()'
  15. // - Call the update function 'UpdateTouches()' from HandleUpdate or equivalent update handler function
  16. const float TOUCH_SENSITIVITY = 5.0;
  17. const float GYROSCOPE_THRESHOLD = 0.1;
  18. const float CAMERA_MIN_DIST = 1.0f;
  19. const float CAMERA_INITIAL_DIST = 5.0f;
  20. const float CAMERA_MAX_DIST = 20.0f;
  21. bool firstPerson = false;
  22. bool touchEnabled = false;
  23. bool zoom = false;
  24. bool newFirstPerson = false;
  25. bool shadowMode = true;
  26. Node@ cameraNode;
  27. float cameraDistance = CAMERA_INITIAL_DIST;
  28. BorderImage@ moveButton;
  29. BorderImage@ fireButton;
  30. int touchButtonSize = 96;
  31. int touchButtonBorder = 12;
  32. int moveTouchID = -1;
  33. int rotateTouchID = -1;
  34. int fireTouchID = -1;
  35. void InitTouchInput() // Create Gamepad Buttons
  36. {
  37. moveButton = ui.root.CreateChild("BorderImage");
  38. moveButton.texture = cache.GetResource("Texture2D", "Textures/TouchInput.png");
  39. moveButton.imageRect = IntRect(0, 0, 96, 96);
  40. moveButton.SetAlignment(HA_LEFT, VA_BOTTOM);
  41. moveButton.SetPosition(touchButtonBorder, -touchButtonBorder);
  42. moveButton.SetSize(touchButtonSize, touchButtonSize);
  43. moveButton.opacity = 0.25;
  44. fireButton = ui.root.CreateChild("BorderImage");
  45. fireButton.texture = cache.GetResource("Texture2D", "Textures/TouchInput.png");
  46. fireButton.imageRect = IntRect(96, 0, 192, 96);
  47. fireButton.SetAlignment(HA_RIGHT, VA_BOTTOM);
  48. fireButton.SetPosition(-touchButtonBorder, -touchButtonBorder);
  49. fireButton.SetSize(touchButtonSize, touchButtonSize);
  50. fireButton.opacity = 0.25;
  51. touchEnabled = true;
  52. }
  53. void SubscribeToTouchEvents()
  54. {
  55. SubscribeToEvent("TouchBegin", "HandleTouchBegin");
  56. SubscribeToEvent("TouchEnd", "HandleTouchEnd");
  57. }
  58. void UpdateTouches(Controls& controls) // Called from HandleUpdate
  59. {
  60. Camera@ camera = cameraNode.GetComponent("Camera");
  61. zoom = false; // reset bool
  62. // Touch Inputs
  63. if (touchEnabled)
  64. {
  65. // Zoom in/out
  66. if (input.numTouches == 2)
  67. {
  68. TouchState@ touch1 = input.touches[0];
  69. TouchState@ touch2 = input.touches[1];
  70. // Check for zoom pattern (touches moving in opposite directions)
  71. if ((touch1.delta.y > 0 && touch2.delta.y < 0) || (touch1.delta.y < 0 && touch2.delta.y > 0))
  72. zoom = true;
  73. else
  74. zoom = false;
  75. if (zoom)
  76. {
  77. int sens = 0;
  78. // Check for zoom direction (in/out)
  79. if (Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y))
  80. sens = -1;
  81. else
  82. sens = 1;
  83. cameraDistance += Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50;
  84. cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST); // Restrict zoom range to [1;20]
  85. }
  86. }
  87. // Switch 1st/3rd person mode
  88. if (input.numTouches == 3)
  89. newFirstPerson = !firstPerson;
  90. // Switch draw debug
  91. if (input.numTouches == 4)
  92. shadowMode = !renderer.drawShadows;
  93. // Rotate and Move
  94. if (!zoom)
  95. {
  96. for (uint i = 0; i < input.numTouches; ++i)
  97. {
  98. TouchState@ touch = input.touches[i];
  99. if (touch.touchID == rotateTouchID)
  100. {
  101. controls.yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.x;
  102. controls.pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.y;
  103. controls.pitch = Clamp(controls.pitch, -80.0f, 80.0f); // Limit pitch
  104. }
  105. if (touch.touchID == moveTouchID)
  106. {
  107. int relX = touch.position.x - moveButton.screenPosition.x - touchButtonSize / 2;
  108. int relY = touch.position.y - moveButton.screenPosition.y - touchButtonSize / 2;
  109. if (relY < 0 && Abs(relX * 3 / 2) < Abs(relY))
  110. controls.Set(CTRL_FORWARD, true);
  111. if (relY > 0 && Abs(relX * 3 / 2) < Abs(relY))
  112. controls.Set(CTRL_BACK, true);
  113. if (relX < 0 && Abs(relY * 3 / 2) < Abs(relX))
  114. controls.Set(CTRL_LEFT, true);
  115. if (relX > 0 && Abs(relY * 3 / 2) < Abs(relX))
  116. controls.Set(CTRL_RIGHT, true);
  117. }
  118. }
  119. }
  120. if (fireTouchID >= 0)
  121. controls.Set(CTRL_JUMP, true);
  122. }
  123. // Gyroscope (emulated by SDL through a virtual joystick)
  124. if (input.numJoysticks > 0) // numJoysticks = 1 on iOS & Android
  125. {
  126. JoystickState@ joystick = input.joysticks[0];
  127. if (joystick.numAxes >= 2)
  128. {
  129. if (joystick.axisPosition[0] < -GYROSCOPE_THRESHOLD)
  130. controls.Set(CTRL_LEFT, true);
  131. if (joystick.axisPosition[0] > GYROSCOPE_THRESHOLD)
  132. controls.Set(CTRL_RIGHT, true);
  133. if (joystick.axisPosition[1] < -GYROSCOPE_THRESHOLD)
  134. controls.Set(CTRL_FORWARD, true);
  135. if (joystick.axisPosition[1] > GYROSCOPE_THRESHOLD)
  136. controls.Set(CTRL_BACK, true);
  137. }
  138. }
  139. }
  140. void HandleTouchBegin(StringHash eventType, VariantMap& eventData)
  141. {
  142. int touchID = eventData["TouchID"].GetInt(); // Get #touches or dragging value
  143. IntVector2 pos(eventData["X"].GetInt(), eventData["Y"].GetInt()); // Get touch coordinates
  144. UIElement@ element = ui.GetElementAt(pos, false); // Get gamepad UIElement touched (if any)
  145. // Check for gamepad button touched. If none, rotate
  146. if (element is moveButton)
  147. moveTouchID = touchID;
  148. else if (element is fireButton)
  149. fireTouchID = touchID;
  150. else
  151. rotateTouchID = touchID;
  152. // Init Raycast (for example to acquire a target)
  153. Camera@ camera = cameraNode.GetComponent("Camera");
  154. Ray cameraRay = camera.GetScreenRay(float(eventData["X"].GetInt()) / graphics.width, float(eventData["Y"].GetInt()) / graphics.height);
  155. // Raycast of RigidBodies
  156. PhysicsRaycastResult result = scene_.physicsWorld.RaycastSingle(cameraRay, camera.farClip, 2); // NB: here we restrict targets to layer 2
  157. if (result.body !is null)
  158. log.Info("Physics raycast hit " + result.body.node.name);
  159. // Raycast of drawable components (for targets without physics)
  160. RayQueryResult result2 = scene_.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, camera.farClip, DRAWABLE_GEOMETRY);
  161. if (result2.drawable !is null)
  162. log.Info("Drawable raycast hit " + result2.drawable.node.name);
  163. }
  164. void HandleTouchEnd(StringHash eventType, VariantMap& eventData)
  165. {
  166. int touchID = eventData["TouchID"].GetInt();
  167. if (touchID == moveTouchID)
  168. moveTouchID = -1;
  169. if (touchID == rotateTouchID)
  170. rotateTouchID = -1;
  171. if (touchID == fireTouchID)
  172. fireTouchID = -1;
  173. // On-release Update
  174. firstPerson = newFirstPerson;
  175. renderer.drawShadows = shadowMode;
  176. }