Combining setLinearVelocities

I have a project where the character moves by pressing w, a, s, d, but, they can only move up, down, left, and right in a straight line. I want to have it set up so that when they press, for example, both a and w, their linerVelocity causes them to move up and sideways at the same time. This is my current code: 

function onKeyEvent( event ) -- Print which key was pressed down/up to the log. --local message = "'" .. event.keyName .. "' is " .. event.phase --print( message ) -- Display the key event's information onscreen. --if event.device then -- message = event.device.displayName .. "\n" .. message --end -- If the "back" key was pressed, then prevent it from backing out of the app. -- We do this by returning true, telling the operating system that we are overriding the key. --if (event.keyName == "back") then -- return true --end -- Return false to indicate that this app is \*not\* overriding the received key. -- This lets the operating system execute its default handling of this key. --return false if event.keyName == "a" then if event.phase == "down" then --transition.to(character, {time = 3000, x = character.x - (display.actualContentWidth / 2)}) character:setLinearVelocity(-200, 0) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "d" then if event.phase == "down" then --transition.to(character, {time = 3000, x = character.x - (display.actualContentWidth / 2)}) character:setLinearVelocity(200, 0) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "w" then if event.phase == "down" then --transition.to(character, {time = 3000, y = character.y - (display.actualContentHeight / 2)}) character:setLinearVelocity(0, -200) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "s" then if event.phase == "down" then --transition.to(character, {time = 3000, y = character.y + (display.actualContentHeight / 2)}) character:setLinearVelocity(0, 200) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end end Runtime:addEventListener( "key", onKeyEvent )

I am stumped as to how to do this, and I don’t want to use transitions, they were fidgety and are no as smooth as setLinearVelocity(). Any help would be appreciated, thank you.

Hi @sdktester15,

There’s a convenient little trick to this. As you probably can see, setting the linear velocity is, by nature, a full execution of both horizontal and vertical velocities (you can’t “stack” them as if you were applying force or impulses). However, you can accomplish a stack-type method by simply getting the existing velocity values first, then immediately applying one of those values to the set function.

For example, let’s say your object is moving directly upward. If you call the following, you may get something like 0 for “vx” and -200 for “vy”:

[lua]

local vx, vy = character:getLinearVelocity()

[/lua]

Now, if you want to make it move diagonally left-up, using the same -200 for the upward velocity that existed, then just supply the existing “vy” to the new set function like this:

[lua]

character:setLinearVelocity( -200, vy )

[/lua]

This can be used in any combination you need. Basically, you just need to gather either the horizontal or vertical velocity which you want to maintain, then apply it to the character along with the new velocity along the other axis.

Hope this helps,

Brent

Thanks, worked like a charm. However, occasionally when a switch directions really quickly it stutters a bit then starts moving. This was also happening before when I was using transitions so this is being caused by the event.phase of the event.keyName. Is there a way to fix this?

This is probably because you’re setting the velocity to 0 on every “up” phase of every key. That will, of course, cause the object to stop even if the user is still pressing another valid key. Imagine if you’re pressing both A and W to move up-left… so your character is moving diagonally… but then if the user releases A, the player should move directly up (W is still pressed). However, in that case you are setting the velocity to 0.

Maybe you have changed your code since the original post. Can you re-post your current code?

This is my current code:

function onKeyEvent( event ) -- Print which key was pressed down/up to the log. --local message = "'" .. event.keyName .. "' is " .. event.phase --print( message ) -- Display the key event's information onscreen. --if event.device then -- message = event.device.displayName .. "\n" .. message --end -- If the "back" key was pressed, then prevent it from backing out of the app. -- We do this by returning true, telling the operating system that we are overriding the key. --if (event.keyName == "back") then -- return true --end -- Return false to indicate that this app is \*not\* overriding the received key. -- This lets the operating system execute its default handling of this key. --return false local vx, vy = character:getLinearVelocity() if event.keyName == "a" then if event.phase == "down" then --transition.to(character, {time = 3000, x = character.x - (display.actualContentWidth / 2)}) character:setLinearVelocity(-300, vy) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "d" then if event.phase == "down" then --transition.to(character, {time = 3000, x = character.x - (display.actualContentWidth / 2)}) character:setLinearVelocity(300, vy) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "w" then if event.phase == "down" then --transition.to(character, {time = 3000, y = character.y - (display.actualContentHeight / 2)}) character:setLinearVelocity(vx, -300) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "s" then if event.phase == "down" then --transition.to(character, {time = 3000, y = character.y + (display.actualContentHeight / 2)}) character:setLinearVelocity(vx, 300) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end end Runtime:addEventListener( "key", onKeyEvent )

