Touch.as 7.7 KB

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