[SOLVED] Applying velocity (on X-axis) to a physics body

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
nice
Party member
Posts: 191
Joined: Sun Sep 15, 2013 12:17 am
Location: Sweden

[SOLVED] Applying velocity (on X-axis) to a physics body

Post by nice »

Hello everyone!

I'm working on a 2D platformer that uses box2d physics (I believe that's what LÖVE is using right?) and I'm trying to apply velocity to my Physics body object (in this case my player object). What I want is to create a platformer that moves around in a similar fashion to Mario from NES (but not necessarily a carbon copy).


I have implemented velocity from an older project where the player object doesn't use a physics body that you can see here:

Code: Select all

local isLeftHeld
local isRightHeld
-- Velocity cap as to how fast the Player can move
local maxVelocity
-- If the Player isn't touching a key, do nothing
isLeftHeld = false
isRightHeld = false
-- Players intial velocity
baseVelocity = -800

-- Player table with variables
Player = {}
Player.Sprite = love.graphics.newImage("player.png")
Player.posX = love.graphics.getWidth() * 0.5
Player.posY = love.graphics.getHeight() - 80
Player.Orientation = 0
Player.scaleX = 1
Player.scaleY = 1
Player.originX = math.ceil(Player.Sprite:getWidth() * 0.5)
Player.originY = 0
Player.Width = Player.Sprite:getWidth()
Player.Height = Player.Sprite:getHeight()
Player.Velocity = 0
Player.maxVelocity = 4.25
Player.Direction = 1
Player.clampLeft = 42
Player.clampRight = love.graphics.getWidth() - 42

function Player:load(arg)
end

function Player:update(dt)

-- Move left if key is down and set direction to left
  if isLeftHeld == true then
    self.Velocity = baseVelocity
    self.Direction = 1
  elseif isRightHeld == true then
    -- Move right if key is down and set direction to right
    self.Velocity = baseVelocity
    self.Direction = -1
  else
    -- Slow down if nothing is happening
    self.Velocity = self.Velocity * 0.94
  end
  -- Update movement
  self.posX = self.posX + (self.Velocity * dt) * self.Direction
  -- Cap the Players velocity to maximum velocity allowed
  if self.Velocity > self.maxVelocity then
    self.Velocity = self.maxVelocity
  end
  -- Prevents the player from moving outside to the left
  if self.posX < self.clampLeft then
    self.posX = self.clampLeft
  elseif self.posX > self.clampRight then
    self.posX = self.clampRight
  end
end

function Player:draw()
  -- Draw the player
  love.graphics.draw(self.Sprite,
                     self.posX,
                     self.posY,
                     self.Orientation,
                     self.scaleX,
                     self.scaleY,
                     self.originX,
                     self.originY)
end

function Player:keypressed(key)
  -- If the player is pressing the key, it's true
  if key == 'left' then
    isLeftHeld = true
  end

  if key == 'right' then
    isRightHeld = true
  end
end

function Player:keyreleased(key)
  -- If the player isn't pressing the key, it's false
  if key == 'left' then
    isLeftHeld = false
  end

  if key == 'right' then
    isRightHeld = false
  end
end

return Player

I've managed to implement a method of movement that works in my platformer project (that uses physics) but I'm having problems figuring out a way to make velocity work with my physics implementation in my platformer.

My current platformer project:
Player.lua

Code: Select all

love.graphics.setDefaultFilter("nearest")

local isLeftHeld
local isRightHeld

baseVelocity = -800

-- Player Table --
Player = {}
-- Sprite Variables --
Player.Sprite = love.graphics.newImage("tempChar.png")
Player.spritePreJump = love.graphics.newImage("spritePreJump.png")
Player.spriteJumping = love.graphics.newImage("spriteJumping.png")
Player.spriteLanding = love.graphics.newImage("spriteLanding.png")

Player.posX = love.graphics.getWidth() * 0.5
Player.posY = 300--love.graphics.getHeight() * 0.5

Player.Body = love.physics.newBody(physicsWorld, 
                                   Player.posX,
                                   Player.posY,
                                   "dynamic")
Player.Shape = love.physics.newRectangleShape(34, 70)
Player.Fixture = love.physics.newFixture(Player.Body, Player.Shape)

Player.Radians = 0 -- Rotation
Player.scaleX = 3
Player.scaleY = 3
Player.originX = Player.Sprite:getWidth() * 0.5
Player.originY = Player.Sprite:getHeight() * 0.5
Player.Width = Player.Sprite:getWidth()
Player.Height = Player.Sprite:getHeight()