I think you should try the same approach during the “up” phase of every key. By strictly setting the velocity to 0,0 in each case, you’re potentially forcing another axis direction to stop, even if it’s still valid. For example, assume the player has both A and W pressed to move diagonally up-left. If the user releases A, the character should still move directly up (W remains pressed). But your code is making the character stop entirely when A is released.

Basically, similar to how you handled the “down” phase in each case, you should only cancel out the velocity of the direction that was released. So, for example:

[lua]

   if event.keyName == “a” then

       if event.phase == “down” then

          character:setLinearVelocity(-300, vy)

        elseif event.phase == “up” then

          character:setLinearVelocity(0, vy)  – Keep ‘vy’ intact, don’t reset vertical velocity to 0

       end

   end

[/lua]

Of course, for ideal multi-key movement, you will eventually need to add in special handling for “opposing directions”. For example, if the player is moving up (W is down), then the player presses S to move down, the character should probably move down (that’s how I would assume it to happen). But then if the player releases W, that will cause the character to stop, even though S remains down. So you’ll probably need to keep track of whether an opposing directional key is pressed and not set the velocity to 0 for the vertical axis.

That will take some more detailed coding, which I’ll let you experiment with. I have some ideas, but see how you go along these lines…

I do have an idea, but this would require me to do a lot of copy and pasting. Usually, I don’t like going for the easy answers, because they are either wrong or inefficient. The other answers are harder to find, but when they work, they work efficiently and it is something to really be proud of. So, I guess what I am asking is… How would you go about doing this?

I would probably keep track of 4 variables, like “isWPressed”, “isDPressed”, “isSPressed”, and “isAPressed”. Each one obviously starts as boolean false and, clearly, when the player presses the associated key, you set that variable to boolean true.

Now the other part occurs when you detect a potentially “conflicting” key combination. For example, when the player presses both left (A) and right (D) at the same time. In that case, let’s imagine the player first pressed A then, sometime after, pressed D. In my opinion, the new press of D should make right-directional movement take over, which your code already does. However, if the player releases A, but keeps D pressed, your code sets horizontal velocity back to 0, which shouldn’t happen (D is pressed so character should move right). These 4 variables would be used to prevent that.

Basically, the conditional check to add would be “on release of a key (up phase), is the player also pressing the key in the exact opposite direction? If true, then move character in the direction of the key still pressed, otherwise (else) set that axis to 0.”

[lua]

   if event.keyName == “a” then

      if event.phase == “down” then

         isAPressed = true  – A is pressed, set its variable to true

         character:setLinearVelocity(-300, vy)

      elseif event.phase == “up” then

         isAPressed = false  – A is released, set its variable to false

         if isDPressed == true then

            character:setLinearVelocity(300, vy)  – A is released but D remains pressed, so move character back to right

         else

            character:setLinearVelocity(0, vy)  – Else, both A and D are released, so stop all horizontal velocity

         end

      end

   end

[/lua]

At least I *think* this will work… I’m just thinking this out in my head, I didn’t actually test it… that’s for you to try. :slight_smile:

Brent

Thanks, this worked perfectly.

Hi @sdktester15,

There’s a convenient little trick to this. As you probably can see, setting the linear velocity is, by nature, a full execution of both horizontal and vertical velocities (you can’t “stack” them as if you were applying force or impulses). However, you can accomplish a stack-type method by simply getting the existing velocity values first, then immediately applying one of those values to the set function.

For example, let’s say your object is moving directly upward. If you call the following, you may get something like 0 for “vx” and -200 for “vy”:

[lua]

local vx, vy = character:getLinearVelocity()

[/lua]

Now, if you want to make it move diagonally left-up, using the same -200 for the upward velocity that existed, then just supply the existing “vy” to the new set function like this:

[lua]

character:setLinearVelocity( -200, vy )

[/lua]

This can be used in any combination you need. Basically, you just need to gather either the horizontal or vertical velocity which you want to maintain, then apply it to the character along with the new velocity along the other axis.

Hope this helps,

Brent

Thanks, worked like a charm. However, occasionally when a switch directions really quickly it stutters a bit then starts moving. This was also happening before when I was using transitions so this is being caused by the event.phase of the event.keyName. Is there a way to fix this?

This is probably because you’re setting the velocity to 0 on every “up” phase of every key. That will, of course, cause the object to stop even if the user is still pressing another valid key. Imagine if you’re pressing both A and W to move up-left… so your character is moving diagonally… but then if the user releases A, the player should move directly up (W is still pressed). However, in that case you are setting the velocity to 0.

Maybe you have changed your code since the original post. Can you re-post your current code?

