How do I get local coords?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
wburton95
Prole
Posts: 5
Joined: Sun Dec 16, 2018 8:13 pm

How do I get local coords?

Post by wburton95 »

I'm new to love and I am building a little space shooter. I need to figure out how to get local coords to manipulate a physics body in the game. Basically I need to figure out how to rotate my ship on screen using the mouse, and then I want to dampen the rotation so it feels slow like a ship or tank turret. I thought I may be able to figure this out if I had the local coords but I'm not sure now. I've done some digging and I cannot figure out how to do this. Any help would be greatly appreciated.


If anyone is curious about my project goal or what I am trying to do here, I want to make a game like Battle Stations (PS1) but with space ships. Maybe later add networking then add the ability to raid ships with a friend or something cool that adds another layer of gameplay. My goal for me with this project is to become a better programmer and have something to show for it. I figured that if I made a game that would be the perfect challenge for me, since games do cover a variety of advanced programming topics.
Last edited by wburton95 on Mon Dec 17, 2018 11:28 pm, edited 2 times in total.
JoshGrams
Prole
Posts: 33
Joined: Sat May 27, 2017 10:58 am

Re: How do I get local coords?

Post by JoshGrams »

I'm not sure what you're using for phyics, but physics bodies are almost always in world coordinates. Local coordinates are generally used for attaching child objects (guns, etc) to a body, not for interacting with other bodies or things like the mouse cursor.

You could possibly use the Transform objects to handle transforms, but for this case you probably don't need anything that complicated.

If your body is at a given angle, then you can get the direction as a vector (xFwd, yFwd = math.cos(angle), math.sin(angle)). That will give you a unit vector (length 1) so you can multiply it by the strength of your force and use that for the acceleration.

