32_Urho2DConstraints.as 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. // Urho2D physics Constraints sample.
  2. // This sample is designed to help understanding and chosing the right constraint.
  3. // This sample demonstrates:
  4. // - Creating physics constraints
  5. // - Creating Edge and Polygon Shapes from vertices
  6. // - Displaying physics debug geometry and constraints' joints
  7. // - Using SetOrderInLayer to alter the way sprites are drawn in relation to each other
  8. // - Using Text3D to display some text affected by zoom
  9. // - Setting the background color for the scene
  10. #include "Scripts/Utilities/Sample.as"
  11. Camera@ camera;
  12. Node@ pickedNode;
  13. RigidBody2D@ dummyBody;
  14. void Start()
  15. {
  16. SampleStart();
  17. CreateScene();
  18. input.mouseVisible = true; // Show mouse cursor
  19. CreateInstructions();
  20. SubscribeToEvents();
  21. }
  22. void CreateScene()
  23. {
  24. scene_ = Scene();
  25. scene_.CreateComponent("Octree");
  26. scene_.CreateComponent("DebugRenderer");
  27. PhysicsWorld2D@ physicsWorld = scene_.CreateComponent("PhysicsWorld2D");
  28. physicsWorld.drawJoint = true; // Display the joints (Note that DrawDebugGeometry() must be set to true to acually draw the joints)
  29. drawDebug = true; // Set DrawDebugGeometry() to true
  30. // Create camera
  31. cameraNode = scene_.CreateChild("Camera");
  32. cameraNode.position = Vector3(0.0f, 0.0f, 0.0f); // Note that Z setting is discarded; use camera.zoom instead (see MoveCamera() below for example)
  33. camera = cameraNode.CreateComponent("Camera");
  34. camera.orthographic = true;
  35. camera.orthoSize = graphics.height * PIXEL_SIZE;
  36. camera.zoom = 1.2f;
  37. renderer.viewports[0] = Viewport(scene_, camera);
  38. renderer.defaultZone.fogColor = Color(0.1f, 0.1f, 0.1f); // Set background color for the scene
  39. // Create 4x3 grid
  40. for (uint i = 0; i<5; ++i)
  41. {
  42. Node@ edgeNode = scene_.CreateChild("VerticalEdge");
  43. RigidBody2D@ edgeBody = edgeNode.CreateComponent("RigidBody2D");
  44. if (dummyBody is null)
  45. dummyBody = edgeBody; // Mark first edge as dummy body (used by mouse pick)
  46. CollisionEdge2D@ edgeShape = edgeNode.CreateComponent("CollisionEdge2D");
  47. edgeShape.SetVertices(Vector2(i*2.5f -5.0f, -3.0f), Vector2(i*2.5f -5.0f, 3.0f));
  48. edgeShape.friction = 0.5f; // Set friction
  49. }
  50. for (uint j = 0; j<4; ++j)
  51. {
  52. Node@ edgeNode = scene_.CreateChild("HorizontalEdge");
  53. RigidBody2D@ edgeBody = edgeNode.CreateComponent("RigidBody2D");
  54. CollisionEdge2D@ edgeShape = edgeNode.CreateComponent("CollisionEdge2D");
  55. edgeShape.SetVertices(Vector2(-5.0f, j*2.0f -3.0f), Vector2(5.0f, j*2.0f -3.0f));
  56. edgeShape.friction = 0.5f; // Set friction
  57. }
  58. // Create a box (will be cloned later)
  59. Node@ box = scene_.CreateChild("Box");
  60. box.position = Vector3(0.8f, -2.0f, 0.0f);
  61. StaticSprite2D@ boxSprite = box.CreateComponent("StaticSprite2D");
  62. boxSprite.sprite = cache.GetResource("Sprite2D", "Urho2D/Box.png");
  63. RigidBody2D@ boxBody = box.CreateComponent("RigidBody2D");
  64. boxBody.bodyType = BT_DYNAMIC;
  65. boxBody.linearDamping = 0.0f;
  66. boxBody.angularDamping = 0.0f;
  67. CollisionBox2D@ shape = box.CreateComponent("CollisionBox2D"); // Create box shape
  68. shape.size = Vector2(0.32, 0.32); // Set size
  69. shape.density = 1.0f; // Set shape density (kilograms per meter squared)
  70. shape.friction = 0.5f; // Set friction
  71. shape.restitution = 0.1f; // Set restitution (slight bounce)
  72. // Create a ball (will be cloned later)
  73. Node@ ball = scene_.CreateChild("Ball");
  74. ball.position = Vector3(1.8f, -2.0f, 0.0f);
  75. StaticSprite2D@ ballSprite = ball.CreateComponent("StaticSprite2D");
  76. ballSprite.sprite = cache.GetResource("Sprite2D", "Urho2D/Ball.png");
  77. RigidBody2D@ ballBody = ball.CreateComponent("RigidBody2D");
  78. ballBody.bodyType = BT_DYNAMIC;
  79. ballBody.linearDamping = 0.0f;
  80. ballBody.angularDamping = 0.0f;
  81. CollisionCircle2D@ ballShape = ball.CreateComponent("CollisionCircle2D"); // Create circle shape
  82. ballShape.radius = 0.16f; // Set radius
  83. ballShape.density = 1.0f; // Set shape density (kilograms per meter squared)
  84. ballShape.friction = 0.5f; // Set friction
  85. ballShape.restitution = 0.6f; // Set restitution: make it bounce
  86. // Create a polygon
  87. Node@ polygon = scene_.CreateChild("Polygon");
  88. polygon.position = Vector3(1.6f, -2.0f, 0.0f);
  89. polygon.SetScale(0.7f);
  90. StaticSprite2D@ polygonSprite = polygon.CreateComponent("StaticSprite2D");
  91. polygonSprite.sprite = cache.GetResource("Sprite2D", "Urho2D/Aster.png");
  92. RigidBody2D@ polygonBody = polygon.CreateComponent("RigidBody2D");
  93. polygonBody.bodyType = BT_DYNAMIC;
  94. CollisionPolygon2D@ polygonShape = polygon.CreateComponent("CollisionPolygon2D");
  95. Array<Vector2> polygonVertices = {Vector2(-0.8f, -0.3f), Vector2(0.5f, -0.8f), Vector2(0.8f, -0.3f), Vector2(0.8f, 0.5f), Vector2(0.5f, 0.9f), Vector2(-0.5f, 0.7f)};
  96. polygonShape.SetVertices(polygonVertices);
  97. polygonShape.density = 1.0f; // Set shape density (kilograms per meter squared)
  98. polygonShape.friction = 0.3f; // Set friction
  99. polygonShape.restitution = 0.0f; // Set restitution (no bounce)
  100. // Create a ConstraintDistance2D
  101. CreateFlag("ConstraintDistance2D", -4.97f, 3.0f); // Display Text3D flag
  102. Node@ boxDistanceNode = box.Clone();
  103. Node@ ballDistanceNode = ball.Clone();
  104. RigidBody2D@ ballDistanceBody = ballDistanceNode.GetComponent("RigidBody2D");
  105. boxDistanceNode.position = Vector3(-4.5f, 2.0f, 0.0f);
  106. ballDistanceNode.position = Vector3(-3.0f, 2.0f, 0.0f);
  107. ConstraintDistance2D@ constraintDistance = boxDistanceNode.CreateComponent("ConstraintDistance2D"); // Apply ConstraintDistance2D to box
  108. constraintDistance.otherBody = ballDistanceBody; // Constrain ball to box
  109. constraintDistance.ownerBodyAnchor = Vector2(boxDistanceNode.position.x, boxDistanceNode.position.y);
  110. constraintDistance.otherBodyAnchor = Vector2(ballDistanceNode.position.x, ballDistanceNode.position.y);
  111. // Make the constraint soft (comment to make it rigid, which is its basic behavior)
  112. constraintDistance.frequencyHz = 4.0f;
  113. constraintDistance.dampingRatio = 0.5f;
  114. // Create a ConstraintFriction2D ********** Not functional. From Box2d samples it seems that 2 anchors are required, Urho2D only provides 1, needs investigation ***********
  115. CreateFlag("ConstraintFriction2D", 0.03f, 1.0f); // Display Text3D flag
  116. Node@ boxFrictionNode = box.Clone();
  117. Node@ ballFrictionNode = ball.Clone();
  118. boxFrictionNode.position = Vector3(0.5f, 0.0f, 0.0f);
  119. ballFrictionNode.position = Vector3(1.5f, 0.0f, 0.0f);
  120. ConstraintFriction2D@ constraintFriction = boxFrictionNode.CreateComponent("ConstraintFriction2D"); // Apply ConstraintDistance2D to box
  121. constraintFriction.otherBody = ballFrictionNode.GetComponent("RigidBody2D"); // Constraint ball to box
  122. //constraintFriction.ownerBodyAnchor = Vector2(boxNode.position.x, boxNode.position.y);
  123. //constraintFriction.otherBodyAnchor = Vector2(ballNode.position.x, ballNode.position.y);
  124. //constraintFriction.maxForce = 10.0f; // ballBody.mass * gravity
  125. //constraintDistance.maxTorque = 10.0f; // ballBody.mass * radius * gravity
  126. // Create a ConstraintGear2D
  127. CreateFlag("ConstraintGear2D", -4.97f, -1.0f); // Display Text3D flag
  128. Node@ baseNode = box.Clone();
  129. RigidBody2D@ tempBody = baseNode.GetComponent("RigidBody2D"); // Get body to make it static
  130. tempBody.bodyType = BT_STATIC;
  131. baseNode.position = Vector3(-3.7f, -2.5f, 0.0f);
  132. Node@ ball1Node = ball.Clone();
  133. ball1Node.position = Vector3(-4.5f, -2.0f, 0.0f);
  134. RigidBody2D@ ball1Body = ball1Node.GetComponent("RigidBody2D");
  135. Node@ ball2Node = ball.Clone();
  136. ball2Node.position = Vector3(-3.0f, -2.0f, 0.0f);
  137. RigidBody2D@ ball2Body = ball2Node.GetComponent("RigidBody2D");
  138. ConstraintRevolute2D@ gear1 = baseNode.CreateComponent("ConstraintRevolute2D"); // Apply constraint to baseBox
  139. gear1.otherBody = ball1Body; // Constrain ball1 to baseBox
  140. gear1.anchor = Vector2(ball1Node.position.x, ball1Node.position.y);
  141. ConstraintRevolute2D@ gear2 = baseNode.CreateComponent("ConstraintRevolute2D"); // Apply constraint to baseBox
  142. gear2.otherBody = ball2Body; // Constrain ball2 to baseBox
  143. gear2.anchor = Vector2(ball2Node.position.x, ball2Node.position.y);
  144. ConstraintGear2D@ constraintGear = ball1Node.CreateComponent("ConstraintGear2D"); // Apply constraint to ball1
  145. constraintGear.otherBody = ball2Body; // Constrain ball2 to ball1
  146. constraintGear.ownerConstraint = gear1;
  147. constraintGear.otherConstraint = gear2;
  148. constraintGear.ratio=1.0f;
  149. ball1Body.ApplyAngularImpulse(0.015f, true); // Animate
  150. // Create a vehicle from a compound of 2 ConstraintWheel2Ds
  151. CreateFlag("ConstraintWheel2Ds compound", -2.45f, -1.0f); // Display Text3D flag
  152. Node@ car = box.Clone();
  153. car.scale = Vector3(4.0f, 1.0f, 0.0f);
  154. car.position = Vector3(-1.2f, -2.3f, 0.0f);
  155. StaticSprite2D@ tempSprite = car.GetComponent("StaticSprite2D"); // Get car Sprite in order to draw it on top
  156. tempSprite.orderInLayer = 0; // Draw car on top of the wheels (set to -1 to draw below)
  157. Node@ ball1WheelNode = ball.Clone();
  158. ball1WheelNode.position = Vector3(-1.6f, -2.5f, 0.0f);
  159. Node@ ball2WheelNode = ball.Clone();
  160. ball2WheelNode.position = Vector3(-0.8f, -2.5f, 0.0f);
  161. ConstraintWheel2D@ wheel1 = car.CreateComponent("ConstraintWheel2D");
  162. wheel1.otherBody = ball1WheelNode.GetComponent("RigidBody2D");
  163. wheel1.anchor = Vector2(ball1WheelNode.position.x, ball1WheelNode.position.y);
  164. wheel1.axis = Vector2(0.0f, 1.0f);
  165. wheel1.maxMotorTorque = 20.0f;
  166. wheel1.frequencyHz = 4.0f;
  167. wheel1.dampingRatio = 0.4f;
  168. ConstraintWheel2D@ wheel2 = car.CreateComponent("ConstraintWheel2D");
  169. wheel2.otherBody = ball2WheelNode.GetComponent("RigidBody2D");
  170. wheel2.anchor = Vector2(ball2WheelNode.position.x, ball2WheelNode.position.y);
  171. wheel2.axis = Vector2(0.0f, 1.0f);
  172. wheel2.maxMotorTorque = 10.0f;
  173. wheel2.frequencyHz = 4.0f;
  174. wheel2.dampingRatio = 0.4f;
  175. // ConstraintMotor2D
  176. CreateFlag("ConstraintMotor2D", 2.53f, -1.0f); // Display Text3D flag
  177. Node@ boxMotorNode = box.Clone();
  178. tempBody = boxMotorNode.GetComponent("RigidBody2D"); // Get body to make it static
  179. tempBody.bodyType = BT_STATIC;
  180. Node@ ballMotorNode = ball.Clone();
  181. boxMotorNode.position = Vector3(3.8f, -2.1f, 0.0f);
  182. ballMotorNode.position = Vector3(3.8f, -1.5f, 0.0f);
  183. ConstraintMotor2D@ constraintMotor = boxMotorNode.CreateComponent("ConstraintMotor2D");
  184. constraintMotor.otherBody = ballMotorNode.GetComponent("RigidBody2D"); // Constrain ball to box
  185. constraintMotor.linearOffset = Vector2(0.0f, 0.8f); // Set ballNode position relative to boxNode position = (0,0)
  186. constraintMotor.angularOffset = 0.1f;
  187. constraintMotor.maxForce = 5.0f;
  188. constraintMotor.maxTorque = 10.0f;
  189. constraintMotor.correctionFactor = 1.0f;
  190. constraintMotor.collideConnected = true; // doesn't work
  191. // ConstraintMouse2D is demonstrated in HandleMouseButtonDown() function. It is used to "grasp" the sprites with the mouse.
  192. CreateFlag("ConstraintMouse2D", 0.03f, -1.0f); // Display Text3D flag
  193. // Create a ConstraintPrismatic2D
  194. CreateFlag("ConstraintPrismatic2D", 2.53f, 3.0f); // Display Text3D flag
  195. Node@ boxPrismaticNode = box.Clone();
  196. tempBody = boxPrismaticNode.GetComponent("RigidBody2D"); // Get body to make it static
  197. tempBody.bodyType = BT_STATIC;
  198. Node@ ballPrismaticNode = ball.Clone();
  199. boxPrismaticNode.position = Vector3(3.3f, 2.5f, 0.0f);
  200. ballPrismaticNode.position = Vector3(4.3f, 2.0f, 0.0f);
  201. ConstraintPrismatic2D@ constraintPrismatic = boxPrismaticNode.CreateComponent("ConstraintPrismatic2D");
  202. constraintPrismatic.otherBody = ballPrismaticNode.GetComponent("RigidBody2D"); // Constrain ball to box
  203. constraintPrismatic.axis = Vector2(1.0f, 1.0f); // Slide from [0,0] to [1,1]
  204. constraintPrismatic.anchor = Vector2(4.0f, 2.0f);
  205. constraintPrismatic.lowerTranslation = -1.0f;
  206. constraintPrismatic.upperTranslation = 0.5f;
  207. constraintPrismatic.enableLimit = true;
  208. constraintPrismatic.maxMotorForce = 1.0f;
  209. constraintPrismatic.motorSpeed = 0.0f;
  210. // ConstraintPulley2D
  211. CreateFlag("ConstraintPulley2D", 0.03f, 3.0f); // Display Text3D flag
  212. Node@ boxPulleyNode = box.Clone();
  213. Node@ ballPulleyNode = ball.Clone();
  214. boxPulleyNode.position = Vector3(0.5f, 2.0f, 0.0f);
  215. ballPulleyNode.position = Vector3(2.0f, 2.0f, 0.0f);
  216. ConstraintPulley2D@ constraintPulley = boxPulleyNode.CreateComponent("ConstraintPulley2D"); // Apply constraint to box
  217. constraintPulley.otherBody = ballPulleyNode.GetComponent("RigidBody2D"); // Constrain ball to box
  218. constraintPulley.ownerBodyAnchor = Vector2(boxPulleyNode.position.x, boxPulleyNode.position.y);
  219. constraintPulley.otherBodyAnchor = Vector2(ballPulleyNode.position.x, ballPulleyNode.position.y);
  220. constraintPulley.ownerBodyGroundAnchor = Vector2(boxPulleyNode.position.x, boxPulleyNode.position.y + 1);
  221. constraintPulley.otherBodyGroundAnchor = Vector2(ballPulleyNode.position.x, ballPulleyNode.position.y + 1);
  222. constraintPulley.ratio = 1.0; // Weight ratio between ownerBody and otherBody
  223. // Create a ConstraintRevolute2D
  224. CreateFlag("ConstraintRevolute2D", -2.45f, 3.0f); // Display Text3D flag
  225. Node@ boxRevoluteNode = box.Clone();
  226. tempBody = boxRevoluteNode.GetComponent("RigidBody2D"); // Get body to make it static
  227. tempBody.bodyType = BT_STATIC;
  228. Node@ ballRevoluteNode = ball.Clone();
  229. boxRevoluteNode.position = Vector3(-2.0f, 1.5f, 0.0f);
  230. ballRevoluteNode.position = Vector3(-1.0f, 2.0f, 0.0f);
  231. ConstraintRevolute2D@ constraintRevolute = boxRevoluteNode.CreateComponent("ConstraintRevolute2D"); // Apply constraint to box
  232. constraintRevolute.otherBody = ballRevoluteNode.GetComponent("RigidBody2D"); // Constrain ball to box
  233. constraintRevolute.anchor = Vector2(-1.0f, 1.5f);
  234. constraintRevolute.lowerAngle = -1.0f; // In radians
  235. constraintRevolute.upperAngle = 0.5f; // In radians
  236. constraintRevolute.enableLimit = true;
  237. constraintRevolute.maxMotorTorque = 10.0f;
  238. constraintRevolute.motorSpeed = 0.0f;
  239. constraintRevolute.enableMotor = true;
  240. // Create a ConstraintRope2D
  241. CreateFlag("ConstraintRope2D", -4.97f, 1.0f); // Display Text3D flag
  242. Node@ boxRopeNode = box.Clone();
  243. tempBody = boxRopeNode.GetComponent("RigidBody2D");
  244. tempBody.bodyType = BT_STATIC;
  245. Node@ ballRopeNode = ball.Clone();
  246. boxRopeNode.position = Vector3(-3.7f, 0.7f, 0.0f);
  247. ballRopeNode.position = Vector3(-4.5f, 0.0f, 0.0f);
  248. ConstraintRope2D@ constraintRope = boxRopeNode.CreateComponent("ConstraintRope2D");
  249. constraintRope.otherBody = ballRopeNode.GetComponent("RigidBody2D"); // Constrain ball to box
  250. constraintRope.ownerBodyAnchor = Vector2(0.0f, -0.5f); // Offset from box (OwnerBody) : the rope is rigid from OwnerBody center to this ownerBodyAnchor
  251. constraintRope.maxLength = 0.9f; // Rope length
  252. constraintRope.collideConnected = true;
  253. // Create a ConstraintWeld2D
  254. CreateFlag("ConstraintWeld2D", -2.45f, 1.0f); // Display Text3D flag
  255. Node@ boxWeldNode = box.Clone();
  256. Node@ ballWeldNode = ball.Clone();
  257. boxWeldNode.position = Vector3(-0.5f, 0.0f, 0.0f);
  258. ballWeldNode.position = Vector3(-2.0f, 0.0f, 0.0f);
  259. ConstraintWeld2D@ constraintWeld = boxWeldNode.CreateComponent("ConstraintWeld2D");
  260. constraintWeld.otherBody = ballWeldNode.GetComponent("RigidBody2D"); // Constrain ball to box
  261. constraintWeld.anchor = Vector2(boxWeldNode.position.x, boxWeldNode.position.y);
  262. constraintWeld.frequencyHz = 4.0f;
  263. constraintWeld.dampingRatio = 0.5f;
  264. // Create a ConstraintWheel2D
  265. CreateFlag("ConstraintWheel2D", 2.53f, 1.0f); // Display Text3D flag
  266. Node@ boxWheelNode = box.Clone();
  267. Node@ ballWheelNode = ball.Clone();
  268. boxWheelNode.position = Vector3(3.8f, 0.0f, 0.0f);
  269. ballWheelNode.position = Vector3(3.8f, 0.9f, 0.0f);
  270. ConstraintWheel2D@ constraintWheel = boxWheelNode.CreateComponent("ConstraintWheel2D");
  271. constraintWheel.otherBody = ballWheelNode.GetComponent("RigidBody2D"); // Constrain ball to box
  272. constraintWheel.anchor = Vector2(ballWheelNode.position.x, ballWheelNode.position.y);
  273. constraintWheel.axis = Vector2(0.0f, 1.0f);
  274. constraintWheel.enableMotor = true;
  275. constraintWheel.maxMotorTorque = 1.0f;
  276. constraintWheel.motorSpeed = 0.0f;
  277. constraintWheel.frequencyHz = 4.0f;
  278. constraintWheel.dampingRatio = 0.5f;
  279. constraintWheel.collideConnected = true; // doesn't work
  280. }
  281. void CreateFlag(const String&in text, float x, float y) // Used to create Tex3D flags
  282. {
  283. Node@ flagNode = scene_.CreateChild("Flag");
  284. flagNode.position = Vector3(x, y, 0.0f);
  285. Text3D@ flag3D = flagNode.CreateComponent("Text3D"); // We use Text3D in order to make the text affected by zoom (so that it sticks to 2D)
  286. flag3D.text = text;
  287. flag3D.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
  288. }
  289. void CreateInstructions()
  290. {
  291. // Construct new Text object, set string to display and font to use
  292. Text@ instructionText = ui.root.CreateChild("Text");
  293. instructionText.text = "Use WASD keys and mouse to move, Use PageUp PageDown to zoom.\n Space to toggle debug geometry and joints - F5 to save the scene.";
  294. instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
  295. instructionText.textAlignment = HA_CENTER; // Center rows in relation to each other
  296. // Position the text relative to the screen center
  297. instructionText.horizontalAlignment = HA_CENTER;
  298. instructionText.verticalAlignment = VA_CENTER;
  299. instructionText.SetPosition(0.0f, ui.root.height / 4);
  300. }
  301. void MoveCamera(float timeStep)
  302. {
  303. if (ui.focusElement !is null) return; // Do not move if the UI has a focused element (the console)
  304. uint MOVE_SPEED = 4; // Movement speed as world units per second
  305. // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
  306. if (input.keyDown['W']) cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
  307. if (input.keyDown['S']) cameraNode.Translate(Vector3(0.0f, -1.0f, 0.0f) * MOVE_SPEED * timeStep);
  308. if (input.keyDown['A']) cameraNode.Translate(Vector3(-1.0f, 0.0f, 0.0f) * MOVE_SPEED * timeStep);
  309. if (input.keyDown['D']) cameraNode.Translate(Vector3(1.0f, 0.0f, 0.0f) * MOVE_SPEED * timeStep);
  310. if (input.keyDown[KEY_PAGEUP]) camera.zoom = camera.zoom * 1.01f; // Zoom In
  311. if (input.keyDown[KEY_PAGEDOWN]) camera.zoom = camera.zoom * 0.99f; // Zoom Out
  312. }
  313. void SubscribeToEvents()
  314. {
  315. SubscribeToEvent("Update", "HandleUpdate");
  316. SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
  317. SubscribeToEvent("MouseButtonDown", "HandleMouseButtonDown");
  318. if (touchEnabled)
  319. SubscribeToEvent("TouchBegin", "HandleTouchBegin3");
  320. // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
  321. UnsubscribeFromEvent("SceneUpdate");
  322. }
  323. void HandleUpdate(StringHash eventType, VariantMap& eventData)
  324. {
  325. float timeStep = eventData["TimeStep"].GetFloat();
  326. MoveCamera(timeStep); // Move the camera according to frame's time step
  327. if (input.keyPress[KEY_SPACE]) drawDebug = !drawDebug; // Toggle debug geometry with space
  328. if (input.keyPress[KEY_F5]) // Save scene
  329. {
  330. File saveFile(fileSystem.programDir + "Data/Scenes/Constraints.xml", FILE_WRITE);
  331. scene_.SaveXML(saveFile);
  332. }
  333. }
  334. void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
  335. {
  336. PhysicsWorld2D@ physicsWorld = scene_.GetComponent("PhysicsWorld2D");
  337. if (drawDebug) physicsWorld.DrawDebugGeometry();
  338. }
  339. void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
  340. {
  341. PhysicsWorld2D@ physicsWorld = scene_.GetComponent("PhysicsWorld2D");
  342. RigidBody2D@ rigidBody = physicsWorld.GetRigidBody(input.mousePosition.x, input.mousePosition.y, M_MAX_UNSIGNED); // Raycast for RigidBody2Ds to pick
  343. if (rigidBody !is null)
  344. {
  345. pickedNode = rigidBody.node;
  346. StaticSprite2D@ staticSprite = pickedNode.GetComponent("StaticSprite2D");
  347. staticSprite.color = Color(1.0f, 0.0f, 0.0f, 1.0f); // Temporary modify color of the picked sprite
  348. // Create a ConstraintMouse2D - Temporary apply this constraint to the pickedNode to allow grasping and moving with the mouse
  349. ConstraintMouse2D@ constraintMouse = pickedNode.CreateComponent("ConstraintMouse2D");
  350. constraintMouse.target = GetMousePositionXY();
  351. constraintMouse.maxForce = 1000 * rigidBody.mass;
  352. constraintMouse.collideConnected = true;
  353. constraintMouse.otherBody = dummyBody; // Use dummy body instead of rigidBody. It's better to create a dummy body automatically in ConstraintMouse2D
  354. constraintMouse.dampingRatio = 0.0f;
  355. }
  356. SubscribeToEvent("MouseMove", "HandleMouseMove");
  357. SubscribeToEvent("MouseButtonUp", "HandleMouseButtonUp");
  358. }
  359. void HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
  360. {
  361. if (pickedNode !is null)
  362. {
  363. StaticSprite2D@ staticSprite = pickedNode.GetComponent("StaticSprite2D");
  364. staticSprite.color = Color(1.0f, 1.0f, 1.0f, 1.0f); // Restore picked sprite color
  365. pickedNode.RemoveComponent("ConstraintMouse2D"); // Remove temporary constraint
  366. pickedNode = null;
  367. }
  368. UnsubscribeFromEvent("MouseMove");
  369. UnsubscribeFromEvent("MouseButtonUp");
  370. }
  371. Vector2 GetMousePositionXY()
  372. {
  373. Vector3 screenPoint = Vector3(float(input.mousePosition.x) / graphics.width, float(input.mousePosition.y) / graphics.height, 0.0f);
  374. Vector3 worldPoint = camera.ScreenToWorldPoint(screenPoint);
  375. return Vector2(worldPoint.x, worldPoint.y);
  376. }
  377. void HandleMouseMove(StringHash eventType, VariantMap& eventData)
  378. {
  379. if (pickedNode !is null)
  380. {
  381. ConstraintMouse2D@ constraintMouse = pickedNode.GetComponent("ConstraintMouse2D");
  382. constraintMouse.target = GetMousePositionXY();
  383. }
  384. }
  385. void HandleTouchBegin3(StringHash eventType, VariantMap& eventData)
  386. {
  387. PhysicsWorld2D@ physicsWorld = scene_.GetComponent("PhysicsWorld2D");
  388. RigidBody2D@ rigidBody = physicsWorld.GetRigidBody(eventData["X"].GetInt(), eventData["Y"].GetInt(), M_MAX_UNSIGNED); // Raycast for RigidBody2Ds to pick
  389. if (rigidBody !is null)
  390. {
  391. pickedNode = rigidBody.node;
  392. StaticSprite2D@ staticSprite = pickedNode.GetComponent("StaticSprite2D");
  393. staticSprite.color = Color(1.0f, 0.0f, 0.0f, 1.0f); // Temporary modify color of the picked sprite
  394. RigidBody2D@ rigidBody = pickedNode.GetComponent("RigidBody2D");
  395. // Create a ConstraintMouse2D - Temporary apply this constraint to the pickedNode to allow grasping and moving with touch
  396. ConstraintMouse2D@ constraintMouse = pickedNode.CreateComponent("ConstraintMouse2D");
  397. Vector3 pos = camera.ScreenToWorldPoint(Vector3(float(eventData["X"].GetInt()) / graphics.width, float(eventData["Y"].GetInt()) / graphics.height, 0.0f));
  398. constraintMouse.target = Vector2(pos.x, pos.y);
  399. constraintMouse.maxForce = 1000 * rigidBody.mass;
  400. constraintMouse.collideConnected = true;
  401. constraintMouse.otherBody = dummyBody; // Use dummy body instead of rigidBody. It's better to create a dummy body automatically in ConstraintMouse2D
  402. constraintMouse.dampingRatio = 0;
  403. }
  404. SubscribeToEvent("TouchMove", "HandleTouchMove3");
  405. SubscribeToEvent("TouchEnd", "HandleTouchEnd3");
  406. }
  407. void HandleTouchMove3(StringHash eventType, VariantMap& eventData)
  408. {
  409. if (pickedNode !is null)
  410. {
  411. ConstraintMouse2D@ constraintMouse = pickedNode.GetComponent("ConstraintMouse2D");
  412. Vector3 pos = camera.ScreenToWorldPoint(Vector3(float(eventData["X"].GetInt()) / graphics.width, float(eventData["Y"].GetInt()) / graphics.height, 0.0f));
  413. constraintMouse.target = Vector2(pos.x, pos.y);
  414. }
  415. }
  416. void HandleTouchEnd3(StringHash eventType, VariantMap& eventData)
  417. {
  418. if (pickedNode !is null)
  419. {
  420. StaticSprite2D@ staticSprite = pickedNode.GetComponent("StaticSprite2D");
  421. staticSprite.color = Color(1.0f, 1.0f, 1.0f, 1.0f); // Restore picked sprite color
  422. pickedNode.RemoveComponent("ConstraintMouse2D"); // Remove temporary constraint
  423. pickedNode = null;
  424. }
  425. UnsubscribeFromEvent("TouchMove");
  426. UnsubscribeFromEvent("TouchEnd");
  427. }
  428. // Create XML patch instructions for screen joystick layout specific to this sample app
  429. String patchInstructions =
  430. "<patch>" +
  431. " <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
  432. " <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" +
  433. " <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
  434. " <element type=\"Text\">" +
  435. " <attribute name=\"Name\" value=\"KeyBinding\" />" +
  436. " <attribute name=\"Text\" value=\"PAGEUP\" />" +
  437. " </element>" +
  438. " </add>" +
  439. " <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
  440. " <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" +
  441. " <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
  442. " <element type=\"Text\">" +
  443. " <attribute name=\"Name\" value=\"KeyBinding\" />" +
  444. " <attribute name=\"Text\" value=\"PAGEDOWN\" />" +
  445. " </element>" +
  446. " </add>" +
  447. "</patch>";