This is my current code:

function onKeyEvent( event ) -- Print which key was pressed down/up to the log. --local message = "'" .. event.keyName .. "' is " .. event.phase --print( message ) -- Display the key event's information onscreen. --if event.device then -- message = event.device.displayName .. "\n" .. message --end -- If the "back" key was pressed, then prevent it from backing out of the app. -- We do this by returning true, telling the operating system that we are overriding the key. --if (event.keyName == "back") then -- return true --end -- Return false to indicate that this app is \*not\* overriding the received key. -- This lets the operating system execute its default handling of this key. --return false local vx, vy = character:getLinearVelocity() if event.keyName == "a" then if event.phase == "down" then --transition.to(character, {time = 3000, x = character.x - (display.actualContentWidth / 2)}) character:setLinearVelocity(-300, vy) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "d" then if event.phase == "down" then --transition.to(character, {time = 3000, x = character.x - (display.actualContentWidth / 2)}) character:setLinearVelocity(300, vy) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "w" then if event.phase == "down" then --transition.to(character, {time = 3000, y = character.y - (display.actualContentHeight / 2)}) character:setLinearVelocity(vx, -300) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end if event.keyName == "s" then if event.phase == "down" then --transition.to(character, {time = 3000, y = character.y + (display.actualContentHeight / 2)}) character:setLinearVelocity(vx, 300) elseif event.phase == "up" then --transition.cancel() character:setLinearVelocity(0, 0) end end end Runtime:addEventListener( "key", onKeyEvent )

I think you should try the same approach during the “up” phase of every key. By strictly setting the velocity to 0,0 in each case, you’re potentially forcing another axis direction to stop, even if it’s still valid. For example, assume the player has both A and W pressed to move diagonally up-left. If the user releases A, the character should still move directly up (W remains pressed). But your code is making the character stop entirely when A is released.

Basically, similar to how you handled the “down” phase in each case, you should only cancel out the velocity of the direction that was released. So, for example:

[lua]

   if event.keyName == “a” then

       if event.phase == “down” then

          character:setLinearVelocity(-300, vy)

        elseif event.phase == “up” then

          character:setLinearVelocity(0, vy)  – Keep ‘vy’ intact, don’t reset vertical velocity to 0

       end

   end

[/lua]

Of course, for ideal multi-key movement, you will eventually need to add in special handling for “opposing directions”. For example, if the player is moving up (W is down), then the player presses S to move down, the character should probably move down (that’s how I would assume it to happen). But then if the player releases W, that will cause the character to stop, even though S remains down. So you’ll probably need to keep track of whether an opposing directional key is pressed and not set the velocity to 0 for the vertical axis.

That will take some more detailed coding, which I’ll let you experiment with. I have some ideas, but see how you go along these lines…

I do have an idea, but this would require me to do a lot of copy and pasting. Usually, I don’t like going for the easy answers, because they are either wrong or inefficient. The other answers are harder to find, but when they work, they work efficiently and it is something to really be proud of. So, I guess what I am asking is… How would you go about doing this?

I would probably keep track of 4 variables, like “isWPressed”, “isDPressed”, “isSPressed”, and “isAPressed”. Each one obviously starts as boolean false and, clearly, when the player presses the associated key, you set that variable to boolean true.

Now the other part occurs when you detect a potentially “conflicting” key combination. For example, when the player presses both left (A) and right (D) at the same time. In that case, let’s imagine the player first pressed A then, sometime after, pressed D. In my opinion, the new press of D should make right-directional movement take over, which your code already does. However, if the player releases A, but keeps D pressed, your code sets horizontal velocity back to 0, which shouldn’t happen (D is pressed so character should move right). These 4 variables would be used to prevent that.

Basically, the conditional check to add would be “on release of a key (up phase), is the player also pressing the key in the exact opposite direction? If true, then move character in the direction of the key still pressed, otherwise (else) set that axis to 0.”

[lua]

   if event.keyName == “a” then

      if event.phase == “down” then

         isAPressed = true  – A is pressed, set its variable to true

         character:setLinearVelocity(-300, vy)

      elseif event.phase == “up” then

         isAPressed = false  – A is released, set its variable to false

         if isDPressed == true then

            character:setLinearVelocity(300, vy)  – A is released but D remains pressed, so move character back to right

         else

            character:setLinearVelocity(0, vy)  – Else, both A and D are released, so stop all horizontal velocity

         end

      end

   end

[/lua]

At least I *think* this will work… I’m just thinking this out in my head, I didn’t actually test it… that’s for you to try. :slight_smile:

Brent

Thanks, this worked perfectly.