Physics goes so very far and asks so little of developers who use it, provided they can bend its mysterious black-box simulation to their will. It’s plain to see how very complicated 2D platforming movement can be to code on your own, and you have to take into account the possible need to add more functionality in the future. The Sonic Finale example has no wall collisions, for example, and you would have to code those in yourself. It is not a trivial task. Hand-coded physics can run a lot faster than using Corona’s Physics implementation, but that is a benefit you have to weigh against the difficulty of programming your own system.
I did some tinkering today and came up with two ways to constrain the movement speed of a physics body. The first is to set the body’s linearDamping property to something other than zero. Higher values cap the sprite at a lower top speed. The downside of this is that the vertical (jump/fall) speed is also constrained, so it isn’t quite ideal.
A better solution is to check the linear velocity of the sprite, compare it to a user-defined limit, and set it to the limit if the sprite is moving too quickly.
Generally, preventing the player from jumping repeatedly in mid air requires that you detect when he is standing on the ground. There are probably many ways to do this, but the one I threw together works by calling the Physics’s queryRegion function and testing a region encompassing the bottom (but not sides or top) of the player. That is to say, checking for objects inside a box not as tall or as wide as the player, positions against the bottom of the player’s physics body. You then iterate through the objects inside the box, ignoring your player object, and if any are present your player is probably standing on something.
You can modify Platformer - Angled PHYSICS thusly to try these out yourself.
local grounded = false local jump = function(event) if event.phase == "began" then display.getCurrentStage():setFocus(event.target, event.id) event.target.isFocus = true if grounded then player:applyLinearImpulse(0, -500, player.x, player.y) end end if event.phase == "ended" or event.phase == "cancelled" then display.getCurrentStage():setFocus( event.target, nil ) event.target.isFocus = false end return true end --ENTERFRAME---------------------------------------------------------------------------- local gameLoop = function(event) local hits = mte.physics.queryRegion(player.x + blockScale / -2 + 8, player.y, player.x + blockScale / 2 - 8, player.y + blockScale) if hits then for i = 1, #hits, 1 do if hits[i] ~= player then grounded = true elseif #hits == 1 then grounded = false end end end local limit = 1000 local velocityX, velocityY = player:getLinearVelocity() if velocityX \> limit then player:setLinearVelocity(limit, velocityY) elseif velocityX \< limit \* -1 then player:setLinearVelocity(limit \* -1, velocityY) end mte.debug() mte.update() player:applyForce(acc \* 2400, 0, player.x, player.y) end