32_Urho2DConstraints.lua 22 KB

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