Player.Speed = 425 -- Temporary variable, may change to a Velocity setup instead
Player.clampLeft = 16
Player.clampRight = 784

Player.impulseX = 0
Player.impulseY = -1250
--Player.isJumping = false
--Player.reachedMaxHeight = false
Player.isGrounded = false
Player.Direction = 3
Player.currentDirection = Player.Direction
Player.lookingLeft = false
Player.lookingRight = true
Player.velocityX = 0
--Player.velocityY = 0
Player.Acceleration = 100
Player.maxSpeed = 600
Player.Friction = 20

function Player:update(dt)  
  self.Body:getX(self.posX + self.velocityX)
  self.velocityX = self.velocityX * (1 - math.min(dt * self.Friction, 1))
  
  if love.keyboard.isDown("left", "a") and self.velocityX > -self.maxSpeed then
    self.Body:setX(Player.Body:getX() - self.velocityX - self.Acceleration * dt)
    self.currentDirection = -self.Direction
    
  elseif love.keyboard.isDown("right", "d") and self.velocityX < self.maxSpeed then
    self.Body:setX(Player.Body:getX() + self.velocityX + self.Acceleration * dt)
    self.currentDirection = self.Direction
  end
  
  if self.Body:getX() <= self.clampLeft then
    self.Body:setX(self.clampLeft)
  elseif self.Body:getX() >= self.clampRight then
    self.Body:setX(self.clampRight)
  end
  
  if self.isGrounded == true then
    self.Sprite = self.Sprite
  else
    self.Sprite = self.spriteLanding 
  end
  print("Player Direction: "..self.currentDirection)
end

function Player:draw()
  love.graphics.draw(self.Sprite,
                     self.Body:getX(),
                     self.Body:getY(),
                     self.Radians,
                     self.currentDirection,
                     self.scaleY,
                     self.originX,
                     self.originY)
end

function Player:keypressed(key)
  if key == 'up' and self.isGrounded == true then
    print("Jumping")
    self.Body:applyLinearImpulse(self.impulseX, self.impulseY)
  end
  if key == 'left' then
    isLeftHeld = true
  end
  
  if key == 'right' then
    isRightHeld = true
  end
end

function Player:keyreleased(key)
  if key == 'left' then
    isLeftHeld = false
    self.currentDirection = -self.Direction
    lookingLeft = true
    lookingRight = false
  end

  if key == 'right' then
    isRightHeld = false
    self.currentDirection = self.Direction
    lookingLeft = false
    lookingRight = true
  end
end

return Player
main.lua

Code: Select all


Platforms = require "platform"

function love.load()
  physicsWorld = love.physics.newWorld(0, 780) -- Move this to a Game Manager
  physicsWorld:setCallbacks(beginContact, endContact, preSolve, postSolve)
  Player = require "Player"
  Platforms:spawnPlatform(0, 550, 800, 30)
end

function love.update(dt)
  physicsWorld:update(dt)
  Player:update(dt)
  
end

function love.draw()
--love.graphics.print("Sprite: "..Player.Sprite, 10, 10)
  Player:draw()
  Platforms:draw()
  if love.keyboard.isDown("c") then
    for _, body in pairs(physicsWorld:getBodies()) do
      for _, fixture in pairs(body:getFixtures()) do
          local shape = fixture:getShape()
   
          if shape:typeOf("CircleShape") then
              local cx, cy = body:getWorldPoints(shape:getPoint())
              love.graphics.circle("fill", cx, cy, shape:getRadius())
          elseif shape:typeOf("PolygonShape") then
              love.graphics.polygon("fill", body:getWorldPoints(shape:getPoints()))
          else
              love.graphics.line(body:getWorldPoints(shape:getPoints()))
          end
      end
    end
  end
end
  
-- Closes down the game window when pressing ESC
function love.keypressed(key)
  Player:keypressed(key)
  if key == 'escape' then
    love.event.quit()
  end
end

function love.keyreleased(key)
  Player:keyreleased(key)
end

function beginContact(a, b, coll) -- put this in a game manager?
  Player.isGrounded = true
end

function endContact(a, b, coll) -- put this in a game manager?
  Player.isGrounded = false
end
platform.lua

Code: Select all

Platforms = {}
--Platforms:spawnPlatform(0, 550, 800, 30)

function Platforms:update(dt)
end

function Platforms:draw()
    for i,p in ipairs(Platforms) do
    love.graphics.rectangle("line",
                             p.platformBody:getX(),
                             p.platformBody:getY(),
                             p.platformWidth,
                             p.platformHeight)
  end