To rotate, you can get the vector from the body to your mouse and compute its angle with math.atan2(y, x) (note that x and y are swapped here: it's a math function, not a vector function). Then you can subtract the body angle and wrap the result into -PI..PI with

Code: Select all

local function wrapAngle(radians)
	return math.pi - (math.pi - radians) % (2 * math.pi)
end
Finding a good function for how much torque to apply to get nice rotation will probably take some experimentation. And you might have to map the mouse from screen coordinates to world coordinates if your view scrolls. That would probably be a function of whatever camera library you're using.

------

Note that since screen coordinates often have the y-coordinate flipped from "math" coordinates, angles will start facing right and go clockwise instead of counter-clockwise. Also some people like to have the zero angle be facing up for spaceships. While you can adjust your coordinates for that by flipping the sign of the y coordinate and/or rotating the whole thing by 90 degrees (in 2D, a 90-degree rotation is x, y = -y, x), I don't recommend it. It's not necessary, and it's a big source of hard-to-diagnose bugs.

I'd highly recommend just accepting that the zero direction is to the right and that angles go clockwise, and mapping your controls and designing your graphics facing right to match that.
User avatar
pgimeno
Party member
Posts: 3549
Joined: Sun Oct 18, 2015 2:58 pm

Re: How do I get local coords?

Post by pgimeno »

Note that 11.2 added Body:setTransform and Body:getTransform. To transform the mouse to local coordinates, you can use something like this:

Code: Select all

  local x, y, angle = body:getTransform()
  local LmouseX, LmouseY = love.mouse.getPosition()
  LmouseX, LmouseY = LmouseX - x, LmouseY - y
  local c, s = math.cos(angle), math.sin(angle)
  LmouseX, LmouseY = c * LmouseX + s * LmouseY, -s * LmouseX + c * LmouseY
That assumes that your units are pixels and the origin of the world is at the top left corner of the screen, which may or may not be the case. If not, then you first need to transform the mouse position from screen coordinates to physics world coordinates.

EDIT: I completely forgot about Body:getLocalPoint which obviates the need for getTransform(). Simpler code:

Code: Select all

  local LmouseX, LmouseY = love.mouse.getPosition()
  LmouseX, LmouseY = body:getLocalPoint(LMouseX, LMouseY)
Last edited by pgimeno on Thu Dec 20, 2018 2:14 pm, edited 3 times in total.
wburton95
Prole
Posts: 5
Joined: Sun Dec 16, 2018 8:13 pm

Re: How do I get local coords?

Post by wburton95 »

Here is my script so far.

Code: Select all

function love.load()
    love.graphics.setDefaultFilter('nearest', 'nearest')
    
    world = love.physics.newWorld(0,0,true)
    
    redship = {}
    redship['body'] = love.physics.newBody(world, 0, 0, "dynamic")
    redship['shape'] = love.physics.newRectangleShape(5,5)
    redship['image'] = love.graphics.newImage("/ships/red-ship.png")
    redship['fixture'] = love.physics.newFixture(redship.body,redship.shape,10)
    
end
function love.update(dt)

    world:update(dt)
    
    --[[
        
    --What do I do here?
        
    local mx, my = love.mouse.getPosition()
    local sx = redship.body:getX()
    local sy = redship.body:getY()
    local angle = math.atan2((my - sy), (mx - sx))
    
    redship.body:setAngle(angle)
    
    ]]
    
    local force = 10

    redship.body:setLinearDamping(0.50)
    redship.body:setAngularDamping(0.50)
    
    if love.keyboard.isDown("w") then
        redship.body:applyForce(0, -force)
    elseif love.keyboard.isDown("s") then
        redship.body:applyForce(0,force)
    elseif love.keyboard.isDown("a") then
        redship.body:applyForce(-force,0)
    elseif love.keyboard.isDown("d") then
        redship.body:applyForce(force,0)
    end
end

function love.draw()
    love.graphics.push()
    love.graphics.scale(3,3)
    love.graphics.draw(redship.image, redship.body:getX(), redship.body:getY(), redship.body:getAngle(), 1, 1, redship.image:getWidth()/2, redship.image:getHeight()/2)
    love.graphics.pop()
end
User avatar
pgimeno
Party member
Posts: 3549
Joined: Sun Oct 18, 2015 2:58 pm

Re: How do I get local coords?

Post by pgimeno »

You're using love.graphics.scale(3, 3) before drawing, and you're drawing using the body's coordinates. That means the mouse coordinates need to be divided by 3 to transform them to physics world coordinates.

To transform the mouse position to coordinates local to the body (LmouseX and LmouseY), the code would become:

Code: Select all

  local LmouseX, LmouseY = love.mouse.getPosition()
  LmouseX, LmouseY = body:getLocalPoint(LmouseX / 3, LmouseY / 3)
(I've just added the / 3 step to the previous code).
Last edited by pgimeno on Thu Dec 20, 2018 2:12 pm, edited 1 time in total.
User avatar
pgimeno
Party member
Posts: 3549
Joined: Sun Oct 18, 2015 2:58 pm

Re: How do I get local coords?

Post by pgimeno »

Sorry for the double post. I've read your OP again now. Certainly I wouldn't use local coordinates to do that. An approach that may give decent results is to turn the ships at constant angular velocity. You need the difference between the original angle and the target angle, and the sign of that will let you decide whether to turn left or right at a certain fixed angular speed, which you can do with Body:setAngularVelocity.

The problem is the handling of overshooting. I think that can be done with this method: if the difference between current angle and new angle is bigger than the difference between current angle and target angle, in the same direction you're rotating, then you keep moving; if not, you're overshooting, so you set the angle to the target angle.
wburton95
Prole
Posts: 5
Joined: Sun Dec 16, 2018 8:13 pm

Re: How do I get local coords?

Post by wburton95 »

pgimeno wrote: Mon Dec 17, 2018 10:41 pm Sorry for the double post. I've read your OP again now. Certainly I wouldn't use local coordinates to do that. An approach that may give decent results is to turn the ships at constant angular velocity. You need the difference between the original angle and the target angle, and the sign of that will let you decide whether to turn left or right at a certain fixed angular speed, which you can do with Body:setAngularVelocity.

The problem is the handling of overshooting. I think that can be done with this method: if the difference between current angle and new angle is bigger than the difference between current angle and target angle, in the same direction you're rotating, then you keep moving; if not, you're overshooting, so you set the angle to the target angle.
Thank you so much for your help! You rock dude! I finally got the ship to point at the mouse now. My ship sprite rotates and follows the mouse as expected. Now I am going to try and figure out how to use physics to spin my object in the way your mentioned above.
wburton95
Prole
Posts: 5
Joined: Sun Dec 16, 2018 8:13 pm

Re: How do I get local coords? Rotating a sprite with physics. [Solved]

Post by wburton95 »

Here is my code. It works! Thank you again for all your help.

Code: Select all

function love.load()

    love.graphics.setDefaultFilter('nearest', 'nearest')
    
    world = love.physics.newWorld(0,0,true)
    
    redship = {}
    redship['body'] = love.physics.newBody(world, 0, 0, "dynamic")
    redship['shape'] = love.physics.newRectangleShape(5,5)
    redship['image'] = love.graphics.newImage("/ships/red-ship.png")
    redship['fixture'] = love.physics.newFixture(redship.body,redship.shape,10)
    
end
function love.update(dt)

    world:update(dt)
    local force = 10

    redship.body:setLinearDamping(0.8)
        
    sx, sy, angle = redship.body:getTransform()
    mx, my = love.mouse.getPosition()
    mx, my = mx / 3, my / 3
    pos = math.atan2((mx - sx), (my - sy))

    if -pos >= angle + 0.02 then
        redship.body:setAngularVelocity(0.5)
    elseif -pos <= angle - 0.02 then
        redship.body:setAngularVelocity(-0.5)
    else
        redship.body:setAngularVelocity(0)
    end
    

    
    if love.keyboard.isDown("w") then
        redship.body:applyForce(0, -force)
    elseif love.keyboard.isDown("s") then
        redship.body:applyForce(0,force)
    elseif love.keyboard.isDown("a") then
        redship.body:applyForce(-force,0)
    elseif love.keyboard.isDown("d") then
        redship.body:applyForce(force,0)
    end
end

function love.draw()
    love.graphics.push()
    love.graphics.scale(3,3)
    love.graphics.draw(redship.image, redship.body:getX(), redship.body:getY(), redship.body:getAngle(), 1, 1, redship.image:getWidth()/2, redship.image:getHeight()/2)
    love.graphics.print(angle)
    love.graphics.print(pos,0,10)
    love.graphics.pop()
end
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 71 guests