end

function Platforms:spawnPlatform(x, y, width, height)
  local Platform = {}
  Platform.platformBody = love.physics.newBody(physicsWorld, x, y, "static")
  Platform.platformShape = love.physics.newRectangleShape(width * 0.5, 
                                                          height * 0.5,
                                                          width, 
                                                          height)
  Platform.platformFixture = love.physics.newFixture(Platform.platformBody, 
                                                     Platform.platformShape)
  Platform.platformWidth = width
  Platform.platformHeight = height


  table.insert(Platforms, Platform)
end

return Platforms

Please keep in mind I'm a little bit unfamiliar exactly how box2d physics works but I think I believe I'm starting to grasp it. Please let me know if there's any other information that you need to know!


edit: got my player object to move by using:

Code: Select all

self.Body:applyForce((self.velocityX - self.Acceleration *dt), 0)
Has to figure out instead why my object only uses "Acceleration" in my prototype
Last edited by nice on Fri Apr 05, 2019 11:12 am, edited 1 time in total.
:awesome: Have a good day! :ultraglee:
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: [HELP] Applying velocity (on X-axis) to a physics body

Post by pgimeno »

So what's the problem?
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: [HELP] Applying velocity (on X-axis) to a physics body

Post by ivan »

With box2d you don't want to set the position directly because it teleports the body (which is kind of pointless since it circumvents the physics system). Ideally you should use impulses and forces, this way your body will be able to react to collisions with other bodies.
Furthermore, what you have implemented here is more like jetpack thrust. When it comes to jumping you want to set the initial velocity (impulse) and let gravity pull the object down.
Here is a short tutorial that I wrote a while ago:
https://2dengine.com/?p=platformers#Jumping
User avatar
nice
Party member
Posts: 191
Joined: Sun Sep 15, 2013 12:17 am
Location: Sweden

Re: [HELP] Applying velocity (on X-axis) to a physics body

Post by nice »

pgimeno wrote: Tue Apr 02, 2019 10:01 am So what's the problem?
Ah sorry if I wasn't clear enough.

The problem that I have is inside Player.luas update in:

Code: Select all

  if love.keyboard.isDown("left", "a") and self.velocityX > -self.maxSpeed then
  -- Right here --
    self.Body:setX(Player.Body:getX() - self.velocityX - self.Acceleration * dt)
    self.currentDirection = -self.Direction
    
  elseif love.keyboard.isDown("right", "d") and self.velocityX < self.maxSpeed then
    self.Body:setX(Player.Body:getX() + self.velocityX + self.Acceleration * dt)
    self.currentDirection = self.Direction
  end
What I'm attempting is trying to apply velocity in "self.Body:setX(Player.Body:getX)", I've attempted different solutions that so far hasn't worked.
:awesome: Have a good day! :ultraglee:
User avatar
nice
Party member
Posts: 191
Joined: Sun Sep 15, 2013 12:17 am
Location: Sweden

Re: [HELP] Applying velocity (on X-axis) to a physics body

Post by nice »

ivan wrote: Tue Apr 02, 2019 10:46 am With box2d you don't want to set the position directly because it teleports the body (which is kind of pointless since it circumvents the physics system). Ideally you should use impulses and forces, this way your body will be able to react to collisions with other bodies.
Furthermore, what you have implemented here is more like jetpack thrust. When it comes to jumping you want to set the initial velocity (impulse) and let gravity pull the object down.
Here is a short tutorial that I wrote a while ago:
https://2dengine.com/?p=platformers#Jumping
How should I avoid setting the position of the body directly? I'm kinda used setting the position of objects for a while now.
And should I prefer Impulse over Force when moving the player object?

I'll see if I can use some time tomorrow to take a look at it.
:awesome: Have a good day! :ultraglee:
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: [HELP] Applying velocity (on X-axis) to a physics body

Post by ivan »

nice wrote: Tue Apr 02, 2019 1:45 pmHow should I avoid setting the position of the body directly?
applyImpulse and applyForce
setLinearVelocity could work but it will make the collision resolution look unnatural if you use it all the time
nice wrote: Tue Apr 02, 2019 1:45 pmshould I prefer Impulse over Force when moving the player object?
applyImpuse is for instant changes in velocity (jumping, projectiles, etc) whereas applyForce accelerates the body over time (jetpacks, attraction, etc).
Post Reply

Who is online

Users browsing this forum: No registered users and 232 guests